#python #pandas
Вопрос:
У меня есть 2 кадра данных:
dfA = pd.DataFrame({'label':[1,5,2,4,2,3],
'group':['A']*3 ['B']*3,
'x':[np.nan]*3 [1,2,3],
'y':[np.nan]*3 [1,2,3]})
dfB = pd.DataFrame({'uniqid':[1,2,3,4,5,6,7],
'horizontal':[34,41,23,34,23,43,22],
'vertical':[98,67,19,57,68,88,77]})
…которые выглядят как:
label group x y
0 1 A NaN NaN
1 5 A NaN NaN
2 2 A NaN NaN
3 4 B 1.0 1.0
4 2 B 2.0 2.0
5 3 B 3.0 3.0
uniqid horizontal vertical
0 1 34 98
1 2 41 67
2 3 23 19
3 4 34 57
4 5 23 68
5 6 43 88
6 7 22 77
В основном dfB содержит «горизонтальные» и «вертикальные» значения для всех уникальных идентификаторов. Я хочу заполнить столбцы » x » и » y «в dfA значениями» по горизонтали » и «по вертикали» в dfB, но только для группы A; данные для группы B должны оставаться неизменными.
Желаемый результат был бы:
label group x y
0 1 A 34.0 98.0
1 5 A 23.0 68.0
2 2 A 41.0 67.0
3 4 B 1.0 1.0
4 2 B 2.0 2.0
5 3 B 3.0 3.0
Я использовал .merge (), чтобы добавить дополнительные столбцы в фрейм данных для обеих групп A и B, а затем скопировать данные в столбцы x и y только для группы A. И, наконец, удалите столбцы из dfB.
dfA = dfA.merge(dfB, how = 'left', left_on = 'label', right_on = 'uniqid')
dfA.loc[dfA['group'] == 'A','x'] = dfA.loc[dfA['group'] == 'A','horizontal']
dfA.loc[dfA['group'] == 'A','y'] = dfA.loc[dfA['group'] == 'A','vertical']
dfA = dfA[['label','group','x','y']]
Будет получен правильный результат:
label group x y
0 1 A 34.0 98.0
1 5 A 23.0 68.0
2 2 A 41.0 67.0
3 4 B 1.0 1.0
4 2 B 2.0 2.0
5 3 B 3.0 3.0
…но это действительно, действительно уродливое решение. Есть ли лучшее решение?
Ответ №1:
combine_first
dfA.set_index(['label', 'group']).combine_first(
dfB.set_axis(['label', 'x', 'y'], axis=1).set_index(['label'])
).reset_index()
label group x y
0 1 A 34.0 98.0
1 5 A 23.0 68.0
2 2 A 41.0 67.0
3 4 B 1.0 1.0
4 2 B 2.0 2.0
5 3 B 3.0 3.0
fillna
Работает так же хорошо
dfA.set_index(['label', 'group']).fillna(
dfB.set_axis(['label', 'x', 'y'], axis=1).set_index(['label'])
).reset_index()
Комментарии:
1. Я раньше не сталкивался с методом .combine_first (), и поэтому это был очень полезный ответ. Спасибо за публикацию. Однако написанное решение не ограничивает обновление строк только строками группы A; если строка в группе B содержит NaN, то эта строка также будет обновлена.
Ответ №2:
Мы можем попытаться loc
извлечь/обновить только ту часть, которую мы хотим. И поскольку вы объединяетесь в одном столбце, который также имеет уникальное значение dfB
, вы можете использовать set_index
и loc/reindex
:
mask = dfA['group']=='A'
dfA.loc[ mask, ['x','y']] = (dfB.set_index('uniqid')
.loc[dfA.loc[mask,'label'],
['horizontal','vertical']]
.values
)
Выход:
label group x y
0 1 A 34.0 98.0
1 5 A 23.0 68.0
2 2 A 41.0 67.0
3 4 B 1.0 1.0
4 2 B 2.0 2.0
5 3 B 3.0 3.0
Обратите внимание, что вышеизложенное не сработает, если некоторые из dfA.label
них не dfB.uniqueid
будут включены . В этом случае нам нужно использовать reindex
:
(dfB.set_index('uniqid')
.reindex[dfA.loc[mask,'label']
[['horizontal','vertical']].values
)
Комментарии:
1. Этот метод действительно хорошо сработал для меня — мне потребовалось некоторое время, чтобы понять, как его использовать . loc с 2 различными кадрами данных будет работать, но я думаю, что это гениальное решение.