import pandas as pd
Un tableau à 3 dimensions¶
Pandas n'est pas limité à un tableau à 2 dimensions. Il peut avoir autant de dimensions qu'on le souhaite par contre pour les afficher on est bien obligé d'utiliser 2 dimensions. Aussi on utilise les sous-indices (sous-index ou sous-colonne) pour augmenter le nombre de dimensions. Ainsi une valeur d'un tableau peut
être incomes
France
1960
ce qui fait 3 dimensions.
Pour faire notre tableau à 3 dimensions, on va fusionner les 2 tableaux suivants ce qui va générer un double index.
# incomes per person, inflation adjusted, US $ 2000, source World Bank
incomes = pd.DataFrame({'Argentina': [5251, 6611, 7540, 5581, 7695, 10749],
'Egypt': [430, 565, 856, 1153, 1475, 1975],
'France': [7499, 11572, 15641, 18731, 21774, 22758],
'Sweden': [11360, 16570, 19470, 23492, 27869, 32631],
'USA': [13723, 18228, 22630, 28298, 35081, 37329]},
index=[1960,1970,1980,1990,2000,2010])
# children per woman
children = pd.DataFrame({'Argentina': [3.11, 3.07, 3.33, 2.99, 2.48, 2.22],
'Egypt': [6.63, 5.94, 5.37, 4.35, 3.31, 2.88],
'France': [2.77, 2.49, 1.83, 1.75, 1.82, 1.98],
'Sweden': [2.2, 1.92, 1.68, 2.14, 1.56, 1.99],
'USA': [3.67, 2.46, 1.82, 2.07, 2.05, 1.93]},
index=[1960,1970,1980,1990,2000,2010])
# concat is explain in lesson "Pandas 6 -- operations with 2 dataframes"
df = pd.concat({'incomes': incomes, 'children': children})
df
Argentina | Egypt | France | Sweden | USA | ||
---|---|---|---|---|---|---|
incomes | 1960 | 5251.00 | 430.00 | 7499.00 | 11360.00 | 13723.00 |
1970 | 6611.00 | 565.00 | 11572.00 | 16570.00 | 18228.00 | |
1980 | 7540.00 | 856.00 | 15641.00 | 19470.00 | 22630.00 | |
1990 | 5581.00 | 1153.00 | 18731.00 | 23492.00 | 28298.00 | |
2000 | 7695.00 | 1475.00 | 21774.00 | 27869.00 | 35081.00 | |
2010 | 10749.00 | 1975.00 | 22758.00 | 32631.00 | 37329.00 | |
children | 1960 | 3.11 | 6.63 | 2.77 | 2.20 | 3.67 |
1970 | 3.07 | 5.94 | 2.49 | 1.92 | 2.46 | |
1980 | 3.33 | 5.37 | 1.83 | 1.68 | 1.82 | |
1990 | 2.99 | 4.35 | 1.75 | 2.14 | 2.07 | |
2000 | 2.48 | 3.31 | 1.82 | 1.56 | 2.05 | |
2010 | 2.22 | 2.88 | 1.98 | 1.99 | 1.93 |
Le résultat est un tableau avec un multi-index.
df.index
MultiIndex([( 'incomes', 1960), ( 'incomes', 1970), ( 'incomes', 1980), ( 'incomes', 1990), ( 'incomes', 2000), ( 'incomes', 2010), ('children', 1960), ('children', 1970), ('children', 1980), ('children', 1990), ('children', 2000), ('children', 2010)], )
Extraire les données¶
Comme toujours, les données sous-jacentes aux DataFrames sont les colonnes donc ici les pays.
df.Egypt
incomes 1960 430.00 1970 565.00 1980 856.00 1990 1153.00 2000 1475.00 2010 1975.00 children 1960 6.63 1970 5.94 1980 5.37 1990 4.35 2000 3.31 2010 2.88 Name: Egypt, dtype: float64
On peut indiquer l'index ou les index qui nous intéressent et cela de différentes manières :
df.Egypt['incomes']
1960 430.0 1970 565.0 1980 856.0 1990 1153.0 2000 1475.0 2010 1975.0 Name: Egypt, dtype: float64
print("Children per women in the USA in 1970 = ", df.USA['children'][1970])
print("Children per women in the USA in 1990 = ", df.USA['children',1990])
Children per women in the USA in 1970 = 2.46 Children per women in the USA in 1990 = 2.07
display('France data in 2000:', df['France'][:, 2000])
'France data in 2000:'
incomes 21774.00 children 1.82 Name: France, dtype: float64
Extraction d'un bloc¶
Avec loc
on récupère un sous-tableau en spécifiant les valeurs des 2 index et les valeurs des colonnes que l'on désire.
On peut aussi spécifier seulement l'index de niveau 0 ou des tranches en spécifiant chaque sous-index.
df.loc[('incomes', [1960,2010]), :]
Argentina | Egypt | France | Sweden | USA | ||
---|---|---|---|---|---|---|
incomes | 1960 | 5251.0 | 430.0 | 7499.0 | 11360.0 | 13723.0 |
2010 | 10749.0 | 1975.0 | 22758.0 | 32631.0 | 37329.0 |
df.loc['children']
Argentina | Egypt | France | Sweden | USA | |
---|---|---|---|---|---|
1960 | 3.11 | 6.63 | 2.77 | 2.20 | 3.67 |
1970 | 3.07 | 5.94 | 2.49 | 1.92 | 2.46 |
1980 | 3.33 | 5.37 | 1.83 | 1.68 | 1.82 |
1990 | 2.99 | 4.35 | 1.75 | 2.14 | 2.07 |
2000 | 2.48 | 3.31 | 1.82 | 1.56 | 2.05 |
2010 | 2.22 | 2.88 | 1.98 | 1.99 | 1.93 |
df.loc[('incomes',1970):('incomes', 2000), 'Egypt':'Sweden']
Egypt | France | Sweden | ||
---|---|---|---|---|
incomes | 1970 | 565.0 | 11572.0 | 16570.0 |
1980 | 856.0 | 15641.0 | 19470.0 | |
1990 | 1153.0 | 18731.0 | 23492.0 | |
2000 | 1475.0 | 21774.0 | 27869.0 |
Tous les éléments d'un index¶
Pour une bête raison de syntaxe Python il n'est pas possible d'utiliser le signe :
entre des parenthèses et donc
on ne peut pas faire df.loc[(:,1960), :]
pour indiquer toutes les valeurs de 1960. Il faut utiliser au choix :
slice(None)
à la place des:
dans la formulation usuellepd.IndexSlice[:,1960]
Attention df.loc['children','France']
ne marche pas car on ne spécifie pas les années. Par contre df.loc['children']['France']
marche car loc
extrait le sous-tableau des enfants et sur ce résultat, on demande la colonne France.
df.loc[(slice(None), 1960), :]
Argentina | Egypt | France | Sweden | USA | ||
---|---|---|---|---|---|---|
incomes | 1960 | 5251.00 | 430.00 | 7499.00 | 11360.0 | 13723.00 |
children | 1960 | 3.11 | 6.63 | 2.77 | 2.2 | 3.67 |
df.loc[pd.IndexSlice[:, 1990:2010], :]
Argentina | Egypt | France | Sweden | USA | ||
---|---|---|---|---|---|---|
incomes | 1990 | 5581.00 | 1153.00 | 18731.00 | 23492.00 | 28298.00 |
2000 | 7695.00 | 1475.00 | 21774.00 | 27869.00 | 35081.00 | |
2010 | 10749.00 | 1975.00 | 22758.00 | 32631.00 | 37329.00 | |
children | 1990 | 2.99 | 4.35 | 1.75 | 2.14 | 2.07 |
2000 | 2.48 | 3.31 | 1.82 | 1.56 | 2.05 | |
2010 | 2.22 | 2.88 | 1.98 | 1.99 | 1.93 |
Multi-colonnes¶
Multi-index peut aussi être multi-colonnes. Tout est symétrique. On peut aussi avoir multi-index et multi-colonnes et ce avec autant de niveaux de profondeur qu'on veut.
df.T # transpose
incomes | children | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
1960 | 1970 | 1980 | 1990 | 2000 | 2010 | 1960 | 1970 | 1980 | 1990 | 2000 | 2010 | |
Argentina | 5251.0 | 6611.0 | 7540.0 | 5581.0 | 7695.0 | 10749.0 | 3.11 | 3.07 | 3.33 | 2.99 | 2.48 | 2.22 |
Egypt | 430.0 | 565.0 | 856.0 | 1153.0 | 1475.0 | 1975.0 | 6.63 | 5.94 | 5.37 | 4.35 | 3.31 | 2.88 |
France | 7499.0 | 11572.0 | 15641.0 | 18731.0 | 21774.0 | 22758.0 | 2.77 | 2.49 | 1.83 | 1.75 | 1.82 | 1.98 |
Sweden | 11360.0 | 16570.0 | 19470.0 | 23492.0 | 27869.0 | 32631.0 | 2.20 | 1.92 | 1.68 | 2.14 | 1.56 | 1.99 |
USA | 13723.0 | 18228.0 | 22630.0 | 28298.0 | 35081.0 | 37329.0 | 3.67 | 2.46 | 1.82 | 2.07 | 2.05 | 1.93 |
df.T.children[1970]
Argentina 3.07 Egypt 5.94 France 2.49 Sweden 1.92 USA 2.46 Name: 1970, dtype: float64
Colonne vers index et inversement¶
Les index et sous-index ne sont pas figés dans leur état mais peut devenir des colonnes du tableau et
dans l'autre sens des colonnes peuvent servir d'index. Pour cela
set_index
permet de modifier un index soit en donnant
directement ses valeurs, soit en prenant une colonne pour qu'elle devienne l'index (cas le plus courant).
L'inverse se fait avec reset_index
.
df2 = df.reset_index(level=1) # index to column
print(df2.columns)
df2.rename(columns={'level_1':'year'}, inplace=True)
df2
Index(['level_1', 'Argentina', 'Egypt', 'France', 'Sweden', 'USA'], dtype='object')
year | Argentina | Egypt | France | Sweden | USA | |
---|---|---|---|---|---|---|
incomes | 1960 | 5251.00 | 430.00 | 7499.00 | 11360.00 | 13723.00 |
incomes | 1970 | 6611.00 | 565.00 | 11572.00 | 16570.00 | 18228.00 |
incomes | 1980 | 7540.00 | 856.00 | 15641.00 | 19470.00 | 22630.00 |
incomes | 1990 | 5581.00 | 1153.00 | 18731.00 | 23492.00 | 28298.00 |
incomes | 2000 | 7695.00 | 1475.00 | 21774.00 | 27869.00 | 35081.00 |
incomes | 2010 | 10749.00 | 1975.00 | 22758.00 | 32631.00 | 37329.00 |
children | 1960 | 3.11 | 6.63 | 2.77 | 2.20 | 3.67 |
children | 1970 | 3.07 | 5.94 | 2.49 | 1.92 | 2.46 |
children | 1980 | 3.33 | 5.37 | 1.83 | 1.68 | 1.82 |
children | 1990 | 2.99 | 4.35 | 1.75 | 2.14 | 2.07 |
children | 2000 | 2.48 | 3.31 | 1.82 | 1.56 | 2.05 |
children | 2010 | 2.22 | 2.88 | 1.98 | 1.99 | 1.93 |
df2.set_index('year', append=True) # without append it would remove the previous index (try it!)
Argentina | Egypt | France | Sweden | USA | ||
---|---|---|---|---|---|---|
year | ||||||
incomes | 1960 | 5251.00 | 430.00 | 7499.00 | 11360.00 | 13723.00 |
1970 | 6611.00 | 565.00 | 11572.00 | 16570.00 | 18228.00 | |
1980 | 7540.00 | 856.00 | 15641.00 | 19470.00 | 22630.00 | |
1990 | 5581.00 | 1153.00 | 18731.00 | 23492.00 | 28298.00 | |
2000 | 7695.00 | 1475.00 | 21774.00 | 27869.00 | 35081.00 | |
2010 | 10749.00 | 1975.00 | 22758.00 | 32631.00 | 37329.00 | |
children | 1960 | 3.11 | 6.63 | 2.77 | 2.20 | 3.67 |
1970 | 3.07 | 5.94 | 2.49 | 1.92 | 2.46 | |
1980 | 3.33 | 5.37 | 1.83 | 1.68 | 1.82 | |
1990 | 2.99 | 4.35 | 1.75 | 2.14 | 2.07 | |
2000 | 2.48 | 3.31 | 1.82 | 1.56 | 2.05 | |
2010 | 2.22 | 2.88 | 1.98 | 1.99 | 1.93 |
Lorsqu'on ajoute un niveau d'index il peut être utile de trier les index avec sort_index
pour améliorer l'affichage et certains algorithmes qui s'appliqueront sur le DataFrame.
Changer la structure avec stack
et unstack
¶
Il est possible de modifier la structure de notre tableau tout en gardant le nombre de dimensions. Pour notre tableau à 3 dimensions, on peut avoir jusqu'à 3 niveaux d'index ou jusqu'à 3 niveaux de colonnes. Pour modifier la structure
stack
augmente la profondeur de l'index et diminue la profondeur des colonnesunstack
fait l'inverse
# let use a smaller DataFrame
df_small =df.loc[(['incomes','children'], [1970,1990,2010]), ['Argentina','Sweden']]
df_small
Argentina | Sweden | ||
---|---|---|---|
incomes | 1970 | 6611.00 | 16570.00 |
1990 | 5581.00 | 23492.00 | |
2010 | 10749.00 | 32631.00 | |
children | 1970 | 3.07 | 1.92 |
1990 | 2.99 | 2.14 | |
2010 | 2.22 | 1.99 |
df_small.stack()
incomes 1970 Argentina 6611.00 Sweden 16570.00 1990 Argentina 5581.00 Sweden 23492.00 2010 Argentina 10749.00 Sweden 32631.00 children 1970 Argentina 3.07 Sweden 1.92 1990 Argentina 2.99 Sweden 2.14 2010 Argentina 2.22 Sweden 1.99 dtype: float64
Le résultat est une séries avec un multi-index de profondeur de 3.
Dans l'autre sens avec unstack
:
df_small.unstack()
Argentina | Sweden | |||||
---|---|---|---|---|---|---|
1970 | 1990 | 2010 | 1970 | 1990 | 2010 | |
incomes | 6611.00 | 5581.00 | 10749.00 | 16570.00 | 23492.00 | 32631.00 |
children | 3.07 | 2.99 | 2.22 | 1.92 | 2.14 | 1.99 |
df_small.unstack().unstack()
Argentina 1970 incomes 6611.00 children 3.07 1990 incomes 5581.00 children 2.99 2010 incomes 10749.00 children 2.22 Sweden 1970 incomes 16570.00 children 1.92 1990 incomes 23492.00 children 2.14 2010 incomes 32631.00 children 1.99 dtype: float64
Là encore on arrive à une séries mais avec l'ordre des index différent du premier cas puisqu'il s'agit des colonnes, sous-colonnes et sous-sous-colonnes (qui deviennent l'index de la séries).
Opérations directes sur les multi-index¶
- Réordoner Pour avoir les index dans l'ordre de son choix on peut utiliser :
swaplevel
qui permet d'invertir 2 index (utiliseraxis = 'columns'
en cas de multi-colonnes)reorder_levels
qui permet de réordonner globalement les index
- Renomer
set_levels
modifie les valeurs d'un niveau d'un multi-index. - Supprimer
droplevel
efface le niveau d'index indiqué
df_small.reorder_levels([1,0], axis='index').sort_index() # swaplevel(0,1) would have done the same
Argentina | Sweden | ||
---|---|---|---|
1970 | children | 3.07 | 1.92 |
incomes | 6611.00 | 16570.00 | |
1990 | children | 2.99 | 2.14 |
incomes | 5581.00 | 23492.00 | |
2010 | children | 2.22 | 1.99 |
incomes | 10749.00 | 32631.00 |
Pour bien voir regardons avec 3 index :
dfss = df_small.stack()
dfss
incomes 1970 Argentina 6611.00 Sweden 16570.00 1990 Argentina 5581.00 Sweden 23492.00 2010 Argentina 10749.00 Sweden 32631.00 children 1970 Argentina 3.07 Sweden 1.92 1990 Argentina 2.99 Sweden 2.14 2010 Argentina 2.22 Sweden 1.99 dtype: float64
dfss.swaplevel(0,1)
1970 incomes Argentina 6611.00 Sweden 16570.00 1990 incomes Argentina 5581.00 Sweden 23492.00 2010 incomes Argentina 10749.00 Sweden 32631.00 1970 children Argentina 3.07 Sweden 1.92 1990 children Argentina 2.99 Sweden 2.14 2010 children Argentina 2.22 Sweden 1.99 dtype: float64
Ce n'est pas très joli d'avoir plusieurs fois la même année dans l'index (simple problème d'affichage). Pour éviter cela on va trier l'index dans le cas suivant après avoir totalement réordonné l'index.
dfss.reorder_levels([2,1,0]).sort_index()
Argentina 1970 children 3.07 incomes 6611.00 1990 children 2.99 incomes 5581.00 2010 children 2.22 incomes 10749.00 Sweden 1970 children 1.92 incomes 16570.00 1990 children 2.14 incomes 23492.00 2010 children 1.99 incomes 32631.00 dtype: float64
# Renomer
df2 = df.copy()
df2.index = df2.index.set_levels(['enfants', 'revenus'], level=0)
df2
Argentina | Egypt | France | Sweden | USA | ||
---|---|---|---|---|---|---|
enfants | 1960 | 5251.00 | 430.00 | 7499.00 | 11360.00 | 13723.00 |
1970 | 6611.00 | 565.00 | 11572.00 | 16570.00 | 18228.00 | |
1980 | 7540.00 | 856.00 | 15641.00 | 19470.00 | 22630.00 | |
1990 | 5581.00 | 1153.00 | 18731.00 | 23492.00 | 28298.00 | |
2000 | 7695.00 | 1475.00 | 21774.00 | 27869.00 | 35081.00 | |
2010 | 10749.00 | 1975.00 | 22758.00 | 32631.00 | 37329.00 | |
revenus | 1960 | 3.11 | 6.63 | 2.77 | 2.20 | 3.67 |
1970 | 3.07 | 5.94 | 2.49 | 1.92 | 2.46 | |
1980 | 3.33 | 5.37 | 1.83 | 1.68 | 1.82 | |
1990 | 2.99 | 4.35 | 1.75 | 2.14 | 2.07 | |
2000 | 2.48 | 3.31 | 1.82 | 1.56 | 2.05 | |
2010 | 2.22 | 2.88 | 1.98 | 1.99 | 1.93 |
# Effacer
df2.droplevel(1)
Argentina | Egypt | France | Sweden | USA | |
---|---|---|---|---|---|
enfants | 5251.00 | 430.00 | 7499.00 | 11360.00 | 13723.00 |
enfants | 6611.00 | 565.00 | 11572.00 | 16570.00 | 18228.00 |
enfants | 7540.00 | 856.00 | 15641.00 | 19470.00 | 22630.00 |
enfants | 5581.00 | 1153.00 | 18731.00 | 23492.00 | 28298.00 |
enfants | 7695.00 | 1475.00 | 21774.00 | 27869.00 | 35081.00 |
enfants | 10749.00 | 1975.00 | 22758.00 | 32631.00 | 37329.00 |
revenus | 3.11 | 6.63 | 2.77 | 2.20 | 3.67 |
revenus | 3.07 | 5.94 | 2.49 | 1.92 | 2.46 |
revenus | 3.33 | 5.37 | 1.83 | 1.68 | 1.82 |
revenus | 2.99 | 4.35 | 1.75 | 2.14 | 2.07 |
revenus | 2.48 | 3.31 | 1.82 | 1.56 | 2.05 |
revenus | 2.22 | 2.88 | 1.98 | 1.99 | 1.93 |