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 levelsdell'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
Serieso DataFrameoggetti.
- Mostrerò anche che
dictpuò essere molto utile.
- Possono essere usati anche generatori e possono essere utili quando si usa
mapcome inmap(f, list_of_df)
Per ora, ci limiteremo a un elenco di alcuni degli oggetti DataFramee Seriesdefiniti sopra. Mostrerò in seguito come utilizzare i dizionari per fornire MultiIndexrisultati 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 axiscui valore predefinito è 0:
asse : {0 / 'indice', 1 / 'colonne'}, valore predefinito 0 L'asse da concatenare.
Due DataFrames con axis=0(sovrapposti)
Per valori di 0o indexintendiamo 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 d2estende l'indice di d1nonostante 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 DataFrames con axis=1(fianco a fianco)
Per i valori 1o columnsintendiamo 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 d1colonne di d2.
Due (o tre) Seriescon axis=0(impilati)
Quando si combinano pandas.Seriesinsieme axis=0, torniamo a pandas.Series. Il nome del risultato Seriessarà a Nonemeno che tutti quelli Seriescombinati non abbiano lo stesso nome. Presta attenzione a 'Name: A'quando stampiamo il risultato Series. Quando non è presente, possiamo presumere che il Seriesnome 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) Seriescon axis=1(fianco a fianco)
Quando si combinano pandas.Seriesinsieme axis=1, è l' nameattributo 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 Seriese DataFramecon axis=0(impilato)
Quando si esegue una concatenazione di a Seriese DataFramelungo axis=0, convertiamo tutto Seriesin una singola colonna DataFrames.
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 Seriescolonna 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_frameme stesso.
Tuttavia, posso controllare il nome della colonna risultante con un parametro a to_frame. Rinominare Seriescon il renamemetodo 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 Seriese DataFramecon axis=1(fianco a fianco)
Questo è abbastanza intuitivo. Seriesil nome della colonna utilizza per impostazione predefinita un'enumerazione di tali Seriesoggetti quando un nameattributo 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 è joinche 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 lefto in rightquanto pd.concatpuò gestire più di due oggetti da unire.
Nel caso di d1e 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 leftfusione 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 d1in 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 Seriesoggetti lungo axis=0(estensione dell'indice).
Quelle chiavi diventano un nuovo livello iniziale di un MultiIndexoggetto 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 keysnell'argomento per creare un valore ancora più profondo MultiIndex. Qui passiamo tuplesdi 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 keysagito come MultiIndexlivelli in aggiunta all'indice esistente. Per axis=1, ci riferiamo a un asse che gli Seriesoggetti non hanno, vale a dire l' columnsattributo.
Variazioni di due
Serieswtih
axis=1
Si noti che denominare s1e è s2importante finché non keysviene passato no , ma viene sovrascritto se keysvengono 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
MultiIndexcon
Seriese
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
DataFramecon
axis=1
Come con gli axis=0esempi, keysaggiungi i livelli a MultiIndex, ma questa volta all'oggetto memorizzato columnsnell'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
Seriese
DataFramecon
axis=1
Questo è complicato. In questo caso, un valore chiave scalare non può fungere da unico livello di indice per l' Seriesoggetto quando diventa una colonna mentre agisce anche come primo livello di a MultiIndexper DataFrame. Quindi Panda utilizzerà nuovamente l' nameattributo Seriesdell'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
keyse
MultiIndexinferrenza.
Panda sembra solo dedurre i nomi delle colonne dal Seriesnome, 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 MultiIndexdell'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 dictinvece di unlist
Quando si passa un dizionario, pandas.concatutilizzerà le chiavi del dizionario come keysparametro.
# 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 keysall'argomento. Quando levelsviene lasciato come valore predefinito di None, Pandas prenderà i valori univoci di ogni livello del risultato MultiIndexe 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' levelsargomento è un elenco di sequenze. Ciò significa che possiamo usarne un altro pandas.Indexcome una di quelle sequenze.
Si consideri il frame di dati dfche è la concatenazione di d1, d2e 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 sumall'interno di un groupbyotteniamo:
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 Thirde Fifth? E li volevo inclusi nei risultati di groupbyun'aggregazione? Possiamo farlo se avessimo un file pandas.CategoricalIndex. E possiamo specificarlo in anticipo con l' levelsargomento.
Quindi, invece, definiamo dfcome:
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 groupbysintesi 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 namesdell'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 d1e d2non è 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]