Mentre rispondevo personalmente a questa domanda, ho imparato molte cose e volevo mettere insieme un catalogo di esempi e alcune spiegazioni.
La risposta specifica al punto levels
dell'argomento arriverà verso la fine.
pandas.concat
: Il manuale mancante
Collegamento alla documentazione corrente
Importazioni e definizione di oggetti
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])
argomenti
objs
Il primo argomento che incontriamo è objs
:
objs : una sequenza o mappatura di oggetti Series, DataFrame o Panel Se viene passato un dict, le chiavi ordinate verranno utilizzate come argomento delle chiavi, a meno che non vengano passate, nel qual caso verranno selezionati i valori (vedere di seguito). Tutti gli oggetti None verranno rilasciati silenziosamente a meno che non siano tutti None, nel qual caso verrà sollevata un'eccezione ValueError
- In genere lo vediamo usato con un elenco di
Series
o DataFrame
oggetti.
- Mostrerò anche che
dict
può essere molto utile.
- Possono essere usati anche generatori e possono essere utili quando si usa
map
come inmap(f, list_of_df)
Per ora, ci limiteremo a un elenco di alcuni degli oggetti DataFrame
e Series
definiti sopra. Mostrerò in seguito come utilizzare i dizionari per fornire MultiIndex
risultati molto utili .
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
Il secondo argomento che incontriamo è il axis
cui valore predefinito è 0
:
asse : {0 / 'indice', 1 / 'colonne'}, valore predefinito 0 L'asse da concatenare.
Due DataFrame
s con axis=0
(sovrapposti)
Per valori di 0
o index
intendiamo dire: "Allinea lungo le colonne e aggiungi all'indice".
Come mostrato sopra, dove abbiamo usato axis=0
, perché 0
è il valore predefinito, e vediamo che l'indice di d2
estende l'indice di d1
nonostante ci sia una sovrapposizione del valore 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
Due DataFrame
s con axis=1
(fianco a fianco)
Per i valori 1
o columns
intendiamo dire: "Allinea lungo l'indice e aggiungi alle colonne",
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
Possiamo vedere che l'indice risultante è l'unione di indici e le colonne risultanti sono l'estensione delle colonne dalle d1
colonne di d2
.
Due (o tre) Series
con axis=0
(impilati)
Quando si combinano pandas.Series
insieme axis=0
, torniamo a pandas.Series
. Il nome del risultato Series
sarà a None
meno che tutti quelli Series
combinati non abbiano lo stesso nome. Presta attenzione a 'Name: A'
quando stampiamo il risultato Series
. Quando non è presente, possiamo presumere che il Series
nome lo sia 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
Due (o Tre) Series
con axis=1
(fianco a fianco)
Quando si combinano pandas.Series
insieme axis=1
, è l' name
attributo a cui ci riferiamo per dedurre un nome di colonna nel file risultante 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
Misto Series
e DataFrame
con axis=0
(impilato)
Quando si esegue una concatenazione di a Series
e DataFrame
lungo axis=0
, convertiamo tutto Series
in una singola colonna DataFrame
s.
Tieni presente che questa è una concatenazione lungo axis=0
; ciò significa estendere l'indice (righe) mentre si allineano le colonne. Negli esempi seguenti, vediamo l'indice [2, 3, 2, 3]
che diventa un'aggiunta indiscriminata di indici. Le colonne non si sovrappongono a meno che non forzi la denominazione della Series
colonna con l'argomento a 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
Puoi vedere i risultati di pd.concat([s1, d1])
sono gli stessi come se avessi ottenuto il to_frame
me stesso.
Tuttavia, posso controllare il nome della colonna risultante con un parametro a to_frame
. Rinominare Series
con il rename
metodo non controlla il nome della colonna nel file risultante 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
Misto Series
e DataFrame
con axis=1
(fianco a fianco)
Questo è abbastanza intuitivo. Series
il nome della colonna utilizza per impostazione predefinita un'enumerazione di tali Series
oggetti quando un name
attributo non è disponibile.
| 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
Il terzo argomento è join
che descrive se l'unione risultante deve essere un'unione esterna (impostazione predefinita) o un'unione interna.
join : {'inner', 'outer'}, default 'outer'
Come gestire gli indici su altri assi.
Si scopre che non esiste un'opzione left
o in right
quanto pd.concat
può gestire più di due oggetti da unire.
Nel caso di d1
e d2
, le opzioni sono:
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
Il quarto argomento è la cosa che ci permette di fare la nostra left
fusione e altro ancora.
join_axes : elenco di oggetti Index
Indici specifici da utilizzare per gli altri n - 1 assi invece di eseguire la logica degli insiemi interno / esterno.
Unisci a sinistra
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
Unione a destra
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, default False
Se True, non utilizzare i valori di indice lungo l'asse di concatenazione. L'asse risultante sarà etichettato 0, ..., n - 1. Ciò è utile se si stanno concatenando oggetti in cui l'asse di concatenazione non ha informazioni di indicizzazione significative. Notare che i valori di indice sugli altri assi sono ancora rispettati nel join.
Come quando ho impilare d1
in cima d2
, se non mi preoccupo per i valori di indice, li avrei potuto reimpostare o ignorarli.
| 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
E quando si utilizza 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
Possiamo passare un elenco di valori scalari o tuple per assegnare valori tuple o scalari al MultiIndex corrispondente. La lunghezza dell'elenco passato deve essere uguale al numero di elementi da concatenare.
keys : sequence, default None
Se vengono passati più livelli, dovrebbe contenere tuple. Costruisci un indice gerarchico utilizzando le chiavi passate come livello più esterno
axis=0
Quando si concatenano Series
oggetti lungo axis=0
(estensione dell'indice).
Quelle chiavi diventano un nuovo livello iniziale di un MultiIndex
oggetto nell'attributo 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
Tuttavia, possiamo usare più di valori scalari keys
nell'argomento per creare un valore ancora più profondo MultiIndex
. Qui passiamo tuples
di lunghezza 2 anteponendo due nuovi livelli di 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
È un po 'diverso quando si estende lungo le colonne. Quando abbiamo usato axis=0
(vedi sopra) il nostro ha keys
agito come MultiIndex
livelli in aggiunta all'indice esistente. Per axis=1
, ci riferiamo a un asse che gli Series
oggetti non hanno, vale a dire l' columns
attributo.
Variazioni di due
Series
wtih
axis=1
Si noti che denominare s1
e è s2
importante finché non keys
viene passato no , ma viene sovrascritto se keys
vengono passati.
| | | 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
con
Series
e
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
Due
DataFrame
con
axis=1
Come con gli axis=0
esempi, keys
aggiungi i livelli a MultiIndex
, ma questa volta all'oggetto memorizzato columns
nell'attributo.
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
e
DataFrame
con
axis=1
Questo è complicato. In questo caso, un valore chiave scalare non può fungere da unico livello di indice per l' Series
oggetto quando diventa una colonna mentre agisce anche come primo livello di a MultiIndex
per DataFrame
. Quindi Panda utilizzerà nuovamente l' name
attributo Series
dell'oggetto come origine del nome della colonna.
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
Limitazioni
keys
e
MultiIndex
inferrenza.
Panda sembra solo dedurre i nomi delle colonne dal Series
nome, ma non riempirà gli spazi vuoti quando si esegue una concatenazione analoga tra frame di dati con un numero diverso di livelli di colonna.
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
Quindi concatenalo con un altro frame di dati con un solo livello nell'oggetto colonne e Panda si rifiuterà di provare a creare tuple MultiIndex
dell'oggetto e combinare tutti i frame di dati come se fosse un unico livello di oggetti, scalari e tuple.
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
Passando un dict
invece di unlist
Quando si passa un dizionario, pandas.concat
utilizzerà le chiavi del dizionario come keys
parametro.
# 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
Viene utilizzato insieme keys
all'argomento. Quando levels
viene lasciato come valore predefinito di None
, Pandas prenderà i valori univoci di ogni livello del risultato MultiIndex
e li utilizzerà come oggetto utilizzato nell'attributo risultante index.levels
.
livelli : elenco di sequenze, predefinito Nessuno
Livelli specifici (valori univoci) da utilizzare per costruire un MultiIndex. Altrimenti verranno dedotti dalle chiavi.
Se i Panda già deducono quali dovrebbero essere questi livelli, che vantaggio c'è a specificarlo noi stessi? Mostrerò un esempio e lascio a te pensare ad altri motivi per cui questo potrebbe essere utile.
Esempio
Secondo la documentazione, l' levels
argomento è un elenco di sequenze. Ciò significa che possiamo usarne un altro pandas.Index
come una di quelle sequenze.
Si consideri il frame di dati df
che è la concatenazione di d1
, d2
e 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
I livelli dell'oggetto colonne sono:
print(df, *df.columns.levels, sep='\n')
Index(['First', 'Second', 'Fourth'], dtype='object')
Index(['A', 'B', 'C', 'D'], dtype='object')
Se usiamo sum
all'interno di un groupby
otteniamo:
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
Ma cosa succede se al posto di ['First', 'Second', 'Fourth']
altre categorie mancanti denominate Third
e Fifth
? E li volevo inclusi nei risultati di groupby
un'aggregazione? Possiamo farlo se avessimo un file pandas.CategoricalIndex
. E possiamo specificarlo in anticipo con l' levels
argomento.
Quindi, invece, definiamo df
come:
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
Ma il primo livello dell'oggetto delle colonne è:
df.columns.levels[0]
CategoricalIndex(
['First', 'Second', 'Third', 'Fourth', 'Fifth'],
categories=['First', 'Second', 'Third', 'Fourth', 'Fifth'],
ordered=True, dtype='category')
E la nostra groupby
sintesi si presenta come:
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
Viene utilizzato per denominare i livelli di un risultato MultiIndex
. La lunghezza names
dell'elenco deve corrispondere al numero di livelli nel risultato MultiIndex
.
nomi : elenco, predefinito Nessuno
Nomi per i livelli nell'indice gerarchico risultante
# 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
Documentazione autoesplicativa
verifica_integrità : booleano, valore predefinito False
Controlla se il nuovo asse concatenato contiene duplicati. Questo può essere molto costoso rispetto all'effettiva concatenazione dei dati.
Poiché l'indice risultante dalla concatenazione d1
e d2
non è univoco, non supererebbe il controllo di integrità.
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
E
pd.concat([d1, d2], verify_integrity=True)
> ValueError: gli indici hanno valori sovrapposti: [2]