Как объединить DataFrames в Pandas - merge (), join (), append (), concat () и update ()

Введение Pandas предоставляет огромный набор методов и функций для управления данными, включая слияние DataFrames. Слияние DataFrames позволяет вам создавать новый DataFrame без изменения исходного источника данных или изменения исходного источника данных. Если вы знакомы с SQL или аналогичным типом табличных данных, вы, вероятно, знакомы с термином соединение, которое означает объединение DataFrames для формирования нового DataFrame. Если вы новичок, может быть трудно полностью понять типы соединения (в

Вступление

Pandas предоставляет огромный набор методов и функций для управления данными, включая слияние DataFrames. Слияние DataFrames позволяет вам создавать новый DataFrame без изменения исходного источника данных или изменения исходного источника данных.

Если вы знакомы с SQL или аналогичным типом табличных данных, вы, вероятно, знакомы с термином join , что означает объединение DataFrames для формирования нового DataFrame. Если вы новичок, может быть трудно полностью понять типы соединения ( внутреннее, внешнее, левое, правое ). В этом руководстве мы рассмотрим типы соединений с примерами.

Наше основное внимание будет сосредоточено на использовании функций merge() и concat() Однако мы обсудим другие методы слияния, чтобы дать вам как можно больше практических альтернатив.

В этом руководстве мы используем Pandas версии 1.1.4 и NumPy версии 1.19.4 .

Для вашего удобства вот содержание:

Объединить фреймы данных с помощью merge ()

Начнем с настройки наших DataFrames, которые мы будем использовать в оставшейся части учебника.

df1 будет включать наш воображаемый список пользователей с именами, адресами электронной почты и идентификаторами.

1
2
3
4
5
6
7
8
     import pandas as pd 
     
     df1 = pd.DataFrame({'user_id': ['id001', 'id002', 'id003', 'id004', 'id005', 'id006', 'id007'], 
                    'first_name': ['Rivi', 'Wynnie', 'Kristos', 'Madalyn', 'Tobe', 'Regan', 'Kristin'], 
                    'last_name': ['Valti', 'McMurty', 'Ivanets', 'Max', 'Riddich', 'Huyghe', 'Illis'], 
                    'email': [' [email protected] ', ' [email protected] ', ' [email protected] ', 
                    ' [email protected] ', ' [email protected] ', ' [email protected] ', ' [email protected] '] 
                    }) 

При разработке баз данных считается хорошей практикой хранить настройки профиля (например, цвет фона, ссылку на изображение аватара, размер шрифта и т. Д.) В отдельной таблице от данных пользователя (электронная почта, дата добавления и т. Д.). Эти таблицы могут иметь взаимно-однозначное отношение.

Чтобы смоделировать этот сценарий, мы сделаем то же самое, создав df2 с URL-адресами изображений и идентификаторами пользователей:

1
2
3
4
5
     df2 = pd.DataFrame({'user_id': ['id001', 'id002', 'id003', 'id004', 'id005'], 
                         'image_url': ['http://example.com/img/id001.png', 'http://example.com/img/id002.jpg', 
                         'http://example.com/img/id003.bmp', 'http://example.com/img/id004.jpg', 
                         'http://example.com/img/id005.png'] 
     }) 

Вот как выглядят наши DataFrames:

 # df1 
 user_id first_name last_name email 
 0 id001 Rivi Valti [email protected] 
 1 id002 Wynnie McMurty [email protected] 
 2 id003 Kristos Ivanets [email protected] 
 3 id004 Madalyn Max [email protected] 
 4 id005 Tobe Riddich [email protected] 
 5 id006 Regan Huyghe [email protected] 
 6 id007 Kristin Illis [email protected] 
 
 #df2 
  user_id image_url 
 0 id001 http://example.com/img/id001.png 
 1 id002 http://example.com/img/id002.jpg 
 2 id003 http://example.com/img/id003.bmp 
 3 id004 http://example.com/img/id004.jpg 
 4 id005 http://example.com/img/id005.png 

Давайте объединим эти DataFrames с функцией merge() . Во-первых, взгляните на все параметры, которые может принимать эта функция:

1
2
3
4
     pd.merge(left, right, how='inner', on=None, left_on=None, right_on=None, 
               left_index=False, right_index=False, sort=True, 
               suffixes=('_x', '_y'), copy=True, indicator=False, 
               validate=None) 

Большинство этих параметров имеют значения по умолчанию, за исключением левого и правого . Эти два параметра являются именами DataFrames, которые мы будем объединять. Сама функция вернет новый DataFrame, который мы сохраним в переменной df3_merged

Введите следующий код в оболочку Python:

1
     df3_merged = pd.merge(df1, df2) 

Поскольку оба наших DataFrames имеют столбец user_id с одинаковым именем, merge() автоматически объединяет две таблицы, соответствующие этому ключу. Если бы у нас было два столбца с разными именами, мы могли бы использовать left_on='left_column_name' и right_on='right_column_name' чтобы явно указать ключи для обоих DataFrames.

df3_merged чтобы увидеть ее содержимое:

1
2
3
4
5
6
     user_id first_name last_name email image_url 
     0 id001 Rivi Valti [email protected] http://example.com/img/id001.png 
     1 id002 Wynnie McMurty [email protected] http://example.com/img/id002.jpg 
     2 id003 Kristos Ivanets [email protected] http://example.com/img/id003.bmp 
     3 id004 Madalyn Max [email protected] http://example.com/img/id004.jpg 
     4 id005 Tobe Riddich [email protected] http://example.com/img/id005.png 

Вы заметите, что df3_merged имеет только 5 строк, в то время как исходный df1 имел 7. Почему?

Когда значение по умолчанию для how установлено на inner , новый DataFrame создается из пересечения левого и правого DataFrame. Следовательно, если user_id отсутствует в одной из таблиц, его не будет в объединенном DataFrame.

Это останется верным, даже если поменять местами левую и правую строки:

1
     df3_merged = pd.merge(df2, df1) 

Результаты по-прежнему:

1
2
3
4
5
6
     user_id image_url first_name last_name email 
     0 id001 http://example.com/img/id001.png Rivi Valti rvalti0 [email protected] 
     1 id002 http://example.com/img/id002.jpg Wynnie McMurty [email protected] 
     2 id003 http://example.com/img/id003.bmp Kristos Ivanets [email protected] 
     3 id004 http://example.com/img/id004.jpg Madalyn Max [email protected] 
     4 id005 http://example.com/img/id005.png Tobe Riddich [email protected] 

Пользователи с идентификаторами 'id006' и 'id007' не являются частью объединенных DataFrames, поскольку они не пересекаются в обеих таблицах.

Однако бывают случаи, когда мы хотим использовать один из DataFrames в качестве основного DataFrame и включать все строки из него, даже если они не все пересекаются друг с другом. То есть, чтобы иметь всех наших пользователей, в то время как image_url является обязательным.

Как? Используя merge() , мы можем передать аргумент 'left' how :

1
2
3
     df_left_merge = pd.merge(df1, df2, how='left') 
     
     print(df_left_merge) 

При левом соединении мы включили все элементы левого DataFrame ( df1 ) и каждый элемент правого DataFrame ( df2 ). Выполнение приведенного выше кода отобразит следующее:

1
2
3
4
5
6
7
8
     user_id first_name last_name email image_url 
     0 id001 Rivi Valti [email protected] http://example.com/img/id001.png 
     1 id002 Wynnie McMurty [email protected] http://example.com/img/id002.jpg 
     2 id003 Kristos Ivanets [email protected] http://example.com/img/id003.bmp 
     3 id004 Madalyn Max [email protected] http://example.com/img/id004.jpg 
     4 id005 Tobe Riddich [email protected] http://example.com/img/id005.png 
     5 id006 Regan Huyghe [email protected] NaN 
     6 id007 Kristin Illis [email protected] NaN 

Ячейки, которые не имеют совпадающих значений с левым DataFrame, заполняются NaN .

Почему бы нам не попробовать правильное соединение? Создайте следующий объединенный DataFrame:

1
2
3
     df_right_merge = pd.merge(df1, df2, how='right') 
     
     print(df_right_merge) 

Как вы могли ожидать, правое соединение вернет каждое значение из левого DataFrame, которое соответствует правому DataFrame:

1
2
3
4
5
6
     user_id first_name last_name email image_url 
     0 id001 Rivi Valti [email protected] http://example.com/img/id001.png 
     1 id002 Wynnie McMurty [email protected] http://example.com/img/id002.jpg 
     2 id003 Kristos Ivanets [email protected] http://example.com/img/id003.bmp 
     3 id004 Madalyn Max [email protected] http://example.com/img/id004.jpg 
     4 id005 Tobe Riddich [email protected] http://example.com/img/id005.png 

Поскольку каждая строка в df2 имеет значение в df1 , в этом случае это right соединение аналогично inner

Давайте посмотрим на outer соединения. Чтобы лучше всего проиллюстрировать, как они работают, давайте поменяем местами наши DataFrames и создадим две новые переменные для левого и внешнего соединений:

1
2
3
4
5
     df_left = pd.merge(df2, df1, how='left', indicator=True) 
     df_outer = pd.merge(df2, df1, how='outer', indicator=True) 
     
     print(df_left) 
     print(df_outer) 

Имейте в виду, что наш левый DataFrame - это df2 а правый DataFrame - это df1 . Использование how='outer' объединяет DataFrames, совпадающие по ключу, но также включает значения, которые отсутствуют или не совпадают.

Мы также добавили indicator и установили для него значение True чтобы Pandas добавил дополнительный столбец _merge в конец нашего DataFrame. Этот столбец сообщает нам, была ли найдена строка в левом, правом или обоих фреймах данных.

df_left выглядит так:

1
2
3
4
5
6
     user_id image_url first_name last_name email _merge 
     0 id001 http://example.com/img/id001.png Rivi Valti [email protected] both 
     1 id002 http://example.com/img/id002.jpg Wynnie McMurty [email protected] both 
     2 id003 http://example.com/img/id003.bmp Kristos Ivanets [email protected] both 
     3 id004 http://example.com/img/id004.jpg Madalyn Max [email protected] both 
     4 id005 http://example.com/img/id005.png Tobe Riddich [email protected] both 

Однако в df_outer есть такие данные:

1
2
3
4
5
6
7
8
     user_id image_url first_name last_name email _merge 
     0 id001 http://example.com/img/id001.png Rivi Valti [email protected] both 
     1 id002 http://example.com/img/id002.jpg Wynnie McMurty [email protected] both 
     2 id003 http://example.com/img/id003.bmp Kristos Ivanets [email protected] both 
     3 id004 http://example.com/img/id004.jpg Madalyn Max [email protected] both 
     4 id005 http://example.com/img/id005.png Tobe Riddich [email protected] both 
     5 id006 NaN Regan Huyghe [email protected] right_only 
     6 id007 NaN Kristin Illis [email protected] right_only 

Обратите внимание, что в df_outer DataFrame id006 и id007 существует только в правом DataFrame (в данном случае это df1 ). Если мы попытаемся сравнить левое и внешнее соединения, не меняя местами, мы получим одинаковые результаты для них обоих.

Объединить фреймы данных с помощью join ()

В отличие от merge() который является методом экземпляра Pandas, join() является методом самого DataFrame. Это означает, что мы можем использовать его как статический метод в DataFrame: DataFrame.join(other, on=None, how='left', lsuffix='', rsuffix='', sort=False) .

DataFrame, из которого мы вызываем join() будет нашим левым DataFrame. DataFrame в other аргументе будет нашим правильным DataFrame.

Параметр on может принимать один или несколько ['key1', 'key2' ...] ) для определения соответствующего ключа, в то время how параметр how принимает один из аргументов дескриптора (левый, правый, внешний, внутренний), и он по умолчанию установлено left

Попробуем присоединить df2 к df1 :

1
2
3
     df_join = df1.join(df2, rsuffix='_right') 
     
     print(df_join) 

Как и функция merge() join() автоматически пытается сопоставить ключи (столбцы) с тем же именем. В нашем случае это ключ user_id

Приведенный выше код распечатывает это:

1
2
3
4
5
6
7
8
     user_id first_name last_name email user_id_right image_url 
     0 id001 Rivi Valti [email protected] id001 http://example.com/img/id001.png 
     1 id002 Wynnie McMurty [email protected] id002 http://example.com/img/id002.jpg 
     2 id003 Kristos Ivanets [email protected] id003 http://example.com/img/id003.bmp 
     3 id004 Madalyn Max [email protected] id004 http://example.com/img/id004.jpg 
     4 id005 Tobe Riddich [email protected] id005 http://example.com/img/id005.png 
     5 id006 Regan Huyghe [email protected] NaN NaN 
     6 id007 Kristin Illis [email protected] NaN NaN 

Вы, наверное, заметили «повторяющийся столбец» под названием user_id_right . Если вы не хотите отображать этот столбец, вы можете установить user_id в качестве индекса для обоих столбцов, чтобы он присоединялся без суффикса:

1
2
3
     df_join_no_duplicates = df1.set_index('user_id').join(df2.set_index('user_id')) 
     
     print(df_join_no_duplicates) 

Таким образом мы избавляемся от user_id и вместо этого устанавливаем его в качестве столбца индекса. Это дает нам более чистый результирующий DataFrame:

1
2
3
4
5
6
7
8
     user_id first_name last_name email image_url 
     id001 Rivi Valti [email protected] http://example.com/img/id001.png 
     id002 Wynnie McMurty [email protected] http://example.com/img/id002.jpg 
     id003 Kristos Ivanets [email protected] http://example.com/img/id003.bmp 
     id004 Madalyn Max [email protected] http://example.com/img/id004.jpg 
     id005 Tobe Riddich [email protected] http://example.com/img/id005.png 
     id006 Regan Huyghe [email protected] NaN 
     id007 Kristin Illis [email protected] NaN 

Объединить фреймы данных с помощью append ()

Как указывает официальная документация Pandas, поскольку concat() и append() возвращают новые копии DataFrames, чрезмерное использование этих методов может повлиять на производительность вашей программы.

Добавление очень полезно, когда вы хотите объединить два DataFrames только по оси строк. Это означает, что вместо сопоставления данных в их столбцах нам нужен новый DataFrame, содержащий все строки 2 DataFrame.

Давайте df2 к df1 и распечатаем результат:

1
2
3
     df_append = df1.append(df2, ignore_index=True) 
     
     print(df_append) 

Использование append() не приведет к сопоставлению DataFrames ни по каким ключам. Он просто добавит другой DataFrame к первому и вернет его копию. Если формы DataFrames не совпадают, Pandas заменит все несовпадающие ячейки на NaN.

Результат для добавления двух DataFrames выглядит следующим образом:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
     user_id first_name last_name email image_url 
     0 id001 Rivi Valti [email protected] NaN 
     1 id002 Wynnie McMurty [email protected] NaN 
     2 id003 Kristos Ivanets [email protected] NaN 
     3 id004 Madalyn Max [email protected] NaN 
     4 id005 Tobe Riddich [email protected] NaN 
     5 id006 Regan Huyghe [email protected] NaN 
     6 id007 Kristin Illis [email protected] NaN 
     7 id001 NaN NaN NaN http://example.com/img/id001.png 
     8 id002 NaN NaN NaN http://example.com/img/id002.jpg 
     9 id003 NaN NaN NaN http://example.com/img/id003.bmp 
     10 id004 NaN NaN NaN http://example.com/img/id004.jpg 
     11 id005 NaN NaN NaN http://example.com/img/id005.png 

Большинство пользователей выбирают concat() append() поскольку он также предоставляет параметр сопоставления ключей и оси.

Объединить фреймы данных с помощью concat ()

Конкатенация немного более гибкая по сравнению с merge() и join() поскольку она позволяет нам комбинировать DataFrames по вертикали (по строкам) или по горизонтали (по столбцам).

Компромисс заключается в том, что любые несоответствующие данные будут отброшены. Вот полная функция с параметрами:

1
2
     pandas.concat(objs, axis=0, join='outer', ignore_index=False, keys=None, 
     levels=None, names=None, verify_integrity=False, sort=False, copy=True) 

Вот наиболее часто используемые параметры функции concat() :

  • objs - это список объектов DataFrame ([df1, df2, ...]) для объединения
  • axis определяет направление конкатенации, 0 для строк и 1 для столбцов
  • join может быть inner (пересечение) или outer (объединение)
  • ignore_index по умолчанию установлен на False что позволяет значениям индекса оставаться такими, какими они были в исходных DataFrames, может привести к дублированию значений индекса. Если установлено значение True , он будет игнорировать исходные значения и повторно назначать значения индекса в последовательном порядке.
  • keys позволяет нам построить иерархический индекс. Подумайте об этом как о другом уровне индекса, который добавлен слева от DataFrame, который помогает нам различать индексы, когда значения не уникальны.

Давайте создадим новый DataFrame с теми же типами столбцов с df2 , но этот включает image_url для id006 и id007 :

1
2
3
4
     df2_addition = pd.DataFrame({'user_id': ['id006', 'id007'], 
                                   'image_url': ['http://example.com/img/id006.png', 
                                   'http://example.com/img/id007.jpg'] 
                                 }) 

Чтобы объединить df2 и df2_addition строкам, мы можем передать их в списке в качестве objs и присвоить полученный DataFrame новой переменной:

1
2
3
     df_row_concat = pd.concat([df2, df2_addition]) 
     
     print(df_row_concat) 

Мы успешно заполнили недостающие значения:

1
2
3
4
5
6
7
8
     user_id image_url 
     0 id001 http://example.com/img/id001.png 
     1 id002 http://example.com/img/id002.jpg 
     2 id003 http://example.com/img/id003.bmp 
     3 id004 http://example.com/img/id004.jpg 
     4 id005 http://example.com/img/id005.png 
     0 id006 http://example.com/img/id006.png 
     1 id007 http://example.com/img/id007.jpg 

Однако обратите внимание на индексы в крайнем левом столбце. Индексы 0 и 1 повторяются. Чтобы получить совершенно новые и уникальные значения индекса, мы передаем True параметру ignore_index

1
     df_row_concat = pd.concat([df2, df2_addition], ignore_index=True) 

Теперь наш df_row_concat имеет уникальные значения индекса:

1
2
3
4
5
6
7
8
     user_id image_url 
     0 id001 http://example.com/img/id001.png 
     1 id002 http://example.com/img/id002.jpg 
     2 id003 http://example.com/img/id003.bmp 
     3 id004 http://example.com/img/id004.jpg 
     4 id005 http://example.com/img/id005.png 
     5 id006 http://example.com/img/id006.png 
     6 id007 http://example.com/img/id007.jpg 

Как мы упоминали ранее, конкатенация может работать как по горизонтали, так и по вертикали. Чтобы объединить два DataFrames вместе по столбцам, нам нужно будет изменить значение axis 0 по 1 :

1
2
3
     df_column_concat = pd.concat([df1, df_row_concat], axis=1) 
     
     print(df_column_concat) 

Вы заметите, что это не работает как слияние, сопоставление двух таблиц по ключу:

1
2
3
4
5
6
7
8
     user_id first_name last_name email user_id image_url 
     0 id001 Rivi Valti [email protected] id001 http://example.com/img/id001.png 
     1 id002 Wynnie McMurty [email protected] id002 http://example.com/img/id002.jpg 
     2 id003 Kristos Ivanets [email protected] id003 http://example.com/img/id003.bmp 
     3 id004 Madalyn Max [email protected] id004 http://example.com/img/id004.jpg 
     4 id005 Tobe Riddich [email protected] id005 http://example.com/img/id005.png 
     5 id006 Regan Huyghe [email protected] id006 http://example.com/img/id006.png 
     6 id007 Kristin Illis [email protected] id007 http://example.com/img/id007.jpg 

Если бы в нашем правом DataFrame даже не было user_id , эта конкатенация все равно вернула бы тот же результат. Функция concat() склеивает два DataFrames вместе, принимая во внимание значения индексов DataFrames и форму таблицы.

Он не выполняет сопоставление ключей, например merge() или join() . Попробуйте разные комбинации конкатенации, изменив join чтобы увидеть различия!

Объединение фреймов данных с помощью comb_first () и update ()

В некоторых случаях вы можете захотеть заполнить недостающие данные в вашем DataFrame, объединив их с другим DataFrame. Таким образом вы сохраните все не пропущенные значения в первом фрейме данных, заменив все NaN доступными не пропущенными значениями из второго фрейма данных (если они есть).

В этом примере мы импортируем NumPy, чтобы использовать значения NaN Если вы установили Pandas с помощью pip , NumPy уже должен быть установлен.

Введите следующий код в оболочку Python или файл сценария:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
     import numpy as np 
     
     df_first = pd.DataFrame({'COL 1': ['X', 'X', np.nan], 
                              'COL 2': ['X', np.nan, 'X'], 
                              'COL 3': [np.nan, 'X', 'X']}, 
                              index=range(0, 3)) 
     
     df_second = pd.DataFrame({'COL 1': [np.nan, 'O', 'O'], 
                              'COL 2': ['O', 'O', 'O']}, 
                              index=range(0, 3)) 
     
     print(df_first) 
     print(df_second) 

df_first df_first имеет 3 столбца и по 1 пропущенному значению в каждом из них:

1
2
3
4
       COL 1 COL 2 COL 3 
     0 X     X     NaN 
     1 X     NaN   X 
     2 NaN   X     X 

В то время как df_second имеет только 2 столбца и одно отсутствующее значение в первом столбце:

1
2
3
4
       COL 1 COL 2 
     0 NaN   O 
     1 O     O 
     2 O     O 

Мы можем использовать df_second для исправления отсутствующих значений в df_first всеми соответствующими значениями:

1
2
3
     df_tictactoe = df_first.combine_first(df_second) 
     
     print(df_tictactoe) 

Как упоминалось ранее, использование combine_first() заменит NaN только в порядке индексации и оставит все не пропущенные значения в первом DataFrame такими, какие они есть:

1
2
3
4
       COL 1 COL 2 COL 3 
     0 X     X     NaN 
     1 X     O     X 
     2 O     X     X 

С другой стороны, если мы хотим перезаписать значения в df_first соответствующими значениями из df_second (независимо от того, являются они NaN или нет), мы бы использовали метод update() .

Давайте сначала добавим в наш код еще один DataFrame:

1
2
3
     df_third = pd.DataFrame({'COL 1': ['O'], 'COL 2': ['O'], 'COL 3': ['O']}) 
     
     print(df_third) 

Форма (1, 3) - 1 строка и три столбца, не считая индекса:

1
2
       COL 1 COL 2 COL 3 
     0 O     O     O 

Теперь давайте обновим df_first значениями из df_third :

1
2
3
     df_first.update(df_third) 
     
     print(df_first) 

Имейте в виду, что в отличие от combine_first() , update() не возвращает новый DataFrame. Он изменяет df_first на месте, изменяя соответствующие значения:

1
2
3
4
       COL 1 COL 2 COL 3 
     0 O     O     O 
     1 X     NaN   X 
     2 NaN   X     X 

Для параметра overwrite update() по умолчанию установлено значение True Вот почему он изменяет все соответствующие значения, а не только значения NaN Мы можем изменить его на False чтобы заменить только значения NaN

1
2
3
     df_tictactoe.update(df_first, overwrite=False) 
     
     print(df_tictactoe) 

Вот окончательное состояние нашего df_tictactoe df_tictactoe:

1
2
3
4
       COL 1 COL 2 COL 3 
     0 X     X     O 
     1 X     O     X 
     2 O     X     X 

Мы не только успешно обновили значения, но и выиграли игру «Крестики-нолики»!

Заключение

Pandas предоставляет мощные инструменты для объединения DataFrames. Но бывает сложно решить, когда что использовать. Хотя в большинстве случаев функции merge() достаточно, в некоторых случаях вы можете использовать concat() для слияния по строкам, или использовать join() с суффиксами, или избавиться от отсутствующих значений с помощью combine_first() и update() . Вы даже можете добавлять строки данных с помощью append() .

Используйте ту функцию, которая вам удобна и лучше всего подходит для вашей задачи. Как эти функции помогут вам управлять данными в Pandas?

comments powered by Disqus