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

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 [].

Type grand petitSexe F 3 2M 5 1

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.