Цель этого поста - дать читателям представление о слиянии SQL-кода с пандами, о том, как его использовать и когда его не использовать.
В частности, вот что пройдет этот пост:
Чему этот пост не пройдёт:
- Обсуждения и время, связанные с производительностью (на данный момент). Наиболее заметные упоминания о лучших альтернативах, где это уместно.
- Обработка суффиксов, удаление лишних столбцов, переименование выходных данных и другие конкретные случаи использования. Есть другие (читай: лучше) посты, которые касаются этого, так что разберись!
Примечание. В
большинстве примеров по умолчанию используются операции INNER JOIN при демонстрации различных функций, если не указано иное.
Кроме того, все DataFrames здесь могут быть скопированы и реплицированы, так что вы можете играть с ними. Также смотрите этот пост
о том, как читать DataFrames из вашего буфера обмена.
Наконец, все визуальное представление операций JOIN было нарисовано от руки с помощью Google Drawings. Вдохновение отсюда .
Достаточно говорить, просто покажи мне, как использовать merge
!
Настроить
np.random.seed(0)
left = pd.DataFrame({'key': ['A', 'B', 'C', 'D'], 'value': np.random.randn(4)})
right = pd.DataFrame({'key': ['B', 'D', 'E', 'F'], 'value': np.random.randn(4)})
left
key value
0 A 1.764052
1 B 0.400157
2 C 0.978738
3 D 2.240893
right
key value
0 B 1.867558
1 D -0.977278
2 E 0.950088
3 F -0.151357
Для простоты ключевой столбец имеет то же имя (пока).
Внутреннее соединение представлено
Обратите внимание
, что вместе с предстоящими фигурами все следуют этой конвенции:
- синий указывает на строки, которые присутствуют в результате слияния
- красный цвет обозначает строки, которые исключены из результата (то есть удалены)
- зеленый цвет указывает на отсутствующие значения, которые заменяются на NaNs в результате
Чтобы выполнить INNER JOIN, вызовите merge
левый DataFrame, указав в качестве аргументов правый DataFrame и ключ соединения (как минимум).
left.merge(right, on='key')
# Or, if you want to be explicit
# left.merge(right, on='key', how='inner')
key value_x value_y
0 B 0.400157 1.867558
1 D 2.240893 -0.977278
Это возвращает только строки из left
и right
которые имеют общий ключ (в этом примере, «B» и «D).
LEFT OUTER JOIN или LEFT JOIN представлена
Это можно выполнить, указав how='left'
.
left.merge(right, on='key', how='left')
key value_x value_y
0 A 1.764052 NaN
1 B 0.400157 1.867558
2 C 0.978738 NaN
3 D 2.240893 -0.977278
Внимательно обратите внимание на размещение NaNs здесь. Если вы укажете how='left'
, то left
будут использоваться только ключи от , а отсутствующие данные right
заменяются на NaN.
И точно так же для ПРАВИЛЬНОГО ВНЕШНЕГО СОЕДИНЕНИЯ или ПРАВИЛЬНОГО СОЕДИНЕНИЯ, которое ...
... указать how='right'
:
left.merge(right, on='key', how='right')
key value_x value_y
0 B 0.400157 1.867558
1 D 2.240893 -0.977278
2 E NaN 0.950088
3 F NaN -0.151357
Здесь используются ключи from right
, а отсутствующие данные left
заменяются на NaN.
Наконец, для полного внешнего соединения , заданного
уточнять how='outer'
.
left.merge(right, on='key', how='outer')
key value_x value_y
0 A 1.764052 NaN
1 B 0.400157 1.867558
2 C 0.978738 NaN
3 D 2.240893 -0.977278
4 E NaN 0.950088
5 F NaN -0.151357
При этом используются ключи из обоих кадров, а NaN вставляются для пропущенных строк в обоих.
Документация суммирует эти различные слияния:
Другие СОЕДИНЕНИЯ - ЛЕВЫЙ, ПРАВИЛЬНЫЙ, И ПОЛНЫЙ, исключающий / АНТИ-СОЕДИНЕНИЯ
Если вам нужны ЛЕВЫЕ исключающие СОЕДИНЕНИЯ и ПРАВИЛЬНЫЕ СОЕДИНЕНИЯ в два этапа.
Для LEFT-исключая JOIN, представленный как
Начните с выполнения LEFT OUTER JOIN, а затем отфильтруйте (исключая!) Строки, поступающие left
только от
(left.merge(right, on='key', how='left', indicator=True)
.query('_merge == "left_only"')
.drop('_merge', 1))
key value_x value_y
0 A 1.764052 NaN
2 C 0.978738 NaN
Куда,
left.merge(right, on='key', how='left', indicator=True)
key value_x value_y _merge
0 A 1.764052 NaN left_only
1 B 0.400157 1.867558 both
2 C 0.978738 NaN left_only
3 D 2.240893 -0.977278 both
И аналогично, для ПРАВА, исключающего СОЕДИНЕНИЕ,
(left.merge(right, on='key', how='right', indicator=True)
.query('_merge == "right_only"')
.drop('_merge', 1))
key value_x value_y
2 E NaN 0.950088
3 F NaN -0.151357
Наконец, если вам необходимо выполнить слияние, при котором ключи сохраняются только слева или справа, но не оба (IOW, выполняя ANTI-JOIN ),
Вы можете сделать это подобным образом -
(left.merge(right, on='key', how='outer', indicator=True)
.query('_merge != "both"')
.drop('_merge', 1))
key value_x value_y
0 A 1.764052 NaN
2 C 0.978738 NaN
4 E NaN 0.950088
5 F NaN -0.151357
Разные имена для ключевых столбцов
Если ключевые столбцы названы по-разному - например, left
имеет keyLeft
и right
имеет keyRight
вместо - key
тогда вы должны будете указать left_on
и в right_on
качестве аргументов вместо on
:
left2 = left.rename({'key':'keyLeft'}, axis=1)
right2 = right.rename({'key':'keyRight'}, axis=1)
left2
keyLeft value
0 A 1.764052
1 B 0.400157
2 C 0.978738
3 D 2.240893
right2
keyRight value
0 B 1.867558
1 D -0.977278
2 E 0.950088
3 F -0.151357
left2.merge(right2, left_on='keyLeft', right_on='keyRight', how='inner')
keyLeft value_x keyRight value_y
0 B 0.400157 B 1.867558
1 D 2.240893 D -0.977278
Как избежать дублирования ключевого столбца в выводе
При объединении keyLeft
из left
и keyRight
из right
, если вы хотите, чтобы в выводе был только один из keyLeft
или keyRight
(но не оба), вы можете начать с установки индекса в качестве предварительного шага.
left3 = left2.set_index('keyLeft')
left3.merge(right2, left_index=True, right_on='keyRight')
value_x keyRight value_y
0 0.400157 B 1.867558
1 2.240893 D -0.977278
Сравните это с выводом команды непосредственно перед (thst, выход left2.merge(right2, left_on='keyLeft', right_on='keyRight', how='inner')
), вы заметите, keyLeft
что отсутствует. Вы можете выяснить, какой столбец хранить, основываясь на том, какой индекс фрейма установлен в качестве ключа. Это может иметь значение, например, при выполнении какой-либо операции OUTER JOIN.
Слияние только одного столбца из одного из DataFrames
Например, рассмотрим
right3 = right.assign(newcol=np.arange(len(right)))
right3
key value newcol
0 B 1.867558 0
1 D -0.977278 1
2 E 0.950088 2
3 F -0.151357 3
Если вам необходимо объединить только «new_val» (без каких-либо других столбцов), вы обычно можете просто подмножество столбцов перед объединением:
left.merge(right3[['key', 'newcol']], on='key')
key value newcol
0 B 0.400157 0
1 D 2.240893 1
Если вы делаете LEFT OUTER JOIN, более эффективное решение будет включать map
:
# left['newcol'] = left['key'].map(right3.set_index('key')['newcol']))
left.assign(newcol=left['key'].map(right3.set_index('key')['newcol']))
key value newcol
0 A 1.764052 NaN
1 B 0.400157 0.0
2 C 0.978738 NaN
3 D 2.240893 1.0
Как уже упоминалось, это похоже на, но быстрее, чем
left.merge(right3[['key', 'newcol']], on='key', how='left')
key value newcol
0 A 1.764052 NaN
1 B 0.400157 0.0
2 C 0.978738 NaN
3 D 2.240893 1.0
Слияние на нескольких столбцах
Чтобы объединить несколько столбцов, укажите список для on
(или left_on
и right_on
, в зависимости от ситуации).
left.merge(right, on=['key1', 'key2'] ...)
Или, если имена разные,
left.merge(right, left_on=['lkey1', 'lkey2'], right_on=['rkey1', 'rkey2'])
Другие полезные merge*
операции и функции
Этот раздел охватывает только самые основы и предназначен только для разжигания аппетита. Для большего количества примеров и случаев, см документации на merge
, join
иconcat
так же , как и ссылки на функцию спецификацию.
На основе индекса * -JOIN (+ столбцы индекса merge
)
Настроить
np.random.seed([3, 14])
left = pd.DataFrame({'value': np.random.randn(4)}, index=['A', 'B', 'C', 'D'])
right = pd.DataFrame({'value': np.random.randn(4)}, index=['B', 'D', 'E', 'F'])
left.index.name = right.index.name = 'idxkey'
left
value
idxkey
A -0.602923
B -0.402655
C 0.302329
D -0.524349
right
value
idxkey
B 0.543843
D 0.013135
E -0.326498
F 1.385076
Как правило, слияние по индексу будет выглядеть так:
left.merge(right, left_index=True, right_index=True)
value_x value_y
idxkey
B -0.402655 0.543843
D -0.524349 0.013135
Поддержка имен индексов
Если ваш индекс называется, то v0.23 пользователи могут также указать имя уровня для on
(или left_on
и по right_on
мере необходимости).
left.merge(right, on='idxkey')
value_x value_y
idxkey
B -0.402655 0.543843
D -0.524349 0.013135
Слияние по индексу одного столбца (столбцов) другого
Можно (и довольно просто) использовать индекс одного и столбец другого для выполнения слияния. Например,
left.merge(right, left_on='key1', right_index=True)
Или наоборот ( right_on=...
а left_index=True
).
right2 = right.reset_index().rename({'idxkey' : 'colkey'}, axis=1)
right2
colkey value
0 B 0.543843
1 D 0.013135
2 E -0.326498
3 F 1.385076
left.merge(right2, left_index=True, right_on='colkey')
value_x colkey value_y
0 -0.402655 B 0.543843
1 -0.524349 D 0.013135
В этом особом случае указывается имя для индекса left
, поэтому вы также можете использовать имя индекса с помощью left_on
, например:
left.merge(right2, left_on='idxkey', right_on='colkey')
value_x colkey value_y
0 -0.402655 B 0.543843
1 -0.524349 D 0.013135
DataFrame.join
Помимо этого, есть еще один лаконичный вариант. Вы можете использовать, DataFrame.join
какие по умолчанию присоединения в индексе. DataFrame.join
делает LEFT OUTER JOIN по умолчанию, поэтому how='inner'
здесь необходимо.
left.join(right, how='inner', lsuffix='_x', rsuffix='_y')
value_x value_y
idxkey
B -0.402655 0.543843
D -0.524349 0.013135
Обратите внимание , что мне нужно было указать lsuffix
и rsuffix
аргументы , так как в join
противном случае ошибки будет из:
left.join(right)
ValueError: columns overlap but no suffix specified: Index(['value'], dtype='object')
Так как имена столбцов совпадают. Это не было бы проблемой, если бы они назывались по-другому.
left.rename(columns={'value':'leftvalue'}).join(right, how='inner')
leftvalue value
idxkey
B -0.402655 0.543843
D -0.524349 0.013135
pd.concat
Наконец, в качестве альтернативы для соединений на основе индекса вы можете использовать pd.concat
:
pd.concat([left, right], axis=1, sort=False, join='inner')
value value
idxkey
B -0.402655 0.543843
D -0.524349 0.013135
Опустите, join='inner'
если вам нужно FULL OUTER JOIN (по умолчанию):
pd.concat([left, right], axis=1, sort=False)
value value
A -0.602923 NaN
B -0.402655 0.543843
C 0.302329 NaN
D -0.524349 0.013135
E NaN -0.326498
F NaN 1.385076
Для получения дополнительной информации см. Этот канонический пост pd.concat
@piRSquared .
Обобщение: merge
объединение нескольких фреймов данных
Часто возникает ситуация, когда несколько DataFrames должны быть объединены вместе. Наивно это можно сделать цепочкой merge
вызовов:
df1.merge(df2, ...).merge(df3, ...)
Тем не менее, это быстро выходит из-под контроля для многих DataFrames. Кроме того, может потребоваться обобщение для неизвестного числа фреймов данных.
Здесь я расскажу pd.concat
о многоходовых соединениях с уникальными ключами и DataFrame.join
о многоходовых соединениях с неуникальными ключами. Сначала настройка.
# Setup.
np.random.seed(0)
A = pd.DataFrame({'key': ['A', 'B', 'C', 'D'], 'valueA': np.random.randn(4)})
B = pd.DataFrame({'key': ['B', 'D', 'E', 'F'], 'valueB': np.random.randn(4)})
C = pd.DataFrame({'key': ['D', 'E', 'J', 'C'], 'valueC': np.ones(4)})
dfs = [A, B, C]
# Note, the "key" column values are unique, so the index is unique.
A2 = A.set_index('key')
B2 = B.set_index('key')
C2 = C.set_index('key')
dfs2 = [A2, B2, C2]
Многостороннее слияние по уникальным ключам (или индексам)
Если ваши ключи (здесь ключ может быть столбцом или индексом) уникальны, то вы можете использовать pd.concat
. Обратите внимание, что pd.concat
присоединяет DataFrames к индексу .
# merge on `key` column, you'll need to set the index before concatenating
pd.concat([
df.set_index('key') for df in dfs], axis=1, join='inner'
).reset_index()
key valueA valueB valueC
0 D 2.240893 -0.977278 1.0
# merge on `key` index
pd.concat(dfs2, axis=1, sort=False, join='inner')
valueA valueB valueC
key
D 2.240893 -0.977278 1.0
Пропустить join='inner'
для полного внешнего соединения. Обратите внимание, что вы не можете указать соединения LEFT или RIGHT OUTER (если они вам нужны, используйте join
, как описано ниже).
Многоходовое слияние ключей с дубликатами
concat
быстрый, но имеет свои недостатки. Он не может обрабатывать дубликаты.
A3 = pd.DataFrame({'key': ['A', 'B', 'C', 'D', 'D'], 'valueA': np.random.randn(5)})
pd.concat([df.set_index('key') for df in [A3, B, C]], axis=1, join='inner')
ValueError: Shape of passed values is (3, 4), indices imply (3, 2)
В этой ситуации мы можем использовать, join
так как он может обрабатывать неуникальные ключи (обратите внимание, что join
присоединяет DataFrames к их индексу; он вызывает изнутри merge
и выполняет LEFT OUTER JOIN, если не указано иное).
# join on `key` column, set as the index first
# For inner join. For left join, omit the "how" argument.
A.set_index('key').join(
[df.set_index('key') for df in (B, C)], how='inner').reset_index()
key valueA valueB valueC
0 D 2.240893 -0.977278 1.0
# join on `key` index
A3.set_index('key').join([B2, C2], how='inner')
valueA valueB valueC
key
D 1.454274 -0.977278 1.0
D 0.761038 -0.977278 1.0
concat
иmerge
с параметром направления являетсяhorizontal
илиvertical
.axis=1
иaxis=0
есть?merge
иconcat
и ось и все. Однако, как показывает @eliu, это все та же концепция слияния с «левым» и «правым» и «горизонтальным» или «вертикальным». Лично я должен заглядывать в документацию каждый раз, когда мне нужно вспомнить, какая "ось" есть,0
а какая есть1
.