Объединение двух DataFrames на основе индексов из двух других DataFrames - PullRequest
6 голосов
/ 16 января 2020

Я новичок в pandas, пробовал просматривать документы и экспериментировать с различными примерами, но эта проблема, с которой я столкнулся, действительно поставила меня в тупик.

У меня есть два следующих кадра данных (DataA / DataB), который я хотел бы объединить на основе global_index / item / values.

DataA                      DataB
row  item_id  valueA       row    item_id  valueB
0    x        A1           0      x        B1
1    y        A2           1      y        B2
2    z        A3           2      x        B3
3    x        A4           3      y        B4
4    z        A5           4      z        B5
5    x        A6           5      x        B6
6    y        A7           6      y        B7
7    z        A8           7      z        B8

Список элементов (item_ids) конечен, и каждый из двух информационных фреймов представляет собой значение признака ( черта A, черта B) для элемента с данным значением global_index.

Глобальный индекс может грубо рассматриваться как единица «времени»

Отображение между каждым кадром данных (DataA / DataB), и global_index выполняется с помощью следующих двух DF преобразователя:

DataA_mapper
global_index  start_row  num_rows
0             0          3
1             3          2
3             5          3


DataB_mapper
global_index  start_row  num_rows
0             0          2
2             2          3
4             5          3

Проще говоря, для данного global_index (например: 1) преобразователь определит список строк в соответствующих DF (DataA или DataB). ), которые связаны с этим global_index.

Например, для значения global_index 0:

  • В DF DataA строки 0..2 связаны с global_index 0
  • в DF Строки DataB 0..1 связаны с global_index 0

Другой пример, для значения global_index 2:

  • В DF DataB строки 2..4 связаны с global_index 2
  • В DF DataA нет строк, связанных с global_index 2

Представленные диапазоны [start_row, start_row + num_rows) не перекрывают друг друга и представляют уникальную последовательность / диапазон строк в соответствующих им фреймах данных (DataA, DataB)

Короче говоря, ни одна строка в DataA или DataB не будет найдена в более чем одном диапазоне.


Я хотел бы объединить DF, чтобы я получил следующий фрейм данных:

row   global_index  item_id   valueA   valueB
0     0             x         A1        B1
1     0             y         A2        B2
2     0             z         A3        NaN
3     1             x         A4        B1
4     1             z         A5        NaN
5     2             x         A4        B3
6     2             y         A2        B4
7     2             z         A5        NaN
8     3             x         A6        B3
9     3             y         A7        B4
10    3             z         A8        B5
11    4             x         A6        B6
12    4             y         A7        B7
13    4             z         A8        B8

В окончательном фрейме данных любая пара из global_index / item_id будет либо:

  1. a значение как для значения A, так и для значения B
  2. значение только для значения A
  3. значение только для значения B

При условии, что для данного заданного значения имеется только одно значение global_index / item (например: valueA, но нет valueB) для последнего значения отсутствующего, которое будет использоваться.

Ответы [ 2 ]

1 голос
/ 17 января 2020

Сначала вы можете создать столбец 'global_index' с помощью функции pd.cut:

for df, m in [(df_A, map_A), (df_B, map_B)]:

    bins = np.insert(m['num_rows'].cumsum().values, 0, 0) # create bins and add zero at the beginning
    df['global_index'] = pd.cut(df['row'], bins=bins, labels=m['global_index'], right=False)

Далее вы можете использовать внешнее объединение для merge обоих фреймов данных:

df = df_A.merge(df_B, on=['global_index', 'item_id'], how='outer')

И, наконец, вы можете использовать функции groupby и ffill для заполнения пропущенных значений:

for val in ['valueA', 'valueB']:
    df[val] = df.groupby('item_id')[val].ffill()

Вывод:

   item_id  global_index  valueA  valueB
0        x             0      A1      B1
1        y             0      A2      B2
2        z             0      A3     NaN
3        x             1      A4      B1
4        z             1      A5     NaN
5        x             3      A6      B1
6        y             3      A7      B2
7        z             3      A8     NaN
8        x             2      A6      B3
9        y             2      A7      B4
10       z             2      A8      B5
11       x             4      A6      B6
12       y             4      A7      B7
13       z             4      A8      B8
0 голосов
/ 17 января 2020

Я не проверял это, так как у меня нет хороших тестовых данных, но я думаю, что что-то подобное должно работать. По сути дела, вместо того, чтобы пытаться осуществить какое-то сложное объединение, он строит серию списков для хранения ваших данных, которые затем можно собрать обратно в окончательный кадр данных в конце.

DataA.set_index('row')
DataB.set_index('row')

#we're going to create the new dataframe from scratch, creating a list for each column we want
global_index = []
AValues = []
AIndex = []
BValues = []
BIndex = []

for indexNum in totalIndexes:
    #for each global index, we get the total number of rows to extract from DataA and DataB
    AStart = DataA_mapper.loc[DataA_mapper['global_index']==indexNum, 'start_row'].values[0]
    ARows = DataA_mapper.loc[DataA_mapper['global_index']==indexNum, 'num_rows'].values[0]
    AStop = AStart + Arows

    BStart = DataB_mapper.loc[DataB_mapper['global_index']==indexNum, 'start_row'].values[0]
    BRows = DataB_mapper.loc[DataB_mapper['global_index']==indexNum, 'num_rows'].values[0]
    BStop = BStart + Brows


    #Next we extract values from DataA and DataB, turn them into lists, and add them to our data
    AValues = AValues + list(DataA.iloc[AStart:AStop, 1].values)
    AIndex = AIndex + list(DataA.iloc[AStart:AStop, 0].values)
    BValues = BValues + list(DataB.iloc[BStart:BStop, 1].values)
    BIndex = BIndex + list(DataA.iloc[AStart:AStop, 0].values)

    #Create a temporary list of the current global_index, and add it to our data
    global_index_temp = []
    for row in range(max(ARows,Brows)):
        global_index_temp.append(indexNum)
    global_index = global_index + global_index_temp


#combine all these individual lists into a dataframe
finalData = list(zip(global_index, AIndex, BIndex, AValues, BValues))
df = pd.DataFrame(data = finalData, columns = ['global_index', 'item1', 'item2', 'valueA', 'valueB'])

#lastly you just need to merge item1 and item2 to get your item_id column

Я попытался красиво это прокомментировать, чтобы, надеюсь, общий план имел смысл, и вы могли следовать и исправлять мои ошибки или переписывать его по-своему.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...