Ответ 1
Цель этого поста - дать читателям представление о слиянии SQL с пандами, о том, как его использовать и когда его не использовать.
В частности, вот что будет проходить через этот пост:
Основы - типы соединений (ВЛЕВО, ВПРАВО, ВНЕШНИЙ, ВНУТРЕННИЙ)
- объединение с разными именами столбцов
- избегая дублирования столбца ключа слияния в выходных данных
- Слияние с индексом в разных условиях
- эффективно использовать ваш именованный индекс
- объединить ключ в качестве индекса одного и столбца другого
- Многостороннее объединение столбцов и индексов (уникальных и неуникальных)
- Известные альтернативы
merge
иjoin
Через что этот пост не пройдет:
- Обсуждения и время, связанные с производительностью (на данный момент). Наиболее заметные упоминания о лучших альтернативах, где это уместно.
- Обработка суффиксов, удаление лишних столбцов, переименование выходных данных и другие конкретные случаи использования. Есть другие (читай: лучше) посты, которые касаются этого, так что разберись!
Примечание
В большинстве примеров по умолчанию используются операции 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
Для простоты ключевой столбец имеет то же имя (пока).
ВНУТРЕННЕЕ СОЕДИНЕНИЕ представлено
Примечание
Это, наряду с предстоящими фигурами, все следуют этому соглашению:
- синий указывает строки, которые присутствуют в результате слияния
- красный указывает на строки, которые исключены из результата (то есть удалены)
- зеленый указывает на пропущенные значения, которые заменяются на NaN в результате
Чтобы выполнить ВНУТРЕННЕЕ СОЕДИНЕНИЕ, вызовите pd.merge
, указав левый DataFrame, правый DataFrame и ключ соединения.
pd.merge(left, right, on='key')
key value_x value_y
0 B 0.400157 1.867558
1 D 2.240893 -0.977278
Это возвращает только строки из left
и right
, которые имеют общий ключ (в этом примере, "B" и "D).
В более поздних версиях панд (v0.21 или около того) merge
теперь является функцией первого порядка, поэтому вы можете вызывать DataFrame.merge
.
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 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
Здесь используются ключи из 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
Сравните это с выводом команды непосредственно перед (это вывод 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*
операции и функции
- Слияние DataFrame с Series в индексе: см. этот ответ.
Помимо
merge
,DataFrame.update
иDataFrame.combine_first
также используются в некоторых случаях для обновления одного DataFrame другим.pd.merge_ordered
- полезная функция для упорядоченных соединений.pd.merge_asof
(читай: merge_asOf) полезно для приблизительных объединений.
Этот раздел охватывает только самые основы и предназначен только для разжигания аппетита. Дополнительные примеры и примеры см. в документации по merge
, join
и concat
, а также по ссылкам на спецификации функций.
На основе индекса * -JOIN (+ индекс-столбец merge
s)
Настройка
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'
, если вам нужно ПОЛНОЕ ВНЕШНЕЕ СОЕДИНЕНИЕ (по умолчанию):
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
присоединяет кадры данных к индексу.
# 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