Я пытаюсь объединить два кадра данных и заменить nan в левом df правым df, я могу сделать это с тремя строками кода, как показано ниже, но я хочу знать, есть ли лучший / более короткий путь?

# Example data (my actual df is ~500k rows x 11 cols)
df1 = pd.DataFrame({'a': [1,2,3,4], 'b': [0,1,np.nan, 1], 'e': ['a', 1, 2,'b']})
df2 = pd.DataFrame({'a': [1,2,3,4], 'b': [np.nan, 1, 0, 1]})

# Merge the dataframes...
df = df1.merge(df2, on='a', how='left')

# Fillna in 'b' column of left df with right df...
df['b'] = df['b_x'].fillna(df['b_y'])

# Drop the columns no longer needed
df = df.drop(['b_x', 'b_y'], axis=1)
11
Kenan 1 Июл 2019 в 23:36

4 ответа

Лучший ответ

Проблема смущения при слиянии заключается в том, что оба кадра данных имеют столбец 'b', но в левой и правой версиях есть NaN в несоответствующих местах. Вы хотите избежать нежелательных множественных столбцов «b», «b_x», «b_y» из merge, в первую очередь :

  • нарезать не общие столбцы "а", "е" из df1
  • do merge(df2, 'left'), это выберет 'b' из правильного кадра данных (так как он существует только в правильном df)
  • наконец, выполните {{ X0}}, это обновит NaN в столбце 'b', взятом из df2, с df1['b']

Решение:

df1.update(df1[['a', 'e']].merge(df2, 'left'))

df1

   a    b  e
0  1  0.0  a
1  2  1.0  1
2  3  0.0  2
3  4  1.0  b

Примечание. Поскольку я использовал merge(..., how='left'), я сохраняю порядок строк в вызывающем фрейме данных. Если бы у моего df1 были значения a, которые были бы не в порядке

   a    b  e
0  1  0.0  a
1  2  1.0  1
2  4  1.0  b
3  3  NaN  2

Результат будет

df1.update(df1[['a', 'e']].merge(df2, 'left'))

df1

   a    b  e
0  1  0.0  a
1  2  1.0  1
2  4  1.0  b
3  3  0.0  2

Что, как и ожидалось.


Дальше...

Если вы хотите быть более точным, когда может быть больше столбцов

df1.update(df1.drop('b', 1).merge(df2, 'left', 'a'))

Еще больше...

Если вы не хотите update кадра данных, мы можем использовать combine_first

< Сильный > Быстрый

df1.combine_first(df1[['a', 'e']].merge(df2, 'left'))

< Сильный > Явное

df1.combine_first(df1.drop('b', 1).merge(df2, 'left', 'a'))

ЕЩЕ БОЛЬШЕ!...

'left' merge может сохранять порядок, но НЕ индекс. Это ультраконсервативный подход:

df3 = df1.drop('b', 1).merge(df2, 'left', on='a').set_index(df1.index)
df1.combine_first(df3)
4
smci 8 Июл 2019 в 02:43

Укороченная версия

df1.b.fillna(df1.a.map(df2.set_index('a').b),inplace=True)
df1
Out[173]: 
   a    b  e
0  1  0.0  a
1  2  1.0  1
2  3  0.0  2
3  4  1.0  b

Так как вы упомянули, будет несколько столбцов

df = df1.combine_first(df1[['a']].merge(df2, on='a', how='left'))
df
Out[184]: 
   a    b  e
0  1  0.0  a
1  2  1.0  1
2  3  0.0  2
3  4  1.0  b

Также мы можем перейти к fillna с помощью df

df1.fillna(df1[['a']].merge(df2, on='a', how='left'))
Out[185]: 
   a    b  e
0  1  0.0  a
1  2  1.0  1
2  3  0.0  2
3  4  1.0  b
5
YO and BEN_W 1 Июл 2019 в 21:08

Вы можете замаскировать данные.

Исходные данные:

print(df)
   one  two  three
0    1  1.0    1.0
1    2  NaN    2.0
2    3  3.0    NaN

print(df2)
   one  two  three
0    4    4      4
1    4    2      4
2    4    4      3

Смотрите ниже, маска просто заполняется в зависимости от состояния.

# mask values where isna()
df1[['two','three']] = df1[['two','three']]\
        .mask(df1[['two','three']].isna(),df2[['two','three']])

Выход:

   one  two  three
0    1  1.0    1.0
1    2  2.0    2.0
2    3  3.0    3.0
2
krewsayder 2 Июл 2019 в 02:09

Только если индексы выровнены (важное примечание), мы можем использовать update:

df1['b'].update(df2['b'])


   a    b  e
0  1  0.0  a
1  2  1.0  1
2  3  0.0  2
3  4  1.0  b

Или просто fillna:

df1['b'].fillna(df2['b'], inplace=True)

Если ваши индексы не выровнены, см. ответ WenNYoBen или комментарий внизу.

2
Erfan 1 Июл 2019 в 20:48