Объединение, соединение и конкатенация
pandas предоставляет различные средства для лёгкого объединения объектов Series, DataFrame и Panel с различными видами логики множеств для индексов и функциональностью реляционной алгебры в случае операций типа объединения/слияния.
Конкатенация объектов
Функция concat() (в основном пространстве имён pandas) выполняет всю тяжёлую работу по выполнению операций конкатенации вдоль оси, выполняя при этом необязательную логику множеств (объединение или пересечение) индексов (если таковые имеются) на других осях. Обратите внимание, что я говорю «если таковые имеются», поскольку для Series существует только одна возможная ось конкатенации.
Прежде чем углубиться во все подробности concat и её возможностей, вот простой пример:
In [1]: df1 = pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'],
...: 'B': ['B0', 'B1', 'B2', 'B3'],
...: 'C': ['C0', 'C1', 'C2', 'C3'],
...: 'D': ['D0', 'D1', 'D2', 'D3']},
...: index=[0, 1, 2, 3])
...:
In [2]: df2 = pd.DataFrame({'A': ['A4', 'A5', 'A6', 'A7'],
...: 'B': ['B4', 'B5', 'B6', 'B7'],
...: 'C': ['C4', 'C5', 'C6', 'C7'],
...: 'D': ['D4', 'D5', 'D6', 'D7']},
...: index=[4, 5, 6, 7])
...:
In [3]: df3 = pd.DataFrame({'A': ['A8', 'A9', 'A10', 'A11'],
...: 'B': ['B8', 'B9', 'B10', 'B11'],
...: 'C': ['C8', 'C9', 'C10', 'C11'],
...: 'D': ['D8', 'D9', 'D10', 'D11']},
...: index=[8, 9, 10, 11])
...:
In [4]: frames = [df1, df2, df3]
In [5]: result = pd.concat(frames)
Как и её функция-аналог для ndarrays, numpy.concatenate, pandas.concat принимает список или словарь однотипных объектов и конкатенирует их с некоторым настраиваемым обращением к «чему делать с другими осями»:
pd.concat(objs, axis=0, join='outer', join_axes=None, ignore_index=False,
keys=None, levels=None, names=None, verify_integrity=False,
copy=True)
-
objs: последовательность или отображение объектов Series, DataFrame или Panel. Если передан словарь, отсортированные ключи будут использованы в качестве аргументаkeys, если он не передан, в противном случае будут выбраны значения (см. ниже). Любые объекты None будут молча удалены, если они не все None, в противном случае будет поднято исключение ValueError. -
axis: {0, 1, …}, по умолчанию 0. Ось для конкатенации. -
join: {‘inner’, ‘outer’}, по умолчанию ‘outer’. Способ обработки индексов на других осях. Outer для объединения и inner для пересечения. -
ignore_index: булево значение, по умолчанию False. Если True, не использовать значения индекса на оси конкатенации. Результирующая ось будет помечена 0, …, n - 1. Это полезно, если вы конкатенируете объекты, где ось конкатенации не содержит осмысленной информации об индексации. Обратите внимание, что значения индекса на других осях по-прежнему учитываются при объединении. -
join_axes: список объектов Index. Конкретные индексы для использования на других осях n - 1 вместо выполнения логики множеств inner/outer. -
keys: последовательность, по умолчанию None. Построение иерархического индекса с использованием переданных ключей как внешнего уровня. Если передано несколько уровней, должно содержать кортежи. -
levels: список последовательностей, по умолчанию None. Конкретные уровни (уникальные значения) для использования при построении MultiIndex. В противном случае они будут определены из ключей. -
names: список, по умолчанию None. Названия уровней в результирующем иерархическом индексе. -
verify_integrity: булево значение, по умолчанию False. Проверка наличия дубликатов в новой оси конкатенации. Это может быть очень дорогостоящим по сравнению с фактической конкатенацией данных. -
copy: булево значение, по умолчанию True. Если False, не копировать данные необоснованно.
Без некоторого контекста многие из этих аргументов не имеют смысла. Давайте пересмотрим предыдущий пример. Предположим, мы хотели связать определённые ключи с каждым из кусков разделённого DataFrame. Мы можем сделать это, используя аргумент keys.
In [6]: result = pd.concat(frames, keys=['x', 'y', 'z'])
Как вы можете видеть (если вы прочитали остальную часть документации), индекс результирующего объекта имеет иерархический индекс. Это означает, что теперь мы можем выбрать каждый фрагмент по ключу:
In [7]: result.loc['y']
Out[7]:
A B C D
4 A4 B4 C4 D4
5 A5 B5 C5 D5
6 A6 B6 C6 D6
7 A7 B7 C7 D7
Несложно понять, как это может быть очень полезно. Более подробная информация об этой функциональности ниже.
Примечание
Следует отметить, что concat() (и, следовательно, append()) создаёт полную копию данных, и постоянное повторное использование этой функции может привести к существенной потере производительности. Если вам нужно использовать операцию над несколькими наборами данных, используйте список понимания.
frames = [ process_your_file(f) for f in files ] result = pd.concat(frames)
Логика множеств на других осях
При склеивании нескольких DataFrame у вас есть выбор, как обработать другие оси (кроме той, которая конкатенируется). Это можно сделать тремя способами:
- Взять объединение всех их,
join='outer'. Это опция по умолчанию, поскольку она приводит к нулевым потерям информации. - Взять пересечение,
join='inner'. - Использовать конкретный индекс, переданный в аргумент
join_axes.
Вот пример каждого из этих методов. Сначала поведение по умолчанию join='outer':
In [8]: df4 = pd.DataFrame({'B': ['B2', 'B3', 'B6', 'B7'],
...: 'D': ['D2', 'D3', 'D6', 'D7'],
...: 'F': ['F2', 'F3', 'F6', 'F7']},
...: index=[2, 3, 6, 7])
...:
In [9]: result = pd.concat([df1, df4], axis=1, sort=False)
Предупреждение
Изменено в версии 0.23.0.
По умолчанию с join='outer' ось (столбцы в этом случае) сортируется. В будущей версии pandas по умолчанию будет сортировка отсутствовать. Мы указали sort=False для включения нового поведения сейчас.
Вот то же самое с join='inner':
In [10]: result = pd.concat([df1, df4], axis=1, join='inner')
Наконец, предположим, что нам просто нужно было повторно использовать точный индекс из исходного DataFrame:
In [11]: result = pd.concat([df1, df4], axis=1, join_axes=[df1.index])
Конкатенация с использованием append
Полезным сокращением для concat() являются методы экземпляров append() для Series и DataFrame. Эти методы фактически предшествовали concat . Они конкатенируют вдоль axis=0 , а именно индекса:
In [12]: result = df1.append(df2)
В случае DataFrame, индексы должны быть непересекающимися, но столбцы не обязательно:
In [13]: result = df1.append(df4, sort=False)
append может принимать несколько объектов для конкатенации:
In [14]: result = df1.append([df2, df3])
Примечание
В отличие от метода append(), который добавляет в исходный список и возвращает None, append() здесь не изменяет df1 и возвращает свою копию с добавленным df2.
Игнорирование индексов на оси конкатенации
Для объектов DataFrame, у которых нет осмысленного индекса, вы можете добавить их, проигнорировав тот факт, что они могут иметь перекрывающиеся индексы. Для этого используйте аргумент ignore_index:
In [15]: result = pd.concat([df1, df4], ignore_index=True, sort=False)
Это также допустимый аргумент для DataFrame.append():
In [16]: result = df1.append(df4, ignore_index=True, sort=False)
Конкатенация со смешанными ndims
Можно конкатенировать смесь объектов Series и DataFrame. Series будет преобразовано в DataFrame со значением столбца в качестве имени Series.
In [17]: s1 = pd.Series(['X0', 'X1', 'X2', 'X3'], name='X') In [18]: result = pd.concat([df1, s1], axis=1)
Примечание
Поскольку мы конкатенируем Series с DataFrame, мы могли бы добиться того же результата с DataFrame.assign(). Для конкатенации произвольного количества объектов pandas (DataFrame или Series), используйте concat.
Если переданы неименованные Series, они будут пронумерованы последовательно.
In [19]: s2 = pd.Series(['_0', '_1', '_2', '_3']) In [20]: result = pd.concat([df1, s2, s2, s2], axis=1)
Передача ignore_index=True удалит все ссылки на имена.
In [21]: result = pd.concat([df1, s1], axis=1, ignore_index=True)
Более детальная конкатенация с групповыми ключами
Довольно часто используемым аргументом keys является перезапись имён столбцов при создании нового DataFrame на основе существующих Series. Обратите внимание, как поведение по умолчанию заключается в том, что результирующий DataFrame наследует имя родительского Series, если таковые имелись.
In [22]: s3 = pd.Series([0, 1, 2, 3], name='foo') In [23]: s4 = pd.Series([0, 1, 2, 3]) In [24]: s5 = pd.Series([0, 1, 4, 5]) In [25]: pd.concat([s3, s4, s5], axis=1) Out[25]: foo 0 1 0 0 0 0 1 1 1 1 2 2 2 4 3 3 3 5
С помощью аргумента keys мы можем перезаписать существующие имена столбцов.
In [26]: pd.concat([s3, s4, s5], axis=1, keys=['red', 'blue', 'yellow']) Out[26]: red blue yellow 0 0 0 0 1 1 1 1 2 2 2 4 3 3 3 5
Давайте рассмотрим вариацию первого примера:
In [27]: result = pd.concat(frames, keys=['x', 'y', 'z'])
Вы также можете передать словарь в concat в котором случае ключи словаря будут использованы для аргумента keys (если не указаны другие ключи):
In [28]: pieces = {'x': df1, 'y': df2, 'z': df3}
In [29]: result = pd.concat(pieces)
In [30]: result = pd.concat(pieces, keys=['z', 'y'])
Созданный MultiIndex имеет уровни, построенные из переданных ключей и индекса фрагментов DataFrame:
In [31]: result.index.levels Out[31]: FrozenList([['z', 'y'], [4, 5, 6, 7, 8, 9, 10, 11]])
Если вы хотите указать другие уровни (что иногда бывает необходимо), вы можете сделать это, используя аргумент levels:
In [32]: result = pd.concat(pieces, keys=['x', 'y', 'z'], ....: levels=[['z', 'y', 'x', 'w']], ....: names=['group_key']) ....:
In [33]: result.index.levels Out[33]: FrozenList([['z', 'y', 'x', 'w'], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]])
Это довольно экзотично, но на самом деле это необходимо для реализации таких вещей, как GroupBy, где порядок категориальной переменной имеет значение.
Добавление строк в DataFrame
Хотя это не особенно эффективно (поскольку должен быть создан новый объект), вы можете добавить одну строку к DataFrame , передав Series или словарь в append, который возвращает новый DataFrame как и выше.
In [34]: s2 = pd.Series(['X0', 'X1', 'X2', 'X3'], index=['A', 'B', 'C', 'D']) In [35]: result = df1.append(s2, ignore_index=True)
Вы должны использовать ignore_index с этим методом, чтобы указать DataFrame на отбрасывание его индекса. Если вы хотите сохранить индекс, вы должны создать DataFrame с соответствующим индексом и добавить или конкатенировать эти объекты.
Вы также можете передать список словарей или Series:
In [36]: dicts = [{'A': 1, 'B': 2, 'C': 3, 'X': 4},
....: {'A': 5, 'B': 6, 'C': 7, 'Y': 8}]
....:
In [37]: result = df1.append(dicts, ignore_index=True, sort=False)
Объединение/слияние DataFrame или именованных Series в стиле баз данных
pandas имеет полнофункциональные и высокопроизводительные операции объединения в памяти, по стилю очень похожие на реляционные базы данных, такие как SQL. Эти методы работают значительно лучше (в некоторых случаях на порядок лучше), чем другие реализации с открытым исходным кодом (например, base::merge.data.frame в R). Причина этого заключается в тщательном проектировании алгоритмов и внутренней структуре данных в DataFrame.
См. кулинарную книгу для некоторых продвинутых стратегий.
Пользователи, знакомые с SQL, но новые для pandas, могут быть заинтересованы в сравнении с SQL.
pandas предоставляет одну функцию, merge(), в качестве точки входа для всех стандартных операций объединения баз данных между DataFrame или именованными Series объектами:
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: Объект DataFrame или именованный Series. -
right: Другой объект DataFrame или именованный Series. -
on: Имена столбцов или уровней индекса для объединения. Должны присутствовать как в левом, так и в правом DataFrame и/или Series. Если не переданы иleft_indexиright_indexявляютсяFalse, то пересечение столбцов в DataFrame и/или Series будет определено как ключи объединения. -
left_on: Столбцы или уровни индексов из левого DataFrame или Series, используемые в качестве ключей. Могут быть именами столбцов, именами уровней индекса или массивами с длиной, равной длине DataFrame или Series. -
right_on: Столбцы или уровни индексов из правого DataFrame или Series, используемые в качестве ключей. Могут быть именами столбцов, именами уровней индекса или массивами с длиной, равной длине DataFrame или Series. -
left_index: ЕслиTrue, используйте индекс (метки строк) из левого DataFrame или Series в качестве его ключей объединения. В случае DataFrame или Series с MultiIndex (иерархическим) количество уровней должно совпадать с количеством ключей объединения из правого DataFrame или Series. -
right_index: Аналогичное использование, как иleft_indexдля правого DataFrame или Series -
how: Один из'left','right','outer','inner'. По умолчаниюinner. Более подробное описание каждого метода см. ниже. -
sort: Сортировать результирующий DataFrame по ключам объединения в лексикографическом порядке. По умолчаниюTrue, установка наFalseзначительно повышает производительность во многих случаях. -
suffixes: Кортеж строчных суффиксов, применяемых к перекрывающимся столбцам. По умолчанию('_x', '_y'). -
copy: Всегда копировать данные (по умолчаниюTrue) из переданных DataFrame или именованных Series объектов, даже когда повторная индексация не требуется. В многих случаях избежать копирования невозможно, но этот параметр все же предоставляется. Случаи, когда копирование можно избежать, несколько патологические, но этот параметр все же предоставляется. -
indicator: Добавить столбец в выходной DataFrame под названием_mergeс информацией о источнике каждой строки._mergeимеет тип Categorical и принимает значениеleft_onlyдля наблюдений, чей ключ объединения встречается только в'left'DataFrame или Series,right_onlyдля наблюдений, чей ключ объединения встречается только в'right'DataFrame или Series, иbothесли ключ объединения наблюдения найден в обоих. -
validate: строка, по умолчанию None. Если указано, проверяет, соответствует ли объединение заданному типу.- “one_to_one” или “1:1”: проверяет, являются ли ключи объединения уникальными в обоих наборах данных.
- “one_to_many” или “1:m”: проверяет, являются ли ключи объединения уникальными в левом наборе данных.
- “many_to_one” или “m:1”: проверяет, являются ли ключи объединения уникальными в правом наборе данных.
- “many_to_many” или “m:m”: разрешено, но не приводит к проверкам.
Добавлено в версии 0.21.0.
Примечание
Поддержка указания уровней индекса в качестве параметров on, left_on, и right_on была добавлена в версии 0.23.0. Поддержка объединения именованных Series объектов была добавлена в версии 0.24.0.
Тип возвращаемого значения будет таким же, как у left. Если left является DataFrame или именованным Series и right является подклассом DataFrame, тип возвращаемого значения все равно будет DataFrame.
merge — функция в пространстве имен pandas, и она также доступна как метод экземпляра DataFrame merge(), где вызываемый DataFrame неявно рассматривается как левый объект в операции объединения.
Сопутствующий метод join() использует merge в качестве внутреннего метода для объединения по индексу (по умолчанию) и столбцу(ам) по индексу. Если вы объединяете только по индексу, вы можете использовать DataFrame.join, чтобы сократить написание кода.
Краткий справочник по методам объединения (реляционная алгебра)
Опытные пользователи реляционных баз данных, таких как SQL, будут знакомы с терминологией, используемой для описания операций объединения между двумя структурами, похожими на таблицы SQL (объекты DataFrame). Необходимо рассмотреть несколько случаев, которые очень важно понять:
-
Одно к одному объединение: например, при объединении двух
DataFrameобъектов по их индексам (которые должны содержать уникальные значения). -
Один ко многим объединение: например, при объединении индекса (уникального) с одним или несколькими столбцами в другом
DataFrame. - Многий ко многим объединение: объединение столбцов по столбцам.
Примечание
При объединении столбцов по столбцам (потенциально объединение многие ко многим), любые индексы на переданных DataFrame объектах будут удалены.
Стоит потратить некоторое время, чтобы понять результат в случае объединения многие ко многим. В SQL/стандартной реляционной алгебре, если комбинация ключей встречается более одного раза в обеих таблицах, результирующая таблица будет содержать декартово произведение соответствующих данных. Вот очень простой пример с одной уникальной комбинацией ключей:
In [38]: left = pd.DataFrame({'key': ['K0', 'K1', 'K2', 'K3'],
....: 'A': ['A0', 'A1', 'A2', 'A3'],
....: 'B': ['B0', 'B1', 'B2', 'B3']})
....:
In [39]: right = pd.DataFrame({'key': ['K0', 'K1', 'K2', 'K3'],
....: 'C': ['C0', 'C1', 'C2', 'C3'],
....: 'D': ['D0', 'D1', 'D2', 'D3']})
....:
In [40]: result = pd.merge(left, right, on='key')
Вот более сложный пример с несколькими ключами объединения. Присутствуют только ключи, встречающиеся в left и right (пересечение), поскольку how='inner' по умолчанию.
In [41]: left = pd.DataFrame({'key1': ['K0', 'K0', 'K1', 'K2'],
....: 'key2': ['K0', 'K1', 'K0', 'K1'],
....: 'A': ['A0', 'A1', 'A2', 'A3'],
....: 'B': ['B0', 'B1', 'B2', 'B3']})
....:
In [42]: right = pd.DataFrame({'key1': ['K0', 'K1', 'K1', 'K2'],
....: 'key2': ['K0', 'K0', 'K0', 'K0'],
....: 'C': ['C0', 'C1', 'C2', 'C3'],
....: 'D': ['D0', 'D1', 'D2', 'D3']})
....:
In [43]: result = pd.merge(left, right, on=['key1', 'key2'])
Аргумент how для merge определяет, как определить, какие ключи включить в результирующую таблицу. Если комбинация ключей не встречается ни в левой, ни в правой таблице, значения в объединённой таблице будут NA. Вот сводка вариантов how и их эквивалентных названий в SQL:
| Метод объединения | Имя объединения SQL | Описание |
|---|---|---|
left | LEFT OUTER JOIN | Используются ключи только из левой таблицы |
right | RIGHT OUTER JOIN | Используются ключи только из правой таблицы |
outer | FULL OUTER JOIN | Используется объединение ключей из обеих таблиц |
inner | INNER JOIN | Используется пересечение ключей из обеих таблиц |
In [44]: result = pd.merge(left, right, how='left', on=['key1', 'key2'])
In [45]: result = pd.merge(left, right, how='right', on=['key1', 'key2'])
In [46]: result = pd.merge(left, right, how='outer', on=['key1', 'key2'])
In [47]: result = pd.merge(left, right, how='inner', on=['key1', 'key2'])
Вот еще один пример с дублирующимися ключами объединения в DataFrame:
In [48]: left = pd.DataFrame({'A': [1, 2], 'B': [2, 2]})
In [49]: right = pd.DataFrame({'A': [4, 5, 6], 'B': [2, 2, 2]})
In [50]: result = pd.merge(left, right, on='B', how='outer')
Предупреждение
Объединение/слияние по дублирующимся ключам может привести к возвращаемой таблице, являющейся произведением размерностей строк, что может привести к переполнению памяти. Пользователь отвечает за управление дублирующими значениями в ключах перед объединением больших DataFrame.
Проверка на дубликаты ключей
Добавлено в версии 0.21.0.
Пользователи могут использовать аргумент validate для автоматической проверки наличия неожиданных дубликатов в ключах объединения. Проверка уникальности ключей выполняется перед операциями объединения, и, таким образом, должна защищать от переполнения памяти. Проверка уникальности ключей также является хорошим способом обеспечения того, что структуры данных пользователя соответствуют ожиданиям.
В следующем примере есть дублирующие значения B в правом DataFrame. Поскольку это не объединение один-к-одному — как указано в аргументе validate — будет выброшено исключение.
In [51]: left = pd.DataFrame({'A' : [1,2], 'B' : [1, 2]})
In [52]: right = pd.DataFrame({'A' : [4,5,6], 'B': [2, 2, 2]})
In [53]: result = pd.merge(left, right, on='B', how='outer', validate="one_to_one") ... MergeError: Merge keys are not unique in right dataset; not a one-to-one merge
Если пользователь знает о дубликатах в правом DataFrame, но хочет убедиться, что в левом DataFrame нет дубликатов, можно использовать аргумент validate='one_to_many' вместо него, что не приведет к исключению.
In [53]: pd.merge(left, right, on='B', how='outer', validate="one_to_many") Out[53]: A_x B A_y 0 1 1 NaN 1 2 2 4.0 2 2 2 5.0 3 2 2 6.0
Индикатор объединения
merge() принимает аргумент indicator. Если True, к выходному объекту будет добавлен столбец типа Categorical под названием _merge, принимающий значения:
| Происхождение наблюдения |
значение _merge |
|---|---|
Ключ объединения только в 'left' таблице | left_only |
Ключ объединения только в 'right' таблице | right_only |
| Ключ объединения в обеих таблицах | both |
In [54]: df1 = pd.DataFrame({'col1': [0, 1], 'col_left': ['a', 'b']})
In [55]: df2 = pd.DataFrame({'col1': [1, 2, 2], 'col_right': [2, 2, 2]})
In [56]: pd.merge(df1, df2, on='col1', how='outer', indicator=True)
Out[56]:
col1 col_left col_right _merge
0 0 a NaN left_only
1 1 b 2.0 both
2 2 NaN 2.0 right_only
3 2 NaN 2.0 right_only
Аргумент indicator также принимает строковые аргументы, в этом случае функция индикатора будет использовать значение переданной строки в качестве имени для индикаторного столбца.
In [57]: pd.merge(df1, df2, on='col1', how='outer', indicator='indicator_column') Out[57]: col1 col_left col_right indicator_column 0 0 a NaN left_only 1 1 b 2.0 both 2 2 NaN 2.0 right_only 3 2 NaN 2.0 right_only
Объединение типов данных
Новое в версии 0.19.0.
Объединение сохранит тип данных ключей объединения.
In [58]: left = pd.DataFrame({'key': [1], 'v1': [10]})
In [59]: left
Out[59]:
key v1
0 1 10
In [60]: right = pd.DataFrame({'key': [1, 2], 'v1': [20, 30]})
In [61]: right
Out[61]:
key v1
0 1 20
1 2 30
Мы можем сохранить ключи объединения:
In [62]: pd.merge(left, right, how='outer') Out[62]: key v1 0 1 10 1 1 20 2 2 30 In [63]: pd.merge(left, right, how='outer').dtypes
© 2008–2012, AQR Capital Management, LLC, Lambda Foundry, Inc. and PyData Development Team
Licensed under the 3-clause BSD License.
https://pandas.pydata.org/pandas-docs/version/0.24.2/user_guide/merging.html