Cet article fait partie d’une série d’articles sur la syntaxe de base Python.
Un dataframe pandas est une structure de données mutable que l’on peut considérer comme un dictionnaire de séries pandas. Les colonnes sont accessibles en utilisant le nom de la colonne en tant que clé du dictionnaire. La valeur extraite avec la clé est une série qui peut avoir un index personnalisé ou non. Les valeurs d’un dataframe peuvent être de type différent toutefois il est possible d’avoir un type par colonne.
Le but de cet article est de présenter les fonctionnalités de base des dataframes pandas.
On peut importer pandas de cette façon pour utiliser les objets dans la bibliothèque:
import pandas as pd
Initialisation
On peut créer un dataframe à partir d’une liste, d’un dictionnaire de listes, d’une liste de dictionnaires ou d’un dictionnaire de séries pandas.
A partir d’une liste
A partir d’une liste, le dataframe ne contient qu’une seule colonne correspondant aux éléments de la liste. Les lignes possèdent un index par défaut non personnalisé, par exemple:
>>> data = [1, 3, 5, 7, 9, 11]
>>> df = pd.DataFrame(data)
>>> print(df)
0
0 1
1 3
2 5
3 7
4 9
5 11
Avec cet exemple, le dataframe ne contient qu’une seule colonne '0'
. Les index des lignes sont les index par défaut. Sans précision, le type est déterminé en fonction du type des valeurs de la liste:
>>> df.dtypes
0 int64
dtype: object
A partir d’une liste de listes
A partir d’une liste de listes, le dataframe contiendra les listes disposées en colonne. Les lignes possèdent un index par défaut non personnalisé.
Par exemple:
>>> data = [[1, 3, 5], ['1', '3', '5'], [1.0, 3.0, 5.0]]
>>> df = pd.DataFrame(data)
>>> print(df)
0 1 2
0 1 3 5
1 1 3 5
2 1.0 3.0 5.0
Dans cet exemple, les types des objets sont différents pour une même colonne donnée. Ainsi si on regarde le type des objets, ils seront de type object
(qui correspond au type le plus large entre les int64
et les float64
):
>>> df.dtypes
0 object
1 object
2 object
dtype: object
A partir d’un dictionnaire de listes
A partir d’un dictionnaire de listes:
- Les clés du dictionnaire correspondent aux colonnes du dataframe et
- Les listes seront les colonnes du dataframe.
Par exemple:
>>> data = {'a': [1, 3, 5, 7, 9, 11], 'b': ['1', '3', '5', '7', '9', '11'], 'c': [1.0, 3.0, 5.0, 7.0, 9.0, 11.0]}
>>> df = pd.DataFrame(data)
>>> print(df)
a b c
0 1 1 1.0
1 3 3 3.0
2 5 5 5.0
3 7 7 7.0
4 9 9 9.0
5 11 11 11.0
Le type des colonnes est déduit du type des éléments dans les listes:
>>> df.dtypes
a int64
b object
c float64
dtype: object
A partir d’une liste de dictionnaires
Dans le cas d’une liste de dictionnaires:
- Les clés des éléments dans les dictionnaires correspondent aux colonnes du dataframe,
- Les valeurs des éléments dans les dictionnaires correspondent aux lignes du dataframe.
Par exemple:
>>> data = [{'a': 1, 'b': '1', 'c': 1.0}, {'a': 3, 'b': '3', 'c': 3.0}, {'a': 5, 'b': '5', 'c': 5.0},
{'a': 7, 'b': '7', 'c': 7.0}, {'a': 9, 'b': '9', 'c': 9.0}]
>>> df = pd.DataFrame(data)
>>> print(df)
a b c
0 1 1 1.0
1 3 3 3.0
2 5 5 5.0
3 7 7 7.0
4 9 9 9.0
Le type des colonnes est déduit du type des éléments dans les dictionnaires:
>>> df.dtypes
a int64
b object
c float64
dtype: object
Dans le cas où tous les dictionnaires ne contiennent pas toutes les clés, les valeurs manquantes sont remplacées par NaN
.
Par exemple, si on considère la liste de dictionnaires suivantes:
>>> data = [{'a': 1, 'b': '1', 'c': 1.0}, {'a': 3}, {'b': '5'}, {'c': 7.0}]
>>> df = pd.DataFrame(data)
a b c
0 1.0 1 1.0
1 3.0 NaN NaN
2 NaN 5 NaN
3 NaN NaN 7.0
A partir d’un dictionnaire de séries pandas
Si on crée un dataframe à partir d’un dictionnaire de séries, chaque série correspond à une colonne du dataframe.
Par exemple:
>>> data1 = pd.Series([1, 3, 5, 7, 9])
>>> data2 = pd.Series(['1', '3', '5', '7', '9'])
>>> data3 = pd.Series([1.0, 3.0, 5.0, 7.0, 9.0])
>>> df = pd.DataFrame({'a': data1, 'b': data2, 'c': data3 })
>>> print(df)
a b c
0 1 1 1.0
1 3 3 3.0
2 5 5 5.0
3 7 7 7.0
4 9 9 9.0
Le type des colonnes est déduit du type des éléments dans les séries:
>>> df.dtypes
a int64
b object
c float64
dtype: object
A partir d’objets de types différents
On peut effectuer l’initialisation du dataframe avec des objets de type différent. Par exemple, si on considère une liste et une série:
>>> data = {'a': [1, 3, 5], 'b': pd.Series(['1', '3', '5'])}
>>> df = pd.DataFrame(data)
a b
0 1 1
1 3 3
2 5 5
Indiquer explicitement des index (argument index)
On utilise le terme “index” pour désigner les labels utilisés pour identifier les lignes en opposition aux labels utilisés pour les colonnes. Par défaut, les index des éléments sont des entiers à partir de 0
. Avec l’argument index
, on peut explicitement préciser des index, par exemple:
>>> data = {'a': [1, 3, 5], 'b': ['1', '3', '5'], 'c': [1.0, 3.0, 5.0]}
>>> i = ['one', 'two', 'three']
>>> df = pd.DataFrame(data, i)
a b c
one 1 1 1.0
two 3 3 3.0
three 5 5 5.0
On peut aussi indiquer l’index de cette façon à l’initialisation:
>>> df = pd.DataFrame(data, index=i)
Si la taille de l’index ne correspond pas au nombre de lignes du dataframe, une erreur survient:
>>> data = {'a': [1, 3, 5], 'b': ['1', '3', '5'], 'c': [1.0, 3.0, 5.0]}
>>> i = ['one', 'two', 'three', 'four']
ValueError: Length mismatch: Expected axis has 3 elements, new values have 4 elements
On peut affecter un index particulier après initialisation avec la propriété <dataframe>.index
. Il faut que la taille du tableau de l’index soit la même que le nombre de lignes du dataframe.
Par exemple:
>>> data = {'a': [1, 3, 5], 'b': ['1', '3', '5'], 'c': [1.0, 3.0, 5.0]}
>>> df = pd.DataFrame(data)
>>> print(df)
a b c
0 1 1 1.0
1 3 3 3.0
2 5 5 5.0
>>> df.index = i
>>> print(df)
a b c
one 1 1 1.0
two 3 3 3.0
three 5 5 5.0
On peut nommer l’index d’un dataframe avec:
<dataframe>.index.name = <chaine de caractères>
Par exemple:
>>> print(df)
a b c
0 1 1 1.0
1 3 3 3.0
2 5 5 5.0
>>> df.index.name = 'Index name'
>>> print(df)
a b c
Index name
0 1 1 1.0
1 3 3 3.0
2 5 5 5.0
Indiquer explicitement les colonnes (argument columns)
On peut indiquer les colonnes du dataframe avec l’argument columns
. Les noms de colonnes doivent être indiqués sous la forme d’une liste de même taille que le nombre de colonnes:
>>> data = [[1, 3, 5], ['1', '3', '5'], [1.0, 3.0, 5.0]]
>>> c = ['a', 'b', 'c']
>>> df = pd.DataFrame(data, columns=c)
>>> print(df)
a b c
0 1 3 5
1 1 3 5
2 1.0 3.0 5.0
Si le tableau fourni pour le nom des colonnes n’est pas de la même taille que le nombre de colonnes du dataframe, une erreur se produit:
>>> data = [[1, 3, 5], ['1', '3', '5'], [1.0, 3.0, 5.0]]
>>> c = ['a', 'b', 'c', 'd']
>>> df = pd.DataFrame(data, columns=c)
ValueError: 4 columns passed, passed data had 3 columns
On peut affecter la propriété <dataframe>.columns
après initialisation pour préciser les noms de colonnes:
>>> data = [[1, 3, 5], ['1', '3', '5'], [1.0, 3.0, 5.0]]
>>> df = pd.DataFrame(data)
>>> print(df)
0 1 2
0 1 3 5
1 1 3 5
2 1.0 3.0 5.0
>>> c = ['a', 'b', 'c']
>>> df.columns = c
a b c
0 1 3 5
1 1 3 5
2 1.0 3.0 5.0
On peut nommer les colonnes d’un dataframe avec:
<dataframe>.columns.name = <chaîne de caractères>
Par exemple:
>>> print(df)
a b c
0 1 1 1.0
1 3 3 3.0
2 5 5 5.0
>>> df.columns.name = 'Column name'
>>> print(df)
Columns name a b c
0 1 1 1.0
1 3 3 3.0
2 5 5 5.0
Indiquer explicitement le type des valeurs (argument dtype)
Pandas reconnait les types numpy donc on peut utiliser la même syntaxe que pour numpy pour indiquer explicitement un type à l’initialisation, par exemple:
>>> data = {'a': [1, 3, 5], 'b': ['1', '3', '5'], 'c': [1.0, 3.0, 5.0]}
>>> df = pd.DataFrame(data, dtype='f8') # f8 pour flottant sur 8 octets/64 bits
>>> print(df)
a b c
0 1.0 1.0 1.0
1 3.0 3.0 3.0
2 5.0 5.0 5.0
>>> df.dtypes
a float64
b float64
c float64
dtype: object
Tous les éléments sont convertis en flottants si c’est possible.
Initialiser sans effectuer de copies (argument copy)
Par défaut, quand un dataframe pandas est initialisé à partir d’une série pandas, une copie des éléments est effectuée. Il est possible d’effectuer une initialisation du dataframe en utilisant des références vers les objets de la structure d’origine avec l’argument copy
(par défaut, la valeur est copy
est True
):
>>> data = {'a': pd.Series([1, 2, 3, 5])}
>>> df = pd.DataFrame(data, copy=False)
>>> print(df)
a
0 1
1 2
2 3
3 5
>>> print(data)
{'a': 0 1
1 2
2 3
3 5
dtype: int64}
>>> df['a'][1] = 1000
>>> print(data)
{'a': 0 1
1 1000
2 3
3 5
dtype: int64}
L’initialisation du dataframe étant faite avec des références, si on modifie une valeur dans le dataframe alors les éléments dans la structure d’origine sont aussi modifiés.
Lire le contenu d’un dataframe
Les attributs et fonctions suivantes permettent d’obtenir des informations sur le dataframe:
<dataframe>.dtypes
: renvoie une série contenant les types des éléments. L’index de la série correspond aux noms de colonnes du dataframe.
<dataframe>.head()
: renvoie un dataframe contenant les 5 premières lignes du dataframe. <dataframe>.head(8)
renvoie les 8 premières lignes.
<dataframe>.tail()
: renvoie un dataframe contenant les 5 dernières lignes du dataframe. <dataframe>.tail(8)
renvoie les 8 dernières lignes.
<dataframe>.info()
: retourne des informations concernant le dataframe (noms, types des colonnes; nombre des valeurs non-nulles; mémoire occupée par l’instance du dataframe), par exemple:
>>> data = {'a': [1, 3, 5], 'b': ['1', '3', '5'], 'c': [1.0, 3.0, 5.0]}
>>> df = pd.DataFrame(data)
>>> df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 3 entries, 0 to 2
Data columns (total 3 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 a 3 non-null int64
1 b 3 non-null object
2 c 3 non-null float64
dtypes: float64(1), int64(1), object(1)
memory usage: 200.0+ bytes
<dataframe>.columns
: renvoie un objet itérable permettant d’obtenir le nom des colonnes. <dataframe>.columns.values
renvoie les noms de colonnes sous la forme d’un tableau numpy.
<dataframe>.index
: renvoie un objet itérable permettant d’obtenir les index (nom de lignes). <dataframe>.index.values
renvoie les noms des index sous la forme d’un tableau numpy.
<dataframe>.values
: retourne l’ensemble des éléments du dataframe sous la forme d’un tableau numpy.
<dataframe>.describe()
: renvoie des informations statistiques sur les éléments du dataframe quand les éléments sont des valeurs numériques.
Par exemple:
>>> data = {'a': [1, 3, 5], 'b': ['1', '3', '5'], 'c': [1.0, 3.0, 5.0]}
>>> df = pd.DataFrame(data)
>>> df.describe()
a c
count 3.0 3.0
mean 3.0 3.0
std 2.0 2.0
min 1.0 1.0
25% 2.0 2.0
50% 3.0 3.0
75% 4.0 4.0
max 5.0 5.0
<dataframe>.shape
renvoie la taille du dataframe sous la forme d’un tuple.
len(<dataframe>)
ou len(<dataframe>.index)
renvoie le nombre de lignes du dataframe.
len(<dataframe>.columns)
renvoie le nombre de colonnes du dataframe.
<dataframe>.memory_usage()
: retourne la taille occupée pour chaque colonne.
Accéder à une valeur à partir de l’index
Comme indiqué auparavant, un dataframe pandas peut être considéré comme un dictionnaire de séries pandas. On peut accéder à chaque série en utilisant les index des colonnes, par exemple si on considère le dataframe:
>>> data = {'a': ['a0', 'a1', 'a2'], 'b': ['b0', 'b1', 'b2'], 'c': ['c0', 'c1', 'c2']}
>>> i = ['l0', 'l1', 'l2']
>>> df = pd.DataFrame(data, index=i)
>>> print(df)
a b c
l0 a0 b0 c0
l1 a1 b1 c1
l2 a2 b2 c2
Alors df['a']
est une série contenant tous les éléments de la colonne 'a'
:
>>> df['a']
l0 a0
l1 a1
l2 a2
Name: a, dtype: object
On peut accéder directement à un élément en utilisant l’index de la série:
>>> df['a']['l1']
'a1'
loc[] et iloc[]
La fonction loc[]
permet d’accéder à un ou plusieurs éléments en utilisant des index sous la forme <index ligne>, <index colonne>
, par exemple:
>>> print(df)
a b c
l0 a0 b0 c0
l1 a1 b1 c1
l2 a2 b2 c2
>>> df.loc['l1', 'b']
'b1'
Dans cet exemple, il existe 2 index:
- L’index personnalisé paramétré à l’initialisation du dataframe:
- Lignes:
list(df.index)
⇒ ['l0', 'l1', 'l2']
- Colonnes:
list(df.columns)
⇒ ['a', 'b', 'c']
- L’index implicite du dataframe sous la forme numérique.
On peut utiliser loc[]
pour les index personnalisés et iloc[]
pour les index numériques. S’il n’existe pas d’index personnalisé alors loc[]
fonctionne avec des index implicites numériques.
Par exemple:
>>> data1 = {'a': ['a0', 'a1', 'a2'], 'b': ['b0', 'b1', 'b2'], 'c': ['c0', 'c1', 'c2']}
>>> i = ['l0', 'l1', 'l2']
>>> df_custom_indexes = pd.DataFrame(data1, index=i)
>>> print(df_custom_indexes)
a b c
l0 a0 b0 c0
l1 a1 b1 c1
l2 a2 b2 c2
>>> data2 = [['a0', 'b0', 'c0'], ['a1', 'b1', 'c1'], ['a2', 'b2', 'c2']]
>>> df_implicit_indexes = pd.DataFrame(data2)
>>> print(df_implicit_indexes)
0 1 2
0 a0 b0 c0
1 a1 b1 c1
2 a2 b2 c2
Dans le cas des index numériques implicites loc[<index ligne>, <index colonne>]
fonctionne:
>>> df_implicit_indexes.loc[1, 2]
'c1'
Si des index personnalisés existent, loc[]
ne fonctionne pas avec les index numériques:
>>> df_custom_indexes[1, 2]
KeyError: (1, 2)
>>> df_custom_indexes.loc['l1', 'c']
'c1'
iloc[<index ligne>, <index colonne>]
fonctionne dans tous les cas seulement pour des index numériques:
>>> df_implicit_indexes.iloc[1, 2]
'c1'
>>> df_custom_indexes.iloc[1, 2]
'c1'
Sous-ensembles
On peut extraire des sous-ensembles en utilisant des index sous la forme slicing:
[<index de début>:<index de fin exclu>:<pas utilisé>]
Ainsi comme pour les listes python classiques:
[2:]
permet de commencer à l’index 2 (3e élément) jusqu’au dernier.
[:3]
permet de commencer du début jusqu’à l’index 2 (3e élément). L’index (4e élément) est exclu.
[:]
désigne tous les éléments de la liste.
Par exemple, si on considère le dataframe définit plus haut:
>>> print(df_implicit_indexes)
0 1 2
0 a0 b0 c0
1 a1 b1 c1
2 a2 b2 c2
Alors:
df_implicit_indexes.iloc[1, 2]
correspond à l’élément à l’index ligne 1 (2e ligne) et colonne 2 (3e colonne):
'c1'
df_implicit_indexes.iloc[:, 2]
correspond à une série contenant toutes les lignes et la colonne 2 (3e colonne):
0 c0
1 c1
2 c2
Name: 2, dtype: object
df_implicit_indexes.iloc[:, 0:2]
correspond à un dataframe contenant toutes les lignes et les colonnes 0 à 2, la colonne 2 étant exclue:
0 1
0 a0 b0
1 a1 b1
2 a2 b2
df_implicit_indexes.iloc[:, 0:2:2]
correspond à un dataframe contenant toutes les lignes et les colonnes 0 à 2 avec la colonne 2 exclue. Les colonnes seront énumérées par pas de 2:
0
0 a0
1 a1
2 a2
df_implicit_indexes.iloc[0:2, 2]
correspond à une série contenant les lignes 0 à 2 avec 2 exclu et la colonne 2 (3e colonne):
0 c0
1 c1
Name: 2, dtype: object
df_implicit_indexes.iloc[1, [0, 2]]
correspond à une série contenant la ligne 1 (2e ligne) et les colonnes 0 (1ère colonne) et 2 (3e colonne):
0 a1
2 c1
Name: 1, dtype: object
Avec loc[]
et des index personnalisés, on peut aussi utiliser la forme slicing [:]
ou [<index 1>, <index 2>, ..., <index i>]
, par exemple:
at[] et iat[]
at[]
et iat[]
permettent d’extraire des éléments d’un dataframe comme les colonnes respectivement loc[]
et iloc[]
c’est-à-dire on peut utiliser:
at[]
pour les index personnalisés et
iat[]
pour les index numériques. S’il n’existe pas d’index personnalisé alors at[]
fonctionne avec des index implicites numériques.
La différence est qu’avec at[]
et iat[]
, il n’est possible d’utiliser que des index simples sans les formes slicing [:]
ou [<index 1>, <index 2>, ..., <index i>]
. Si on prend les exemples de dataframes définis plus haut:
>>> print(df_implicit_indexes)
0 1 2
0 a0 b0 c0
1 a1 b1 c1
2 a2 b2 c2
Alors:
>>> print(df_custom_indexes)
a b c
l0 a0 b0 c0
l1 a1 b1 c1
l2 a2 b2 c2
Alors:
df_custom_indexes.at['l1', 'c']
renvoie l’élément à la ligne l1
et la colonne c
:
'c1'
df_custom_indexes.at[1, '2]
renvoie une erreur car il existe des index personnalisés pour df_custom_indexes
.
df_implicit_indexes.at[1, 2]
ne renvoie pas d’erreur car il n’existe que des index numériques pour df_implicit_indexes
:
'c1'
Multi-index
Les multi-index permettent d’avoir des index et des index de colonnes sur plusieurs niveaux de façon à identifier des valeurs suivant un ensemble de clés. Pour accéder aux éléments du dataframe, au lieu d’utiliser un index à une valeur, on peut utiliser un agrégat de valeurs. Les multi-index peuvent être utilisés pour identifier des colonnes ou des lignes du dataframe. Plus précisemment, le terme “index” est utilisé pour désigner des labels pour les lignes toutefois le terme “multi-index” est applicable pour les lignes et les colonnes.
Par exemple, si on considère le dataframe suivant:
>>> data = [['a0', 'a1', 'a2', 'a3'], ['b0', 'b1', 'b2', 'b3'], ['c0', 'c1', 'c2', 'c3'], ['d0', 'd1', 'd2', 'd3']]
>>> df = pd.DataFrame(data)
>>> print(df)
0 1 2 3
0 a0 a1 a2 a3
1 b0 b1 b2 b3
2 c0 c1 c2 c3
3 d0 d1 d2 d3
On utilise un index implicite pour les colonnes et les lignes:
>>> list(df.index)
[0, 1, 2, 3]
>>> list(df.columns)
[0, 1, 2, 3]
Si on utilise un index explicite pour les colonnes:
>>> df_index = ['a', 'b', 'c', 'd']
>>> df.columns = df_index
>>> print(df)
a b c d
0 a0 a1 a2 a3
1 b0 b1 b2 b3
2 c0 c1 c2 c3
3 d0 d1 d2 d3
On peut identifier les colonnes en utilisant une valeur parmi les valeurs de l’index explicite:
>>> df.loc[1, 'b']
'b1'
Si on considère le multi-index suivant à 2 niveaux:
>>> df_index_values = [['group1', 'group1', 'group2', 'group2'], ['a', 'b', 'c', 'd']]
>>> df_index = pd.MultiIndex.from_arrays(df_index_values)
On affecte le multi-index aux colonnes du dataframe:
>>> df.columns = df_index
>>> print(df)
group1 group2
a b c d
0 a0 a1 a2 a3
1 b0 b1 b2 b3
2 c0 c1 c2 c3
3 d0 d1 d2 d3
Pour identifier une colonne, on doit utiliser un agrégat de valeurs:
>>> df.loc[1, ('group1', 'b')]
'b1'
On peut utiliser des multi-index sur n
niveaux, par exemple sur 3 niveaux:
>>> df_index_values = [['A', 'B', 'B', 'C'], ['group1', 'group1', 'group2', 'group2'], ['a', 'b', 'c', 'd']]
>>> df_index = pd.MultiIndex.from_arrays(df_index_values)
>>> df.columns = df_index
>>> print(df)
A B C
group1 group1 group2 group2
a b c d
0 a0 a1 a2 a3
1 b0 b1 b2 b3
2 c0 c1 c2 c3
3 d0 d1 d2 d3
Accès aux éléments en utilisant un multi-index
On peut accéder aux éléments du dataframe en utilisant 1, 2 ou 3 niveaux:
>>> print(df.loc[:, 'B']) # 1 niveau
group1 group2
b c
0 a1 a2
1 b1 b2
2 c1 c2
3 d1 d2
>>> print(df.loc[:, ('B', 'group1')]) # 2 niveaux
b
0 a1
1 b1
2 c1
3 d1
>>> df.loc[:, ('B', 'group1', 'b')] # 3 niveaux
0 a1
1 b1
2 c1
3 d1
Name: (B, group1, b), dtype: object
On ne peut pas changer l’ordre des éléments dans le multi-index
Par exemple
>>> print(df.loc[:, ('B', 'group1')]) # OK
b
0 a1
1 b1
2 c1
3 d1
>>> df.loc[:, ('group1', 'B')]
KeyError: ('group1', 'B')
Utilisation de multi-index pour les lignes
On peut utiliser des multi-index pour les lignes:
>>> data = [['a0', 'a1', 'a2', 'a3'], ['b0', 'b1', 'b2', 'b3'], ['c0', 'c1', 'c2', 'c3'], ['d0', 'd1', 'd2', 'd3']]
>>> df = pd.DataFrame(data)
>>> df_index_values = [['A', 'A', 'B', 'B'], ['a', 'b', 'c', 'd']]
>>> df_index = pd.MultiIndex.from_arrays(df_index_values)
>>> df.index = df_index
>>> print(df)
0 1 2 3
A a a0 a1 a2 a3
b b0 b1 b2 b3
B c c0 c1 c2 c3
d d0 d1 d2 d3
Création de multi-index à partir d’un tableau
Comme l’exemple précédent, on peut créer un multi-index à partir d’un tableau en indiquant directement les différents niveaux.
Par exemple pour 2 niveaux:
>>> df_index_values = [['A', 'A', 'B', 'B'], ['a', 'b', 'c', 'd']]
>>> df_index = pd.MultiIndex.from_arrays(df_index_values)
Si on affecte ce multi-index aux colonnes du dataframe d’origine:
>>> df.columns = df_index
>>> print(df)
A B
a b c d
0 a0 a1 a2 a3
1 b0 b1 b2 b3
2 c0 c1 c2 c3
3 d0 d1 d2 d3
Création de multi-index à partir de tuples
La création de multi-index à partir de tuples est assez directe:
>>> df_index_values = [('A', 'a'), ('A', 'b'), ('B', 'c'), ('B', 'd')]
>>> df_index = pd.MultiIndex.from_tuples(df_index_values)
Si on affecte ce multi-index aux colonnes du dataframe d’origine, on obtient le même résultat que précédemment:
>>> df.columns = df_index
>>> print(df)
A B
a b c d
0 a0 a1 a2 a3
1 b0 b1 b2 b3
2 c0 c1 c2 c3
3 d0 d1 d2 d3
Création de multi-index en effectuant un produit cartésien
Pour réduire la complexité de création d’un multi-index, il est possible d’effectuer un produit cartésien entre plusieurs valeurs pour obtenir les différents niveaux, par exemple:
>>> first_level = ['a', 'b']
>>> second_level = ['A', 'B']
>>> df_index = pd.MultiIndex.from_product([first_level, second_level])
Avec le dataframe d’origine, on obtient:
>>> df.columns = df_index
>>> print(df)
a b
A B A B
0 a0 a1 a2 a3
1 b0 b1 b2 b3
2 c0 c1 c2 c3
3 d0 d1 d2 d3
Création de multi-index à partir d’un dataframe
Pour créer un dataframe à partir d’un dataframe
>>> df_multiindex = pd.DataFrame([['1', 'A'], ['1', 'B'], ['2', 'A'], ['2', 'B']])
>>> print(df_multiindex)
0 1
0 1 A
1 1 B
2 2 A
3 2 B
>>> df_index = pd.MultiIndex.from_frame(df_multiindex)
Avec le dataframe d’origine, on obtient:
>>> df.columns = df_index
>>> print(df)
0 1 2
1 A B A B
0 a0 a1 a2 a3
1 b0 b1 b2 b3
2 c0 c1 c2 c3
3 d0 d1 d2 d3
Création de multi-index avec les arguments levels et codes
Les arguments levels
et codes
permettent d’indiquer de quelle façon les niveaux sont définis:
- L’argument
levels
permet d’indiquer un tableau pour lequel chaque ligne contient les labels pour un niveau donné.
- L’argument
codes
utilise un tableau pour lequel chaque ligne indique comment les labels sont utilisés par rapport aux colonnes du dataframe. Ces indications sont faites en indiquant un index de la ligne correspondante dans le tableau de l’argument levels
.
Par exemple on souhaite définir un multi-index à 2 niveaux:
- 1er niveau avec les labels
'1'
et '2'
- 2e niveau avec les labels
'A'
et 'B'
.
Le but étant d’avoir le multi-index suivant:
0 1 2
1 A B A B
L’argument levels
permettant de définir les niveaux contiendra le tableau suivant: [['1', '2'], ['A, 'B']]
.
L’argument codes
va indiquer la façon dont les labels sont utilisés suivant les colonnes du dataframe. Sachant que le dataframe contient 4 colonnes donc chaque ligne du tableau de codes
contiendra 4 éléments. Ces éléments indiquent l’index de la ligne dans levels
à utiliser pour une colonne du dataframe:
- Pour le 1er niveau:
[0, 0, 1, 1]
car 0
est l’index de '1'
et 1
est l’index de '2'
dans ['1', '2']
.
- Pour le 2e niveau:
[0, 1, 0, 1]
car 0
est l’index de 'A'
et 1
est l’index de 'B'
dans ['A', 'B']
.
On définit le multi-index de cette façon:
df_index = pd.MultiIndex(levels = [['1', '2'], ['A', 'B']],
codes = [[0, 0, 1, 1], [0, 1, 0, 1]])
Avec le dataframe d’origine, on obtient:
>>> df.columns = df_index
>>> print(df)
0 1 2
1 A B A B
0 a0 a1 a2 a3
1 b0 b1 b2 b3
2 c0 c1 c2 c3
3 d0 d1 d2 d3
Nommer les niveaux du multi-index
Pour plus de clarté, il est possible de nommer les différents niveaux du multi-index. Il faut utiliser le paramètre names
lors de la création du multi-index.
Par exemple pour un multi-index à 3 niveaux:
>>> df_multiindex = pd.DataFrame([['1', 'A', 'a'], ['1', 'B', 'b'], ['2', 'A', 'c'], ['2', 'B', 'd']])
>>> print(df_multiindex)
0 1 2
0 1 A a
1 1 B b
2 2 A c
3 2 B d
>>> df_index = pd.MultiIndex.from_frame(df_multiindex, names=['level1', 'level2', 'level3'])
En affectant ce multi-index au dataframe d’origine, on peut effectuer le nommage des différents niveaux:
>>> df.columns = df_index
>>> print(df)
level1 1 2
level2 A B A B
level3 a b c d
0 a0 a1 a2 a3
1 b0 b1 b2 b3
2 c0 c1 c2 c3
3 d0 d1 d2 d3
Modifier un dataframe
Un dataframe est un objet mutable. On peut modifier des éléments d’un dataframe en utilisant les mêmes opérateurs ou fonctions utilisés pour accéder à ces éléments. En effet, ces opérateurs et fonctions permettent de renvoyer une référence de l’élément dans le dataframe, on peut donc aussi les utiliser pour modifier la valeur de l’élément.
Ainsi les syntaxes suivantes permettent de modifier un élément dans un dataframe:
<dataframe>[<index colonne>][<index ligne>] = <nouvelle valeur>
<dataframe>.loc[<index ligne>, <index colonne>] = <nouvelle valeur>
<dataframe>.iloc[<index ligne numérique>, <index colonne numérique>] = <nouvelle valeur>
<dataframe>.at[<index ligne>, <index colonne>] = <nouvelle valeur>
<dataframe>.iat[<index ligne numérique>, <index colonne numérique>] = <nouvelle valeur>
Par exemple, si on considère le dataframe suivant:
>>> data = {'a': ['a0', 'a1', 'a2'], 'b': ['b0', 'b1', 'b2'], 'c': ['c0', 'c1', 'c2']}
>>> i = ['l0', 'l1', 'l2']
>>> df = pd.DataFrame(data, index=i)
>>> print(df)
a b c
l0 a0 b0 c0
l1 a1 b1 c1
l2 a2 b2 c2
Les syntaxes suivantes permettent de modifier les éléments dans le dataframe:
>>> df['a']['l1'] = 'NEW - a1'
>>> df.loc['l1', 'b'] = 'NEW - b1'
>>> df.iloc[1, 2] = 'NEW - c1'
>>> df.at['l2', 'a'] = 'NEW - a2'
>>> df.iloc[2, 1] = 'NEW - b2'
>>> print(df)
a b c
l0 a0 b0 c0
l1 NEW - a1 NEW - b1 NEW - c1
l2 NEW - a2 NEW - b2 c2
On peut modifier une colonne entière en affectant une série. Il faut que la série utilise le même index que le dataframe, par exemple si on considère le dataframe d’origine dans l’exemple précédent:
>>> df['b'] = pd.Series(['NEW/b0', 'NEW/b1', 'NEW/b2'], index=i)
>>> print(df)
a b c
l0 a0 NEW/b0 c0
l1 a1 NEW/b1 c1
l2 a2 NEW/b2 c2
Modifier les colonnes ou lignes
Renommer des lignes ou colonnes
On peut affecter de nouveaux labels pour les lignes ou les colonnes en utilisant les propriétés <dataframe>.index
pour les lignes et <dataframe>.columns
pour les colonnes.
Par exemple:
>>> df.columns = ['A', 'B', 'C']
>>> df.index = ['1', '2', '3']
>>> print(df)
A B C
1 a0 b0 c0
2 a1 b1 c1
3 a2 b2 c2
Pour renommer un ou plusieurs noms de lignes/colonnes sans tout modifier, on peut utiliser <dataframe>.rename()
mais il ne faut pas oublier l’argument inplace=True
car rename()
renvoie un nouveau dataframe.
Par exemple:
>>> df.rename(columns = {'a': 'B'}, index = {'l1': 'n1'}, inplace=True)
>>> print(df)
A b c
l0 a0 b0 c0
n1 a1 b1 c1
l2 a2 b2 c2
Ajouter une colonne
On peut ajouter une colonne en affectant une série dans un label de colonne qui n’existe pas. Il faut que l’index de la série soit le même que celui du dataframe.
Par exemple:
>>> print(df)
a b c
l0 a0 b0 c0
l1 a1 b1 c1
l2 a2 b2 c2
>>> df['d'] = pd.Series(['d0', 'd1', 'd2'], index=['l0', 'l1', 'l2'])
>>> print(df)
a b c d
l0 a0 b0 c0 d0
l1 a1 b1 c1 d1
l2 a2 b2 c2 d2
Si on crée la nouvelle colonne de la façon suivante, toutes les valeurs de la colonne contiendront la même valeur:
>>> print(df)
a b c
l0 a0 b0 c0
l1 a1 b1 c1
l2 a2 b2 c2
>>> df['d'] = 'NEW'
>>> print(df)
a b c d
l0 a0 b0 c0 NEW
l1 a1 b1 c1 NEW
l2 a2 b2 c2 NEW
<dataframe>.assign()
On peut utiliser une autre syntaxe avec assign()
pour ajouter une nouvelle colonne. assign()
renvoie un nouveau dataframe, il n’est pas possible d’utiliser un argument inplace
.
Par exemple:
>>> print(df)
a b c
l0 a0 b0 c0
l1 a1 b1 c1
l2 a2 b2 c2
>>> new_column = pd.Series(['NEW/b0', 'NEW/b1', 'NEW/b2'], index=i)
>>> new_df = df.assign(d = new_column)
>>> print(new_df)
a b c d
l0 a0 b0 c0 NEW/b0
l1 a1 b1 c1 NEW/b1
l2 a2 b2 c2 NEW/b2
Ajouter une colonne à un index donné
Avec <dataframe>.insert()
, on peut ajouter une colonne à un index particulier dans un dataframe existant en utilisant la syntaxe:
<dataframe>.insert(<index>, <nom nouvelle colonne>, <nouveaux éléments>)
Par exemple:
>>> print(df)
a b c
l0 a0 b0 c0
l1 a1 b1 c1
l2 a2 b2 c2
>>> df.insert(1, 'd', ['NEW/d0', 'NEW/d1', 'NEW/d2'])
>>> print(df)
a d b c
l0 a0 NEW/d0 b0 c0
l1 a1 NEW/d1 b1 c1
l2 a2 NEW/d2 b2 c2
Changer l’ordre des colonnes
Avec reindex()
, on peut réordonner les colonnes d’un dataframe. La fonction génère un nouveau dataframe:
<dataframe>.reindex(columns=['<colonne 1>', '<colonne 2>', ..., '<colonne N>'])
On peut utiliser l’option copy=False
pour éviter de générer un nouveau dataframe.
Par exemple:
>>> printf(df)
a b c
l0 a0 b0 c0
l1 a1 b1 c1
l2 a2 b2 c2
>>> new_df = df.reindex(columns=['c', 'b', 'a'])
>>> print(new_df)
c b a
l0 c0 b0 a0
l1 c1 b1 a1
l2 c2 b2 a2
Une autre méthode est d’utiliser cette syntaxe (le résultat est le même):
>>> new_df = df[['c', 'b', 'a']]
Supprimer des colonnes
Plusieurs syntaxes sont possibles pour supprimer une ou plusieurs colonnes. Pour supprimer une colonne dans un dataframe en modifiant l’instance directement:
del <dataframe>['<colonne i>']
Par exemple:
>>> printf(df)
a b c
l0 a0 b0 c0
l1 a1 b1 c1
l2 a2 b2 c2
>>> del df['c']
>>> print(df)
a b
l0 a0 b0
l1 a1 b1
l2 a2 b2
Les syntaxes suivantes sont équivalentes et permettent de supprimer plusieurs colonnes en générant un nouveau dataframe. Le dataframe courant n’est pas modifié. Pour modifier le dataframe courant, il faut rajouter l’option inplace = True
.
<dataframe>.drop(['<colonne x>', '<colonne y>', '<colonne z>'], axis = 1)
<dataframe>.drop(columns = ['<colonne x>', '<colonne y>', '<colonne z>'])
La fonction <dataframe>.pop('<colonne i>')
permet de supprimer une colonne et de renvoyer la série correspondant à la colonne supprimée.
Par exemple:
>>> print(df)
a b c
l0 a0 b0 c0
l1 a1 b1 c1
l2 a2 b2 c2
>>> deleted_column = df.pop('b')
>>> deleted_column
l0 b0
l1 b1
l2 b2
Name: b, dtype: object
Supprimer des lignes
Les syntaxes suivantes permettent de supprimer des lignes d’un dataframe en générant un nouveau dataframe. Le dataframe courant n’est pas modifié. Pour modifier le dataframe courant, il faut rajouter l’option inplace = True
.
<dataframe>.drop(['<ligne x>', '<ligne y>', '<ligne z>'])
<dataframe>.drop(index = ['<ligne x>', '<ligne y>', '<ligne z>'])
Modification de valeurs d’un dataframe
De nombreuses fonctions existent pour modifier directement un dataframe sans avoir à exécuter une boucle pour parcourir toutes les valeurs du dataframe:
<dataframe>.astype(<nouveau type>)
: modifie le type de toutes les valeurs d’un dataframe en générant un nouveau dataframe. Le type est le même que celui indiqué lors de l’initialisation. On peut utiliser l’option copy=False
pour modifier le dataframe courant au lieu de générer un nouveau dataframe.
<dataframe>.astype({'colonne x': <type 1>, 'colonne y': <type 2>, 'colonne z': <type 3>})
: permet de modifier le type de plusieurs colonnes. Le type est le même que celui indiqué lors de l’initialisation. On peut utiliser l’option copy=False
pour modifier le dataframe courant au lieu de générer un nouveau dataframe.
<dataframe>.replace(<valeur à remplacer>, <valeur remplaçante>)
: permet de remplacer toute occurence d’une valeur dans un dataframe par une autre. Cette fonction génère un nouveau dataframe, pour modifier le dataframe courant, il faut utiliser l’option: inplace=True
.
On peut utiliser des syntaxes différentes pour effectuer plusieurs remplacements en une fois:
<dataframe>.replace([<valeur à remplacer 1>, <valeur remplaçante 1>], [<valeur à remplacer 2>, <valeur remplaçante 2>], [<valeur à remplacer 3>, <valeur remplaçante 3>])
<dataframe>.replace({<valeur à remplacer 1>: <valeur remplaçante 1>, <valeur à remplacer 2>: <valeur remplaçante 2>, <valeur à remplacer 3>: <valeur remplaçante 3>)
<dataframe>.fillna(<valeur remplaçante>)
: remplacer tous les NaN
par une valeur particulière. Cette fonction génère un nouveau dataframe, pour modifier le dataframe courant, il faut utiliser l’option: inplace=True
.
<dataframe>.dropna()
: supprime toutes les lignes contenant au moins une valeur NaN
. Un nouveau dataframe est renvoyé, le dataframe courant n’est pas modifié. On peut utiliser les options suivantes:
how = 'all'
: une ligne est supprimée si toutes les valeurs de cette ligne sont NaN
.
inplace=True
pour modifier le dataframe courant.
axis = 1
pour supprimer les colonnes contenant des valeurs NaN
plutôt que des lignes.
<dataframe>.apply(<lambda>)
: applique une lambda à toutes les valeurs d’un dataframe. Le dataframe courant n’est pas modifié, le type de la sortie dépend des options.
Par exemple, on considère le dataframe suivant:
>>> df = pd.DataFrame({'a': [2, 3, 4], 'b': [5, 6, 7], 'c': [8, 9, 10]},
index=['l0', 'l1', 'l2'])
>>> print(df)
a b c
l0 2 5 8
l1 3 6 9
l2 4 7 10
- Par défaut et sans option, la lambda est exécutée pour chaque valeur du dataframe d’origine:
>>> print(df.apply(lambda x: x ** 2))
a b c
l0 4 25 64
l1 9 36 81
l2 16 49 100
La taille et la structure du dataframe d’origine sont maintenues.
- Avec l’option
axis = 0
: la lambda est appliquée sur chaque colonne. Le type de l’objet passé en paramètre de la lambda est une série. La sortie de la lambda n’est pas forcément dans la même dimension que l’entrée:
>>> print(df.apply(lambda x: x['l0'] + x['l1'] + x['l2'], axis = 0))
a 9
b 18
c 27
dtype: int64
La traitement se fait en colonne (par série). La sortie de la lambda est une valeur scalaire alors la sortie de df.apply()
est une série.
>>> print(df.apply(lambda x: [x['l0'], x['l1']], axis = 0))
a b c
0 2 5 8
1 3 6 9
La sortie de la lambda est un tableau avec 2 valeurs donc la sortie de df.apply()
est un dataframe à 2 lignes. La dimension de l’objet en sortie dépend de la sortie de la lambda.
- Avec l’option
axis = 1
: la lambda est appliquée sur chaque ligne. Le type de l’objet passé en paramètre de la lambda est une série. La sortie de la lambda n’est pas forcément dans la même dimension que l’entrée:
>>> print(df.apply(lambda x: x['a'] + x['b'] + x['c'], axis = 1))
l0 15
l1 18
l2 21
dtype: int64
La traitement se fait en ligne. La sortie de la lambda est une valeur scalaire alors la sortie de df.apply()
est une série.
>>> print(df.apply(lambda x: pd.Series([x['a'], x['b']]), axis = 1))
0 1
l0 2 5
l1 3 6
l2 4 7
Si la sortie de la lambda est une série, alors la sortie de df.apply()
sera un dataframe.
Pour avoir des labels de colonnes particuliers il faut indiquer un index dans la série en sortie de la lambda:
>>> print(df.apply(lambda x: pd.Series([x['a'], x['b']], index=['c0', 'c1']), axis = 1))
c0 c1
l0 2 5
l1 3 6
l2 4 7
>>> print(df.apply(lambda x: [x['a'], x['b']], axis = 1))
l0 [2, 5]
l1 [3, 6]
l2 [4, 7]
dtype: object
Si la sortie de la lambda est un tableau, alors la sortie de df.apply()
est une série de tableaux.
result_type = 'expand'
permet d’indiquer que la sortie de la lambda sera considérée comme des colonnes. Si on renvoie un tableau alors les éléments du tableau seront des colonnes:
>>> print(df.apply(lambda x: [x['a'], x['b']], axis = 1, result_type='expand'))
0 1
l0 2 5
l1 3 6
l2 4 7
Chaque élément du tableau de sortie de la lambda est considéré comme un élément de colonne du dataframe en sortie. Les labels des colonnes d’origine sont toutefois perdus.
result_type = 'reduce'
permet d’indiquer que la sortie de la lambda sera considérée comme une réduction des colonnes du dataframe d’origine. Si la lambda renvoie un tableau, la sortie de df.apply()
sera une série de tableau:
>>> print(df.apply(lambda x: [x['a'], x['b']], axis = 1, result_type='reduce'))
l0 [2, 5]
l1 [3, 6]
l2 [4, 7]
dtype: object
result_type = 'broadcast'
permet d’indiquer que la structure du dataframe d’origine sera préservée donc les labels des colonnes seront maintenus en sortie. Il n’est pas nécessaire que la lambda génère une série ou d’indiquer des index en sortie de la lambda:
>>> print(df.apply(lambda x: [x['a'], x['b'], x['c']], axis = 1, result_type='broadcast'))
a b c
l0 2 5 8
l1 3 6 9
l2 4 7 10
Les labels des colonnes sont maintenus dans le dataframe en sortie.
Modifier les valeurs d’un dataframe par condition
Il est possible de modifier plusieurs valeurs d’un dataframe en une seule ligne en indiquant des conditions.
Par exemple, si on considère le dataframe suivant:
>>> df = pd.DataFrame({'a': [2, 3, 4], 'b': [5, 6, 7], 'c': [8, 9, 10]},
index=['l0', 'l1', 'l2'])
>>> print(df)
a b c
l0 2 5 8
l1 3 6 9
l2 4 7 10
On peut modifier toutes les valeurs d’une colonne de cette façon:
>>> df['b'] = 0
>>> print(df)
a b c
l0 2 0 8
l1 3 0 9
l2 4 0 10
On peut indiquer une condition valable pour toutes les valeurs du dataframe:
>>> print(df)
a b c
l0 2 5 8
l1 3 6 9
l2 4 7 10
>>> df[df > 6] = 0
>>> print(df)
a b c
l0 2 5 0
l1 3 6 0
l2 4 0 0
On affectera 0
à toutes les valeurs strictement supérieures à 6
.
On peut aussi modifier une colonne suivant des conditions appliquées sur d’autres colonnes. Par exemple, si un élément de la colonne 'a' >= 3
alors on affecte 0
à l’élément de la même ligne dans la colonne 'b'
:
>>> print(df)
a b c
l0 2 5 8
l1 3 6 9
l2 4 7 10
>>> df.loc[df['a'] >= 3,'b'] = 0
>>> print(df)
a b c
l0 2 5 8
l1 3 0 9
l2 4 0 10
Modifier un dataframe en appliquant des fonctions
<dataframe>.drop_duplicates()
Cette fonction permet de supprimer les lignes redondantes dans un dataframe et de ne conserver qu’une seule occurence. Par défaut, la fonction renvoie un dataframe avec les duplicats supprimés. Cette fonction peut être utilisée avec les options suivantes:
inplace = True
: permet de modifier le dataframe courant plutôt que de renvoyer un dataframe avec les modifications.
keep = False
pour ne pas conserver au moins une ligne parmi les duplicats. keep = 'last'
permet de ne conserver que la dernière occurence parmi les duplicats et keep = 'first'
est la valeur par défaut, seule la 1ère occurence est conservée.
subset = [<nom des colonnes>]
: permet d’indiquer quelles sont les colonnes à vérifier pour trouver les duplicats. Par défaut, toutes les colonnes sont vérifiées.
Par exemple, si on considère le dataframe suivant:
>>> df = pd.DataFrame([['1', 'A', 'a'], ['1', 'B', 'b'], ['1', 'B', 'c'], ['2', 'A', 'c'], ['2', 'B', 'd'], ['2', 'B', 'd']])
>>> print(df)
0 1 2
0 1 A a
1 1 B b
2 1 B c
3 2 A c
4 2 B d
5 2 B d
Par défaut, toutes les colonnes sont vérifiées et seule la 1ère occurence est conservée:
>>> print(df.drop_duplicates())
0 1 2
0 1 A a
1 1 B b
2 1 B c
3 2 A c
4 2 B d
Avec subset = [0]
, seule la colonne 0
est vérifiée pour trouver les duplicats:
>>> print(df.drop_duplicates(subset = [0]))
0 1 2
0 1 A a
3 2 A c
Avec keep = 'last'
, les duplicats sont cherchés en vérifiant la colonne 0
et on ne garde que la dernière occurence.
>>> print(df.drop_duplicates(subset = [0], keep = 'last'))
0 1 2
2 1 B c
5 2 B d
Tri des index et colonnes avec <dataframe>.sort_index()
<dataframe>.sort_index()
permet de trier les éléments d’un dataframe suivant les index ou les colonnes. Par défaut le tri se fait dans le sens ascendant sur les index et en générant un nouveau dataframe. On peut utiliser les options suivantes:
inplace = True
pour modifier le dataframe courant.
ascending = False
pour trier par ordre décroissant.
axis=1
pour trier suivant les noms de colonnes.
level = <int>
pour indiquer le niveau à trier dans le cas d’un index multi-niveau.
Par exemple:
>>> df = pd.DataFrame([['1', 'A', 'a'], ['1', 'B', 'b'], ['1', 'C', 'c']],
columns = ['c', 'b', 'a'],
index = ['z', 'y', 'x'])
>>> print(df)
c b a
z 1 A a
y 1 B b
x 1 C c
>>> print(df.sort_index())
c b a
x 1 C c
y 1 B b
z 1 A a
>>> print(df.sort_index(axis=1, ascending=True))
a b c
z a A 1
y b B 1
x c C 1
Tri des valeurs avec <dataframe>.sort_values()
<dataframe>.sort_values()
permet de trier les valeurs d’un dataframe. Il faut indiquer la colonne ou les colonnes à trier avec l’argument by
. Par défaut le tri se fait dans le sens ascendant en générant un nouveau dataframe. On peut utiliser les options suivantes:
inplace = True
pour modifier le dataframe courant.
ascending = False
pour trier par ordre décroissant.
axis=1
pour trier suivant les noms de colonnes.
na_position
indique où les valeurs NaN
seront placées:
'first'
: elles seront placées avant les valeurs triées
'last'
: elles seront placées après les valeurs triées
key
: permet d’indiquer une lambda qui sera utilisée pour appliquer un tri particulier sur les colonnes indiquées avec by
. Le type de la valeur d’entrée de la lambda sera une série.
Par exemple:
>>> df = pd.DataFrame([['1', 'A', 'a', np.NaN], ['2', 'B', 'b', '0'], ['3', 'C', 'c', '1'], ['4', 'D', 'd', np.NaN]],
columns = ['a', 'b', 'c', 'd'],
index = ['w', 'x', 'y', 'z'])
>>> print(df)
a b c d
w 1 A a NaN
x 2 B b 0
y 3 C c 1
z 4 D d NaN
>>> print(df.sort_values(by = 'd', na_position='first'))
a b c d
w 1 A a NaN
z 4 D d NaN
x 2 B b 0
y 3 C c 1
On trie suivant la colonne 'd'
et on place les valeurs NaN
avant les valeurs triées.
>>> print(df.sort_values(by = ['d', 'c'], na_position='last'))
a b c d
0 2 B b 0
1 3 C c 1
2 1 A a NaN
3 4 D d NaN
On trie suivant 2 colonnes 'd'
et 'c'
et on place les valeurs NaN
après les valeurs triées.
>>> print(df.sort_values(by = ['a'], key=lambda x: x.apply(lambda y: -int(y))))
a b c d
z 4 D d NaN
y 3 C c 1
x 2 B b 0
w 1 A a NaN
On effectue le tri suivant la colonne 'a'
et on applique la lambda x: x.apply(lambda y: -int(y))
à la colonne 'a'
avant d’effectuer le tri. x
dans la lambda est une série. x.apply()
permet d’appliquer une autre lambda aux valeurs de la série.
La série est entrée de la lambda est:
>>> df['a']
w 1
x 2
y 3
z 4
Name: a, dtype: object
Après application de la lambda:
>>> df['a'].apply(lambda y: -int(y))
w -1
x -2
y -3
z -4
Name: a, dtype: int64
Agrégation
Avec <dataframe>.groupby()
La fonction <dataframe>.groupby()
permet de calculer des agrégats suivant une colonne, plusieurs colonnes ou suivant un mapping particulier. L’argument by
permet d’indiquer les groupes des agrégats en indiquant:
- Une colonne:
<dataframe>.groupby([<nom colonne>])
- Plusieurs colonnes:
<dataframe>.groupby([<nom colonne 1>, <nom colonne 2>, ... , <nom colonne i>])
- Une fonction de mapping:
<dataframe>.groupby(<fonction effectuant le mapping>)
Il est possible d’affiner les critères du groupby
en précisant certains arguments:
axis=1
pour trier suivant les noms de colonnes.
level = <int>
pour indiquer le niveau à trier dans le cas d’un index multi-niveau.
sort=False
pour ne pas trier les groupes.
group_keys=True
: si on utilise apply()
en sortie du groupby()
, cet argument permet d’indiquer si on veut rajouter un index indiquant les clés de groupage.
dropna=False
pour ne pas ignorer les valeurs NaN
.
Le type de retour de la fonction <dataframe>.groupby()
est pandas.core.groupby.generic.DataFrameGroupBy
. Ce type autorise d’appliquer des méthodes lors de l’agrégation. Si la colonne ne permet pas l’agrégation, elle sera ignorée:
size()
pour préciser le nombre de lignes par groupe.
sum()
pour sommer les valeurs des groupes. Les colonnes où une somme n’est pas possible seront ignorées.
min()
pour indiquer le minimum des valeurs groupées.
max()
pour indiquer le maximum des valeurs groupées.
describe()
pour générer des statistiques descriptives.
count()
pour compter le nombre de groupes.
first()
pour renvoyer la 1ère ligne de chaque groupe.
last()
pour renvoyer la dernière ligne de chaque groupe.
nth()
pour renvoyer la n-ième ligne de chaque groupe.
std()
pour renvoyer l’écart-type (i.e. standard déviation) pour chaque groupe.
var()
pour renvoyer la variance pour chaque groupe.
sem()
pour renvoyer l’erreur type de la moyenne pour chaque groupe.
apply(<lambda>)
pour effectuer un traitement sur les groupes d’agrégation.
Par exemple:
>>> df = pd.DataFrame([['1', 'A', 10, np.NaN], ['2', 'B', 15, 3], ['1', 'B', 5, 1], ['2', 'D', 35, np.NaN], ['2', 'A', 25, 3]],
columns = ['a', 'b', 'c', 'd'])
>>> print(df)
a b c d
0 1 A 10 NaN
1 2 B 15 3.0
2 1 B 5 1.0
3 2 D 35 NaN
4 2 A 25 3.0
>>> df.groupby(['a'])
<pandas.core.groupby.generic.DataFrameGroupBy object at 0xffff58e920d0>
Le résultat d’une agrégation est de type DataFrameGroupBy
sur lequel on peut appliquer les fonctions d’agrégation:
>>> print(df.groupby(['a']).sum())
c d
a
1 15 1.0
2 75 6.0
La somme est effectuée sur les colonnes où c’est possible. Les autres colonnes sont ignorées.
>>> print(df.groupby(['a', 'b']).sum())
c d
a b
1 A 10 0.0
B 5 1.0
2 A 25 3.0
B 15 3.0
D 35 0.0
On peut effectuer l’agrégation sur plusieurs colonnes. Le dataframe résultant contient un index multi-niveau.
Si on applique l’agrégation suivant la colonne 'd'
, inclure ou non les valeurs NaN
produit des résultats différents:
>>> print(df.groupby(['d']).sum())
c
d
1.0 5
3.0 40
>>> print(df.groupby(['d'], dropna=False).sum())
c
d
1.0 5
3.0 40
NaN 45
Si on effectue des agrégations en indiquant une fonction:
>>> def make_group(input_value):
if input_value < 2:
return 'group1'
elif input_value == 2:
return 'group2'
else:
return 'group3'
>>> print(df.groupby(make_group).sum())
c d
group1 25 3.0
group2 5 1.0
group3 60 3.0
Si on applique la fonction d’agrégation avec apply()
:
>>> df.groupby(['a']).apply(lambda x: x['b'])
a
1 0 A
2 B
2 1 B
3 D
4 A
Name: b, dtype: object
Le résultat est sous la forme d’une série avec un index multi-niveau. On peut éviter d’avoir un index correspondant à la clé d’agrégation avec l’option group_keys=False
:
>>> df.groupby(['a'], group_keys=False).apply(lambda x: x['b'])
0 A
2 B
1 B
3 D
4 A
Name: b, dtype: object
Fonction d’agrégation aggregate() ou agg()
La fonction aggregate() ou agg() permet d’appliquer un traitement plus sophistiquée sur des agrégations. Elle s’applique sur un type pandas.core.groupby.generic.DataFrameGroupBy
à la suite d’une fonction <dataframe>.groupby()
.
La fonction aggregate()
ou agg()
prend comme argument:
- Une fonction, par exemple
np.sum
pour effectuer une somme des agrégats ou,
- Une chaine de caractère indiquant le nom d’une fonction, par exemple
'sum'
ou,
- Une liste de fonctions à appliquer en indiquant directement la fonction ou en indiquant son nom, par exemple
[np.mean, 'sum']
et,
- Un dictionnaire permettant d’indiquer un nom de colonne et la fonction à appliquer et,
- Des indications sur le nom de la colonne résultante, la colonne du dataframe où la fonction s’applique et la fonction à appliquer.
On peut utiliser les mêmes fonctions d’agrégation que celles indiquées plus haut:
np.size
ou 'size'
pour préciser le nombre de lignes par groupe.
np.sum
ou 'sum'
pour sommer les valeurs des groupes. Les colonnes où une somme n’est pas possible seront ignorées.
np.min
ou 'min'
pour indiquer le minimum des valeurs groupées.
np.max
ou 'max'
pour indiquer le maximum des valeurs groupées.
'describe'
pour générer des statistiques descriptives.
'count'
pour compter le nombre de groupes.
'first'
pour renvoyer la 1ère ligne de chaque groupe.
'last'
pour renvoyer la dernière ligne de chaque groupe.
np.std
ou 'std'
pour renvoyer l’écart-type (i.e. standard déviation) pour chaque groupe.
np.var
ou 'var'
pour renvoyer la variance pour chaque groupe.
'sem'
pour renvoyer l’erreur type de la moyenne pour chaque groupe.
Par exemple, si on considère le dataframe suivant:
>>> df = pd.DataFrame([['1', 'A', 10, np.NaN], ['2', 'B', 15, 3], ['1', 'B', 5, 1], ['2', 'D', 35, np.NaN], ['2', 'A', 25, 3]],
columns = ['a', 'b', 'c', 'd'])
>>> print(df)
a b c d
0 1 A 10 NaN
1 2 B 15 3.0
2 1 B 5 1.0
3 2 D 35 NaN
4 2 A 25 3.0
On peut effectuer une agrégation sur une colonne et effectuer la somme du contenu des autres colonnes en indiquant directement la fonction à exécuter:
>>> print(df.groupby(['a']).agg(np.sum))
c d
a
1 15 1.0
2 75 6.0
En indiquant le nom de la fonction:
>>> print(df.groupby(['a']).agg('sum'))
c d
a
1 15 1.0
2 75 6.0
En indiquant plusieurs fonctions à exécuter dans une liste (les fonctions s’appliquent à toutes les colonnes):
>>> print(df.groupby(['a']).agg([np.mean, 'sum']))
c d
mean sum mean sum
a
1 7.5 15 1.0 1.0
2 25.0 75 3.0 6.0
En utilisant un dictionnaire, on peut indiquer la colonne sur laquelle on souhaite appliquer une fonction:
>>> print(df.groupby(['a']).agg({'c': np.mean, 'd': 'sum'}))
c d
a
1 7.5 1.0
2 25.0 6.0
On peut créer de nouvelles colonnes en indiquant le nom de la colonne résultante, la colonne du dataframe où la fonction s’applique et la fonction à appliquer:
>>> print(df.groupby(['a']).agg(moyenne_c = ('c', np.mean), sum_d = ('d', 'sum')))
moyenne_c sum_d
a
1 7.5 1.0
2 25.0 6.0
Fonctions mathématiques
Pour les fonctions suivantes, l’opération est appliquée, par défaut, pour chaque colonne. Le résultat est renvoyé sous la forme d’une série.
Pour que l’opération soit effectuée suivant les lignes, il faut utiliser l’argument axis = 1
.
Quelques fonctions:
Fonction |
Explication |
<dataframe>.mean() |
Moyenne des valeurs de chaque colonne. |
<dataframe>.median() |
Moyenne médiane des valeurs de chaque colonne. |
<dataframe>.min() |
Minimum pour chaque colonne. |
<dataframe>.max() |
Maximum pour chaque colonne. |
<dataframe>.std() |
Ecart-type pour chaque colonne. |
<dataframe>.var() |
Variance pour chaque colonne. |
<dataframe>.idxmax() |
Index du maximum de chaque colonne. |
<dataframe>.idxmin() |
Index du minimim de chaque colonne. |
<dataframe>.transpose() ou <dataframe>.T |
Transposée du dataframe. |
<dataframe>.round(<nombre de décimales>) |
Arrondi de toutes les valeurs du dataframe. |
<dataframe>.abs() |
Valeur absolue des valeurs numériques. |
Opérations sur les dataframes
Lorsque qu’une opération parmi les opérations suivantes est appliquée sur des dataframes pandas, les opérations ne sont pas appliquées au sens mathématique comme s’il s’agissait de matrices: l’opération est appliquée sur chaque élément du dataframe. Dans le cas où 2 dataframes sont concernés, les calculs sont appliqués entre les éléments des dataframes situés à la même position.
Par exemple si on considère 2 dataframes:
>>> df1 = pd.DataFrame([[1, 2, 3, 4], [10, 20, 30, 40], [100, 200, 300, 400], [1000, 2000, 3000, 4000]])
>>> print(df1)
0 1 2 3
0 1 2 3 4
1 10 20 30 40
2 100 200 300 400
3 1000 2000 3000 4000
>>> df2 = pd.DataFrame([[1, 2, 3, 4], [11, 21, 31, 41], [101, 201, 301, 401], [1001, 2001, 3001, 4001]])
>>> print(df2)
0 1 2 3
0 1 2 3 4
1 10 20 30 40
2 100 200 300 400
3 1000 2000 3000 4000
Si on applique l’opération df1 * df2
, on peut voir qu’il ne s’agit pas d’une multiplication de matrice:
>>> print(df1 * df2)
0 1 2 3
0 1 4 9 16
1 110 420 930 1640
2 10100 40200 90300 160400
3 1001000 4002000 9003000 16004000
Les opérations suivantes sont applicables:
- Les opérations arithmétiques classiques:
+
ou <dataframe1>.add(<dataframe2>)
,
-
ou <dataframe1>.sub(<dataframe2>)
,
/
ou <dataframe1>.div(<dataframe2>)
,
*
ou <dataframe1>.mul(<dataframe2>)
,
//
pour la division entière et
%
ou <dataframe1>.mod(<opérande>)
pour le reste de la division entière.
<dataframe> ** 2
ou <dataframe>.pow(2)
pour le carré
- Les opérations booléennes en utilisant:
&
pour “and”,
|
pour “ou”,
~
ou – pour “not”,
^
pour le “ou exclusif”.
- Les opérateurs de comparaison:
==
ou <dataframe1>.eq(<dataframe2>)
pour tester l’égalité,
!=
ou <dataframe1>.ne(<dataframe2>)
pour tester l’inégalité,
<
ou <dataframe1>.lt(<dataframe2>)
,
>
ou <dataframe1>.gt(<dataframe2>)
,
<=
ou <dataframe1>.le(<dataframe2>)
,
>=
ou <dataframe1>.ge(<dataframe2>)
.
ne pas confondre eq()
et equals()
eq()
compare tous les éléments de 2 dataframes et renvoie un dataframe de même taille avec des booléens résultant de la comparaison de tous les éléments.
equals()
compare tous les éléments des 2 dataframes et ne renvoient qu’un seul booléen qui est True
si tous les éléments des dataframes sont égaux.
Si on prend les dataframes précédents:
>>> print(df1.eq(df2))
0 1 2 3
0 True True True True
1 False False False False
2 False False False False
3 False False False False
>>> df1.equals(df2)
False
Opérations entre un dataframe et un autre type d’objet
Les opérations ne sont pas exclusivement réservées aux dataframes entre eux. On peut appliquer des opérations entre:
- Un dataframe et un scalaire: l’opération avec le scalaire est appliquée sur tous les éléments.
Par exemple:
>>> df1 = pd.DataFrame([[1, 2, 3], [10, 20, 30], [100, 200, 300]], index = ['a', 'b', 'c'])
>>> print(df1 * 3)
0 1 2
a 3 6 9
b 30 60 90
c 300 600 900
- Un dataframe et une liste: la taille de la liste doit être de la même taille que la dimension du dataframe sur laquelle l’opération est appliquée. L’opération est appliquée sur toutes les lignes ou toutes les colonnes suivant le sens d’application de l’opération.
Par exemple:
>>> print(df1 + [5, 15, 25])
0 1 2
a 6 17 28
b 15 35 55
c 105 215 325
Dans le cas précédent, l’addition des éléments de la liste est appliquée sur toutes les lignes.
Le résultat est le même en exécutant df1.add([5, 15, 25], axis=1)
ou df1.add([5, 15, 25], axis='columns')
.
Dans le cas suivant, l’opération avec la liste est appliquée sur toutes les colonnes:
>>> print(df1.add([5, 15, 25], axis=0))
0 1 2
a 6 7 8
b 25 35 45
c 125 225 325
Une autre syntaxe équivalente est: df1.add([5, 15, 25], axis='index')
.
- Un dataframe et une série pandas: l’opération est appliquée sur les labels communs. Si des labels ne sont pas communs, la valeur résultante est
NaN
.
Par exemple:
>>> s = pd.Series([5, 15, 25]
>>> print(df1 + s)
0 1 2
a 6 17 28
b 15 35 55
c 105 215 325
L’opération est appliquée dans le sens des lignes. Les labels utilisés sont les labels par défaut.
>>> print(df1 + pd.Series([5, 15])
0 1 2
a 6.0 17.0 NaN
b 15.0 35.0 NaN
c 105.0 215.0 NaN
La série ne possède pas le label 2
donc la colonne 2
n’est pas commune aux 2 objets et la valeur affectée est NaN
.
Dans l’exemple suivant, l’opération est appliquée en colonne:
>>> print(df1.add(pd.Series([5, 15, 25], index=['a', 'b', 'c']), axis=0))
0 1 2
a 6 7 8
b 25 35 45
c 125 225 325
Il faut indiquer les mêmes labels que le dataframe pour que l’opération soit possible.
- Un dataframe et un dictionnaire: l’opération est appliquée pour les éléments du dictionnaire pour lesquels la clé correspond à un index du dataframe.
Par exemple, dans le cas suivant l’opération est appliquée dans le sens des lignes:
>>> d = {0: 5, 1: 15, 2: 25}
>>> print(df1 + d)
0 1 2
a 6 17 28
b 15 35 55
c 105 215 325
Dans le cas suivant, l’opération est appliquée dans le sens des colonnes.
>>> d = {'a': 5, 'b': 15, 'c': 25}
>>> print(df1.add(d, axis=0))
0 1 2
a 6 7 8
b 25 35 45
c 125 225 325
Argument fill_value
On peut utiliser l’argument fill_value
pour indiquer la valeur à appliquer lorsque les index qui ne sont pas communs.
Par exemple:
>>> df1 = pd.DataFrame([[1, 2, 3], [10, 20, 30], [100, 200, 300]], index = ['a', 'b', 'c'])
>>> df2 = pd.DataFrame([[1, 2, 3], [11, 21, 31], [101, 201, 301]], index = ['a', 'b', 'd'])
>>> print(df1.add(df2))
0 1 2
a 2.0 4.0 6.0
b 21.0 41.0 61.0
c NaN NaN NaN
d NaN NaN NaN
La valeur NaN
est appliquée lorsque les index ne sont pas communs.
>>> print(df1.add(df2, fill_value=0))
0 1 2
a 2.0 4.0 6.0
b 21.0 41.0 61.0
c 100.0 200.0 300.0
d 101.0 201.0 301.0
Les index 'c'
et 'd'
ne sont pas communs entre les 2 dataframes. La valeur 0
est utilisée lorsque les index ne sont pas communs.
Opérations avec des index ou des colonnes non communs
Les opérations sont appliquées si les labels des dataframes sont les mêmes. Dans les exemples suivants, les labels utilisés sont les labels par défaut. Si certains labels ne sont pas égaux alors le résultat est NaN
pour les éléments ayant des labels différents.
Par exemple si on considère les dataframes suivants:
>>> df1 = pd.DataFrame([[1, 2, 3], [10, 20, 30], [100, 200, 300]], columns = ['a', 'b', 'c'])
>>> df2 = pd.DataFrame([[1, 2, 3], [11, 21, 31], [101, 201, 301]], columns = ['a', 'b', 'd'])
>>> print(df1 + df2)
a b c d
0 2 4 NaN NaN
1 21 41 NaN NaN
2 201 401 NaN NaN
Les colonnes non communes contiennent NaN
.
Jointures
Il est possible d’utiliser la fonction merge()
pour effectuer des jointures entre 2 dataframes ayant au moins une colonne en commun et obtenir un dataframe avec le résultat de cette jointure.
Par exemple:
>>> df1 = pd.DataFrame({'id': [2, 1, 5], 'value1': [100, 165, 628]});
>>> print(df1)
id value1
0 2 100
1 1 165
2 5 628
>>> df2 = pd.DataFrame({'id': [1, 2, 8, 10], 'value2': [92, 27, 87, 43]});
>>> print(df2)
id value2
0 1 92
1 2 27
2 8 87
3 10 43
>>> print(df1.merge(df2))
id value1 value2
0 2 100 27
1 1 165 92
La jointure est effectuée sur la colonne id
puisque c’est la seule colonne commune. Les valeurs 1
et 2
dans cette colonne sont communes.
merge()
peut être utilisé avec les options suivantes:
on
: indique la colonne avec laquelle la jointure sera effectuée. Cette option est utile si plusieurs colonnes sont communes.
Par exemple:
>>> df1 = pd.DataFrame({'id': [2, 1, 5], 'id2': ['a', 'b', 'c'], 'value1': [100, 165, 628]});
>>> print(df1)
id id2 value1
0 2 a 100
1 1 b 165
2 5 c 628
>>> df2 = pd.DataFrame({'id': [1, 2, 8, 10], 'id2': ['c', 'e', 'd', 'a'], 'value2': [92, 27, 87, 43]});
>>> print(df2)
id id2 value2
0 1 c 92
1 2 e 27
2 8 d 87
3 10 a 43
>>> print(df1.merge(df2, on='id2'))
id_x id2 value1 id_y value2
0 2 a 100 10 43
1 5 c 628 1 92
left_on
et right_on
: ces arguments permettent d’indiquer les colonnes sur lesquelles la jointure doit être effectuée respectivement pour le 1er (avant merge()
) ou le 2e dataframe (fourni en argument de merge()
).
Par exemple, si on considère ces dataframes n’ayant pas de colonnes communes:
>>> df1 = pd.DataFrame({'id_1': [2, 1, 5], 'value1': [100, 165, 628]});
>>> print(df1)
id_1 value1
0 2 100
1 1 165
2 5 628
>>> df2 = pd.DataFrame({'id_2': [1, 2, 8, 10], 'value2': [92, 27, 87, 43]});
>>> print(df2)
id_2 value2
0 1 92
1 2 27
2 8 87
3 10 43
Si on effectue la jointure sur les colonnes id_1
et id_2
:
>>> print(df1.merge(df2, left_on='id_1', right_on='id_2'))
id_1 value1 id_2 value2
0 2 100 2 27
1 1 165 1 92
left_index
et right_index
: ces arguments permettent d’effectuer une jointure sur les index plutôt que sur les colonnes. La jointure est effectuée pour le 1er dataframe (avant merge()
) avec left_index
et pour le 2e dataframe (fourni en argument de merge()
) avec right_index
.
how
: cet argument permet d’imiter le comportement d’une jointure ouverte “outer join”, “left outer join” ou “right outer join”. Par défaut, la valeur est 'inner'
pour jointure fermée. Les autres valeurs possibles:
'outer'
pour une jointure ouverte. Les entrées ayant des colonnes ou des index communs entre les 2 dataframes sont retournées. A ces résultats se rajoutent aussi les entrées qui ne sont pas communes entre les 2 dataframes.
'left'
pour une jointure ouverte par la gauche. Les entrées ayant des colonnes ou des index communs entre les 2 dataframes sont retournées. A ces résultats se rajoutent les entrées du 1er dataframe qui ne sont pas communs avec le 2e dataframe.
'right'
pour une jointure ouverte par la droite. Les entrées ayant des colonnes ou des index communs entre les 2 dataframes sont retournées. A ces résultats se rajoutent les entrées du 2e dataframe qui ne sont pas communs avec le 1er dataframe.
'cross'
pour effectuer un produit cartésien sur les entrées ayant des colonnes ou des index communs entre les 2 dataframes.
indicator
: permet de rajouter une colonne nommée _merge
permettant d’indiquer d’où provient l’entrée.
Par exemple, si on considère les dataframes et la jointure suivants:
>>> df1 = pd.DataFrame({'id': [2, 1, 5], 'value1': [100, 165, 628]});
>>> df2 = pd.DataFrame({'id': [1, 2, 8, 10], 'value2': [92, 27, 87, 43]});
>>> print(df1.merge(df2, how='outer', indicator=True))
id value1 value2 _merge
0 2 100.0 27.0 both
1 1 165.0 92.0 both
2 5 628.0 NaN left_only
3 8 NaN 87.0 right_only
4 10 NaN 43.0 right_only
Concaténation
On peut effectuer des concaténations de dataframes ou de séries avec la fonction pd.concat([<dataframes ou series>])
.
Par exemple, si on considère les dataframes suivants:
>>> df1 = pd.DataFrame([[1, 2, 3], [10, 20, 30]])
>>> df2 = pd.DataFrame([[11, 21, 31], [100, 200, 300]])
>>> print(pd.concat([df1, df2]))
0 1 2
0 1 2 3
1 10 20 30
0 11 21 31
1 100 200 300
On peut remarquer que les colonnes sont les mêmes entre les 2 dataframes (colonnes implicites). Les index sont réutilisés.
Dans le cas où des colonnes ne sont pas communes, les valeurs sont complétées avec des NaN
:
>>> df1 = pd.DataFrame([[1, 2, 3], [10, 20, 30]], columns=['a', 'b', 'c'])
>>> df2 = pd.DataFrame([[11, 21, 31], [100, 200, 300]], columns=['a', 'b', 'd'])
>>> print(pd.concat([df1, df2]))
a b c d
0 1 2 3.0 NaN
1 10 20 30.0 NaN
0 11 21 NaN 31.0
1 100 200 NaN 300.0
On peut concaténer une série à un dataframe:
>>> s = pd.Series([100, 200])
>>> print(pd.concat([df1, s], axis=1))
0 1 2 0
0 1 2 3 100
1 10 20 30 200
On peut utiliser les arguments suivants pour modifier le comportement. Pour la suite, on définit les dataframes suivants:
>>> df1 = pd.DataFrame([[1, 2, 3], [10, 20, 30]])
>>> print(df1)
0 1 2
0 1 2 3
1 10 20 30
>>> df2 = pd.DataFrame([[11, 21, 31], [100, 200, 300]])
>>> print(df2)
0 1 2
0 11 21 31
1 100 200 300
ignore_index
: permet d’ignorer les index d’origine des dataframes pour utiliser des index implicites.
Si on considère les dataframes suivants:
>>> print(pd.concat([df1, df2], ignore_index=True))
0 1 2
0 1 2 3
1 10 20 30
2 11 21 31
3 100 200 300
axis
: permet d’indiquer l’axe sur lequel la concaténation est effectuée: 0
pour index (valeur par défaut) et 1
pour colonne.
Par exemple:
>>> print(pd.concat([df1, df2], axis=1))
0 1 2 0 1 2
0 1 2 3 11 21 31
1 10 20 30 100 200 300
Les labels de colonnes sont maintenus. On peut utiliser ignore_index
pour recréer des labels de colonnes implicites:
>>> print(pd.concat([df1, df2], axis=1, ignore_index=True))
0 1 2 3 4 5
0 1 2 3 11 21 31
1 10 20 30 100 200 300
keys
: permet de rajouter un niveau dans les index du dataframe de sortie correspondant permettant d’indiquer le dataframe de départ.
>>> print(pd.concat([df1, df2], keys=['a', 'b']))
0 1 2
a 0 1 2 3
1 10 20 30
b 0 11 21 31
1 100 200 300
join
: permet d’indiquer le comportement lorsque les colonnes ou les index ne sont pas communs. Par défaut, la valeur est 'outer'
pour indiquer les index ou les colonnes (suivant le sens de la concaténation) sont ajoutés dans le dataframe en sortie même s’ils ne sont pas communs.
Si la valeur est 'inner'
alors seuls les colonnes ou index communs seront pris en compte dans le dataframe en sortie.
Par exemple, si on considère les dataframes suivants:
>>> df3 = pd.DataFrame([[1, 2, 3], [10, 20, 30]], columns=['a', 'b', 'c'])
>>> print(df3)
a b c
0 1 2 3
1 10 20 30
>>> df4 = pd.DataFrame([[11, 21, 31], [100, 200, 300]], columns=['a', 'b', 'd'])
>>> print(df4)
a b d
0 11 21 31
1 100 200 300
On peut voir la différence de comportement avec l’option join='inner'
:
>>> print(pd.concat([df3, df4]))
a b c d
0 1 2 3.0 NaN
1 10 20 30.0 NaN
0 11 21 NaN 31.0
1 100 200 NaN 300.0
>>> print(pd.concat([df3, df4], join='inner'))
a b
0 1 2
1 10 20
0 11 21
1 100 200