Les dataframes python avec le module Pandas
Gérer des tableaux pour l'analyse de données et le dataviz
L'essentiel de cette page
Un tableau classique de données est rectangulaire. Les colonnes sont les variables (les dimensions) et les lignes les individus.
Sous R, l'analyse de ces jeux de données sous forme de tableau est optimisé et ces tableaux portent le nom de dataframe.
Il existe sous python un module, pandas, qui permet de gérer avec une équivalence relative des jeux de données pour des analyses statistiques, du dataviz et du marchine learning.
Remarque : pour installer pandas (et les autres modules utiles), il faut taper la commande suivante dans la console windows :
python -m pip install --user easygui pandas numpy
1- Ouvrir ou créer un jeu de données
1.1- Ouvrir des données au format dataframe
L'ouverture des données au format dataframe est détaillé dans l'item de ce site :
On retiendra l'exemple suivant pour ouvrir facilement un tableau excel :
import easygui
import pandas as pd
df2 = pd.read_excel(easygui.fileopenbox()) # On peut remplacer easygui.fileopenbox() par le nom du fichier.
Le même code simplifiée sera :
from easygui import *
from pandas import *
df2 = read_excel(fileopenbox())
1.2- Créer des données au format dataframe
# Voici 2 jeux de valeurs simulées :
# Voir l'aide pour simuler des valeurs plus complexes (loi normal. etc) avec numpy
sx = ["M"]*5 + ["F"] * 5 + ["M"]
age = [50,60,62,30,40,51,18,74,68,57,35]
# Une conversion en dictionnaire s'impose au préalable
mydict = {"age":age,"sx":sx}
# Puis la conversion en DataFrame est possible
import pandas as df
mydata = df.DataFrame(mydict)
print(mydata)
age sx
0 50 M
1 60 M
2 62 M
3 30 M
4 40 M
5 51 F
6 18 F
7 74 F
8 68 F
9 57 F
10 35 M
1.3- Transposer : basculer une dataframe en inversant colonnes et lignes
#Si on reprend l'exemple simulé en 1.2.
mydata2 = mydata.transpose()
print(mydata2)
0 1 2 3 4 5 6 7 8 9 10
age 50 60 62 30 40 51 18 74 68 57 35
sx M M M M M F F F F F M
1.4- Définir les colonnes de type category (sert à la coloration automatique des catégories)
import pandas as pd
# Directement sur les listes - on reprend l'exemple de valeurs données en 1.2-
sexe = pd.Series(sexe, dtype="category")
# Ou à partir d'une colonne d'une dataframe
compil["Sexe"] = compil["Sexe"].astype('category')
print(compil.head())
print(compil.Sexe.cat.codes) # Code automatique de coloration des catégories
dataframe.col.astype('category')
dataframe.col.cat.codes
2- Décrire une data.frame
Pour obtenir un résumé de la structure d'une data.frame, colonne par colonne :
mydata.describe().T
2.1 - Afficher les X premières lignes d'une dataframe ou les X dernières lignes
Cet exemple reprend la partie 1, cas où ma dataframe s'appelerait mydata :
mydata.head() # Titres + 5 premières lignes (par défaut)
mydata.head(3) # Titres plus 3 premières lignes
mydata.tail(3) # Affiche les 3 dernières lignes du tableau
age sx
0 50 M
1 60 M
2 62 M
2.2 - Connaître le nombre de colonnes et le nombre de lignes d'une dataframe
mydata.shape() # description générale
print(len(mydata.columns)) # Nombre de colonnes (équivalent de ncol sous R)
print(len(mydata.index)) # Nombre de lignes (équivalent de nrow sous R)
2.3- Afficher les titres des colonnes et les titres des lignes
colnames = list(mydata.columns) ; print(colnames) # Méthode 1 pour afficher les titres des colonnes (ou les récupérer dans une liste)
colnames = mydata.columns.values ; print(colnames) # Méthode 2 pour afficher les titres des colonnes
rownames = list(mydata.index.values ) ; print(rownames) # Méthode 3 pour afficher les titres des lignes
2.4- Modifier les titres des colonnes ou les titres des lignes
Changer les noms des colonnes
# Changer le nom des colonnes à partir d'une liste (méthode simple)
new_colnames = ["Âges","Sexe"]
mydata.columns = new_colnames ; print(mydata) # mydata est la dataframe construite avec le code 1.2- de cette page
# Méthode plus subtile où l'on indique pour les colonnes à renommer la nouvelle correspondance
mydata = mydata.rename(columns={"age":"Âge","sx":"Sexe"}) ; print(mydata)
# Méthode la plus "simple" pour renommer seulement une colonne (ici la 2ème)
colnames = list(mydata.columns); colnames[1] = "Groupe"; mydata.columns = colnames
Changer les noms des lignes
# Changer le nom des lignes à partir d'un liste
mydata.index = range(2,13,1) # On remplace les numéros de ligne de 0 à 10 par de 2 à 12
# Méthode plus subtile où l'on indique pour les lignes à renommer la nouvelle correspondance
mydata = mydata.rename({2:"Première",6:"Au milieu"}) ; print(mydata)
2.5- Filtrer par type les colonnes (numériques, caractères, booléens...)
df.dtypes
# Ne conserver du tableau que les données numériques
df2 = df.select_dtypes(include=['int64','float64'])
# Ne conserver du tableau que les données non-numériques
df2 = df.select_dtypes(exclude=['int64','float64'])
On peut aussi collecter les identifiants des colonnes répondant à des dtypes et ainsi appliquer dessus des transformations sans changer les autres colonnes :
ind_num = np.isin(df.dtypes,['int16','int32','int64','float64','float16','float32'])
df.iloc[:,ind_num] # Affichage des colonnes numériques
3- Accéder aux valeurs d'une dataframe
3.1- Appeler une ou plusieurs colonnes en utilisant des titres
Je reprends une fois de plus la dataframe construite en collant le code de la partie 1.2- (avec une colonne age et sexe)
print(mydata.age)
print(mydata["age"])
print(mydata[["age","sx"]])
Les noms de colonnes ici sont au format list, mais on pourrait très bien compiler des données au format index de pandas comme avec ces deux fonctions :
mydata.keys() ; mydata.columns
3.2- Appeler une ou plusieurs colonnes en utilisant les numéro des colonnes (inclusion ou exclusion de colonnes en fonction des indices)
# Jeu de données simulé
from numpy import random as rd
age = [18,19,20,18,19,20,17,23]
sx = ["M","F"]*4
poids = rd.normal(loc=65, scale=6, size=8)
taille = rd.normal(loc=175, scale=12, size=8)
import pandas as pd
mydata = pd.DataFrame({"Age":age,"Sexe":sx,"Poids":poids,"Taille":taille})
Afficher les colonnes par indices :
print( mydata.iloc[:,1] ) # Afficher la colonne 2, indice 1
print( mydata.iloc[:,1:3] ) # Afficher les colonnes 2 à 3 (indice 1 et 2)
print( mydata.iloc[:,[0,2,3]] ) # Afficher les col pour les indices 0, 2 et 3
AU lieu d'indiquer les numéros de colonnes que l'on veut, on peut aussi réaliser un index où l'on a indiqué les colonnes qu'on exclue avec la commande dataframe.columns.drop() : cf. exemple rubrique régression.
dataframe.iloc[row,col]
3.3- Appeler une ou plusieurs lignes en utilisant les numéro de lignes (inclusion ou exclusion de lignes en fonction des indices)
Afficher les lignes par indices (en reprenant la dataframe du point 3.2)
print( mydata.iloc[3 ,:] ) # Afficher la ligne indice 3
print( mydata.iloc[3:8 ,:] ) # Afficher les lignes ind 3 à 7
print( mydata.iloc[[1,2,4,0],:] ) # Afficher les lignes 1, 2, 4 et 0
dataframe.iloc[]
3.4- Appeler les lignes par noms
# Jeu de données simulé
from numpy import random as rd
age = [18,19,20,18,19,20,17,23]
sx = ["M","F"]*4
poids = rd.normal(loc=65, scale=6, size=8)
taille = rd.normal(loc=175, scale=12, size=8)
import pandas as pd
mydata = pd.DataFrame({"Age":age,"Sexe":sx,"Poids":poids,"Taille":taille})
mydata = mydata.rename({0:"Témoin",1:"Malade"}) ; print(mydata)
Afficher les lignes par noms
print( mydata.loc["Témoin"] ) # Afficher la ligne Témoin
print( mydata.loc[["Malade",2]] ) # Afficher les lignes Malade et celle ayant l'indice 2
3.5- Combiner titres et indices pour appeler des plages
Appeler le contenu d'une cellule ou d'une plage de cellules
print( mydata.iloc[ [1,2,3] , [1,2] ] ) # Afficher la plage : lignes (indices 1 à 3) et colonnes (indices 1 et 2)
Appeler le contenu d'une cellule ou d'une plage de cellules en utilisant titres et indices
mydata[["Sexe","Age"]].iloc[[1,2,3],:] # Afficher les lignes 1, 2 et 3 des colonnes Sexe et Age
4- Réaliser des tris et extractions sur les données d'une dataframe
4.1- Trier les colonnes d'une dataframe
# En reprenant un des jeux de données ci-dessus
mydata2 = mydata[sorted(mydata)]
4.2- Sélection conditionnelle : récupérer des indices ou des valeurs d'une dataframe répondant à certains conditions
Voici juste un exemple en reprenant le jeu de données de la partie 3.4. :
mydata.Poids[ (mydata.Age > 18) & (mydata.Sexe == "F")] # Poids des filles de plus de 18 ans
Si vous voulez plus d'exemple, consulter la rubrique Sélection par condition.
5- Explorer graphiquement les données d'une dataframe
5.1- Mettre en relation toutes les variables d'une dataframe sous d'une matrice de scatter-plot
from pandas.plotting import scatter_matrix
scatter_matrix(mydata)
On peut voir d'autres graphiques directement grâce aux fonctionnalités de pandas.plotting :
6- Appliquer des calculs, transformations et concaténations
6.1- Appliquer des calculs colonne par colonne ou ligne par ligne
Voici un jeu de données
taille = [175,160,186,173,176]
poids = [50,60,62,70,40]
age= [18,19,20,21,20]
mytab = pd.DataFrame({"Poids":poids,"Taille":taille,"Age":age})
Calculer la moyenne de chaque colonne ou de chaque ligne
mytab.mean() # Moyenne de chaque colonne
mytab.mean(axis=1) # Moyenne de chaque ligne
Calculer l'écart-type de chaque colonne
mytab.std()
Réaliser une matrice de corrélation pour la dataframe
mytab.corr()
Centrer et réduire les données d'une dataframe
temp = mytab.sub(mytab.mean()) # Soustraire la moyenne de chaque colonne à chaque valeur : centrer les valeurs
temp = temp.div(temp.std()) # Divisé les valeurs de la dataframe par l'écart-type de chaque colonne
Appliquer des calculs/fonctions valeur par valeur, colonne par colonne ou ligne par ligne
Exemple 1
mytab.apply(np.sqrt) # Racine carrée de chaque valeur (exige l'importation de numpy as np)
Exemple 2
mytab.apply(np.sum,axis=0) # Somme de chaque colonne
Exemple 3
def myfunc(serie) :
temp = np.mean(serie)-np.median(serie)
return(temp)
mytab.apply(myfunc, axis=1) # Différence entre médiane et moyenne ligne par ligne
6.2- Concaténer des dataframe en ajoutant des colonnes ou en ajoutant des lignes
import pandas as pd
sx = ["M"]*3 + ["F"] * 2
age = [50,60,62,30,40]
tab1 = pd.DataFrame({"age":age,"sx":sx})
sx = ["M"]*2 + ["F"] * 3
age = [51,18,68,57,35]
tab2 =pd.DataFrame({"age":age,"sx":sx})
# Concaténer en ajoutant des lignes
tab3 = pd.concat([tab1,tab2],axis=0); print(tab3) # rbind
# Concaténer en ajoutant des colonnes
tab3 = pd.concat([tab1,tab2],axis=1); print(tab3) # cbind
age sx
0 50 M
1 60 M
2 62 M
3 30 F
4 40 F
0 51 M
1 18 M
2 68 F
3 57 F
4 35 F
Résultat 1
age sx age sx
0 50 M 51 M
1 60 M 18 M
2 62 M 68 F
3 30 F 57 F
4 40 F 35 F
Résultat 2
6.3- Supprimer une ou plusieurs colonnes
print(mytab)
temp = mytab.drop (["Taille"],axis=1) ; print(temp) # Suppression de la colonne Taille (on peut en mettre plusieurs)
ou encore :
index = mytab.columns.drop(["Taille"]) # On fait un index qui répertorie toutes les colonnes sauf celles que l'on ne désire pas.
temp = mydata[index] ; print(temp)
6.4- Remplacer du texte dans une série, une colonne
Remplacer le contenu d'une colonnes, remplacer des caractères
print(mytab)
# Remplacer M et F par 1 et 2 pour numériser
mytab["sx"]= mytab["sx"].replace("M",1, case = False)
mytab["sx"]= mytab["sx"].replace("F",2, case = False)
Convertir en numérique (équivalent de as.numeric() de R)
import pandas as df
mytab[['sx']] = mytab[['sx']].apply(df.to_numeric)
Remplacer le contenu sur plusieurs colonnes pandas (ex, les colonnes sx1 et sx2)
mytab[["sx1","sx2"]]=mytab[["sx1","sx2"]].replace('\D*',"", regex=True)
Ici, on utilise une expression régulière qui va remplacer tous les caractères non numériques "\D" en mode regex=T. Le * implique un nombre non précisé de caractères, un nombre variable de caractères.
6.5- Agglomérer des colonnes, les joindre, coller leurs contenus
L'équivalent de la fonction paste() de R est facile à mettre en place.
Dans cet exemple, on colle 2 colonnes texte (ne fonctionne pas pour les données numériques si on ne les convertit pas en caractère avant).
import pandas as pd
sx = ["M"]*3 + ["F"] * 2
age = ["age"]*5
tab1 = pd.DataFrame({"age":age,"sx":sx})
tab1["sx"] + "." + tab1["age"]
# Attention les variables doivent être au format string (texte) sinon une conversion s'impose :
tab1["sx"].astype("str") + "." + tab1["age"].astype("str")
0 M.age
1 M.age
2 M.age
3 F.age
4 F.age
7- Gérer les données manquantes
Voici un jeu de données avec des données manquantes
import pandas as pd
sx = ["M"]*3 + ["F"] * 2 + [None]
age = [50,60,None,30,40,18]
mytab = pd.DataFrame({"age":age,"sx":sx})
print(mytab)
age sx
0 50.0 M
1 60.0 M
2 NaN M
3 30.0 F
4 40.0 F
5 18.0 NaN
Détecter les données manquantes
print(mytab.isna())
age sx
0 False False
1 False False
2 True False
3 False False
4 False False
5 False True
Supprimer les données manquantes
mytab.dropna() # Cette fonction est paramétrable
age sx
0 50.0 M
1 60.0 M
3 30.0 F
4 40.0 F
Exemple final : un jeu de données à copier-coller avec des catégories (sexe) pour s'entraîner :
from numpy.random import normal as norm
f_poids = list(norm(loc = 55, size=20, scale=11))
f_taille = list(norm(loc = 163, size=20, scale=14))
m_poids = list(norm(loc = 70, size=20, scale=11))
m_taille = list(norm(loc = 175, size=20, scale=15))
poids = f_poids+m_poids ; poids = np.array(poids)
taille = f_taille+m_taille ; taille = np.array(taille)
imc = poids/(taille/100)**2
sexe = ["F"]*20+["M"]*20
compil = {"Sexe" : sexe , "Poids" : poids, "Taille" : taille, "IMC" : imc}
compil = DataFrame(compil) ; print(compil)
Sexe Poids Taille IMC
0 F 44.255210 141.849754 21.994153
14 F 69.373732 151.854690 30.084212
15 F 65.871833 161.046898 25.397737
18 F 60.540960 173.276399 20.163711
19 F 67.024892 182.470403 20.130347
20 M 74.662253 159.965913 29.177374
24 M 68.850271 141.203655 34.531368
25 M 80.831543 190.286823 22.323559
8- Croiser des variables pour mettre en place des contingents
Voici l'équivalent de la fonction table() de R pour compter les différentes catégories qui constituent une colonne de valeurs ou encore pour contingenter le croisement de deux variables :
Mots clefs : function table R to Python
Compter le nombre d'occurence de chaque valeur
sx = ["M"]*5 + ["F"] * 5 + ["M"]
import pandas as pd
mydata = pd.DataFrame({"sx":sx})
effectif = mydata.sx.value_counts() ; print(effectif)
# Afficher le détail à convertir éventuellement avec list()
effectif.index
effectif.values
M 6
F 5
Croiser deux variables pour comptabiliser les catégories croisées
1- Voici un jeu de données qui indique le sexe et le type de taille ("grand" ou "petit") - code à copier-coller :
sx = ["M"]*5 + ["F"] * 5 + ["M"]
taille = [174,167,170,170,171,177,155,150,176,178,173]
type = []
for i in taille :
if i < 170 : type = type+["petit"]
else : type = type+["grand"]
import pandas as pd
mydata = pd.DataFrame({"type":type,"sx":sx,'taille':taille})
2- On va pouvoir ainsi compter les garçons et les filles de chaque type (grand ou petit)
resultat = pd.crosstab(mydata.sx,mydata.type, rownames = ['Sexe'], colnames = ['Type']);
print(resultat)
resultat.plot(kind="bar",rot=0) ; plt.show()
Attention de formater rownames et colnames au format list avec les [].
On peut ainsi directement tracer un diagramme en barre sur ce contingent grâce à la fonction plot de pandas.plotting avec légende automatique.
Toutefois, la méthode crosstab() présente une faiblesse : on obtient un tableau à 2 entrées (ici le sexe et la taille).
Tabeau qu'il serait pertinent de basculer en 1 tableau simple à une entrée :
Type grand petit
Sexe
F 3 2
M 5 1
deviendrait :
Avec, un tel tableau, on peut faire des bubble plot ou bubble chart facilement.
sx type Effectif
0 F grand 3
1 F petit 2
2 M grand 5
3 M petit 1
Pour faire un tableau à 1 seule entrée en faisant un tri croisé avec crosstab.
resultat = pd.crosstab(mydata.sx, mydata.type).stack().reset_index(name='Effectif')
Remarque, normalement, on pourrait faire plus simplement avec la méthode melt mais la sortie de crosstab ne s'y prête pas.