television (nettoyage)#

à télécharger

pour réaliser ce TP localement sur votre ordi, commencez par télécharger le zip

cet exercice est originellement proposé ici:

http://www.xavierdupre.fr/app/ensae_teaching_cs/helpsphinx3/notebooks/td1a_cenonce_session_10.html#exercice-1-creer-un-fichier-excel

imports#

import numpy as np
import pandas as pd
# juste un utilitaire pour regarder le début d'un fichier

def head(filename, nb_lines=5):
    with open(filename) as f:
        for lineno, line in enumerate(f, 1):
            print(f"{lineno:02d}", line, end="")
            if lineno >= nb_lines:
                break

la source#

l’idée est de se mettre en vraie situation; les données qu’on trouve ici ou là sont souvent très sales !

# de prime abord ça a l'air pas trop mal

# NOTE: si vous n'avez pas le module head, ouvrez le fichier dans votre éditeur favori

head("data/television.txt", 10)
01 POIDLOG	POIDSF	cLT1FREQ	cLT2FREQ																												
02 0.8894218317	4766.8652013	2	1																												
03 2.3102092815	12381.589746	30	1																												
04 2.740069772	14685.431344	6	2																												
05 1.7755447679	9516.0499388	1	1																												
06 0.7325124103	3925.9075881	3	1																												
07 1.7583343823	9423.810705	3	1																												
08 1.6407330347	8793.525107	3	1																												
09 0.4139788891	2218.7239959	0																													
10 1.3790330065	7390.9411886	1	1																												
# sauf que si on le charge: ouh là !

df = pd.read_csv("data/television.txt", sep="\t")
df
POIDLOG POIDSF cLT1FREQ cLT2FREQ Unnamed: 4 Unnamed: 5 Unnamed: 6 Unnamed: 7 Unnamed: 8 Unnamed: 9 ... Unnamed: 22 Unnamed: 23 Unnamed: 24 Unnamed: 25 Unnamed: 26 Unnamed: 27 Unnamed: 28 Unnamed: 29 Unnamed: 30 Unnamed: 31
0 0.889422 4766.865201 2 1.0 NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1 2.310209 12381.589746 30 1.0 NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2 2.740070 14685.431344 6 2.0 NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
3 1.775545 9516.049939 1 1.0 NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
4 0.732512 3925.907588 3 1.0 NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
8398 0.306852 1644.574141 6 1.0 NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
8399 2.501181 13405.104689 6 1.0 NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
8400 1.382758 7410.905653 1 1.0 NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
8401 0.343340 1840.132652 3 1.0 NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
8402 1.088486 5833.750895 0 NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN

8403 rows × 32 columns

# et en particulier, ceci n'est pas du tout ce qu'on veut

df.shape
(8403, 32)

survol de ce qu’il faut faire#

le TP comporte plusieurs étapes

  1. enlever les colonnes pleines de vide; pour fixer les idées, nous nettoyons les colonnes qui contiennent seulement des n/a ou des 0

    dans le corrigé on va voir deux méthodes

  • rapide

  • manuelle: comment on ferait si le nettoyage devait être fait sur un critère plus spécifique; on verra comment faire sur la base d’une fonction qui, pour une colonne, indique si elle doit être gardée ou pas

  1. calculer les valeurs uniques de la colonne cLT2FREQ; le texte de l’exercice suggère qu’on doit trouver une poignée de valeurs

  2. à ce stade, combien de lignes ont leur cLT2FREQ non renseignée ?
    combien doit-on avoir de lignes si on nettoie sur cette base ?
    (i.e. si on enlève toutes les lignes qui n’ont pas cette colonne renseignée) faites ce nettoyage et vérifiez votre résultat

  3. sauver le résultat dans un fichier excel

toujours pour fixer les idées, on doit trouver à la fin une dataframe qui a une forme de (7386, 4)

indices#

je vous signale des fonctions utiles dans tout le TP:

# df.dropna?
# df.drop?

# pd.Series.unique?

# df.to_excel?
# !pip install openpyxl

colonnes vides#

la première étape donc, consiste à supprimer les colonnes vides

# on recharge pour être sûr
df = pd.read_csv("data/television.txt", sep="\t")
df.shape
(8403, 32)

la méthode rapide#

le mieux c’est d’utiliser dropna

# pour voir la doc
# df.dropna?
# à vous
...
# ceci doit afficher True
df.shape == (8403, 4)
False
df.head()
POIDLOG POIDSF cLT1FREQ cLT2FREQ Unnamed: 4 Unnamed: 5 Unnamed: 6 Unnamed: 7 Unnamed: 8 Unnamed: 9 ... Unnamed: 22 Unnamed: 23 Unnamed: 24 Unnamed: 25 Unnamed: 26 Unnamed: 27 Unnamed: 28 Unnamed: 29 Unnamed: 30 Unnamed: 31
0 0.889422 4766.865201 2 1.0 NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1 2.310209 12381.589746 30 1.0 NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2 2.740070 14685.431344 6 2.0 NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
3 1.775545 9516.049939 1 1.0 NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
4 0.732512 3925.907588 3 1.0 NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN

5 rows × 32 columns

la méthode pédestre#

dans ce cas précis, dropna est le mieux bien sûr

maintenant, dans certains cas le critère pour ‘oublier’ des colonnes peut être moins simple - imaginez par exemple qu’on veuille supprimer toutes les colonnes qui contiennent un certain pourcentage de valeurs parmi GARBAGE et TRASH et un vrai n/a…

donc voyons comment on peut faire le même nettoyage, mais de manière plus fine

# on recharge pour être sûr
df = pd.read_csv("data/television.txt", sep="\t")

en deux étapes:

d’abord comment feriez-vous, étant donné le nom d’une colonne, pour savoir si elle est pleine de vide ?

# à vous 
def is_empty_column(df, colname):
    ...
# ceci doit afficher True

# on teste
col1 = 'POIDLOG'
not is_empty_column(df, col1)
True
# ceci doit afficher True

col5 = 'Unnamed: 4'
is_empty_column(df, col5)

ensuite il ne reste qu’à calculer la liste des colonnes vides, pour la passer à df.drop()

# à vous

# calculez la liste des colonnes vides
empty_columns = ...

# puis utilisez df.drop
# ceci doit afficher True
df.shape == (8403, 4)
False

Bien sûr on a découpé le problème en deux mais en fait ça peut se récrire en une seule ligne

# en option

# à vous

# récrire tout ceci en une seule passe
# ceci doit afficher True
df.shape == (8403, 4)
False

obtenir les valeurs distinctes#

comment obtenir les valeurs distinctes de la colonne cLT2FREQ

le texte de l’exercice initial nous apprend qu’on ne devrait avoir que 3 valeurs; et une inspection visuelle rapide vous le confirme, plus la présence de pas mal de vide dans cette colonne

la méthode la plus simple consiste à utiliser Series.unique qui renvoie le résultat sous la forme d’un numpy.ndarray

# à vous
uniques = ...
uniques
Ellipsis
# ceci doit afficher True
uniques.sort()
np.all(uniques[:-1] == np.arange(1, 4)) and np.isnan(uniques[-1])
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[22], line 2
      1 # ceci doit afficher True
----> 2 uniques.sort()
      3 np.all(uniques[:-1] == np.arange(1, 4)) and np.isnan(uniques[-1])

AttributeError: 'ellipsis' object has no attribute 'sort'
# point de réflexion : pourquoi ceci ne renvoie-t-il pas True ?
uniques.sort()
np.all(uniques == np.array([1., 2., 3., np.nan]))
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[23], line 2
      1 # point de réflexion : pourquoi ceci ne renvoie-t-il pas True ?
----> 2 uniques.sort()
      3 np.all(uniques == np.array([1., 2., 3., np.nan]))

AttributeError: 'ellipsis' object has no attribute 'sort'

compter les lignes à nettoyer#

on veut maintenant nettoyer les données en enlevant les lignes qui n’ont pas la colonne cLT2FREQ renseignée

dans un premier temps on vous demande de calculer le nombre de lignes concernées

# à vous
nb_lines_to_clean = ...
# ceci doit afficher True

nb_lines_to_clean == 1017
False
# ce qui signifie qu'à la fin on doit avoir ce nombre de lignes
8403-1017
7386
# ou encore, plus proprement
expected_lines = len(df) - nb_lines_to_clean
expected_lines
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[27], line 2
      1 # ou encore, plus proprement
----> 2 expected_lines = len(df) - nb_lines_to_clean
      3 expected_lines

TypeError: unsupported operand type(s) for -: 'int' and 'ellipsis'

nettoyage des lignes#

option 1: df.drop()#

# on recharge à tout hasard
df = pd.read_csv("data/television.txt", sep="\t").dropna(axis='columns', how='all')
print(df.shape)
(8403, 4)

remarquez que df.drop prend un paramètre optionnel inplace qui peut être souvent utile

#df.drop?

option 1: on peut utiliser df.drop(), l’avantage étant qu’on peut faire l’opération en place

# à vous

# df.drop(...)
# ceci doit afficher True

# la forme après nettoyage
df.shape == (7386, 4)
False

option 2: sélection avec un masque et []#

# on recharge à tout hasard
df = pd.read_csv("data/television.txt", sep="\t").dropna(axis='columns', how='all')
print(df.shape)
(8403, 4)

option 2: il y a plein d’autres façons de faire, on peut aussi utiliser tout simplement un masque

# à vous
# df = ...
# ceci doit afficher True

# la forme après nettoyage
df.shape == (7386, 4)
False

sauver un fichier excel#

je vous laisse conclure le TP, il s’agit d’enregistrer nos données nettoyées dans un fichier excel

# à vous

filename = "television.xlsx"

# df.to_excel?

je vous laisse éventuellement vérifier votre code en rechargeant sous excel le fichier produit

../../_images/television1.png