Рубрики
Без рубрики

Как объединить фреймы данных в Pandas – merge(), join(), append(), concat() и update()

В этом уроке мы будем объединять фреймы данных в Панд с помощью функции слияния. Мы также объединим данные с примерами join, append, concat, combine_first и update.

Автор оригинала: Ruslan Hasanov.

Как объединить фреймы данных в Pandas – merge(), join(), append(), concat() и update()

Вступление

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

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

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

Для этого урока мы используем версию Pandas 1.1.4 и NumPy версия 1.19.4 .

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

  • Слияние Фреймов Данных С Помощью merge()
  • Слияние Фреймов Данных С Помощью join()
  • Слияние Фреймов Данных С Помощью append()
  • Слияние Фреймов Данных С Помощью concat()
  • Слияние Фреймов данных С помощью combine_first() и update()

Слияние Фреймов Данных С Помощью merge()

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

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

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-адресами изображений и идентификаторами пользователей:

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']
                    })

Вот как выглядят наши фреймы данных:

# 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

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

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)

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

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

df3_merged = pd.merge(df1, df2)

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

Давайте напечатаем переменную df3_merged , чтобы увидеть ее содержимое:

  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

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

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

Это останется верным даже если поменять местами левый и правый ряды:

df3_merged = pd.merge(df2, df1)

Результаты все еще есть:

  user_id                         image_url first_name last_name                  email
0   id001  http://example.com/img/id001.png       Rivi     Valti    [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]

Пользователи с идентификаторами 'id 006' и 'id 007' не являются частью объединенных фреймов данных, так как они не пересекаются в обеих таблицах.

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

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

df_left_merge = pd.merge(df1, df2, how='left')

print(df_left_merge)

С помощью left join мы включили все элементы левого фрейма данных ( df1 ) и каждый элемент правого фрейма данных ( df2 ). Запуск приведенного выше кода будет отображать следующее:

  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

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

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

df_right_merge = pd.merge(df1, df2, how='right')

print(df_right_merge)

Как вы, возможно, и ожидали, right join вернет все значения из левого фрейма данных, которые соответствуют правому фрейму данных:

  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 join в данном случае аналогично inner join.

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

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)

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

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

Переменная df_left выглядит следующим образом:

  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 имеет эти данные:

  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 существуют только в правом фрейме данных (в данном случае это df1 ). Если бы мы попытались сравнить левое и внешнее соединения, не меняя местами, то получили бы одинаковые результаты для обоих.

Слияние Фреймов Данных С Помощью join()

В отличие от merge () , который является методом экземпляра Pandas, join() является методом самого фрейма данных. Это означает, что мы можем использовать его как статический метод в DataFrame: DataFrame.join(other,,,,,) .

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

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

Давайте попробуем присоединиться df2 к df1 :

df_join = df1.join(df2, rsuffix='_right')

print(df_join)

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

Приведенный выше код выводит следующее:

  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 в качестве индекса для обоих столбцов, чтобы они соединялись без суффикса:

df_join_no_duplicates = df1.set_index('user_id').join(df2.set_index('user_id'))

print(df_join_no_duplicates)

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

        first_name last_name                  email                         image_url
user_id                                                                              
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() возвращают новые копии фреймов данных, чрезмерное использование этих методов может повлиять на производительность вашей программы.

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

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

df_append = df1.append(df2, ignore_index=True)

print(df_append)

Использование append() не будет соответствовать кадрам данных ни на одном из ключей. Он просто добавит другой фрейм данных к первому и вернет его копию. Если формы Фреймов данных не совпадают, Панды заменят любые несопоставимые клетки Человеком.

Вывод для добавления двух фреймов данных выглядит следующим образом:

   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 () , поскольку она позволяет нам объединять фреймы данных либо вертикально (по строкам), либо горизонтально (по столбцам).

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

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,…]), которые должны быть объединены
  • ось определяет направление конкатенации, 0 для гребных и 1 для колонны-мудро
  • join может быть либо inner (пересечение), либо outer (объединение)
  • ignore_index по умолчанию установлено значение False , которое позволяет значениям индекса оставаться такими, какими они были в исходных кадрах данных, может привести к дублированию значений индекса. Если установлено значение True , он будет игнорировать исходные значения и повторно назначать значения индекса в последовательном порядке
  • keys позволяет нам построить иерархический индекс. Думайте об этом как о другом уровне индекса, добавляемом во внешнюю левую часть фрейма данных, который помогает нам различать индексы, когда значения не уникальны

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

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 по строкам, мы можем передать их в список в качестве параметра obj и назначить результирующий фрейм данных новой переменной:

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

print(df_row_concat)

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

  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 :

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

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

  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

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

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

print(df_column_concat)

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

  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

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

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

Слияние Фреймов данных С помощью combine_first() и update()

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

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

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

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 имеет 3 столбца и 1 пропущенное значение в каждом из них:

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

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

  COL 1 COL 2
0   NaN     O
1     O     O
2     O     O

Мы можем использовать df_second to patch missing values in df_first со всеми соответствующими значениями:

df_tictactoe = df_first.combine_first(df_second)

print(df_tictactoe)

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

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

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

Давайте сначала добавим еще один фрейм данных в наш код:

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

print(df_third)

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

  COL 1 COL 2 COL 3
0     O     O     O

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

df_first.update(df_third)

print(df_first)

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

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

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

df_tictactoe.update(df_first, overwrite=False)

print(df_tictactoe)

Вот конечное состояние нашего df_tic tac toe фрейма данных:

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

Мы не только успешно обновили ценности, но и выиграли игру в крестики-нолики!

Вывод

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

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