Spec-Zone.ru › pandas 0.24

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

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

Предупреждение

Изменено в версии 0.23.0.

По умолчанию с join='outer' ось (столбцы в этом случае) сортируется. В будущей версии pandas по умолчанию будет сортировка отсутствовать. Мы указали sort=False для включения нового поведения сейчас.

Вот то же самое с 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, sort=False)
../_images/merging_append2.png

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

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

Примечание

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

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

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

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

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

In [16]: result = df1.append(df4, ignore_index=True, sort=False)
../_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 с 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)
../_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([['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'])
   ....: 
../_images/merging_concat_dict_keys_names.png
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)
../_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, sort=False)
../_images/merging_append_dits.png

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

Вот более сложный пример с несколькими ключами объединения. Присутствуют только ключи, встречающиеся в 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'])
../_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

Вот еще один пример с дублирующимися ключами объединения в 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')
../_images/merging_merge_on_key_dup.png

Предупреждение

Объединение/слияние по дублирующимся ключам может привести к возвращаемой таблице, являющейся произведением размерностей строк, что может привести к переполнению памяти. Пользователь отвечает за управление дублирующими значениями в ключах перед объединением больших 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

Spec-Zone.ru

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