Объединение, присоединение и конкатенация
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.ix['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) создаёт полную копию данных, и постоянное повторное использование этой функции может привести к существенным потерям производительности. Если вам нужно использовать операцию над несколькими наборами данных, используйте list comprehension.
frames = [ process_your_file(f) for f in files ] result = pd.concat(frames)
Логические операции над другими осями
При склеивании нескольких DataFrame (или Panel и т.д.), например, у вас есть выбор, как обращаться с другими осями (кроме той, которая конкатенируется). Это можно сделать тремя способами:
- Взять (отсортированное) объединение всех их,
join='outer'. Это вариант по умолчанию, так как он приводит к нулевым потерям информации. - Взять пересечение,
join='inner'. - Использовать конкретный индекс (в случае DataFrame) или индексы (в случае Panel или будущих объектов более высокой размерности), т.е. аргумент
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)
Обратите внимание, что индексы строк были объединены и отсортированы. Вот то же самое с 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)
append может принимать несколько объектов для конкатенации:
In [14]: result = df1.append([df2, df3])
Примечание
В отличие от метода list.append который добавляет в исходный список и ничего не возвращает, метод append здесь не изменяет df1 и возвращает его копию с добавленным df2.
Игнорирование индексов на оси конкатенации
Для DataFrame, которые не имеют осмысленного индекса, вы можете добавить их, проигнорировав тот факт, что у них могут быть перекрывающиеся индексы:
Для этого используйте аргумент ignore_index:
In [15]: result = pd.concat([df1, df4], ignore_index=True)
Это также допустимый аргумент для DataFrame.append:
In [16]: result = df1.append(df4, ignore_index=True)
Конкатенация с смешанными 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 без имени, они будут пронумерованы последовательно.
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([[u'z', u'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([[u'z', u'y', u'x', u'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)
Объединение/слияние DataFrame в стиле баз данных
pandas имеет полностью функциональные, высокопроизводительные операции объединения в памяти, очень похожие по стилю на реляционные базы данных, такие как SQL. Эти методы работают значительно лучше (в некоторых случаях в несколько раз лучше), чем другие реализации с открытым исходным кодом (например, base::merge.data.frame в R). Причина в тщательном алгоритмическом дизайне и внутренней структуре данных в DataFrame.
См. пособие для некоторых продвинутых стратегий.
Пользователи, знакомые с SQL, но новые для pandas, могут быть заинтересованы в сравнении со SQL.
pandas предоставляет одну функцию merge, как точку входа для всех стандартных операций объединения баз данных между объектами DataFrame:
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)
-
left: Объект DataFrame -
right: Другой объект DataFrame -
on: Столбцы (названия) для объединения. Должны присутствовать как в левом, так и в правом объектах DataFrame. Если не указаны, иleft_indexиright_indexявляютсяFalse, в качестве ключей объединения будут взяты пересечения столбцов в DataFrame -
left_on: Столбцы из левого DataFrame, используемые в качестве ключей. Могут быть именами столбцов или массивами с длиной, равной длине DataFrame -
right_on: Столбцы из правого DataFrame, используемые в качестве ключей. Могут быть именами столбцов или массивами с длиной, равной длине DataFrame -
left_index: ЕслиTrue, в качестве ключа объединения используется индекс (метки строк) левого DataFrame. В случае DataFrame с MultiIndex (иерархическим), количество уровней должно совпадать с количеством ключей объединения из правого DataFrame -
right_index: Аналогичное использование, как уleft_indexдля правого DataFrame -
how: Один из'left','right','outer','inner'. По умолчаниюinner. Более подробное описание каждого метода приведено ниже -
sort: Сортировать результирующий DataFrame по ключам объединения в лексикографическом порядке. По умолчаниюTrue, установка наFalseзначительно улучшит производительность во многих случаях -
suffixes: Кортеж строчных суффиксов, применяемых к перекрывающимся столбцам. По умолчанию('_x', '_y'). -
copy: Всегда копировать данные (по умолчаниюTrue) из переданных объектов DataFrame, даже если переиндексация не требуется. В многих случаях этого избежать нельзя, но это может улучшить производительность/использование памяти. Случаи, когда копирование можно избежать, являются несколько патологическими, но этот параметр все же предоставляется. -
indicator: Добавить столбец в результирующий DataFrame под названием_mergeс информацией о источнике каждой строки._mergeимеет тип Categorical и принимает значениеleft_onlyдля наблюдений, ключ объединения которых встречается только в'left'DataFrame,right_onlyдля наблюдений, ключ объединения которых встречается только в'right'DataFrame, иbothесли ключ объединения наблюдения встречается в обоих.Добавлена в версии 0.17.0.
Тип возвращаемого значения будет таким же, как у left. Если left является DataFrame и right является подклассом DataFrame, тип возвращаемого значения по-прежнему будет DataFrame.
merge — функция в пространстве имен pandas, и она также доступна как метод экземпляра DataFrame, при этом вызываемый DataFrame неявно рассматривается как левый объект в объединении.
Сопутствующий метод 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')
Вот более сложный пример с несколькими ключами объединения:
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'])
Индикатор объединения
Добавлена в версии 0.17.0.
merge теперь принимает аргумент indicator. Если True, столбец типа Categorical под названием _merge будет добавлен к выходному объекту, принимающему значения:
| Источник наблюдения |
_merge значение |
|---|---|
Ключ объединения только в 'left' таблице | left_only |
Ключ объединения только в 'right' таблице | right_only |
| Ключ объединения в обеих таблицах | both |
In [48]: df1 = pd.DataFrame({'col1': [0, 1], 'col_left':['a', 'b']})
In [49]: df2 = pd.DataFrame({'col1': [1, 2, 2],'col_right':[2, 2, 2]})
In [50]: pd.merge(df1, df2, on='col1', how='outer', indicator=True)
Out[50]:
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 [51]: pd.merge(df1, df2, on='col1', how='outer', indicator='indicator_column') Out[51]: 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
Объединение по индексу
DataFrame.join — удобный метод для объединения столбцов двух потенциально разных DataFrame с различными индексами в единый результирующий DataFrame. Вот очень простой пример:
In [52]: left = pd.DataFrame({'A': ['A0', 'A1', 'A2'],
....: 'B': ['B0', 'B1', 'B2']},
....: index=['K0', 'K1', 'K2'])
....:
In [53]: right = pd.DataFrame({'C': ['C0', 'C2', 'C3'],
....: 'D': ['D0', 'D2', 'D3']},
....: index=['K0', 'K2', 'K3'])
....:
In [54]: result = left.join(right)
In [55]: result = left.join(right, how='outer')
In [56]: result = left.join(right, how='inner')
Выравнивание данных выполняется по индексам (меткам строк). Этот же результат можно получить, используя merge с дополнительными аргументами, которые указывают на использование индексов:
In [57]: result = pd.merge(left, right, left_index=True, right_index=True, how='outer')
In [58]: result = pd.merge(left, right, left_index=True, right_index=True, how='inner');
Объединение столбцов ключей по индексу
join принимает необязательный аргумент on который может быть именем столбца или несколькими именами столбцов, указывающих на то, что переданный DataFrame должен быть выровнен по этому столбцу в DataFrame. Эти два вызова функции полностью эквивалентны:
left.join(right, on=key_or_keys)
pd.merge(left, right, left_on=key_or_keys, right_index=True,
how='left', sort=False)
Очевидно, вы можете выбрать тот формат, который вам удобнее. Для объединений многие-к-одному (где один из DataFrame уже индексирован по ключу объединения), использование join может быть удобнее. Вот простой пример:
In [59]: left = pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'],
....: 'B': ['B0', 'B1', 'B2', 'B3'],
....: 'key': ['K0', 'K1', 'K0', 'K1']})
....:
In [60]: right = pd.DataFrame({'C': ['C0', 'C1'],
....: 'D': ['D0', 'D1']},
....: index=['K0', 'K1'])
....:
In [61]: result = left.join(right, on='key')
In [62]: result = pd.merge(left, right, left_on='key', right_index=True, ....: how='left', sort=False); ....:
Для объединения по нескольким ключам переданный DataFrame должен иметь MultiIndex:
In [63]: left = pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'],
....: 'B': ['B0', 'B1', 'B2', 'B3'],
....: 'key1': ['K0', 'K0', 'K1', 'K2'],
....: 'key2': ['K0', 'K1', 'K0', 'K1']})
....:
In [64]: index = pd.MultiIndex.from_tuples([('K0', 'K0'), ('K1', 'K0'),
....: ('K2', 'K0'), ('K2', 'K1')])
....:
In [65]: right = pd.DataFrame({'C': ['C0', 'C1', 'C2', 'C3'],
....: 'D': ['D0', 'D1', 'D2', 'D3']},
....: index=index)
....:
Теперь это можно объединить, передав имена двух столбцов ключей:
In [66]: result = left.join(right, on=['key1', 'key2'])
По умолчанию для DataFrame.join выполняется левое объединение (по существу, операция «VLOOKUP» для пользователей Excel), которое использует только ключи, найденные в вызываемом DataFrame. Другие типы объединения, например внутреннее объединение, также могут быть легко выполнены:
In [67]: result = left.join(right, on=['key1', 'key2'], how='inner')
Как видно, это отбрасывает любые строки, где не было совпадения.
Объединение одного индекса с многоуровневым индексом
Добавлена в версии 0.14.0.
Вы можете объединить одноуровневый DataFrame с уровнем многоуровневого DataFrame. Уровень будет соответствовать имени индекса одноуровневого DataFrame имени уровня многоуровневого DataFrame.
In [68]: left = pd.DataFrame({'A': ['A0', 'A1', 'A2'],
....: 'B': ['B0', 'B1', 'B2']},
....: index=pd.Index(['K0', 'K1', 'K2'], name='key'))
....:
In [69]: index = pd.MultiIndex.from_tuples([('K0', 'Y0'), ('K1', 'Y1'),
....: ('K2', 'Y2'), ('K2', 'Y3')],
....: names=['key', 'Y'])
....:
In [70]: right = pd.DataFrame({'C': ['C0', 'C1', 'C2', 'C3'],
....: 'D': ['D0', 'D1', 'D2', 'D3']},
....: index=index)
....:
In [71]: result = left.join(right, how='inner')
Это эквивалентно, но менее громоздко и более экономично с точки зрения памяти/быстрее, чем это.
In [72]: result = pd.merge(left.reset_index(), right.reset_index(), ....: on=['key'], how='inner').set_index(['key','Y']) ....:
Объединение с двумя многоуровневыми индексами
Это не реализовано через join в настоящее время, однако это можно сделать следующим образом.
In [73]: index = pd.MultiIndex.from_tuples([('K0', 'X0'), ('K0', 'X1'),
....: ('K1', 'X2')],
....: names=['key', 'X'])
....:
In [74]: left = pd.DataFrame({'A': ['A0', 'A1', 'A2'],
....: 'B': ['B0', 'B1', 'B2']},
....: index=index)
....:
In [75]: result = pd.merge(left.reset_index(), right.reset_index(),
....: on=['key'], how='inner').set_index(['key','X','Y'])
....:
Перекрывающиеся столбцы значений
Аргумент объединения suffixes принимает кортеж списков строк, которые добавляются к именам перекрывающихся столбцов в входных DataFrame, чтобы избежать неоднозначности столбцов в результате:
In [76]: left = pd.DataFrame({'k': ['K0', 'K1', 'K2'], 'v': [1, 2, 3]})
In [77]: right = pd.DataFrame({'k': ['K0', 'K0', 'K3'], 'v': [4, 5, 6]})
In [78]: result = pd.merge(left, right, on='k')
In [79]: result = pd.merge(left, right, on='k', suffixes=['_l', '_r'])
DataFrame.join имеет аргументы lsuffix и rsuffix, которые ведут себя аналогично.
In [80]: left = left.set_index('k')
In [81]: right = right.set_index('k')
In [82]: result = left.join(right, lsuffix='_l', rsuffix='_r')
Объединение нескольких объектов DataFrame или Panel
Список или кортеж DataFrame также можно передать в DataFrame.join для объединения их вместе по их индексам. То же самое верно для Panel.join.
In [83]: right2 = pd.DataFrame({'v': [7, 8, 9]}, index=['K1', 'K1', 'K2'])
In [84]: result = left.join([right, right2])
Объединение значений в столбцах Series или DataFrame
Еще одна довольно распространенная ситуация заключается в том, что у вас есть два объекта Series или DataFrame с одинаковыми индексами (или аналогичными индексами), и вы хотите «заменить» значения в одном объекте значениями для соответствующих индексов в другом. Вот пример:
In [85]: df1 = pd.DataFrame([[np.nan, 3., 5.], [-4.6, np.nan, np.nan], ....: [np.nan, 7., np.nan]]) ....: In [86]: df2 = pd.DataFrame([[-42.6, np.nan, -8.2], [-5., 1.6, 4]], ....: index=[1, 2]) ....:
Для этого используйте метод combine_first:
In [87]: result = df1.combine_first(df2)
Обратите внимание, что этот метод использует значения из правой DataFrame только в случае их отсутствия в левой DataFrame. Связанный метод, update, изменяет значения, отличные от NA, на месте:
In [88]: df1.update(df2)
Объединение временных рядов
Объединение упорядоченных данных
Функция merge_ordered() позволяет объединять временные ряды и другие упорядоченные данные. В частности, она имеет необязательный параметр fill_method для заполнения/интерполяции отсутствующих данных:
In [89]: left = pd.DataFrame({'k': ['K0', 'K1', 'K1', 'K2'],
....: 'lv': [1, 2, 3, 4],
....: 's': ['a', 'b', 'c', 'd']})
....:
In [90]: right = pd.DataFrame({'k': ['K1', 'K2', 'K4'],
....: 'rv': [1, 2, 3]})
....:
In [91]: pd.merge_ordered(left, right, fill_method='ffill', left_by='s')
Out[91]:
k lv s rv
0 K0 1.0 a NaN
1 K1 1.0 a 1.0
2 K2 1.0 a 2.0
3 K4 1.0 a 3.0
4 K1 2.0 b 1.0
5 K2 2.0 b 2.0
6 K4 2.0 b 3.0
7 K1 3.0 c 1.0
8 K2 3.0 c 2.0
9 K4 3.0 c 3.0
10 K1 NaN d 1.0
11 K2 4.0 d 2.0
12 K4 4.0 d 3.0
Объединение AsOf
Новая версия 0.19.0.
Функция merge_asof() похожа на упорядоченное левое объединение, за исключением того, что мы сопоставляем ближайшие ключи, а не равные. Для каждой строки в left DataFrame мы выбираем последнюю строку в right DataFrame, у которой ключ on меньше ключа левой строки. Обе DataFrame должны быть отсортированы по ключу.
Необязательно, объединение asof может выполнять объединение по группам. Это сопоставляет ключ by одинаково, в дополнение к ближайшему совпадению по ключу on.
Например, у нас могут быть trades и quotes, и мы хотим asof объединить их.
In [92]: trades = pd.DataFrame({
....: 'time': pd.to_datetime(['20160525 13:30:00.023',
....: '20160525 13:30:00.038',
....: '20160525 13:30:00.048',
....: '20160525 13:30:00.048',
....: '20160525 13:30:00.048']),
....: 'ticker': ['MSFT', 'MSFT',
....: 'GOOG', 'GOOG', 'AAPL'],
....: 'price': [51.95, 51.95,
....: 720.77, 720.92, 98.00],
....: 'quantity': [75, 155,
....: 100, 100, 100]},
....: columns=['time', 'ticker', 'price', 'quantity'])
....:
In [93]: quotes = pd.DataFrame({
....: 'time': pd.to_datetime(['20160525 13:30:00.023',
....: '20160525 13:30:00.023',
....: '20160525 13:30:00.030',
....: '20160525 13:30:00.041',
....: '20160525 13:30:00.048',
....: '20160525 13:30:00.049',
....: '20160525 13:30:00.072',
....: '20160525 13:30:00.075']),
....: 'ticker': ['GOOG', 'MSFT', 'MSFT',
....: 'MSFT', 'GOOG', 'AAPL', 'GOOG',
....: 'MSFT'],
....: 'bid': [720.50, 51.95, 51.97, 51.99,
....: 720.50, 97.99, 720.50, 52.01],
....: 'ask': [720.93, 51.96, 51.98, 52.00,
....: 720.93, 98.01, 720.88, 52.03]},
....: columns=['time', 'ticker', 'bid', 'ask'])
....:
In [94]: trades
Out[94]:
time ticker price quantity
0 2016-05-25 13:30:00.023 MSFT 51.95 75
1 2016-05-25 13:30:00.038 MSFT 51.95 155
2 2016-05-25 13:30:00.048 GOOG 720.77 100
3 2016-05-25 13:30:00.048 GOOG 720.92 100
4 2016-05-25 13:30:00.048 AAPL 98.00 100
In [95]: quotes
Out[95]:
time ticker bid ask
0 2016-05-25 13:30:00.023 GOOG 720.50 720.93
1 2016-05-25 13:30:00.023 MSFT 51.95 51.96
2 2016-05-25 13:30:00.030 MSFT 51.97 51.98
3 2016-05-25 13:30:00.041 MSFT 51.99 52.00
4 2016-05-25 13:30:00.048 GOOG 720.50 720.93
5 2016-05-25 13:30:00.049 AAPL 97.99 98.01
6 2016-05-25 13:30:00.072 GOOG 720.50 720.88
7 2016-05-25 13:30:00.075 MSFT 52.01 52.03
По умолчанию мы используем asof для котировок.
In [96]: pd.merge_asof(trades, quotes,
....: on='time',
....: by='ticker')
....:
Out[96]:
time ticker price quantity bid ask
0 2016-05-25 13:30:00.023 MSFT 51.95 75 51.95 51.96
1 2016-05-25 13:30:00.038 MSFT 51.95 155 51.97 51.98
2 2016-05-25 13:30:00.048 GOOG 720.77 100 720.50 720.93
3 2016-05-25 13:30:00.048 GOOG 720.92 100 720.50 720.93
4 2016-05-25 13:30:00.048 AAPL 98.00 100 NaN NaN
Мы выполняем asof только в пределах 2ms между временем котировки и временем сделки.
In [97]: pd.merge_asof(trades, quotes,
....: on='time',
....: by='ticker',
....: tolerance=pd.Timedelta('2ms'))
....:
Out[97]:
time ticker price quantity bid ask
0 2016-05-25 13:30:00.023 MSFT 51.95 75 51.95 51.96
1 2016-05-25 13:30:00.038 MSFT 51.95 155 NaN NaN
2 2016-05-25 13:30:00.048 GOOG 720.77 100 720.50 720.93
3 2016-05-25 13:30:00.048 GOOG 720.92 100 720.50 720.93
4 2016-05-25 13:30:00.048 AAPL 98.00 100 NaN NaN
Мы выполняем asof только в пределах 10ms между временем котировки и временем сделки, и исключаем точные совпадения по времени. Обратите внимание, что хотя мы исключаем точные совпадения (котировок), предыдущие котировки распространяются до этого момента во времени.
In [98]: pd.merge_asof(trades, quotes,
....: on='time',
....: by='ticker',
....: tolerance=pd.Timedelta('10ms'),
....: allow_exact_matches=False)
....:
Out[98]:
time ticker price quantity bid ask
0 2016-05-25 13:30:00.023 MSFT 51.95 75 NaN NaN
1 2016-05-25 13:30:00.038 MSFT 51.95 155 51.97 51.98
2 2016-05-25 13:30:00.048 GOOG 720.77 100 NaN NaN
3 2016-05-25 13:30:00.048 GOOG 720.92 100 NaN NaN
4 2016-05-25 13:30:00.048 AAPL 98.00 100 NaN NaN
© 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.19.2/merging.html