Каковы аргументы 'level', 'keys' и names для concat-функции в Pandas?
Вопросы
- Как я использую
pd.concat
? - Что
levels
аргумент для? - Что
keys
аргумент для? - Есть ли примеры, которые помогут объяснить, как использовать все аргументы?
Панды concat
функция швейцарского армейского ножа объединяющихся утилит. Разнообразие ситуаций, в которых это полезно, многочисленно. Существующая документация опускает некоторые детали некоторых необязательных аргументов. Среди них есть levels
а также keys
аргументы. Я решил выяснить, что делают эти аргументы.
Я задам вопрос, который станет воротами во многие аспекты pd.concat
,
Рассмотрим кадры данных d1
, d2
, а также d3
:
import pandas as pd
d1 = pd.DataFrame(dict(A=.1, B=.2, C=.3), [2, 3])
d2 = pd.DataFrame(dict(B=.4, C=.5, D=.6), [1, 2])
d3 = pd.DataFrame(dict(A=.7, B=.8, D=.9), [1, 3])
Если бы я связал их вместе с
pd.concat([d1, d2, d3], keys=['d1', 'd2', 'd3'])
Я получаю ожидаемый результат с pandas.MultiIndex
для меня columns
объект:
A B C D
d1 2 0.1 0.2 0.3 NaN
3 0.1 0.2 0.3 NaN
d2 1 NaN 0.4 0.5 0.6
2 NaN 0.4 0.5 0.6
d3 1 0.7 0.8 NaN 0.9
3 0.7 0.8 NaN 0.9
Тем не менее, я хотел использовать levels
Аргументная документация:
уровни: список последовательностей, по умолчанию нет. Конкретные уровни (уникальные значения), используемые для построения MultiIndex. В противном случае они будут выведены из ключей.
Итак, я прошел
pd.concat([d1, d2, d3], keys=['d1', 'd2', 'd3'], levels=[['d1', 'd2']])
И получить KeyError
ValueError: Key d3 not in level Index(['d1', 'd2'], dtype='object')
Это имело смысл. Уровни, которые я прошел, были неадекватны для описания необходимых уровней, указанных клавишами. Если бы я не прошел ничего, как я сделал выше, уровни выведены (как указано в документации). Но как еще я могу использовать этот аргумент для лучшего эффекта?
Если бы я попробовал это вместо этого:
pd.concat([d1, d2, d3], keys=['d1', 'd2', 'd3'], levels=[['d1', 'd2', 'd3']])
Я и получил те же результаты, что и выше. Но когда я добавляю еще одно значение к уровням,
df = pd.concat([d1, d2, d3], keys=['d1', 'd2', 'd3'], levels=[['d1', 'd2', 'd3', 'd4']])
Я получаю тот же самый фрейм данных, но в результате MultiIndex
имеет неиспользованный уровень.
df.index.levels[0]
Index(['d1', 'd2', 'd3', 'd4'], dtype='object')
Так в чем смысл level
аргумент и я должен использовать keys
по-другому?
Я использую Python 3.6 и Pandas 0.22.
1 ответ
В процессе ответа на этот вопрос для себя я узнал много вещей, и я хотел собрать каталог примеров и некоторые объяснения.
Конкретный ответ на вопрос 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, отсортированные ключи будут использоваться в качестве аргумента keys, если только он не передан, и в этом случае будут выбраны значения (см. ниже). Любые объекты 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
(Уложены)
При выполнении конкатенации Series
а также DataFrame
вместе axis=0
конвертируем все Series
в один столбец DataFrame
s.
Обратите особое внимание, что это соединение 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
,
# Effectively renames | |
# `s1` but does not align | # Does not rename. So | # Renames to something
# with columns in `d1` | # Pandas defaults to `0` | # that does align with `d1`
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: {'inner', 'external'}, по умолчанию 'external'
Как обрабатывать индексы на другой оси.
Оказывается, нет left
или же right
вариант как pd.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: список объектов Index
Специальные индексы для использования для других n - 1 осей вместо выполнения внутренней / внешней логики набора.
Left Merge
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: логическое, по умолчанию 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
Объект в атрибуте индекса.
# length 3 length 3 # length 2 length 2
# /--------\ /-----------\ # /----\ /------\
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 предваряют два новых уровня 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
передаются, но это переопределяется, если 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
, Так что Панды снова будут использовать 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
inferrence.Панды, кажется, только выводят названия столбцов из 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
параметр.
# axis=0 | # axis=1
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
argument.When levels
остается в качестве значения по умолчанию None
, Панды будут принимать уникальные значения каждого уровня в результате MultiIndex
и использовать это как объект, используемый в результирующем index.levels
приписывать.
уровни: список последовательностей, по умолчанию нет
Конкретные уровни (уникальные значения), используемые для построения MultiIndex. В противном случае они будут выведены из ключей.
Если Панды уже делают вывод о том, какими должны быть эти уровни, какое преимущество можно определить самим? Я покажу один пример и оставлю на ваше усмотрение придумать другие причины, по которым это может быть полезно.
пример
Согласно документации, 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
в пределах 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
,
имена: список, по умолчанию нет
Имена уровней в результирующем иерархическом индексе
# axis=0 | # axis=1
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: логическое, по умолчанию 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]