En répondant à cette question par moi-même, j'ai appris beaucoup de choses, et j'ai voulu réunir un catalogue d'exemples et quelques explications.
La réponse spécifique au point de la levels
L'argument viendra vers la fin.
pandas.concat
: Le manuel manquant
Lien vers la documentation actuelle
Importation et définition d'objets
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])
Arguments
objs
Le premier argument que nous rencontrons est objs
:
Objets : une séquence ou un mappage d'objets Series, DataFrame ou Panel Si un dict est passé, les clés triées seront utilisées comme argument keys, sauf si un dict est passé, auquel cas les valeurs seront sélectionnées (voir ci-dessous). Tous les objets None seront supprimés silencieusement, sauf s'ils sont tous None, auquel cas une ValueError sera générée.
- On utilise généralement cette méthode avec une liste de
Series
ou DataFrame
objets.
- Je vais montrer que
dict
peut également être très utile.
- Des générateurs peuvent également être utilisés et peuvent être utiles lors de l'utilisation de
map
dans le cas de map(f, list_of_df)
Pour l'instant, nous nous contenterons d'une liste de quelques-uns des éléments suivants DataFrame
et Series
définis ci-dessus. Je montrerai comment les dictionnaires peuvent être exploités pour donner des résultats très utiles MultiIndex
les résultats plus tard.
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
Le deuxième argument que nous rencontrons est axis
dont la valeur par défaut est 0
:
axe : {0/'index', 1/'colonnes'}, par défaut 0 L'axe à concaténer.
Deux DataFrame
s avec axis=0
(empilé)
Pour les valeurs de 0
ou index
nous voulons dire : "Aligner le long des colonnes et ajouter à l'index".
Comme indiqué ci-dessus où nous avons utilisé axis=0
parce que 0
est la valeur par défaut, et nous voyons que l'indice de d2
prolonge l'indice de d1
malgré un chevauchement de la valeur 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
Deux DataFrame
s avec axis=1
(côte à côte)
Pour les valeurs 1
ou columns
nous voulons dire : "Aligner le long de l'index et ajouter aux colonnes",
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
Nous pouvons voir que l'index résultant est l'union des index et que les colonnes résultantes sont l'extension des colonnes de l'index. d1
par les colonnes de d2
.
Deux (ou trois) Series
avec axis=0
(empilé)
En combinant pandas.Series
le long de axis=0
nous obtenons un pandas.Series
. Le nom du fichier résultant Series
sera None
à moins que tous les Series
qui sont combinés portent le même nom. Faites attention à la 'Name: A'
lorsque nous imprimons le résultat Series
. Lorsqu'il n'est pas présent, on peut supposer que la Series
le nom est 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
Deux (ou trois) Series
avec axis=1
(côte à côte)
En combinant pandas.Series
le long de axis=1
c'est le name
auquel nous nous référons pour déduire le nom d'une colonne dans le résultat de la recherche 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
Mixte Series
et DataFrame
avec axis=0
(empilé)
Lorsque vous effectuez une concaténation d'un Series
et DataFrame
le long de axis=0
nous convertissons tous les Series
à une seule colonne DataFrame
s.
Notez bien que c'est une concaténation le long de axis=0
cela signifie étendre l'index (rangées) tout en alignant les colonnes. Dans les exemples ci-dessous, nous voyons que l'index devient [2, 3, 2, 3]
qui est une addition sans discernement d'indices. Les colonnes ne se chevauchent pas, à moins que je ne force l'attribution d'un nom à l'index. Series
avec l'argument de 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
Vous pouvez voir les résultats de pd.concat([s1, d1])
sont les mêmes que si j'avais perfromé les to_frame
moi-même.
Cependant, je peux contrôler le nom de la colonne résultante avec un paramètre de la fonction to_frame
. Renommer le Series
avec le rename
La méthode fait pas contrôler le nom de la colonne dans le résultat 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
Mixte Series
et DataFrame
avec axis=1
(côte à côte)
C'est assez intuitif. Series
Le nom de la colonne est par défaut une énumération de ces colonnes. Series
lorsqu'un name
n'est pas disponible.
| 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
Le troisième argument est join
qui décrit si la fusion résultante doit être une fusion externe (par défaut) ou une fusion interne.
rejoindre : {'inner', 'outer'}, par défaut 'outer'.
Comment gérer les index sur d'autres axes.
Il s'avère qu'il n'y a pas left
ou right
comme pd.concat
peut gérer plus que deux objets à fusionner.
Dans le cas de d1
et d2
les options sont les suivantes :
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
Le quatrième argument est la chose qui nous permet de faire notre left
fusionner et plus encore.
axes_joints Liste des objets de l'index
Index spécifiques à utiliser pour les n - 1 autres axes au lieu d'exécuter la logique de réglage intérieur/extérieur.
Fusion à gauche
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
Fusion à droite
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 : booléen, par défaut False
Si Vrai, ne pas utiliser les valeurs d'index le long de l'axe de concaténation. L'axe résultant sera étiqueté 0, ..., n - 1. Ceci est utile si vous concaténer des objets où l'axe de concaténation ne contient pas d'informations d'indexation significatives. Notez que les valeurs d'indexation des autres axes sont toujours respectées dans la jointure.
Comme quand j'empile d1
sur le dessus de d2
Si je ne me soucie pas des valeurs d'index, je peux les réinitialiser ou les ignorer.
| 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
Et quand on utilise 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
Nous pouvons passer une liste de valeurs scalaires ou de tuples afin d'assigner des valeurs scalaires ou de tuple aux MultiIndex correspondants. La longueur de la liste passée doit être la même que le nombre d'éléments à concaténer.
clés : séquence, par défaut None
Si plusieurs niveaux sont passés, ils doivent contenir des tuples. Construit un index hiérarchique en utilisant les clés passées comme niveau le plus éloigné.
axis=0
Lors de la concaténation Series
des objets le long de axis=0
(extension de l'index).
Ces clés, deviennent un nouveau niveau initial d'une MultiIndex
dans l'attribut 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
Cependant, nous pouvons utiliser plus que des valeurs scalaires dans la fonction keys
pour créer un argument encore plus profond MultiIndex
. Ici, nous passons tuples
de longueur 2, on ajoute deux nouveaux niveaux de 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
C'est un peu différent lorsque l'on s'étend le long des colonnes. Lorsque nous avons utilisé axis=0
(voir ci-dessus) notre keys
a agi en tant que MultiIndex
en plus de l'indice existant. Pour axis=1
nous faisons référence à un axe qui Series
n'ont pas, à savoir le columns
attribut.
Variations de deux Series
avec axis=1
Remarquez que le fait de nommer le s1
et s2
tant qu'aucune keys
sont passés, mais ils sont surchargés si keys
sont passés.
| | | 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
avec Series
et 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
Deux DataFrame
avec axis=1
Comme pour le axis=0
exemples, keys
ajouter des niveaux à un MultiIndex
mais cette fois-ci vers l'objet stocké dans le fichier columns
attribut.
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
et DataFrame
avec axis=1
Ceci est délicat. Dans ce cas, une valeur de clé scalaire ne peut pas servir d'unique niveau d'index pour le fichier Series
lorsqu'il devient une colonne, tout en faisant office de premier niveau d'un objet MultiIndex
pour le DataFrame
. Ainsi, les Pandas utiliseront à nouveau le name
de l'attribut Series
comme source du nom de la colonne.
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
Limites de la keys
et MultiIndex
inférence.
Pandas ne semble déduire les noms de colonnes qu'à partir de Series
mais il ne remplira pas les blancs lors d'une concaténation analogue entre des cadres de données ayant un nombre différent de niveaux de colonnes.
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
Ensuite, concaténons-le avec un autre cadre de données avec un seul niveau dans l'objet colonnes et Pandas refusera d'essayer de faire des tuples de l'objet MultiIndex
et combine tous les cadres de données comme s'il s'agissait d'un seul niveau d'objets, de scalaires et de tuples.
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
Passage d'un dict
au lieu d'un list
Lors du passage d'un dictionnaire, pandas.concat
utilisera les clés du dictionnaire en tant qu'objet keys
paramètre.
# 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
Il est utilisé en conjonction avec le keys
Lorsque levels
est laissé à sa valeur par défaut de None
Pandas prendra les valeurs uniques de chaque niveau de l'arbre résultant. MultiIndex
et l'utiliser comme objet utilisé dans le résultat de l'opération. index.levels
attribut.
niveaux : liste de séquences, par défaut None
Niveaux spécifiques (valeurs uniques) à utiliser pour construire un MultiIndex. Sinon, ils seront déduits des clés.
Si Pandas déduit déjà ce que doivent être ces niveaux, quel avantage y a-t-il à les spécifier nous-mêmes ? Je vais vous montrer un exemple et je vous laisse le soin de trouver d'autres raisons pour lesquelles cela pourrait être utile.
Exemple
Selon la documentation, le levels
est une liste de séquences. Cela signifie que nous pouvons utiliser une autre pandas.Index
comme l'une de ces séquences.
Considérons le cadre de données df
qui est la concaténation de d1
, d2
et 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
Les niveaux de l'objet colonnes sont :
print(df, *df.columns.levels, sep='\n')
Index(['First', 'Second', 'Fourth'], dtype='object')
Index(['A', 'B', 'C', 'D'], dtype='object')
Si nous utilisons sum
dans un groupby
on obtient :
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
Mais si au lieu de ['First', 'Second', 'Fourth']
il y avait d'autres catégories manquantes nommées Third
et Fifth
? Et je voulais qu'ils soient inclus dans les résultats d'un groupby
agrégation ? Nous pouvons le faire si nous avions un pandas.CategoricalIndex
. Et nous pouvons le spécifier à l'avance avec la fonction levels
argument.
Donc, à la place, définissons df
comme :
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
Mais le premier niveau de l'objet colonnes est :
df.columns.levels[0]
CategoricalIndex(
['First', 'Second', 'Third', 'Fourth', 'Fifth'],
categories=['First', 'Second', 'Third', 'Fourth', 'Fifth'],
ordered=True, dtype='category')
Et notre groupby
ressemble à un résumé :
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
Ceci est utilisé pour nommer les niveaux d'un résultat. MultiIndex
. La longueur de la names
doit correspondre au nombre de niveaux dans la liste résultante MultiIndex
.
noms : liste, par défaut None
Noms des niveaux de l'index hiérarchique résultant.
# 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
Documentation auto-explicative
vérifier_intégrité : booléen, par défaut False
Vérifiez si le nouvel axe concaténé contient des doublons. Cela peut être très coûteux par rapport à la concaténation réelle des données.
Parce que l'index résultant de la concaténation d1
et d2
n'est pas unique, il échouerait au contrôle d'intégrité.
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
Et
pd.concat([d1, d2], verify_integrity=True)
> ValueError : Les index ont des valeurs qui se chevauchent : [2]