У процесі відповіді на це запитання для себе я дізнався багато речей і хотів скласти каталог прикладів та пояснення.
Конкретна відповідь на суть 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 / 'індекс', 1 / 'стовпці'}, за замовчуванням 0 Вісь, що об'єднується вздовж.
Два DataFrame
s з 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
s з 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
along 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 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 : {'внутрішній', 'зовнішній'}, за замовчуванням 'зовнішній'
Як обробляти індекси на інших вісях.
Це не виходить, немає 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 : список об’єктів індексу
Специфічні індекси, які слід використовувати для інших осей 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
If 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.
# 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, додаємо два нові рівні 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
додайте рівні до a 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
об'єкта, коли він стає стовпцем, одночасно виконуючи роль першого рівня a 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
висновки.
Панда, здається, лише виводить імена стовпців з 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
замість alist
При передачі 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
аргументом. Якщо levels
це значення залишається за замовчуванням None
, Pandas приймає унікальні значення кожного рівня результуючого 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
в межах 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
.
імена : список, за замовчуванням Немає
Імена рівнів у результуючому ієрархічному індексі
# 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 : 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]