Объединение Панд 101

  • Как выполнить соединение (LEFT | RIGHT | FULL) (INNER | OUTER) с пандами?
  • Как добавить NaN для пропущенных строк после слияния?
  • Как избавиться от NaNs после слияния?
  • Можно ли слить по индексу?
  • Крест соединиться с пандами?
  • Как объединить несколько фреймов данных?
  • merge? join? concat? update? Кто? Какие? Зачем?!

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

Предполагается, что эта QnA станет следующей статьей в серии полезных руководств по распространенным идиомам панд (см. Этот пост о поворотах и этот пост о конкатенации, о котором я расскажу позже).

Пожалуйста, обратите внимание, что этот пост не предназначен для замены документации, поэтому, пожалуйста, прочитайте это! Некоторые из примеров взяты оттуда.

Ответ 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 вставляются для пропущенных строк в обоих.

Документация обобщает эти различные слияния:

enter image description here

Другие СОЕДИНЕНИЯ - ЛЕВЫЙ, ПРАВИЛЬНЫЙ и ПОЛНОСТЬЮ/АНТИ-СОЕДИНЕНИЯ

Если вам нужны СОЕДИНИТЕЛЬНЫЕ СЛЕДУЮЩИЕ СОЕДИНЕНИЯ и НЕЗАКОННЫЕ СОЕДИНЕНИЯ в два этапа.

Для 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

Ответ 2

Дополнительный визуальный обзор pd.concat([df0, df1], kwargs). Обратите внимание, что значение kwarg axis=0 или axis=1 не так интуитивно понятно, как df.mean() или df.apply(func)


on pd.concat([df0, df1])

Ответ 3

Я думаю, что вы должны включить это в свое объяснение, так как это уместное слияние, которое я вижу довольно часто, которое называется cross-join я считаю. Это слияние, которое происходит, когда уникальный df не разделяет столбцы, и это просто слияние 2 dfs рядом:

Настройка:

names1 = [{'A':'Jack', 'B':'Jill'}]

names2 = [{'C':'Tommy', 'D':'Tammy'}]

df1=pd.DataFrame(names1)
df2=pd.DataFrame(names2)
df_merged= pd.merge(df1.assign(X=1), df2.assign(X=1), on='X').drop('X', 1)

Это создает фиктивный столбец X, сливается с X, а затем отбрасывает его для получения

df_merged:

      A     B      C      D
0  Jack  Jill  Tommy  Tammy