Traitement de données structurées

pandas est un module Python très riche qui facilite :

Le module pandas

Un module vient compléter le jeu d'instructions de base du langage Python. Il doit donc être installé avant d'être utilisé. La commande sous Windows est la suivante :

python.exe -m pip install pandas

Et sous Linux ou macOS

python3 -m pip install pandas

Attention !

Cette commande doit être exécutée en mode administrateur. Sous Windows, dans le menu Démarrer, clic droit sur l'application "invite de commandes" et choisir "exécuter en tant qu'administrateur".

L'environnement une fois configuré, nous pouvons commencer la découverte des éléments principaux de pandas.

Le type DataFrame

Il s'agit d'une structure de données représentant un tableau bidimensionnel avec entête, en quelque sorte la représentation en mémoire d'un fichier CSV ou d'une feuille Excel.

Initialisation

import pandas as pd

df = pd.DataFrame(
    [
        [ 1, 'Atlanta Hawks',  'ATL', 'Southeast' ],
        [ 2, 'Boston Celtics', 'BOS', 'Atlantic' ],
        [ 3, 'Brooklyn Nets',  'BKN', 'Atlantic' ],
    ],
    columns = [ 'id', 'full_name', 'abbr', 'division' ])

print(df)

Le script Python ci-dessus produit le résultat suivant :

   id       full_name abbr   division
0   1   Atlanta Hawks  ATL  Southeast
1   2  Boston Celtics  BOS   Atlantic
2   3   Brooklyn Nets  BKN   Atlantic

les attributs columns et index permettent respectivement de récupérer les noms des colonnes et la liste des indices des lignes de l'objet DataFrame.

print('Colonnes : ')
print(df.columns)

print('\nIndices : ')
print(df.index)
Colonnes : 
Index(['id', 'full_name', 'abbr', 'division'], dtype='object')

Indices : 
RangeIndex(start=0, stop=3, step=1)

Extraction de valeurs

Comme pour les listes, l'opérateur [] permet d'extraire une ou plusieurs valeurs du tableau.

Par exemple, df['abbr'] correspond aux données de la colonne abbr de l'objet df. Et df['abbr'][0] retourne la valeur de la première ligne, c'est à dire 'ATL'.

Attention

Cette méthode renvoie une copie de la valeur figurant dans l'objet DataFrame, elle ne peut donc pas être utilisée pour modifier le contenu du tableau. Pour toute modification de valeur, il faut privilégier la propriété loc qui prend deux paramètres : le numéro de ligne suivi du nom de la colonne.
df.loc[0, 'abbr'] = 'LAL'

Parcours des lignes

Il est possible de parcourir l'ensemble des lignes d'un DataFrame en utilisant l'attribut index dans une boucle.

for i in df.index:
    print(df['full_name'][i])
for i in df.index:
    print(df.loc[i, 'full_name'])
Atlanta Hawks
Boston Celtics
Brooklyn Nets

Opérations sur les colonnes

pandas propose des fonctions permettant de calculer certaines quantités au sein d'une colonne.

Fonction Définition
df['col'].mean() Moyenne des valeurs de la colonne col
df['col'].std() Écart type des valeurs de la colonne col
df['col'].max() Valeur maximale de la colonne col
df['col'].idxmax() Indice (i.e. emplacement) de la valeur maximale de la colonne col
df['col'].min() Valeur minimale de la colonne col
df['col'].idxmin() Indice de la valeur minimale de la colonne col

Chargement de fichiers

pandas facilite aussi le chargement de fichiers afin d'initaliser un DataFrame avec un volume important de données.

Format CSV

import pandas as pd

# Initialisation du DataFrame df
# à partir des données du fichier CSV
df = pd.read_csv('data.csv', sep = ';')

# Enregistrement du DataFrame df au format CSV
df.to_csv('data_updated.csv', sep = ';', index = False)

Format Excel

Ce format, plus complexe, nécessite l'installation de deux modules supplémentaires pour le chargement et la sauvegarde. Sous Windows :

python.exe -m pip install xlrd xlsxwriter

et sous Linux ou macOS

python3 -m pip install xlrd xlsxwriter
import pandas as pd

# Initialisation du DataFrame df
# à partir des données du fichier Excel
df = pd.read_excel('data.xlsx', sheet_name = 'Feuille1')

# Enregistrement du DataFrame df au format Excel
writer = pd.ExcelWriter('data_updated.xlsx', engine = 'xlsxwriter')
df.to_excel(writer, sheet_name = 'Feuille1', index = False)
writer.save()

Exercice

Le fichier measures_20191114_20191122.xls recense les concentrations de certains polluants atmosphériques mesurées par le capteur Flow.

Produisez l'affichage suivant concernant la présence de composés organiques volatiles (COV).

Concentration de COV :
----------------------

Densité moyenne  :  159.0 ppm
Écart type       :  49.0
Densité maximale :  479 ppm ( 2019-11-20 08:12:14 )
Correction
import pandas as pd

# Chargement des données Excel en mémoire
df = pd.read_excel('data/measures_20191114_20191122.xls', sheet_name = 'Feuille1')

# Recherche de l'indice de la valeur maximale dans la colonne VOC
maxVOCindex = df['VOC (ppb)'].idxmax()

# Affichage de la date et de la concentration
print('\n\nConcentration de COV :')
print('-----------------------\n')
print('Densité moyenne  : ', round(df['VOC (ppb)'].mean()), 'ppm')
print('Écart type       : ', round(df['VOC (ppb)'].std()))
print('Densité maximale : ', df['VOC (ppb)'][maxVOCindex], 'ppm (', df['date (UTC)'][maxVOCindex], ')')

L'archive NBA2018-2019.zip contient les fichiers suivants :

teams.xlsx
liste des équipes NBA
ATL.xlsx, BKN.xlsx, BOS.xlsx, ...
liste des matchs (à domicile ou à l'extérieur) de l'équipe

Exercice

À partir du fichier teams.xlsx, affichez la liste des équipes de la division Pacific.

Correction
import pandas as pd

# Chargement des données Excel en mémoire
df = pd.read_excel('data/NBA2018-2019/teams.xlsx', sheet_name = 'teams')

# Afficher le nom des équipes de la division Pacific

# Parcours de l'ensemble des lignes du DataFrame df
for i in df.index:
    # Pour chaque ligne, tester la valeur de la colonne 'division'
    # pour savoir si on doit afficher ou pas le nom de l'équipe 'full_name'
    if df['division'][i] == 'Pacific':
        print(df['full_name'][i])

Exercice

À partir du fichier ATL.xlsx, calculez le nombre de victoires à domicile et à l'extérieur de l'équipe des Atlanta Hawks.

Correction
import pandas as pd

# Chargement des données Excel en mémoire
df = pd.read_excel('data/NBA2018-2019/ATL.xlsx', sheet_name='games')

# Initialisation des variables 
home_wins = 0
visitor_wins = 0

# Parcours de l'ensemble des lignes du DataFrame df
for i in df.index:
    # Match à domicile ?
    if df['home_team'][i] == 'ATL':
        # Victoire à domicile ?
        if df['home_team_score'][i] > df['visitor_team_score'][i]:
            home_wins += 1
    else:
        # Victoire à l'extérieur ?
        if df['visitor_team_score'][i] > df['home_team_score'][i]:
            visitor_wins += 1

print('home wins :', home_wins)
print('visitor wins :', visitor_wins)

Traitement par lots

L'intérêt d'un script réside dans sa capacité à automatiser une tâche sur un grand nombre de données, par exemple sur plusieurs fichiers. Le module glob permet de filtrer les noms de fichiers à l'aide d'une expression rationnelle.

import glob

# Affiche les noms de fichiers situés dans le sous-répertoire
# data ayant pour extension .txt
for filename in glob.glob('data/*.txt'):
  print(filename)

# Affiche les noms de fichiers csv situés dans le sous-répertoire
# data commençant par measure et suivis de 2 caractères
for filename in glob.glob('data/measure??.csv'):
  print(filename)

Exercice

Créez un fichier teams_stats.xlsx à partir du fichier teams.xlsx en y ajoutant 3 colonnes :

home_wins
Nombre de victoires à domicile
visitor_wins
Nombre de victoires à l'extérieur
win_percentage
Pourcentage de victoires

Info

Il existe plusieurs méthodes pour ajouter une colonne à un objet DataFrame, l'une d'elles consiste à créer une liste remplie de zéros de la taille des colonnes existantes :
df['col'] = [0] * len(df.index)
col correspondant au nom de la nouvelle colonne.
Correction
import pandas as pd

# Chargement des équipes en mémoire
df_teams = pd.read_excel('data/NBA2018-2019/teams.xlsx', sheet_name = 'teams')

# Ajout de quatre nouvelles colonnes (remplies de 0)
df_teams['home_wins']      = [0] * len(df_teams.index)
df_teams['visitor_wins']   = [0] * len(df_teams.index)
df_teams['win_percentage'] = [0] * len(df_teams.index)

for filename in glob.glob('data/NBA2018-2019/???.xlsx'):
    # Extraction des trois lettres avant l'extension .xlsx
    abbr = filename[-8:-5]
    
    # Chargement des matchs en mémoire
    df = pd.read_excel(filename, sheet_name = 'games')

    # Parcours de l'ensemble des lignes du DataFrame df
    home_wins    = 0
    visitor_wins = 0

    for i in df.index:
        if df['home_team'][i] == abbr:
            if df['home_team_score'][i]>df['visitor_team_score'][i]:
                home_wins += 1
        else:
            if df['visitor_team_score'][i]>df['home_team_score'][i]:
                visitor_wins += 1
        
    df_teams.loc[df_teams['abbreviation'] == abbr, 'home_wins']      = home_wins
    df_teams.loc[df_teams['abbreviation'] == abbr, 'visitor_wins']   = visitor_wins
    df_teams.loc[df_teams['abbreviation'] == abbr, 'win_percentage'] = 100 * (home_wins + visitor_wins)/ len(list(df.index))
                
# Enregistre le DataFrame df au format Excel
writer = pd.ExcelWriter('data/NBA2018-2019/teams_stats.xlsx', engine = 'xlsxwriter')
df_teams.to_excel(writer, sheet_name = 'stats', index = False)
writer.save()

Tri et filtre

Enfin, le module pandas permet d'effectuer des opérations globales sur un objet DataFrame.

Tri

La méthode df.sort_values() permet de trier l'ensemble des lignes d'un objet DataFrame nommé df avec les paramètres suivants :

Paramètre Signification
by Nom de la colonne utilisée comme critère de tri
ascending Ordre du tri : True pour croissant et False pour décroissant
inplace True pour un tri sur place et False pour créer une copie du DataFrame
df_sorted = df.sort_values(by='division', ascending=True, inplace=False)

Exercice

Réalisez un palmarès de la saison NBA 2018-2019 en triant dans l'ordre décroissant des pourcentages de victoire le fichier teams_stats.xlsx.

Correction
import pandas as pd

# Chargement des équipes en mémoire
df_teams = pd.read_excel('data/NBA2018-2019/teams_stats.xlsx', sheet_name = 'stats')

# Recherche des max et min
maxPercentageIndex = df_teams['win_percentage'].idxmax()
minPercentageIndex = df_teams['win_percentage'].idxmin()

print('\n\nNBA 2018-2019 statistics :')
print('--------------------------\n')
print('Best team  : ', df_teams['full_name'][maxPercentageIndex])
print('Worst team : ', df_teams['full_name'][minPercentageIndex])

# Tri par ordre décroissant en fonction du pourcentage de victoires
df_teams.sort_values(by='win_percentage', ascending=False, inplace=True)

# Sauvegarde au format Excel
writer = pd.ExcelWriter('data/NBA2018-2019/teams_stats.xlsx', engine = 'xlsxwriter')
df_teams.to_excel(writer, sheet_name = 'stats', index = False)
writer.save()

Filtre

La méthode df.filter() permet de filter le contenu de l'objet DataFrame nommé df en sélectionnant les colonnes à retenir

df_filtered = df.filter(['full_name', 'abbr'])

Ou en sélectionnant les indices des lignes à conserver avec la propriété df.iloc

df_filtered = df.iloc[1:3, :]

Enfin, il est aussi possible de filtrer le contenu d'un objet DataFrame en sélectionnant les lignes vérifiant un critère. Par exemple :

df_filtered = df[df['division'] == 'Atlantic']

Exercice

À partir du fichier teams_stats.xlsx, générez deux nouveaux fichiers Excel donnant les palmarès pour les conference East et West, en ne retenant que les colonnes abbreviation, city, division, full_name et win_percentage.

Correction
import pandas as pd

# Chargement des équipes en mémoire
df_teams = pd.read_excel('data/NBA2018-2019/teams_stats.xlsx', sheet_name = 'stats')

# Filtrage des lignes pour la conference East
df_teams_east = df_teams[df_teams['conference'] == 'East']
df_teams_short = df_teams_east.filter(['abbreviation', 'city', 'division', 'full_name', 'win_percentage'])

# Sauvegarde dans un fichier Excel
writer = pd.ExcelWriter('data/NBA2018-2019/teams_stats_east.xlsx', engine = 'xlsxwriter')
df_teams_short.to_excel(writer, sheet_name = 'stats', index = False)
writer.save()

# Filtrage des lignes de la conference West
df_teams_west = df_teams[df_teams['conference'] == 'West']
df_teams_short = df_teams_west.filter(['abbreviation', 'city', 'division', 'full_name', 'win_percentage'])

# Sauvegarde dans un fichier Excel
writer = pd.ExcelWriter('data/NBA2018-2019/teams_stats_west.xlsx', engine = 'xlsxwriter')
df_teams_short.to_excel(writer, sheet_name = 'stats', index = False)
writer.save()