Accueil

Traduction

Tutoriel Python - sommaire

Tutoriel Python - recherche

L'auteur : Patrick Darcheville

Vous pouvez me contacter via Facebook pour questions & suggestions : Page Facebook relative à mon site

Bibliothèque pandas pour traiter fichiers CSV & JSON

Dans le chapitre 11 je vous ai montré comment manipuler des fichiers TXT (données non structurées). Python est capable aussi de manipuler des fichiers de données structurées (au format CSV ou JSON) via la bibliothèque pandas.

Les modules csv et json

Avant d'aborder la bibliothèque pandas je veux vous montrer que l'on peut effectuer des traitements basiques sur les fichiers CSV & JSON grâce à deux modules pré-installés.

Manipuler un fichier CSV

Le sigle CSV signifie 'comma separated values' qui veut dire 'valeurs séparées par des virgules'. Il définit un format de fichier pour l'échange de données tabulaires c'est à dire de données provenant d'un tableur.

La feuille de calcul

Je dispose d'une feuille de calcul réalisée avec calc de libre Office (ce pourrait être un classeur Excel). J'ai besoin de ces données pour un traitement via Python.
Je dois d'abord enregistrer le classeur dans un fichier CSV à partir de Calc. Commande : Fichier/Enregistrez sous / type : texte csv , nom : bareme.csv Ce fichier CSV est enregistré dans c :/python_prog sous le nom "bareme.csv".
Lorsque j'ai créé ce fichier, Calc m'a demandé quel délimiteur je voulais comme délimiteur de champs ; j'ai répondu la virgule.

Contenu du fichier "bareme.csv"

référence,désignation ,prix ht
bo 001,boulons de 7 par 100,5
bo 0002,boulons de 8 par 100,6
bo 0003,boulons de 9 par 100,8
cl 001,clous 50mm par 100,4
cl 002,clous 70mm par 100,5

Manipulation de ce fichier dans la console

>>> import csv
>>> import os
>>> os.chdir("c:/python_prog") 	#chgt de directory
>>> fichier =open('bareme.csv','r')
>>> contenu =csv.reader(fichier,delimiter =',')

>>> for ligne in contenu:
...     	print(ligne)		#affichage de toutes les colonnes du tableau
...
['référence', 'désignation ', 'prix ht']
['bo 001', 'boulons de 7 par 100', '5']
['bo 0002', 'boulons de 8 par 100', '6']
['bo 0003', 'boulons de 9 par 100', '8']
['cl 001', 'clous 50mm par 100', '4']
['cl 002', 'clous 70mm par 100', '5']
>>> for ligne in contenu:
...       print(ligne[0], ligne[2])		#affichage des colonnes 'références' et 'prix ht'
référence prix ht
bo 001 5
bo 0002 6
bo 0003 8
cl 001 4
cl 002 5
>>>

Il faut importer le module csv qui permet de manipuler les fichiers CSV.
Il faut aussi importer le module os pour disposer de la fonction chdir() qui permet de changer de répertoire courant.

Le fichier "bareme.csv" est ouvert en lecture (mode : "r").
Le contenu du fichier est récupéré avec la méthode reader() appliquée à l'objet de type file.
Cette méthode permet de récupérer le contenu du fichier sous forme de listes à raison d'un liste par ligne ; la première liste correspond aux en-têtes de colonne.

Avec une boucle on peut parcourir toutes les listes.
On peut afficher seulement certaines colonnes sachant que la première colonne a l'indice 0.

Récupérer le contenu du fichier CSV sous forme de dictionnaires

On peut récupérer le contenu d'un fichier CSV sous forme de dictionnaires.

>>> fichier =open('bareme.csv','r')
>>> dicos = csv.DictReader(fichier,delimiter =',')
>>> for dico in dicos :
...     print(dico)
...
{'référence': 'bo 001', 'désignation ': 'boulonsde 7 par 100', 'prix ht': '5'}
{'référence': 'bo 0002', 'désignation ': 'boulons de 8 par 100', 'prix ht': '6'}
{'référence': 'bo 0003', 'désignation ': 'boulons de 9 par 100', 'prix ht': '8'}
{'référence': 'cl 001', 'désignation ': 'clous 50mm par 100', 'prix ht': '4'}
{'référence': 'cl 002', 'désignation ': 'clous 70mm par 100', 'prix ht': '5'}

Pour récupérer un fichier CSV sous forme de dictionnaires il faut utiliser la fonction DictReader().
Pour chaque ligne du fichier CSV d'origine on obtient un dictionnaire.
Comme le fichier d'origine avait trois colonnes, chaque dictionnaire comprend trois paires de clé:valeur.
Les ent-têtes de colonnes deviennent les clés dans chaque dictionnaire.

Créer un fichier CSV

Nous pouvons bien sûr créer un fichier CSV à partir d'une liste de listes.
Le script :

#nom programme : fichier_csv_ecrire.py
#objet : écrire dans un fichier csv

import csv

listes = [ ['id', 'Nom', 'Age', 'Taille'] ,
        [1, 'alain', 19, 180],
        [2, 'bernard', 30, 185],
        [3, 'claude', 27, 175] ]
with open('donnees.csv', 'w') as fichier:
    contenu= csv.writer(fichier, delimiter=',')
    for ligne in listes:
        contenu.writerow(ligne)
print("fichier crée")

On crée le fichier "donnees.csv' à partir d'une liste de listes ; la première liste tient lieu d'en-têtes.
Toutes les sous-listes ont la même structure : id, nom, âge, taille.

Le contenu du fichier "donnees.csv" obtenu

id,Nom,Age,Taille
1,alain,19,180
2,bernard,30,185
3,claude,27,175

Remarque importante : ouverture en mode "w" ; donc si le fichier existe il est écrasé et s'il n'existe pas il est créé.
Pour écrire dans un fichier CSV il faut utiliser les méthodes writer() & writerow()
La première méthode a pour argument l'objet "file" et produit un objet "contenu".
La deuxième méthode appliquée à l'objet "contenu" permet d'écrire une ligne dans le fichier.

Manipuler des fichiers JSON

JSON (JavaScript Object Notation) est un format de données standard utilisé pour représenter et stocker des données structurées constitués d'items (paires clé-valeur), donc similaire à un dictionnaire Python.

Ce format initié d'abord pour le langage JavaScript est maintenant devenu un standard indépendant de ce langage.

Contenu d'un fichier JSON

Fichier créé avec l'application "Bloc-notes" et nommé "fiche.json".

{"nom": "Allemand",
"prenom" : "claude",
"adresse" : "rue des Poilus",
"localite": "62100 Calais",
"email": "allemandclaude@gmail.com", 
"loisirs": ["Sport", "Cinema", "Lecture" , "Voyage"]
}

Notez que le contenu d'un fichier JSON est une séquence de type 'dictionnaire' (paires de clé-valeur).

Ouvrir un fichier JSON

Ouvrons le fichier "fiche.json" en mode commande.

>>> import json
>>> import os
>>> os.chdir("c:/python_prog")
>>> with open("fiche.json") as fichier:
...     contenu = json.load(fichier)
...
>>> print(contenu)
{'nom': 'Allemand', 'prenom': 'claude', 'adresse': 'rue des Poilus', 'localite': '62100 Calais', 
'email': 'allemandclaude@gmail.com', 'loisirs': ['Sport', 'Cinema', 'Lecture', 'Voyage']}
>>>
>>> for item in contenu.items():
...     print(item)
...
('nom', 'Allemand')
('prenom', 'claude')
('adresse', 'rue des Poilus')
('localite', '62100 Calais')
('email', 'allemandclaude@gmail.com')
('loisirs', ['Sport', 'Cinema', 'Lecture', 'Voyage'])
>>>fichier.close()

Il faut importer le module json qui permet de manipuler des fichiers de ce format.
Pour récupérer le contenu du fichier, il faut utiliser la méthode load de ce module.
L'objet "contenu" est itérable (peut être parcouru avec une boucle).

Création d'un fichier JSON à partir d'un dictionnaire

Voyons maintenant comment créer un fichier JSON.

>>> personne = {'nom': 'Albert',
...                 'email': 'albert99@gmail.com',
...                 'loisirs': ['Sport', 'Cinema', 'Lecture', 'Voyage']
...                 }
>>> with open('fiche.json', 'w') as fichier:
...     json.dump(personne, fichier)
>>>fichier.close()

Le fichier JSON "fiche.json" est créé à partir d'un dictionnaire.

Pour écrire dans un fichier JSON il faut utiliser la méthode dump du module json.

Le nouveau contenu du fichier "fiche.json"

Le contenu précédent de "fiche.json" a été écrasé et remplacé par :

{"nom": "Albert", "email": "albert99@gmail.com", "loisirs": ["Sport", "Cinema", "Lecture", "Voyage"]}

La bibliothèque pandas

Dans la pratique les fichiers CSV / JSON peuvent être très volumineux.
Avant de les analyser il faut les "nettoyer" : corriger les erreurs et omissions.
En effet ces énormes fichiers découlent souvent d'encodage manuels sur de longues périodes. Il y a forcément des erreurs de saisie.
La librairie pandas est parfaitement adaptée pour "nettoyer" puis analyser de grosses masses de données et ainsi faire des prédictions ...

Présentation de Pandas

Pandas est une bibliothèque de Python spécialisée dans la manipulation et l'analyse des données, offrant des structures comme les "dataframes" et les "séries" pour un traitement efficace de données structurées.
Des données pertinentes sont essentielles en science des données.
La science des données est une branche de l'informatique qui étudie comment stocker, utiliser et analyser d'énormes masses de données afin d'en extraire des informations.

Attention Pandas est une bibliothèque ; il faut donc au préalable l'installer via l'utilitaire PIP.

Notion de série

Une série pandas est comparable à une colonne d'un tableau. Il s'agit donc d'un tableau unidimensionnel.

Exemple de série

Je crée une série à partir d'une simple liste.

>>>import pandas as pd
>>> a = [5, 7, 9]
>>> maserie =pd.Series(a)
>>> print(maserie)
0    5
1    7
2    9
dtype: int64
>>>print(maserie[0])
5

J'ai utilisé la fonction Serie() de pandas pour créer une série nommée "maserie".
Sauf indication contraire, les valeurs sont identifiées par leur numéro d'index.
La première valeur a l'indice 0, la deuxième l'indice 1, etc.
print(maserie) : cet indice permet d'accéder à une valeur spécifique de la série.

Créer des indices personnalisés : étiquettes

>>> # pandas ayant été importé
>>> maliste = [9,7,5]
>>> maserie = pd.Series(maliste,index=["L1","L2","L3"])
>>> print(maserie)
L1    9
L2    7
L3    5
dtype: int64
>>> print(maserie["L2"])
7

L'argument index de la fonction Series() vous permet de nommer vos des "étiquettes" (index personnalisés).

Créer une série à partir d'un dictionnaire

>>> calories = {"jour1": 420, "jour2": 380, "jour3": 390}
>>> maserie = pd.Series(calories)
>>> print(maserie)
jour1    420
jour2    380
jour3    390
dtype: int64

Les clés du dictionnaire deviennent les index de la série.

Notion de dataframe

Dans Pandas, les jeux de données sont généralement des tableaux multidimensionnels appelés dataframes.

Première dataframe

# pandas ayant été installé et importé
>>> mondict = {'cars': ["BMW", "Volvo", "Ford"],'passagers': [3, 7, 2]}
>>> df = pd.DataFrame(mondict)
>>> print(df)
    cars  passagers
0    BMW         3
1  Volvo         7
2   Ford         2

Je viens de créer un dataframe nommé "df" en utilisant la fonction DataFrame() de la librairie Pandas. Notez les index créés par défaut : 0,1,2,...

Afficher une ligne du DataFrame

>>>print(df.loc[0])
cars        BMW
passings      3
Name: 0, dtype: object

Créer ses propres index

L'argument index de la fonction DataFrame() vous permet de personnaliser les index.

>>> # pandas ayant été installé et importé
>>> data = {"calories": [420, 380, 390],"durée": [50, 40, 45]}       
>>> df = pd.DataFrame(data, index = ["lundi", "mardi", "mercredi"])
>>> print(df)
      calories  durée
lundi       420        50
mardi       380        40
mercredi    390        45

>>> print(df.loc["mardi"])
calories    380
duration     40
Name: day2, dtype: int64

Ci-dessus je personnalise les index puis j'accède à la ligne 2 du dataframe nommé "df" avec la clé 'mardi'.

Créer un dataframe à partir d'un fichier

Ces fichiers peuvent contenir des masses très importante de données.
Une analyse humaine d'une telle masse de données n'est pas envisageable.
Dans le cadre d'un traitement automatisé il faut d'abord créer un dataframe à partir du fichier.

Créer un dataframe à partir d'un fichier CSV

On peut exporter au format CSV les données d'un tableur (excel, calc) vers un fichier CSV.
Il faut ensuite faire appel à Pandas pour créer un dataframe.

Le fichier "data2.csv"

Ce fichier est stocké dans le dossier "c:/prog_python" (comme les scripts).

Durée,Pouls,Pouls_max,Calories
60,110,130,409.1
60,117,145,479.0
60,103,135,340.0
45,109,175,282.4
45,117,148,406.0
60,102,127,300.5
60,110,136,374.0
45,104,134,253.3
30,109,133,195.1
60,98,124,269.0
60,103,147,329.3
60,100,120,250.7
60,106,128,345.3
60,104,132,379.3
...
60,105,140,290.8
60,110,145,300.4
60,115,145,310.2
75,120,150,320.4
75,125,150,330.4

Les données sont séparées par des virgules.
La présentation est tabulaire.
La première ligne contient les en-têtes.
Les durées sont exprimées en minutes.
Le tableau de données contient 168 lignes de données auquel il faut rajouter une ligne d'en-tête.

Le programme

# dataframe_csv.py
# créer un dataframe à partir d'un fichier CSV
import pandas as pd
df = pd.read_csv('data2.csv')
print(df) 

Utilisation de la fonction read_csv() de pandas pour créer un dataframe à partir du fichier CSV.

Le rendu

     Duree  Pouls  Pouls_max  Calories
0       60    110        130     409.1
1       60    117        145     479.0
2       60    103        135     340.0
3       45    109        175     282.4
4       45    117        148     406.0
..     ...    ...        ...       ...
164     60    105        140     290.8
165     60    110        145     300.4
166     60    115        145     310.2
167     75    120        150     320.4
168     75    125        150     330.4

[169 rows x 4 columns]

Si vous avez un Dataframe volumineux l'instruction print(nomDataframe) renverra par défaut que les 5 premières lignes, et les 5 dernières lignes.
Utilisez la fonction to_string() pour afficher l’intégralité du DataFrame.

Version améliorée du programme

Remplacez l'instruction print(df) par print(df.to_string())
Cette fois la totalité du dataframe est affichée.

Créer un dataframe à partir d'un fichier JSON

Une grande masse de données peut aussi être stockée dans un fichier au format JSON.
Le format JSON n'est plus réservé pour un traitement via JS ; la bibliothèque Pandas, peut manipuler ce type de fichier.

Le fichier JSON

Il se nomme "data1.json".
Il reprend les mêmes données que data2.csv mais avec une structure json.

{
  "Durée":{
    "0":60,
    "1":60,
    "2":60,
    "3":45,
    "4":45,
    "5":60,
	...
	"168":75
	 },
  "Pouls":{
    "0":110,
    "1":117,
    "2":103,
    "3":109,
    "4":117,
    "5":102,
	...
	"168":125
  },
  "Pouls_max":{
    "0":130,
    "1":145,
    "2":135,
    "3":175,
    "4":148,
    "5":127,
	...
	"168":150
	 },
  "Calories":{
    "0":409.1,
    "1":479.0,
    "2":340.0,
    "3":282.4,
    "4":406.0,
    "5":300.5,
	...
	"168":330.4
  }
}

Ici il y a quatre objets JSON (identifiés : Durée, Pouls, Pouls_max & Calories) et chaque objet contient 168 paires de clé:valeur.
Chaque objet JSON a en fait une structure identique à un dictionnaire Python.
Cependant dans un objet JSON, les clés sont obligatoirement entre guillemets doubles.

Le programme pour lire le fichier JSON

# dataframe_json.py
# créer un dataframe à partir d'un fichier JSON
import pandas as pd
df = pd.read_json('data1.json')
print(df.to_string())

Emploi de la fonction read_json() de pandas pour produire un dataframe à partir d'un fichier JSON.

Le rendu (extraits)

Affichage d'un dataframe avec 168 lignes.

     Durée  Pouls  Pouls_max  Calories
0       60    110        130     409.1
1       60    117        145     479.0
2       60    103        135     340.0
3       45    109        175     282.4
4       45    117        148     406.0
5       60    102        127     300.5
...
164     60    105        140     290.8
165     60    110        145     300.4
166     60    115        145     310.2
167     75    120        150     320.4
168     75    125        150     330.4

On obtient et c'est heureux, le même dataframe qu'à partir du fichier CSV.

Afficher seulement le début d'un dataFrame

Un dataframe obtenu à partir d'un fichier CSV ou JSON peut contenir des milliers de lignes ...
Afficher seulement des les 20 premières lignes ?

Le code
...
print(df.head(20))
print("-----------------")
print(df.info()) 
...

Emploi de la méthode head() appliquée à l'objet 'dataframe' argumentée avec le nombre de lignes à afficher puis de la méthode info().

Le rendu
 Durée  Pouls  Pouls_max  Calories
0      60    110        130     409.1
1      60    117        145     479.0
2      60    103        135     340.0
3      45    109        175     282.4
4      45    117        148     406.0
...
17     45     90        112       NaN
18     60    103        123     323.0
19     45     97        125     243.0
-----------------
class 'pandas.DataFrame'
RangeIndex: 169 entries, 0 to 168
Data columns (total 4 columns):
 #   Column     Non-Null Count  Dtype  
---  ------     --------------  -----  
 0   Durée      169 non-null    int64  
 1   Pouls      169 non-null    int64  
 2   Pouls_max  169 non-null    int64  
 3   Calories   164 non-null    float64
dtypes: float64(1), int64(3)
memory usage: 5.4 KB
None

Affichage de 20 premières lignes y compris la ligne d'en-têtes.
Si le nombre de lignes n’est pas spécifié, la méthode head() renvoit les 5 premières lignes.

La méthode info() indique entre autres le nombre de valeurs non nulles présentes dans chaque colonne.
Ici il y a 164 valeurs non nulles sur 169 dans la colonne "Calories". Donc il ya 5 lignes sans aucune valeur pour cette colonne ...

Le nettoyage des données

Les cellules vides peuvent être problématiques lors de l'analyse des données.
Le nettoyage des données consiste à corriger dans le dataframe les données erronnées provenant du fichier source.

Supprimer les lignes avec des cellules vides

Pour gérer quelques cellules vides, vous pouvez supprimer les lignes qui les contiennent.
Cette solution est généralement acceptable pour un ensemble de données très volumineux ; la suppression de quelques lignes n'aura pas d'impact significatif sur le résultat.

Script pour obtenir un dataframe débarassé des lignes contenant des cellules vides

# dataframe_csv_clean.py
# créer un dataframe à partir d'un fichier .csv
# puis dupliquer ce dataframe pour obtnir une version nettoyée

import pandas as pd
df = pd.read_csv('data3.csv')

print(df.to_string())

new_df = df.dropna()
print("------------------------")
print(new_df.to_string())

Cette fois nous créons un dataframe à partir d'un fichier de taille modeste ("data3.csv") ; il ne comprend que 31 lignes de données.
Ainsi vous serez capable d'appréhender d'un seul regard les données omises ou erronnées.

Ce programme crée deux dataframes à partir du fichier "data3.csv".
Par défaut, la méthode dropna() renvoie un nouveau DataFrame nettoyé et ne modifie pas l’original.

Le premier dataframe nommé "df"

C'est la copie fidèle du fichier "data3.csv".

    Durée          Date  Pouls  Pouls_max  Calories
0      60  '2020/12/01'    110        130     409.1
1      60  '2020/12/02'    117        145     479.0
2      60  '2020/12/03'    103        135     340.0
3      45  '2020/12/04'    109        175     282.4
4      45  '2020/12/05'    117        148     406.0
5      60  '2020/12/06'    102        127     300.0
6      60  '2020/12/07'    110        136     374.0
7     450  '2020/12/08'    104        134     253.3
8      30  '2020/12/09'    109        133     195.1
9      60  '2020/12/10'     98        124     269.0
10     60  '2020/12/11'    103        147     329.3
11     60  '2020/12/12'    100        120     250.7
12     60  '2020/12/12'    100        120     250.7
13     60  '2020/12/13'    106        128     345.3
14     60  '2020/12/14'    104        132     379.3
15     60  '2020/12/15'     98        123     275.0
16     60  '2020/12/16'     98        120     215.2
17     60  '2020/12/17'    100        120     300.0
18     45  '2020/12/18'     90        112       NaN
19     60  '2020/12/19'    103        123     323.0
20     45  '2020/12/20'     97        125     243.0
21     60  '2020/12/21'    108        131     364.2
22     45           NaN    100        119     282.0
23     60  '2020/12/23'    130        101     300.0
24     45  '2020/12/24'    105        132     246.0
25     60  '2020/12/25'    102        126     334.5
26     60      20201226    100        120     250.0
27     60  '2020/12/27'     92        118     241.0
28     60  '2020/12/28'    103        132       NaN
29     60  '2020/12/29'    100        132     280.0
30     60  '2020/12/30'    102        129     380.3
31     60  '2020/12/31'     92        115     243.0

Les données erronnées

Le deuxième dataframe nommé "new_df" (version corrigée du premier)

 Durée          Date  Pouls  Pouls_max  Calories
0      60  '2020/12/01'    110        130     409.1
1      60  '2020/12/02'    117        145     479.0
2      60  '2020/12/03'    103        135     340.0
3      45  '2020/12/04'    109        175     282.4
4      45  '2020/12/05'    117        148     406.0
5      60  '2020/12/06'    102        127     300.0
6      60  '2020/12/07'    110        136     374.0
7     450  '2020/12/08'    104        134     253.3
8      30  '2020/12/09'    109        133     195.1
9      60  '2020/12/10'     98        124     269.0
10     60  '2020/12/11'    103        147     329.3
11     60  '2020/12/12'    100        120     250.7
12     60  '2020/12/12'    100        120     250.7
13     60  '2020/12/13'    106        128     345.3
14     60  '2020/12/14'    104        132     379.3
15     60  '2020/12/15'     98        123     275.0
16     60  '2020/12/16'     98        120     215.2
17     60  '2020/12/17'    100        120     300.0
19     60  '2020/12/19'    103        123     323.0
20     45  '2020/12/20'     97        125     243.0
21     60  '2020/12/21'    108        131     364.2
23     60  '2020/12/23'    130        101     300.0
24     45  '2020/12/24'    105        132     246.0
25     60  '2020/12/25'    102        126     334.5
26     60      20201226    100        120     250.0
27     60  '2020/12/27'     92        118     241.0
29     60  '2020/12/29'    100        132     280.0
30     60  '2020/12/30'    102        129     380.3
31     60  '2020/12/31'     92        115     243.0

Ici les lignes 18, 22, 28 (lignes ayant des cellules vides) ont disparu !

Nouvelle version du script

# dataframe_csv_clean.py
# créer sur place un dataframe nettoyé

import pandas as pd
df = pd.read_csv('data3.csv')

df.dropna(inplace = True)
print(df.to_string())

La fonction dropna(inplace = True) ne renvoit pas deux dataframes ; la version nettoyée écrase la première version.

Insérer une nouvelle valeur dans les cellules vides

Une autre façon de gérer les cellules vides consiste à insérer une valeur.

Remplacer les valeurs nulles par 130

df = pd.read_csv('data3.csv')
df.fillna(130, inplace = True)
print(df.to_string())

La méthode fillna() permet de remplacer les cellules vides (de n'importe quelle colonne) par une valeur, ici 130.
Le résultat n'est pas très heureux puisque une cellule "Date" de la ligne 26 contient désormais 130 ...

Corriger les erreurs de façon plus ciblée

Le programme

#dataframe_csv_clean2.py
import pandas as pd
df = pd.read_csv('data3.csv')
print(df.to_string())
print("---------------")
      
calories_moy = df["Calories"].mean()
# écrire dans les cellules 'calorie' vides
df = df.fillna({"Calories": calories_moy},inplace=True )

# remplacer une valeur dans une cellule 'Durée'
df.loc[7, 'Durée'] = 45

#supprimer ligne en doublon
df.drop_duplicates(inplace = True)

# convertir au format date
df['Date'] = pd.to_datetime(df['Date'], format='mixed')

# supprimer la ligne avec une date nulle
df.dropna(subset=['Date'], inplace = True)

#afficher un dataframe entièrement corrigé
print(df.to_string())

Analyse du script

Ce programme vise à obtenir un dataframe débarassé de toutes les erreurs et omissions : cellules vides, données aberrantes, format incorrect, lignes en double.

Le rendu

La première partie du rendu affiche le dataframe brut mais aussi affiche la moyenne pour la colonne "Calories".

...
---------------
moyenne de la colonne 'Calories' :  304.68

    Durée       Date  Pouls  Pouls_max  Calories
0      60 2020-12-01    110        130    409.10
1      60 2020-12-02    117        145    479.00
2      60 2020-12-03    103        135    340.00
3      45 2020-12-04    109        175    282.40
4      45 2020-12-05    117        148    406.00
5      60 2020-12-06    102        127    300.00
6      60 2020-12-07    110        136    374.00
7      45 2020-12-08    104        134    253.30
8      30 2020-12-09    109        133    195.10
9      60 2020-12-10     98        124    269.00
10     60 2020-12-11    103        147    329.30
11     60 2020-12-12    100        120    250.70
13     60 2020-12-13    106        128    345.30
14     60 2020-12-14    104        132    379.30
15     60 2020-12-15     98        123    275.00
16     60 2020-12-16     98        120    215.20
17     60 2020-12-17    100        120    300.00
18     45 2020-12-18     90        112    304.68
19     60 2020-12-19    103        123    323.00
20     45 2020-12-20     97        125    243.00
21     60 2020-12-21    108        131    364.20
23     60 2020-12-23    130        101    300.00
24     45 2020-12-24    105        132    246.00
25     60 2020-12-25    102        126    334.50
26     60 2020-12-26    100        120    250.00
27     60 2020-12-27     92        118    241.00
28     60 2020-12-28    103        132    304.68
29     60 2020-12-29    100        132    280.00
30     60 2020-12-30    102        129    380.30
31     60 2020-12-31     92        115    243.00

On obtient enfin donc un dataframe propre : corrigé de toutes les erreurs et avec seulement une ligne supprimée.

Analyse des données

Maintenant on peut procéder à l'analyse automatique de ce dataframe et toujours avec la librairie Pandas.

La fonction corr()

Je crée un dataframe à partir du fichier "data2.csv" qui je le rappelle contient 168 lignes de données.

Commandes dans la console

>>> import pandas as pd
>>> import os
>>> os.chdir("c:/python_prog")
>>> df = pd.read_csv('data2.csv')
>>> print(df.to_string())
     Durée  Pouls  Pouls_max  Calories
0       60    110        130     409.1
1       60    117        145     479.0
2       60    103        135     340.0
3       45    109        175     282.4
4       45    117        148     406.0
5       60    102        127     300.5
...
164     60    105        140     290.8
165     60    110        145     300.4
166     60    115        145     310.2
167     75    120        150     320.4
168     75    125        150     330.4
>>> print(df.corr())
              Durée     Pouls  Pouls_max  Calories
Durée      1.000000 -0.155408   0.009403  0.922721
Pouls     -0.155408  1.000000   0.786535  0.025120
Pouls_max  0.009403  0.786535   1.000000  0.203814
Calories   0.922721  0.025120   0.203814  1.000000

La méthode corr() ignore les colonnes non numériques.

Le résultat de la méthode corr() est un tableau contenant de nombreux nombres qui représentent la force de la corrélation entre deux colonnes.
Ces nombres varient de -1 à 1.
1 indique une corrélation parfaite (1 à 1) : pour cet ensemble de données, chaque augmentation d'une valeur dans la première colonne entraîne une augmentation de l'autre.
0,9 indique également une bonne corrélation : si vous augmentez une valeur, l'autre augmentera probablement aussi.
-0,9 indique une corrélation tout aussi forte que 0,9, mais si vous augmentez une valeur, l'autre diminuera d'autant.
0,2 indique une très faible corrélation.

Analyse du dataframe nommée "df"

On constate que les variables "Durée" et "Durée" présentent une corrélation de 1,000000. Ce qui est logique : chaque colonne est toujours parfaitement corrélée à elle-même.
Les variables "Durée" et "Calories" présentent une corrélation de 0,922721, ce qui est très bonne corrélation positive. On peut donc prédire que plus l’entraînement est long, plus on brûle de calories. Et inversement : si l’on brûle beaucoup de calories, l’entraînement a été plus long.
Les variables "Durée" et "Pouls_max" (fréquence cardiaque maxi) présentent une corrélation de 0,009403, ce qui est très faible. Cela signifie qu’on ne peut pas prédire la fréquence cardiaque maximale à partir de la seule durée de l’entraînement et vice-versa.

J'ai emploi le terme "prédiction" ; on aborde alors le "machine learning", qui est une composante de l'IA ...

Exemple complet

Pour résumer mon propos je vous présente un exemple complet.
Dans le programme ci-dessous je produis un dataframe à partir du fichier "data3.csv" (31 lignes seulement).
Je crée une version nettoyée de ce dataframe et je calcule les corrélations les différentes colonnes.

Le programme

#dataframe_csv_complet.py
import pandas as pd
df = pd.read_csv('data3.csv')
     
calories_moy = df["Calories"].mean()
print("moyenne de la colonne 'Calories' : " , calories_moy)
print("--------------------")

# écrire dans les cellules 'calories' vides
df = df.fillna({"Calories": calories_moy},inplace=True )

# remplacer une valeur dans une cellule 'Durée'
df.loc[7, 'Durée'] = 45

#supprimer ligne en doublon
df.drop_duplicates(inplace = True)

# convertir au format date
df['Date'] = pd.to_datetime(df['Date'],format='mixed')

# supprimer la ligne avec une date nulle
df.dropna(subset=['Date'], inplace = True)

#afficher un dataframe entièrement corrigé
print("dataframe nettoyé:")
print(df.to_string())
print("-----------------")
print("corrélations :" )
print(df.corr())

Le rendu (partie finale)

-----------------
corrélations :
              Durée      Date     Pouls  Pouls_max  Calories
Durée      1.000000  0.212084 -0.083417  -0.296585  0.338213
Date       0.212084  1.000000 -0.369328  -0.517827 -0.358014
Pouls     -0.083417 -0.369328  1.000000   0.261426  0.478723
Pouls_max -0.296585 -0.517827  0.261426   1.000000  0.329600
Calories   0.338213 -0.358014  0.478723   0.329600  1.000000

La masse de données n'est peut-être pas suffisante pour en tirer des prédictions ...

Essayez vous-même !

Téléchargez le fichier compressé contenant les fichiers CSV & JSON

Statistiques avec Pandas

Comme l'illustre l'article de presse ci-dessus on utilise beaucoup les fréquences cumulées en économie et démographies.
Une fréquence est exprimée pour 1 ou pour 100, donc toujours par rapport à l'effectif total.
La bibliothèque Pandas est parfaitement adaptée pour traiter statistiquement des tableaux de données ('dataframes') pour produire entre autres des fréquences et fréquences cumulées.

Thème

Ci-dessous : notes obtenues par une classe à une épreuve.
Les notes sont comprises entre 1 et 5.
Concevez le programme qui calcule les effectifs, effectifs cumulés, fréquences et fréquences cumulées.

Le programme

# FRC.py
# effectif simple et cumulés

import statistics as stat
import pandas as pd
import matplotlib.pyplot as plt

# liste des notes d'une classe (note entre 1 et 5)
notes = [5,4,3,4,1,3,2,3,4,1,5,4,3,2,1,1,2,3,4,5]

#utilisation du module statistics
moyenne = stat.mean(notes)
mode = stat.multimode(notes)
mediane =stat.median(notes)
print("Note moyenne : ", moyenne)
print("Note(s) modale(s) : ", mode)
print("Note médiane : ", mediane)
print("-----------------")

# création d'une série pandas à partir de la liste des notes
notes =pd.Series(notes)

# calcul des effectifs simples et cumulés
effectif = notes.value_counts().sort_index()
print("affichage de la série 'effectif' ")
print(effectif)
print("--------------------")
effectif_cum = effectif.cumsum()
print("affichage de la série 'effectif_cum'")
print(effectif_cum)

# calcul des fréquences simples et cumulées. 
frequence = effectif / effectif.sum()              
frequence_cum = frequence.cumsum()             

# Tableau des effectifs et fréquences
print("tableau des effectifs et fréquences")
df = pd.DataFrame({
    'Effectif'      : effectif,
    'Effectif cumulé' : effectif_cum,
    'Effectif en % '    : frequence *100,
    'Effectif cumulé en %': frequence_cum *100
})
print(df)

# graphique
largeur = 1
plt.bar(df.index,frequence_cum *100, largeur)
plt.title("Fréquences cumulées")
plt.xlabel("Notes")
plt.ylabel("fréquences cumulées en %")
plt.show()

Notez les importations. J'ai besoin du module statistics pour calculer les valeurs centrales. J'ai besoin de matplotlib.pyplot pour représenter le diagramme des fréquences cumulées.

Remarque : une série statistique discrète peut avoir plusieurs modes.
C'est le cas ici : pour la note 3 et pour la note 4 l'effectif est maximum : 5 copies.
Il faut donc mieux utiliser la fonction statistics.multimode() Cette fonction retourne une liste.

effectif = notes.value_counts().sort_index() : que fait cette instruction ?
La méthode value_counts() compte les occurrences pour chaque valeur dans la série "notes".
la méthode sort_index() trie la nouvelle série par valeur.
Les valeurs sont les index de cette nouvelle série nommée "effectif".

Ce qui donne pour l'exemple :

1    4en
2    3
3    5
4    5
5    3

effectif_cum = effectif.cumsum() :
la méthode cum_sum() appliquée à la série "effectif" produit une nouvelle série nommée "effectif_cum" avec les cumuls.

frequence = effectif / effectif.sum() : utilisation de la fonction sum() pour obtenir la série des fréquences nommée "frequence". Ce qui donne : 4/20 , 3/20, 5/20 ... Donc ici les fréquences sont exprimées pour 1 !

Il y a ensuite construction d'un dataframe nommé "df" à partir des différentes séries produites (effectif, effectif_cum, frequence, frequence_cum).

L'histogramme des fréquences cumulées est bâti à partir des index du dataframe "df" et de la colonne "frequences_cum".

Le rendu

Note moyenne :  3
Note(s) modale(s) :  [4, 3]
Note médiane :  3.0
-----------------
affichage de la série 'effectif' 
1    4
2    3
3    5
4    5
5    3
--------------------
affichage de la série 'effectif_cum'
1     4
2     7
3    12
4    17
5    20
--------------------------
Tableau des effectifs et fréquences
   Effectif  Effectif cumulé  Effectif en %   Effectif cumulé en %
1         4                4            20.0                  20.0
2         3                7            15.0                  35.0
3         5               12            25.0                  60.0
4         5               17            25.0                  85.0
5         3               20            15.0                 100.0
--------------------

Analyse du tableau :
60% des élèves ont 3 ou moins
85% des élèves ont 4 ou moins
Donc 25% des élèves ont la note 4
etc.

Histogramme des fréquences cumulées

Revoyez la partie sur le module matplotlib.pyplot dans le chapitre 14.
Mathématiques et statistiques en Python

Travaux pratiques

Dans beaucoup de pays la notation est basée sur des lettres.

La liste des notes à analyser :
notes = ['A','B','C','B','E','C','D','D','B','E','A','B','C','D','C','E','D','C','B','A','E']
Je vous communique ci-dessous le rendu du programme.
À vous de reconstituer ce script .

Le rendu

nombre de notes :  21
Note(s) modale(s) :  ['B', 'C']
notes triées :  ['A', 'A', 'A', 'B', 'B', 'B', 'B', 'B', 'C', 'C', 'C', 'C', /
'C', 'D', 'D', 'D', 'D', 'E', 'E', 'E', 'E']
médiane :  C
affichage de la série 'effectif' 
A    3
B    5
C    5
D    4
E    4
--------------------
affichage de la série 'effectif_cum'
A     3
B     8
C    13
D    17
E    21
---------------
tableau des effectifs et fréquences
   Effectif  Effectif cumulé  Effectif en %   Effectif cumulé en %
A         3                3       14.285714             14.285714
B         5                8       23.809524             38.095238
C         5               13       23.809524             61.904762
D         4               17       19.047619             80.952381
E         4               21       19.047619            100.000000
----------------------------

On ne peut pas calculer la moyenne d'une série de lettres.
La fonction stat.multimode() fonctionne même avec une série de lettres.
Par contre la fonction stat.median() 'plante'. Pour déterminer la médiane, l'astuce consiste à trier les notes par valeurs croissantes et extraire la note de rang 11 (puisque la série comprend 21 notes).

Histogramme des fréquences cumulées

Numpy

En complément de Pandas on peut aussi utiliser les nombreuses fonctions de la bibliothèque Numpy.

Revoyez éventuellement la partie sur la librairie Numpy dans le chapitre 14.
Mathématiques et statistiques en Python
Numpy est très à l'aise pour calculer les centiles (déciles, quartiles) fort utilisées pour étudier les inégalités dans une population.