I. Accéder aux colonnes▲
Nous avons vu dans l'article précédent que nous pouvons accéder aux colonnes du DataFrame grâce aux variables loc et iloc, en fournissant soit un index, soit le nom de la colonne.
Mais il existe également un autre moyen : vous pouvez utiliser l’opérateur point (.) sur le DataFrame et appeler directement le nom de la colonne. Cela se traduira par un ensemble d’éléménts de la colonne sélectionnée :
2.
3.
4.
5.
6.
7.
>>> baby_names.BRTH_YR.head()
0 2011
1 2011
2 2011
3 2011
4 2011
Name: BRTH_YR, dtype: int64
Il est parfaitement acceptable de s'en tenir à n'importe quelle approche. Parfois, j'utilise l'accès direct, d'autres fois, je tranche.
II. Typos et autres erreurs de saisie▲
C'est un sujet difficile à résoudre, car les fautes de frappe sont de petites erreurs que vous ne remarquez pas et il est difficile de trouver une solution automatisée pour les corriger. Cependant, vous pouvez parfois voir et identifier des problèmes dans le jeu de données qui sont faciles à résoudre, comme une dénomination différente de la même valeur. Si vous avez consulté le fichier baby_names.csv, vous avez probablement remarqué que l’appartenance ethnique se présente sous différentes formes :
2.
3.
4.
5.
>>> import numpy as np
>>> np.unique(baby_names.iloc[:, 2])
array(['ASIAN AND PACI', 'ASIAN AND PACIFIC ISLANDER', 'BLACK NON HISP',
'BLACK NON HISPANIC', 'HISPANIC', 'WHITE NON HISP',
'WHITE NON HISPANIC'], dtype=object)
Des valeurs plus longues ont été coupées dans certains cas. Pour éviter les ambiguïtés plus tard, nous amènerons chaque entrée dans sa forme la plus longue :
2.
3.
4.
5.
6.
>>> baby_names.loc[baby_names.ETHCTY == 'ASIAN AND PACI', 'ETHCTY'] = 'ASIAN AND PACIFIC ISLANDER'
>>> baby_names.loc[baby_names.ETHCTY == 'BLACK NON HISP', 'ETHCTY'] = 'BLACK NON HISPANIC'
>>> baby_names.loc[baby_names.ETHCTY == 'WHITE NON HISP', 'ETHCTY'] = 'WHITE NON HISPANIC'
>>> np.unique(baby_names.iloc[:, 2])
array(['ASIAN AND PACIFIC ISLANDER', 'BLACK NON HISPANIC', 'HISPANIC',
'WHITE NON HISPANIC'], dtype=object)
Pour ce faire, nous créons un sélecteur basé sur l'appartenance ethnique de chacune des trois valeurs qui se présentent sous deux variantes, nous l' appliquons à l' emplacement du DataFrame, puis nous définissons la nouvelle valeur. Cela remplace vraiment les anciennes valeurs par les nouvelles. À la fin, nous vérifions que les valeurs sont réellement mises à jour et que les valeurs abrégées ont disparu de l'ensemble de données.
Un autre problème peut être que nous avons des noms dont toutes les lettres sont majuscules. Nous pouvons utiliser les connaissances de l'exemple ci-dessus pour améliorer davantage l'ensemble de données :
Vous pouvez voir que nous avons écrasé les valeurs dans les colonnes avec leur représentation en majuscules. Cela améliore l'affichage des données et facilite l’élimination des doublons éventuels.
III. Identification des données en double et effacement du jeu de données▲
Ce cas de figure est vraiment utile. Chaque fois que vous obtenez un nouvel ensemble de données, vous devez faire attention aux doublons et, si vous en trouvez, demander à votre client ce que vous devez faire avec les informations dupliquées. La plupart du temps, la réponse sera de mettre en ordre l'ensemble de données, puis de réexécuter votre analyse.
Les doublons peuvent être identifiés avec la méthode duplicated() de la classe DataFrame. Cette méthode retourne une série de valeurs True et False que vous pouvez utiliser pour afficher les lignes dupliquées :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
>>> baby_names.duplicated().head()
0 False
1 False
2 False
3 False
4 False
dtype: bool
>>> baby_names[baby_names.duplicated()].head()
BRTH_YR GNDR ETHCTY NM CNT RNK
1963 2011 FEMALE WHITE NON HISPANIC Scarlett 31 60
1964 2011 FEMALE WHITE NON HISPANIC Serena 16 75
1965 2011 FEMALE WHITE NON HISPANIC Shaina 16 75
1966 2011 FEMALE WHITE NON HISPANIC Shaindel 15 76
1967 2011 FEMALE WHITE NON HISPANIC Shaindy 48 44
Vous pouvez voir que lorsque nous transmettons la série contenant les doublons identifiés relativement au DataFrame d'origine en tant qu'argument de tri, nous récupérons toutes les lignes (un sous-ensemble du DataFrame) dupliquées. L'extraction ne montre rien d'intéressant, il semble que les valeurs ne soient pas des doublons. Mais si nous trions les résultats, nous verrons qu’il y a un problème :
2.
3.
4.
5.
6.
7.
>>> baby_names[baby_names.duplicated()].sort_values(by='NM').head()
BRTH_YR GNDR ETHCTY NM CNT RNK
2582 2011 FEMALE BLACK NON HISPANIC Aaliyah 69 5
4082 2011 FEMALE BLACK NON HISPANIC Aaliyah 69 5
2766 2011 FEMALE HISPANIC Aaliyah 63 30
4266 2011 FEMALE HISPANIC Aaliyah 63 30
6045 2011 FEMALE BLACK NON HISPANIC Aaliyah 69 5
Nous pouvons voir qu'il y a des lignes dupliquées dans l'ensemble de données. Si nous approfondissons un peu, nous pouvons constater que tous les doublons se trouvent dans le jeu de données de 2011 :
2.
3.
4.
5.
6.
7.
8.
9.
10.
>>> baby_names[baby_names.duplicated()].describe()
BRTH_YR CNT RNK
count 5889.0 5889.000000 5889.000000
mean 2011.0 34.602140 56.494142
std 0.0 40.513511 25.012470
min 2011.0 10.000000 1.000000
25% 2011.0 13.000000 37.000000
50% 2011.0 20.000000 60.000000
75% 2011.0 36.000000 77.000000
max 2011.0 426.000000 97.000000
Il est maintenant temps de ranger notre liste. Heureusement, Pandas a pensé à cela aussi et DataFrame a une méthode appelée drop_duplicates () qui, comme son nom le suggère déjà, supprime les doublons et renvoie le résultat nettoyé. Vous pouvez indiquer certains arguments à la méthode pour ajuster le rangement, par exemple pour ranger les données sur place, mais il est recommandé de conserver le jeu de données d'origine si vous testez vos données, car vous avez parfois besoin de la version d'origine. Il ne sera pas facile de retrouver cette version originelle si vous y avez apporté beaucoup de transformations.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
>>> clean_baby_names = baby_names.drop_duplicates()
>>> clean_baby_names.describe()
BRTH_YR CNT RNK
count 8073.000000 8073.000000 8073.000000
mean 2012.522482 34.480243 57.272761
std 1.118043 39.931078 25.609985
min 2011.000000 10.000000 1.000000
25% 2012.000000 13.000000 38.000000
50% 2013.000000 20.000000 59.000000
75% 2014.000000 36.000000 78.000000
max 2014.000000 426.000000 102.000000
>>> clean_baby_names[clean_baby_names.NM == 'Aaliyah']
BRTH_YR GNDR ETHCTY NM CNT RNK
1110 2011 FEMALE BLACK NON HISPANIC Aaliyah 69 5
1294 2011 FEMALE HISPANIC Aaliyah 63 30
8031 2012 FEMALE BLACK NON HISPANIC Aaliyah 55 10
8202 2012 FEMALE HISPANIC Aaliyah 68 26
10031 2013 FEMALE BLACK NON HISPANIC Aaliyah 73 3
10212 2013 FEMALE HISPANIC Aaliyah 56 33
12072 2014 FEMALE BLACK NON HISPANIC Aaliyah 67 4
12259 2014 FEMALE HISPANIC Aaliyah 55 36
J'ai assigné les données nettoyées à une nouvelle variable. Nous voyons maintenant que les données semblent être propres, car la moyenne de la colonne BRTH_YR se situe entre 2012 et 2013, ce qui correspond au milieu de notre ensemble de données.
La méthode drop_duplicates () définit les lignes en double si toutes leurs colonnes ont la même valeur. Par conséquent, nous pouvons être assurés que nous n'avons perdu aucune information au cours de notre processus.
Nous pouvons aussi sélectionner des colonnes pour identifier les doublons. Par exemple, imaginons qu'un nom soit entré deux fois pour la même année et la même ethnie, mais avec des comptes et des rangs différents. Dans ce cas, ces lignes resteraient dans le jeu de données, mais nous ne le voulons pas, car cela modifie notre analyse. Pour identifier de telles entrées, nous pouvons affiner notre requête en la personnalisant :
Il semble que nous ayons trouvé des données dupliquées. Vérifions qu'il s'agit bien de doublons avant de les supprimer:
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
>>> clean_baby_names[clean_baby_names.duplicated(['NM', 'BRTH_YR','ETHCTY'])].sort_values('NM').head()
BRTH_YR GNDR ETHCTY NM CNT RNK
13344 2014 MALE HISPANIC Alexis 40 68
1132 2011 FEMALE BLACK NON HISPANIC Angel 16 39
9551 2012 MALE WHITE NON HISPANIC Ariel 20 84
9268 2012 MALE HISPANIC Ariel 18 92
1340 2011 FEMALE HISPANIC Ariel 17 71
>>> clean_baby_names[(clean_baby_names.NM == 'Alexis') & (clean_baby_names.ETHCTY == 'HISPANIC') & (clean_baby_names.BRTH_YR == 2014)]
BRTH_YR GNDR ETHCTY NM CNT RNK
12272 2014 FEMALE HISPANIC Alexis 13 70
13344 2014 MALE HISPANIC Alexis 40 68
Après avoir créé le filtrage, nous n’obtenons qu’un résultat pour le nom Alexis. C'est suspect, alors nous avons toutes les entrées pour Alexis, pour l'année 2014 ayant l'ethnie hispanique. Nous voyons maintenant qu’il y a deux résultats : un pour les femmes et un pour les hommes. Donc, ce ne sont pas de vrais doublons. Nous pourrions poursuivre la recherche des différents noms de notre liste pour vérifier qu'ils sont bien divisés entre MALE et FEMALE, mais nous pouvons ajouter la colonne de genre à notre filtre de doublon :
2.
3.
>>> clean_baby_names.duplicated(['NM', 'BRTH_YR','ETHCTY','GNDR']).sum()
0
_YR','ETHCTY','GNDR']).sum() 0
OK, plus de doublon. Naturellement, si vous avez une bonne connaissance du domaine, vous pouvez identifier des noms qui ne peuvent être que masculins ou féminins et les filtrer ou les agréger.
IV. Traitement NaN et N/A▲
Parfois, vous devez gérer des jeux de données dans lesquels certains champs indispensables pour vos calculs ne sont pas renseignés, par exemple avec le Machine Learning, mais nous n'avons pas à aller aussi loin, car les fonctions d'agrégation ont également besoin de valeurs. Pour résoudre ce problème, Pandas dispose d’une méthode : fillna.
Par exemple, modifions certaines des valeurs de notre DataFrame baby_names pour générer des valeurs manquantes :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
>>> import numpy as np
>>> na_baby_names = clean_baby_names.copy()
>>> na_baby_names.loc[na_baby_names.NM == 'Riley', 'CNT'] = np.nan
>>> na_baby_names.loc[na_baby_names.NM == 'Avery', 'RNK'] = ''
>>> na_baby_names.describe()
BRTH_YR CNT
count 8073.000000 8049.000000
mean 2012.522482 34.527519
std 1.118043 39.978346
min 2011.000000 10.000000
25% 2012.000000 13.000000
50% 2013.000000 20.000000
75% 2014.000000 36.000000
max 2014.000000 426.000000
>>> na_baby_names.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 8073 entries, 0 to 13961
Data columns (total 6 columns):
BRTH_YR 8073 non-null int64
GNDR 8073 non-null object
ETHCTY 8073 non-null object
NM 8073 non-null object
CNT 8049 non-null float64
RNK 8073 non-null object
dtypes: float64(1), int64(1), object(4)
memory usage: 441.5+ KB
J'ai créé une copie dans le jeu de données nettoyé pour conserver les données d'origine intactes. Maintenant, si nous examinons les résultats de la méthode describe(), nous constatons que la colonne RNK est manquante, car elle contient des chaînes de caractères. Elle ne peut donc pas être utilisée pour une analyse statistique, et le nombre de la colonne CNT est inférieur au nombre indiqué dans la colonne BRTH_YR. parce qu'il y a des valeurs nan. En appelant la méthode info() , nous obtenons une description plus détaillée.
2.
3.
4.
5.
6.
7.
>>> na_baby_names[na_baby_names.CNT.isnull()].head()
BRTH_YR GNDR ETHCTY NM CNT RNK
156 2011 FEMALE HISPANIC Riley NaN 76
473 2011 FEMALE WHITE NON HISPANIC Riley NaN 63
1250 2011 FEMALE BLACK NON HISPANIC Riley NaN 40
1608 2011 MALE HISPANIC Riley NaN 86
1889 2011 MALE WHITE NON HISPANIC Riley NaN 93
Pour résoudre cette question, nous prendrons la valeur moyenne des noms de chaque année pour renseigner les valeurs manquantes. Je sais que ce n'est pas la meilleure approche, mais c'est mieux que de deviner une valeur ou de laisser les champs en nan :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
>>> for y in np.unique(na_baby_names.loc[(na_baby_names.CNT.isnull()), 'BRTH_YR']):
... na_baby_names.loc[(na_baby_names.CNT.isnull()) & (na_baby_names.BRTH_YR == y), 'CNT'] = na_baby_names.loc[(na_baby_names.BRTH_YR == y), 'CNT'].mean().astype(int)
...
>>> na_baby_names.describe()
BRTH_YR CNT
count 8073.000000 8073.000000
mean 2012.522482 34.525579
std 1.118043 39.918905
min 2011.000000 10.000000
25% 2012.000000 13.000000
50% 2013.000000 20.000000
75% 2014.000000 36.000000
max 2014.000000 426.000000
La solution itère sur les années uniques et définit la valeur manquante sur le résultat entier (en coupant la partie décimale) de la moyenne des autres résultats de la même année.
Il est maintenant temps de gérer les valeurs vides dans la colonne RNK. Il existe à nouveau différentes approches en fonction de votre jeu de données. Nous savons ici que la colonne ne doit contenir que des valeurs numériques (ou un nombre entier pour être plus spécifique).
Pour ce faire, nous pouvons remplacer les champs vides par np.nan :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
>>> na_baby_names.loc[(na_baby_names.RNK == ''), 'RNK'] = np.nan
>>> na_baby_names.loc[(na_baby_names.RNK == ''), 'RNK']
Series([], Name: RNK, dtype: object)
>>> na_baby_names.loc[(na_baby_names.RNK.isnull()), 'RNK']
249 NaN
723 NaN
936 NaN
7888 NaN
8255 NaN
8552 NaN
9079 NaN
9273 NaN
9559 NaN
9890 NaN
10068 NaN
10273 NaN
10567 NaN
10915 NaN
11085 NaN
11285 NaN
11579 NaN
11926 NaN
12107 NaN
12323 NaN
12611 NaN
13170 NaN
13359 NaN
Name: RNK, dtype: object
Cette approche est très basique et ne fonctionne qu'avec une chaîne vide. Si nous avons également différentes chaînes dans la colonne, nous devons rechercher ces valeurs et les définir sur nan individuellement - et ceci est impossible. Heureusement, nous avons une solution pour cela dans Pandas :
2.
3.
4.
5.
6.
7.
8.
9.
10.
>>> import pandas as pd
>>> na_baby_names.loc[na_baby_names.NM == 'Avery', 'RNK'] = ''
>>> na_baby_names.RNK = pd.to_numeric(na_baby_names.RNK)
>>> na_baby_names.loc[(na_baby_names.NM == 'Avery'), 'RNK'].head()
249 NaN
723 NaN
936 NaN
7888 NaN
8255 NaN
Name: RNK, dtype: float64
La fonction to_numeric convertit les valeurs d'une colonne spécifiée (Series) ou d'une table (DataFrame) en valeurs numériques et ne déclenche pas d'exception si une valeur non numérique est rencontrée, comme une chaîne de caractères dans le cas de l'exemple.
V. Informations sur les jeux de données▲
Nous savons comment accéder à des informations telles que la forme ou le nombre d'éléments d'un DataFrame. Mais Pandas offre plus d'informations en utilisant la méthode info(). Celle-ci nous donne également des données sur l’utilisation de la mémoire, ce qui nous aide à identifier de grands ensembles de données et à décider si on filtre sur place ou s’il faut effectuer la purge des données inutiles.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
>>> clean_baby_names.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 8073 entries, 0 to 13961
Data columns (total 6 columns):
BRTH_YR 8073 non-null int64
GNDR 8073 non-null object
ETHCTY 8073 non-null object
NM 8073 non-null object
CNT 8073 non-null int64
RNK 8073 non-null int64
dtypes: int64(3), object(3)
memory usage: 441.5+ KB
Le symbole + indique que l'utilisation réelle de la mémoire peut être supérieure, car Pandas ne compte pas la mémoire utilisée par les valeurs des colonnes avec dtype = object. Mais si nous sommes assez enthousiastes, nous pouvons dire à Pandas de nous donner le véritable usage de la mémoire. Pour cela, nous devons fournir l’argument memory_usage avec la valeur 'deep' à la méthode info() :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
>>> clean_baby_names.info(memory_usage='deep')
<class 'pandas.core.frame.DataFrame'>
Int64Index: 8073 entries, 0 to 13961
Data columns (total 6 columns):
BRTH_YR 8073 non-null int64
GNDR 8073 non-null object
ETHCTY 8073 non-null object
NM 8073 non-null object
CNT 8073 non-null int64
RNK 8073 non-null int64
dtypes: int64(3), object(3)
memory usage: 1.9 MB
Nous voyons maintenant que notre ensemble de données consomme près de 2 Mo. Ce n'est pas si grave, mais rappelez-vous, nous travaillons avec un exemple de jeu de données. Si nous adoptons des projets de la vie réelle, nous pouvons obtenir des données beaucoup plus volumineuses et dans ce cas, vous devez tenir compte de l'utilisation de la mémoire dans vos applications.
VI. Conclusion▲
Nous avons vu que les véritables ensembles de données sont imparfaits par défaut. Nous devons prendre soin de valider l'ensemble de données avant de nous y plonger et de procéder à une analyse. Pour cela, nous avons vu comment identifier les doublons et nettoyer les données manquantes.
Dans le prochain article, nous poursuivrons notre cheminement et apprendrons comment agréger nos données. Nous utiliserons ces connaissances pour identifier et éliminer les données aberrantes dans l'ensemble de données, ce qui pourrait fausser notre analyse.
Assurez-vous d'utiliser la recherche de DiscoverSDK pour trouver tous les meilleurs outils de développement en un seul endroit.
VII. Remerciements▲
Nous remercions Gabor Laszlo Hajba de nous avoir autorisés à publier son tutoriel.
Nous tenons également à remercier vavavoum74 pour la traduction de ce tutoriel, Christophe pour la validation technique et escartefigue pour la relecture orthographique



