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