Слияние, объединение и конкатенация
pandas предоставляет различные средства для лёгкого объединения Series или DataFrame с различными типами логических операций над индексами и функциональностью реляционной алгебры в случае операций типа объединения/слияния.
Конкатенация объектов
Функция 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', ignore_index=False, keys=None,
levels=None, names=None, verify_integrity=False, copy=True)
-
objs: последовательность или отображение объектов Series или DataFrame. Если передан словарь, отсортированные ключи будут использоваться как аргументkeys, если он не передан, в противном случае будут выбраны значения (см. ниже). Любые объекты None будут отброшены молча, если они все None, в этом случае будет поднято исключение ValueError. -
axis: {0, 1, …}, по умолчанию 0. Ось для конкатенации. -
join: {‘inner’, ‘outer’}, по умолчанию ‘outer’. Способ обработки индексов на других осях. Outer для объединения и inner для пересечения. -
ignore_index: boolean, по умолчанию False. Если True, не использовать значения индексов на оси конкатенации. Результирующая ось будет иметь метки 0, …, n - 1. Это полезно, если вы конкатенируете объекты, где ось конкатенации не имеет осмысленной информации об индексе. Обратите внимание, что значения индексов на других осях всё ещё учитываются при объединении. -
keys: последовательность, по умолчанию None. Построить иерархический индекс, используя переданные ключи как внешний уровень. Если передано несколько уровней, должен содержать кортежи. -
levels: список последовательностей, по умолчанию None. Конкретные уровни (уникальные значения) для использования при построении MultiIndex. В противном случае они будут вычислены по ключам. -
names: список, по умолчанию None. Имена уровней в результирующем иерархическом индексе. -
verify_integrity: boolean, по умолчанию False. Проверить, содержит ли новая ось конкатенации дубликаты. Это может быть очень дорого относительно фактической конкатенации данных. -
copy: boolean, по умолчанию 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)
Логика множеств на других осях
При склеивании нескольких DataFrames у вас есть выбор, как обрабатывать другие оси (кроме той, которая конкатенируется). Это можно сделать двумя способами:
- Взять объединение всех из них,
join='outer'. Это по умолчанию, так как это приводит к нулевой потере информации. - Взять пересечение,
join='inner'.
Вот пример каждого из этих методов. Сначала поведение по умолчанию 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).reindex(df1.index)
Аналогично, мы могли бы индексировать перед конкатенацией:
In [12]: pd.concat([df1, df4.reindex(df1.index)], axis=1)
Out[12]:
A B C D B D F
0 A0 B0 C0 D0 NaN NaN NaN
1 A1 B1 C1 D1 NaN NaN NaN
2 A2 B2 C2 D2 B2 D2 F2
3 A3 B3 C3 D3 B3 D3 F3
Конкатенация с использованием append
Полезным сокращением для concat() являются методы экземпляра append() для Series и DataFrame. Эти методы фактически появились раньше concat. Они конкатенируют вдоль axis=0, а именно, индекса:
In [13]: result = df1.append(df2)
В случае DataFrame, индексы должны быть непересекающимися, но колонки не обязательно:
In [14]: result = df1.append(df4, sort=False)
append может принимать несколько объектов для конкатенации:
In [15]: result = df1.append([df2, df3])
Примечание
В отличие от метода append() , который добавляет в исходный список и возвращает None, append() здесь не изменяет df1 и возвращает его копию с добавленным df2.
Игнорирование индексов на оси конкатенации
Для объектов DataFrame , у которых нет осмысленного индекса, вы можете добавить их и проигнорировать тот факт, что у них могут быть перекрывающиеся индексы. Для этого используйте аргумент ignore_index:
In [16]: result = pd.concat([df1, df4], ignore_index=True, sort=False)
Это также допустимый аргумент для DataFrame.append():
In [17]: result = df1.append(df4, ignore_index=True, sort=False)
Конкатенация с смешанными ndims
Можно конкатенировать смесь Series и DataFrame объектов. Series будет преобразовано в DataFrame с именем колонки в качестве имени Series.
In [18]: s1 = pd.Series(['X0', 'X1', 'X2', 'X3'], name='X') In [19]: result = pd.concat([df1, s1], axis=1)
Примечание
Поскольку мы конкатенируем Series с DataFrame, мы могли бы достичь того же результата с помощью DataFrame.assign(). Для конкатенации произвольного числа объектов pandas (DataFrame или Series) используйте concat.
Если переданы безымянные Series , они будут пронумерованы последовательно.
In [20]: s2 = pd.Series(['_0', '_1', '_2', '_3']) In [21]: result = pd.concat([df1, s2, s2, s2], axis=1)
Передача ignore_index=True отбросит все ссылки на имена.
In [22]: result = pd.concat([df1, s1], axis=1, ignore_index=True)
Более подробная конкатенация с ключами групп
Довольно распространённое использование аргумента keys — переопределение имён столбцов при создании нового DataFrame на основе существующих Series. Обратите внимание, как поведение по умолчанию состоит в том, чтобы позволить результирующему DataFrame унаследовать имя родительского Series , если они существовали.
In [23]: s3 = pd.Series([0, 1, 2, 3], name='foo') In [24]: s4 = pd.Series([0, 1, 2, 3]) In [25]: s5 = pd.Series([0, 1, 4, 5]) In [26]: pd.concat([s3, s4, s5], axis=1) Out[26]: foo 0 1 0 0 0 0 1 1 1 1 2 2 2 4 3 3 3 5
С помощью аргумента keys мы можем переопределить существующие имена столбцов.
In [27]: pd.concat([s3, s4, s5], axis=1, keys=['red', 'blue', 'yellow']) Out[27]: red blue yellow 0 0 0 0 1 1 1 1 2 2 2 4 3 3 3 5
Давайте рассмотрим изменение первого примера:
In [28]: result = pd.concat(frames, keys=['x', 'y', 'z'])
Вы также можете передать словарь в concat , в этом случае ключи словаря будут использоваться для аргумента keys (если не указаны другие ключи):
In [29]: pieces = {'x': df1, 'y': df2, 'z': df3}
In [30]: result = pd.concat(pieces)
In [31]: result = pd.concat(pieces, keys=['z', 'y'])
Созданный MultiIndex имеет уровни, построенные из переданных ключей и индекса DataFrame фрагментов:
In [32]: result.index.levels Out[32]: FrozenList([['z', 'y'], [4, 5, 6, 7, 8, 9, 10, 11]])
Если вы хотите указать другие уровни (что иногда бывает необходимо), вы можете сделать это с помощью аргумента levels:
In [33]: result = pd.concat(pieces, keys=['x', 'y', 'z'], ....: levels=[['z', 'y', 'x', 'w']], ....: names=['group_key']) ....:
In [34]: result.index.levels Out[34]: FrozenList([['z', 'y', 'x', 'w'], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]])
Это довольно сложно, но это необходимо для реализации таких вещей, как GroupBy, где порядок категориальной переменной имеет значение.
Добавление строк в DataFrame
Хотя это не особенно эффективно (поскольку должен быть создан новый объект), вы можете добавить одну строку в DataFrame , передав Series или словарь в append, который возвращает новый DataFrame , как и выше.
In [35]: s2 = pd.Series(['X0', 'X1', 'X2', 'X3'], index=['A', 'B', 'C', 'D']) In [36]: result = df1.append(s2, ignore_index=True)
Вы должны использовать ignore_index с этим методом, чтобы указать DataFrame отбросить свой индекс. Если вы хотите сохранить индекс, вы должны создать DataFrame с соответствующим индексом и добавить или конкатенировать эти объекты.
Вы также можете передать список словарей или Series:
In [37]: dicts = [{'A': 1, 'B': 2, 'C': 3, 'X': 4},
....: {'A': 5, 'B': 6, 'C': 7, 'Y': 8}]
....:
In [38]: 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— категориального типа и принимает значение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 [39]: left = pd.DataFrame({'key': ['K0', 'K1', 'K2', 'K3'],
....: 'A': ['A0', 'A1', 'A2', 'A3'],
....: 'B': ['B0', 'B1', 'B2', 'B3']})
....:
In [40]: right = pd.DataFrame({'key': ['K0', 'K1', 'K2', 'K3'],
....: 'C': ['C0', 'C1', 'C2', 'C3'],
....: 'D': ['D0', 'D1', 'D2', 'D3']})
....:
In [41]: result = pd.merge(left, right, on='key')
Вот более сложный пример с несколькими ключами объединения. Только ключи, присутствующие в left и right, присутствуют (пересечение), так как how='inner' по умолчанию.
In [42]: left = pd.DataFrame({'key1': ['K0', 'K0', 'K1', 'K2'],
....: 'key2': ['K0', 'K1', 'K0', 'K1'],
....: 'A': ['A0', 'A1', 'A2', 'A3'],
....: 'B': ['B0', 'B1', 'B2', 'B3']})
....:
In [43]: right = pd.DataFrame({'key1': ['K0', 'K1', 'K1', 'K2'],
....: 'key2': ['K0', 'K0', 'K0', 'K0'],
....: 'C': ['C0', 'C1', 'C2', 'C3'],
....: 'D': ['D0', 'D1', 'D2', 'D3']})
....:
In [44]: 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 [45]: result = pd.merge(left, right, how='left', on=['key1', 'key2'])
In [46]: result = pd.merge(left, right, how='right', on=['key1', 'key2'])
In [47]: result = pd.merge(left, right, how='outer', on=['key1', 'key2'])
In [48]: result = pd.merge(left, right, how='inner', on=['key1', 'key2'])
Вот еще один пример с дублирующими ключами объединения в DataFrame:
In [49]: left = pd.DataFrame({'A': [1, 2], 'B': [2, 2]})
In [50]: right = pd.DataFrame({'A': [4, 5, 6], 'B': [2, 2, 2]})
In [51]: result = pd.merge(left, right, on='B', how='outer')
Предупреждение
Объединение/слияние по дублирующим ключам может привести к возвращаемой таблице, которая является произведением размерностей строк, что может привести к переполнению памяти. Пользователь несет ответственность за управление дублирующими значениями в ключах перед объединением больших DataFrame.
Проверка на дублирующие ключи
Введено в версии 0.21.0.
Пользователи могут использовать аргумент validate для автоматической проверки наличия неожиданных дубликатов в ключах объединения. Уникальность ключей проверяется перед операциями объединения и, таким образом, должна защитить от переполнения памяти. Проверка уникальности ключей также является хорошим способом проверки того, что структуры данных пользователя соответствуют ожиданиям.
В следующем примере есть дублирующие значения B в правом DataFrame. Поскольку это не объединение один-к-одному – как указано в аргументе validate – будет выброшено исключение.
In [52]: left = pd.DataFrame({'A' : [1,2], 'B' : [1, 2]})
In [53]: 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 [54]: pd.merge(left, right, on='B', how='outer', validate="one_to_many") Out[54]: 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 [55]: df1 = pd.DataFrame({'col1': [0, 1], 'col_left': ['a', 'b']})
In [56]: df2 = pd.DataFrame({'col1': [1, 2, 2], 'col_right': [2, 2, 2]})
In [57]: pd.merge(df1, df2, on='col1', how='outer', indicator=True)
Out[57]:
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 [58]: pd.merge(df1, df2, on='col1', how='outer', indicator='indicator_column') Out[58]: 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 [59]: left = pd.DataFrame({'key': [1], 'v1': [10]})
In [60]: left
Out[60]:
key v1
0 1 10
In [61]: right = pd.DataFrame({'key': [1, 2], 'v1': [20, 30]})
In [62]: right
Out[62]:
key v1
0 1 20
1 2 30
Мы можем сохранить ключи объединения:
In [63]: pd.merge(left, right, how='outer') Out[63]: key v1 0 1 10 1 1 20 2 2 30 In [64]: 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.25.0/user_guide/merging.html