Spec-Zone.ru › pandas 0.19

Объединение, присоединение и конкатенация

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)
_images/merging_concat_basic.png

Подобно своей функции для 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'])
_images/merging_concat_keys.png

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

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)
_images/merging_concat_axis1.png

Обратите внимание, что индексы строк были объединены и отсортированы. Вот то же самое с join='inner':

In [10]: result = pd.concat([df1, df4], axis=1, join='inner')
_images/merging_concat_axis1_inner.png

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

In [11]: result = pd.concat([df1, df4], axis=1, join_axes=[df1.index])
_images/merging_concat_axis1_join_axes.png

Конкатенация с использованием append

Полезным сокращением для concat являются методы экземпляров append для Series и DataFrame. Эти методы появились раньше concat. Они выполняют конкатенацию вдоль axis=0, а именно индекса:

In [12]: result = df1.append(df2)
_images/merging_append1.png

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

In [13]: result = df1.append(df4)
_images/merging_append2.png

append может принимать несколько объектов для конкатенации:

In [14]: result = df1.append([df2, df3])
_images/merging_append3.png

Примечание

В отличие от метода list.append который добавляет в исходный список и ничего не возвращает, метод append здесь не изменяет df1 и возвращает его копию с добавленным df2.

Игнорирование индексов на оси конкатенации

Для DataFrame, которые не имеют осмысленного индекса, вы можете добавить их, проигнорировав тот факт, что у них могут быть перекрывающиеся индексы:

Для этого используйте аргумент ignore_index:

In [15]: result = pd.concat([df1, df4], ignore_index=True)
_images/merging_concat_ignore_index.png

Это также допустимый аргумент для DataFrame.append:

In [16]: result = df1.append(df4, ignore_index=True)
_images/merging_append_ignore_index.png

Конкатенация с смешанными 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)
_images/merging_concat_mixed_ndim.png

Если переданы Series без имени, они будут пронумерованы последовательно.

In [19]: s2 = pd.Series(['_0', '_1', '_2', '_3'])

In [20]: result = pd.concat([df1, s2, s2, s2], axis=1)
_images/merging_concat_unnamed_series.png

Передача ignore_index=True удалит все ссылки на имена.

In [21]: result = pd.concat([df1, s1], axis=1, ignore_index=True)
_images/merging_concat_series_ignore_index.png

Дополнительная конкатенация с ключами групп

Довольно распространённое использование аргумента 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'])
_images/merging_concat_group_keys2.png

Вы также можете передать словарь в concat, в этом случае ключи словаря будут использоваться для аргумента keys (если не указаны другие ключи):

In [28]: pieces = {'x': df1, 'y': df2, 'z': df3}

In [29]: result = pd.concat(pieces)
_images/merging_concat_dict.png
In [30]: result = pd.concat(pieces, keys=['z', 'y'])
_images/merging_concat_dict_keys.png

Созданный 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'])
   ....: 
_images/merging_concat_dict_keys_names.png
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)
_images/merging_append_series_as_row.png

Вы должны использовать 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)
_images/merging_append_dits.png

Объединение/слияние 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')
_images/merging_merge_on_key.png

Вот более сложный пример с несколькими ключами объединения:

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'])
_images/merging_merge_on_key_multiple.png

Аргумент 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'])
_images/merging_merge_on_key_left.png
In [45]: result = pd.merge(left, right, how='right', on=['key1', 'key2'])
_images/merging_merge_on_key_right.png
In [46]: result = pd.merge(left, right, how='outer', on=['key1', 'key2'])
_images/merging_merge_on_key_outer.png
In [47]: result = pd.merge(left, right, how='inner', on=['key1', 'key2'])
_images/merging_merge_on_key_inner.png

Индикатор объединения

Добавлена в версии 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)
_images/merging_join.png
In [55]: result = left.join(right, how='outer')
_images/merging_join_outer.png
In [56]: result = left.join(right, how='inner')
_images/merging_join_inner.png

Выравнивание данных выполняется по индексам (меткам строк). Этот же результат можно получить, используя merge с дополнительными аргументами, которые указывают на использование индексов:

In [57]: result = pd.merge(left, right, left_index=True, right_index=True, how='outer')
_images/merging_merge_index_outer.png
In [58]: result = pd.merge(left, right, left_index=True, right_index=True, how='inner');
_images/merging_merge_index_inner.png

Объединение столбцов ключей по индексу

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')
_images/merging_join_key_columns.png
In [62]: result = pd.merge(left, right, left_on='key', right_index=True,
   ....:                   how='left', sort=False);
   ....: 
_images/merging_merge_key_columns.png

Для объединения по нескольким ключам переданный 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'])
_images/merging_join_multikeys.png

По умолчанию для DataFrame.join выполняется левое объединение (по существу, операция «VLOOKUP» для пользователей Excel), которое использует только ключи, найденные в вызываемом DataFrame. Другие типы объединения, например внутреннее объединение, также могут быть легко выполнены:

In [67]: result = left.join(right, on=['key1', 'key2'], how='inner')
_images/merging_join_multikeys_inner.png

Как видно, это отбрасывает любые строки, где не было совпадения.

Объединение одного индекса с многоуровневым индексом

Добавлена в версии 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')
_images/merging_join_multiindex_inner.png

Это эквивалентно, но менее громоздко и более экономично с точки зрения памяти/быстрее, чем это.

In [72]: result = pd.merge(left.reset_index(), right.reset_index(),
   ....:       on=['key'], how='inner').set_index(['key','Y'])
   ....: 
_images/merging_merge_multiindex_alternative.png

Объединение с двумя многоуровневыми индексами

Это не реализовано через 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'])
   ....: 
_images/merging_merge_two_multiindex.png

Перекрывающиеся столбцы значений

Аргумент объединения 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')
_images/merging_merge_overlapped.png
In [79]: result = pd.merge(left, right, on='k', suffixes=['_l', '_r'])
_images/merging_merge_overlapped_suffix.png

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')
_images/merging_merge_overlapped_multi_suffix.png

Объединение нескольких объектов 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])
_images/merging_join_multi_df.png

Объединение значений в столбцах 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)
_images/merging_combine_first.png

Обратите внимание, что этот метод использует значения из правой DataFrame только в случае их отсутствия в левой DataFrame. Связанный метод, update, изменяет значения, отличные от NA, на месте:

In [88]: df1.update(df2)
_images/merging_update.png

Объединение временных рядов

Объединение упорядоченных данных

Функция 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API