Отвечая на этот вопрос для себя, я многому научился и хотел составить каталог примеров и некоторые объяснения.
Конкретный ответ на суть levelsспора будет ближе к концу.
pandas.concat: Пропавшее руководство
Ссылка на текущую документацию
Импорт и определение объектов
import pandas as pd
d1 = pd.DataFrame(dict(A=.1, B=.2, C=.3), index=[2, 3])
d2 = pd.DataFrame(dict(B=.4, C=.5, D=.6), index=[1, 2])
d3 = pd.DataFrame(dict(A=.7, B=.8, D=.9), index=[1, 3])
s1 = pd.Series([1, 2], index=[2, 3])
s2 = pd.Series([3, 4], index=[1, 2])
s3 = pd.Series([5, 6], index=[1, 3])
Аргументы
objs
Первый аргумент, с которым мы сталкиваемся objs:
objs : последовательность или отображение объектов Series, DataFrame или Panel. Если передан dict, отсортированные ключи будут использоваться в качестве аргумента ключей, если он не передан, и в этом случае будут выбраны значения (см. ниже). Любые объекты None будут отброшены без уведомления, если только они не все None, и в этом случае будет вызвана ошибка ValueError.
- Обычно мы видим, что это используется со списком объектов
Seriesили DataFrame.
- Я покажу это
dict тоже может быть очень полезно.
- Генераторы также могут быть использованы и могут быть полезны при использовании
mapкак вmap(f, list_of_df)
На данный момент, мы будем придерживаться список некоторых из DataFrameи Seriesобъекты , определенные выше. MultiIndexПозже я покажу, как можно использовать словари для получения очень полезных результатов.
pd.concat([d1, d2])
A B C D
2 0.1 0.2 0.3 NaN
3 0.1 0.2 0.3 NaN
1 NaN 0.4 0.5 0.6
2 NaN 0.4 0.5 0.6
axis
Второй аргумент, с которым мы сталкиваемся, имеет axisзначение по умолчанию 0:
ось : {0 / 'index', 1 / 'columns'}, по умолчанию 0 Ось для объединения.
Два DataFrameсaxis=0 (сложены)
Для значений 0илиindex мы имеем в виду: «Выровнять по столбцам и добавить в индекс».
Как показано выше, где мы использовали axis=0, потому что 0это значение по умолчанию, и мы видим, что индекс d2расширяет индекс, d1несмотря на перекрытие значений 2:
pd.concat([d1, d2], axis=0)
A B C D
2 0.1 0.2 0.3 NaN
3 0.1 0.2 0.3 NaN
1 NaN 0.4 0.5 0.6
2 NaN 0.4 0.5 0.6
Два DataFrameс axis=1(бок о бок)
Для значений 1или columnsмы хотим сказать: «Выровнять по индексу и добавить в столбцы»,
pd.concat([d1, d2], axis=1)
A B C B C D
1 NaN NaN NaN 0.4 0.5 0.6
2 0.1 0.2 0.3 0.4 0.5 0.6
3 0.1 0.2 0.3 NaN NaN NaN
Мы можем видеть, что результирующий индекс представляет собой объединение индексов, а результирующие столбцы являются расширением столбцов из d1столбцами d2.
Два (или три) Seriesс axis=0(сложенными)
При объединении pandas.Seriesвместе axis=0, мы получаем обратно pandas.Series. Имя результата Seriesбудет, Noneесли все Seriesобъединенные не будут иметь одинаковое имя. Обратите внимание на то, 'Name: A'когда мы распечатываем результат Series. Когда его нет, мы можем предположить, что Seriesимя есть None.
| | | pd.concat(
| pd.concat( | pd.concat( | [s1.rename('A'),
pd.concat( | [s1.rename('A'), | [s1.rename('A'), | s2.rename('B'),
[s1, s2]) | s2]) | s2.rename('A')]) | s3.rename('A')])
-------------- | --------------------- | ---------------------- | ----------------------
2 1 | 2 1 | 2 1 | 2 1
3 2 | 3 2 | 3 2 | 3 2
1 3 | 1 3 | 1 3 | 1 3
2 4 | 2 4 | 2 4 | 2 4
dtype: int64 | dtype: int64 | Name: A, dtype: int64 | 1 5
| | | 3 6
| | | dtype: int64
Два (или Три) Seriesс axis=1(рядом)
При объединении pandas.Seriesвместе axis=1, это nameатрибут , который мы называем для того , чтобы вывести имя столбца в результирующем pandas.DataFrame.
| | pd.concat(
| pd.concat( | [s1.rename('X'),
pd.concat( | [s1.rename('X'), | s2.rename('Y'),
[s1, s2], axis=1) | s2], axis=1) | s3.rename('Z')], axis=1)
---------------------- | --------------------- | ------------------------------
0 1 | X 0 | X Y Z
1 NaN 3.0 | 1 NaN 3.0 | 1 NaN 3.0 5.0
2 1.0 4.0 | 2 1.0 4.0 | 2 1.0 4.0 NaN
3 2.0 NaN | 3 2.0 NaN | 3 2.0 NaN 6.0
Смешанные Seriesи DataFrameс axis=0(сложенные)
При выполнении конкатенации a Seriesи DataFrameвместе axis=0мы конвертируем все Seriesв один столбец DataFrames.
Обратите особое внимание на то, что это объединение вместе axis=0; это означает расширение индекса (строк) при выравнивании столбцов. В приведенных ниже примерах мы видим, что индекс превращается [2, 3, 2, 3]в неразборчивое добавление индексов. Столбцы не перекрываются, если я не принудительно назвал Seriesстолбец с аргументом to_frame:
pd.concat( |
[s1.to_frame(), d1]) | pd.concat([s1, d1])
------------------------- | ---------------------
0 A B C | 0 A B C
2 1.0 NaN NaN NaN | 2 1.0 NaN NaN NaN
3 2.0 NaN NaN NaN | 3 2.0 NaN NaN NaN
2 NaN 0.1 0.2 0.3 | 2 NaN 0.1 0.2 0.3
3 NaN 0.1 0.2 0.3 | 3 NaN 0.1 0.2 0.3
Вы можете видеть, что результаты pd.concat([s1, d1])такие же, как если бы я делал to_frameсам.
Однако я могу контролировать имя результирующего столбца с помощью параметра to_frame. Переименование Seriesс помощью renameметода не влияет на имя столбца в результате DataFrame.
pd.concat( | pd.concat( | pd.concat(
[s1.to_frame('X'), d1]) | [s1.rename('X'), d1]) | [s1.to_frame('B'), d1])
---------------------------- | -------------------------- | ----------------------------
A B C X | 0 A B C | A B C
2 NaN NaN NaN 1.0 | 2 1.0 NaN NaN NaN | 2 NaN 1.0 NaN
3 NaN NaN NaN 2.0 | 3 2.0 NaN NaN NaN | 3 NaN 2.0 NaN
2 0.1 0.2 0.3 NaN | 2 NaN 0.1 0.2 0.3 | 2 0.1 0.2 0.3
3 0.1 0.2 0.3 NaN | 3 NaN 0.1 0.2 0.3 | 3 0.1 0.2 0.3
Смешанные Seriesи DataFrameс axis=1(рядом)
Это довольно интуитивно понятно. Seriesимя столбца по умолчанию представляет собой перечисление таких Seriesобъектов, когда nameатрибут недоступен.
| pd.concat(
pd.concat( | [s1.rename('X'),
[s1, d1], | s2, s3, d1],
axis=1) | axis=1)
------------------- | -------------------------------
0 A B C | X 0 1 A B C
2 1 0.1 0.2 0.3 | 1 NaN 3.0 5.0 NaN NaN NaN
3 2 0.1 0.2 0.3 | 2 1.0 4.0 NaN 0.1 0.2 0.3
| 3 2.0 NaN 6.0 0.1 0.2 0.3
join
Третий аргумент joinописывает, должно ли полученное слияние быть внешним (по умолчанию) или внутренним.
join : {'внутренний', 'внешний'}, по умолчанию 'внешний'
Как обрабатывать индексы на другой оси (осях).
Оказывается, нет опции leftили, чтобы объединить более двух объектов.rightpd.concat
В случае d1и d2варианты выглядят так:
outer
pd.concat([d1, d2], axis=1, join='outer')
A B C B C D
1 NaN NaN NaN 0.4 0.5 0.6
2 0.1 0.2 0.3 0.4 0.5 0.6
3 0.1 0.2 0.3 NaN NaN NaN
inner
pd.concat([d1, d2], axis=1, join='inner')
A B C B C D
2 0.1 0.2 0.3 0.4 0.5 0.6
join_axes
Четвертый аргумент - это то, что позволяет нам делать наше leftслияние и многое другое.
join_axes : список объектов
индекса. Конкретные индексы для использования для других осей n - 1 вместо выполнения логики внутреннего / внешнего набора.
Левое слияние
pd.concat([d1, d2, d3], axis=1, join_axes=[d1.index])
A B C B C D A B D
2 0.1 0.2 0.3 0.4 0.5 0.6 NaN NaN NaN
3 0.1 0.2 0.3 NaN NaN NaN 0.7 0.8 0.9
Правое слияние
pd.concat([d1, d2, d3], axis=1, join_axes=[d3.index])
A B C B C D A B D
1 NaN NaN NaN 0.4 0.5 0.6 0.7 0.8 0.9
3 0.1 0.2 0.3 NaN NaN NaN 0.7 0.8 0.9
ignore_index
ignore_index : boolean, по умолчанию - False.
Если True, не использовать значения индекса по оси конкатенации. Результирующая ось будет помечена 0, ..., n - 1. Это полезно, если вы объединяете объекты, у которых ось объединения не имеет значимой информации для индексации. Обратите внимание, что значения индексов на других осях по-прежнему учитываются при объединении.
Например, когда я складываюсь d1поверх d2, если меня не волнуют значения индекса, я могу сбросить их или игнорировать.
| pd.concat( | pd.concat(
| [d1, d2], | [d1, d2]
pd.concat([d1, d2]) | ignore_index=True) | ).reset_index(drop=True)
--------------------- | ----------------------- | -------------------------
A B C D | A B C D | A B C D
2 0.1 0.2 0.3 NaN | 0 0.1 0.2 0.3 NaN | 0 0.1 0.2 0.3 NaN
3 0.1 0.2 0.3 NaN | 1 0.1 0.2 0.3 NaN | 1 0.1 0.2 0.3 NaN
1 NaN 0.4 0.5 0.6 | 2 NaN 0.4 0.5 0.6 | 2 NaN 0.4 0.5 0.6
2 NaN 0.4 0.5 0.6 | 3 NaN 0.4 0.5 0.6 | 3 NaN 0.4 0.5 0.6
И при использовании axis=1:
| pd.concat(
| [d1, d2], axis=1,
pd.concat([d1, d2], axis=1) | ignore_index=True)
------------------------------- | -------------------------------
A B C B C D | 0 1 2 3 4 5
1 NaN NaN NaN 0.4 0.5 0.6 | 1 NaN NaN NaN 0.4 0.5 0.6
2 0.1 0.2 0.3 0.4 0.5 0.6 | 2 0.1 0.2 0.3 0.4 0.5 0.6
3 0.1 0.2 0.3 NaN NaN NaN | 3 0.1 0.2 0.3 NaN NaN NaN
keys
Мы можем передать список скалярных значений или кортежей, чтобы назначить кортеж или скалярные значения соответствующему MultiIndex. Длина переданного списка должна быть такой же, как и количество конкатенированных элементов.
ключи : последовательность, по умолчанию Нет.
Если пройдено несколько уровней, они должны содержать кортежи. Создайте иерархический индекс, используя переданные ключи в качестве самого внешнего уровня
axis=0
При конкатенации Seriesобъектов axis=0(расширении индекса).
Эти ключи становятся новым начальным уровнем MultiIndexобъекта в атрибуте index.
pd.concat([s1, s2, s3], keys=['A', 'B', 'C']) pd.concat([s1, s2], keys=['A', 'B'])
---------------------------------------------- -------------------------------------
A 2 1 A 2 1
3 2 3 2
B 1 3 B 1 3
2 4 2 4
C 1 5 dtype: int64
3 6
dtype: int64
Однако мы можем использовать в keysаргументе не только скалярные значения, чтобы создать еще более глубокое MultiIndex. Здесь мы передаем tuplesдлину 2 и добавляем два новых уровня a MultiIndex:
pd.concat(
[s1, s2, s3],
keys=[('A', 'X'), ('A', 'Y'), ('B', 'X')])
-----------------------------------------------
A X 2 1
3 2
Y 1 3
2 4
B X 1 5
3 6
dtype: int64
axis=1
Немного иначе обстоит дело с удлинением по столбцам. Когда мы использовали axis=0(см. Выше), наши keysдействовали как MultiIndexуровни в дополнение к существующему индексу. Для axis=1, мы имеем в виду ось, котораяSeries которой нет у объектов, а именноcolumns атрибут.
Вариации двух
Seriesс
axis=1
Обратите внимание, что присвоение имен s1и s2имеет значение, пока не keysпереданы no , но оно отменяется, если keysони переданы.
| | | pd.concat(
| pd.concat( | pd.concat( | [s1.rename('U'),
pd.concat( | [s1, s2], | [s1.rename('U'), | s2.rename('V')],
[s1, s2], | axis=1, | s2.rename('V')], | axis=1,
axis=1) | keys=['X', 'Y']) | axis=1) | keys=['X', 'Y'])
-------------- | --------------------- | ---------------------- | ----------------------
0 1 | X Y | U V | X Y
1 NaN 3.0 | 1 NaN 3.0 | 1 NaN 3.0 | 1 NaN 3.0
2 1.0 4.0 | 2 1.0 4.0 | 2 1.0 4.0 | 2 1.0 4.0
3 2.0 NaN | 3 2.0 NaN | 3 2.0 NaN | 3 2.0 NaN
MultiIndexс
Seriesи
axis=1
pd.concat(
[s1, s2],
axis=1,
keys=[('W', 'X'), ('W', 'Y')])
-----------------------------------
W
X Y
1 NaN 3.0
2 1.0 4.0
3 2.0 NaN
Два
DataFrameс
axis=1
Как и в axis=0примерах, keysдобавьте уровни к MultiIndex, но на этот раз к объекту, хранящемуся в columnsатрибуте.
pd.concat( | pd.concat(
[d1, d2], | [d1, d2],
axis=1, | axis=1,
keys=['X', 'Y']) | keys=[('First', 'X'), ('Second', 'X')])
------------------------------- | --------------------------------------------
X Y | First Second
A B C B C D | X X
1 NaN NaN NaN 0.4 0.5 0.6 | A B C B C D
2 0.1 0.2 0.3 0.4 0.5 0.6 | 1 NaN NaN NaN 0.4 0.5 0.6
3 0.1 0.2 0.3 NaN NaN NaN | 2 0.1 0.2 0.3 0.4 0.5 0.6
| 3 0.1 0.2 0.3 NaN NaN NaN
Seriesи
DataFrameс
axis=1
Это сложно. В этом случае значение скалярного ключа не может действовать как единственный уровень индекса для Seriesобъекта, когда он становится столбцом, а также выступать в качестве первого уровня MultiIndexдля DataFrame. Итак, Pandas снова будет использовать nameатрибут Seriesобъекта в качестве источника имени столбца.
pd.concat( | pd.concat(
[s1, d1], | [s1.rename('Z'), d1],
axis=1, | axis=1,
keys=['X', 'Y']) | keys=['X', 'Y'])
--------------------- | --------------------------
X Y | X Y
0 A B C | Z A B C
2 1 0.1 0.2 0.3 | 2 1 0.1 0.2 0.3
3 2 0.1 0.2 0.3 | 3 2 0.1 0.2 0.3
Ограничения
keysи
MultiIndexвыводы.
Кажется, что Pandas только выводит имена столбцов из Seriesимени, но он не заполняет пробелы при выполнении аналогичной конкатенации между фреймами данных с другим количеством уровней столбцов.
d1_ = pd.concat(
[d1], axis=1,
keys=['One'])
d1_
One
A B C
2 0.1 0.2 0.3
3 0.1 0.2 0.3
Затем объедините это с другим фреймом данных только с одним уровнем в объекте столбцов, и Pandas откажется пытаться создать кортежи MultiIndexобъекта и объединить все фреймы данных, как если бы один уровень объектов, скаляров и кортежей.
pd.concat([d1_, d2], axis=1)
(One, A) (One, B) (One, C) B C D
1 NaN NaN NaN 0.4 0.5 0.6
2 0.1 0.2 0.3 0.4 0.5 0.6
3 0.1 0.2 0.3 NaN NaN NaN
Передача dictвместоlist
При передаче pandas.concatсловаря в качестве keysпараметра будут использоваться ключи из словаря .
pd.concat( | pd.concat(
{0: d1, 1: d2}) | {0: d1, 1: d2}, axis=1)
----------------------- | -------------------------------
A B C D | 0 1
0 2 0.1 0.2 0.3 NaN | A B C B C D
3 0.1 0.2 0.3 NaN | 1 NaN NaN NaN 0.4 0.5 0.6
1 1 NaN 0.4 0.5 0.6 | 2 0.1 0.2 0.3 0.4 0.5 0.6
2 NaN 0.4 0.5 0.6 | 3 0.1 0.2 0.3 NaN NaN NaN
levels
Это используется в сочетании с keysаргументом. Если levelsоставить значение по умолчанию None, Pandas будет принимать уникальные значения каждого уровня результата MultiIndexи использовать их в качестве объекта, используемого в index.levelsатрибуте результата .
уровни : список последовательностей, по умолчанию нет.
Определенные уровни (уникальные значения) для использования при построении MultiIndex. В противном случае они будут выведены из ключей.
Если Pandas уже делает вывод, какими должны быть эти уровни, какое преимущество указывать это самим? Я покажу один пример и предоставлю вам возможность придумать другие причины, по которым это может быть полезно.
пример
Согласно документации, levelsаргумент - это список последовательностей. Это означает, что мы можем использовать другую pandas.Indexкак одну из этих последовательностей.
Рассмотрим фрейм данных, dfкоторый является объединением d1, d2и d3:
df = pd.concat(
[d1, d2, d3], axis=1,
keys=['First', 'Second', 'Fourth'])
df
First Second Fourth
A B C B C D A B D
1 NaN NaN NaN 0.4 0.5 0.6 0.7 0.8 0.9
2 0.1 0.2 0.3 0.4 0.5 0.6 NaN NaN NaN
3 0.1 0.2 0.3 NaN NaN NaN 0.7 0.8 0.9
Уровни объекта столбцов:
print(df, *df.columns.levels, sep='\n')
Index(['First', 'Second', 'Fourth'], dtype='object')
Index(['A', 'B', 'C', 'D'], dtype='object')
Если мы используем sumвнутри a, groupbyмы получим:
df.groupby(axis=1, level=0).sum()
First Fourth Second
1 0.0 2.4 1.5
2 0.6 0.0 1.5
3 0.6 2.4 0.0
Но что, если бы вместо ['First', 'Second', 'Fourth']других недостающих категорий с именем Thirdи Fifth? И я хотел, чтобы они были включены в результаты groupbyагрегации? Мы можем это сделать, если бы у нас был pandas.CategoricalIndex. И мы можем уточнить это заранее с levelsаргументом.
Вместо этого давайте определим dfкак:
cats = ['First', 'Second', 'Third', 'Fourth', 'Fifth']
lvl = pd.CategoricalIndex(cats, categories=cats, ordered=True)
df = pd.concat(
[d1, d2, d3], axis=1,
keys=['First', 'Second', 'Fourth'],
levels=[lvl]
)
df
First Fourth Second
1 0.0 2.4 1.5
2 0.6 0.0 1.5
3 0.6 2.4 0.0
Но первый уровень объекта столбцов:
df.columns.levels[0]
CategoricalIndex(
['First', 'Second', 'Third', 'Fourth', 'Fifth'],
categories=['First', 'Second', 'Third', 'Fourth', 'Fifth'],
ordered=True, dtype='category')
И наше groupbyсуммирование выглядит так:
df.groupby(axis=1, level=0).sum()
First Second Third Fourth Fifth
1 0.0 1.5 0.0 2.4 0.0
2 0.6 1.5 0.0 0.0 0.0
3 0.6 0.0 0.0 2.4 0.0
names
Это используется для обозначения уровней результата MultiIndex. Длина namesсписка должна соответствовать количеству уровней в результате MultiIndex.
имена : список, по умолчанию Нет
Имена уровней в итоговом иерархическом индексе
pd.concat( | pd.concat(
[d1, d2], | [d1, d2],
keys=[0, 1], | axis=1, keys=[0, 1],
names=['lvl0', 'lvl1']) | names=['lvl0', 'lvl1'])
----------------------------- | ----------------------------------
A B C D | lvl0 0 1
lvl0 lvl1 | lvl1 A B C B C D
0 2 0.1 0.2 0.3 NaN | 1 NaN NaN NaN 0.4 0.5 0.6
3 0.1 0.2 0.3 NaN | 2 0.1 0.2 0.3 0.4 0.5 0.6
1 1 NaN 0.4 0.5 0.6 | 3 0.1 0.2 0.3 NaN NaN NaN
2 NaN 0.4 0.5 0.6 |
verify_integrity
Не требующая пояснений документация
verify_integrity : boolean, по умолчанию False
Проверить, содержит ли новая объединенная ось дубликаты. Это может быть очень дорого по сравнению с фактическим объединением данных.
Поскольку в результате индекс от конкатенации d1и d2не является уникальным, оно не будет проверки целостности.
pd.concat([d1, d2])
A B C D
2 0.1 0.2 0.3 NaN
3 0.1 0.2 0.3 NaN
1 NaN 0.4 0.5 0.6
2 NaN 0.4 0.5 0.6
А также
pd.concat([d1, d2], verify_integrity=True)
> ValueError: индексы имеют перекрывающиеся значения: [2]