
EDA en public (Partie 1) : Nettoyage et exploration des données de vente avec Pandas
! Bienvenue au début d’un voyage majeur en matière de données que j’appelle « EDA in Public ». Pour ceux qui me connaissent, je pense que la meilleure façon d’apprendre quelque chose est de s’attaquer à un problème du monde réel et de partager l’ensemble du processus compliqué, y compris les erreurs, les victoires et tout le reste. Si vous cherchez à améliorer vos compétences en Pandas et en analyse de données, cette série est faite pour vous.
Nous allons agir en tant qu’analystes de données pour une entreprise de commerce électronique fictive de taille moyenne que j’appellerai NovaShop. Ils nous ont remis un CSV de ventes brut et désordonné et ont posé une question simple : « Comment allons-nous ?
Le but de Partie 1 est fondamental : nous allons nettoyer cet ensemble de données de commerce électronique complexe, explorer sa structure de base et maîtriser les compétences EDA de base dans Pandas que chaque scientifique des données utilise quotidiennement. Cette série est structurée pour vous emmener d’un débutant (Partie 1) à un avancé analyste de données (partie 3), alors n’hésitez pas à intervenir où que vous soyez.
Avant de nous lancer dans le code, définissons notre motivation principale. Pour NovaShop, nous devons répondre à quelques questions simples mais puissantes : quels produits génèrent le plus de revenus ? Quels pays génèrent le plus de ventes ? Découvrons.
Présentation de l’ensemble de données : décompression des données de vente
Pour commencer notre analyse pour NovaShop, nous utiliserons le Ensemble de données de vente au détail en ligne UCI. Il s’agit d’un ensemble de données formidable, très réaliste et non rationalisé, qui capture toutes les transactions d’une entreprise de vente au détail en ligne hors magasin basée au Royaume-Uni entre fin 2010 et fin 2011.
Cet ensemble de données est sous licence Creative Commons Attribution 4.0 Internationale (CC BY 4.0).
Cela permet le partage et l’adaptation des ensembles de données à n’importe quelle fin, à condition que le crédit approprié soit accordé.
L’ensemble de données contient plus d’un demi-million de lignes et regorge des types d’anomalies que vous rencontrez dans le monde réel : valeurs manquantes, nombres négatifs et formatage de texte incohérent. C’est exactement ce que nous voulons !
Voici les huit colonnes clés avec lesquelles nous travaillerons et ce qu’elles nous disent d’un point de vue commercial :
- Numéro de facture : C’est le Numéro de facture. Un numéro unique à 6 chiffres attribué à chaque transaction. Si le code commence par « C », cela indique un annulation (un retour).
- Code Stock/Code Produit: Un code unique à 5 chiffres attribué à chaque produit distinct.
- Description: Le nom de l’élément. Cela nécessite souvent un nettoyage (espaces supplémentaires, cas incohérents).
- Quantité: Nombre d’articles achetés. Combien d’unités étaient impliquées par transaction ? Peut être négatif pour les retours.
- Date de facture : La date et l’heure de la transaction. Ceci est essentiel pour l’analyse ultérieure des séries chronologiques.
- Prix unitaire: Prix du produit par unité en livres sterling (GBP). Le prix d’un article. Il peut parfois être 0 ou négatif en raison d’erreurs/d’éléments gratuits.
- Numéro client : Un numéro unique à 5 chiffres attribué à chaque client enregistré. Surtout, cela manque souvent, ce qui signifie que nous avons de nombreuses transactions de la part des clients.
- Pays: Nom du pays où réside le client. Ce serait idéal pour segmenter les ventes internationales.
Jetons un coup d’œil rapide aux premières lignes pour voir à quoi nous avons affaire. C’est la sortie de df.head():
Importons l’ensemble de données dans Pandas et voyons combien de lignes nous traitons.
import pandas as pd
import numpy as np
df = pd.read_csv(‘Online Retail.csv’)
df.shape
Sortir:
(541909, 8)
Cela fait beaucoup de lignes. Je devrai peut-être découper un peu les lignes.
Chargement et découpage des données : gérer le volume
Pour la première partie, nous allons prendre un Échantillon aléatoire de 10 % de l’ensemble de données complet. Cela nous donne une taille beaucoup plus gérable – environ 54 000 lignes – tout en conservant les caractéristiques des données d’origine. Il s’agit d’une technique courante et pratique lorsqu’il s’agit d’environnements Big Data ou lorsque l’on se concentre sur le prototypage.
# Data Loading and Slicing
FILE_PATH = ‘Online Retail.csv’
SAMPLE_FRACTION = 0.1 # We will sample 10% of the data
full_df = pd.read_csv(FILE_PATH, encoding=’unicode_escape’)
# Take a random 10% sample for faster processing in Part 1
df = full_df.sample(frac=SAMPLE_FRACTION, random_state=42).reset_index(drop=True)
Maintenant, vérifions à nouveau la forme en utilisant le df.shape
Sortir:
(54191, 8)
Parfait! Nous pouvons maintenant commencer.
Inspection initiale des données : se salir les mains
Ma première étape sera de découvrir ce que nous nettoyons. Je dois répondre à ces questions
- Que contient l’ensemble de données ?
- Quels sont les types de données et les décomptes non nuls ?
- A quoi ressemblent les chiffres ?
Vérification visuelle : df.head() et df.tail()
En regardant les première et dernière lignes, j’ai pu confirmer quelques choses :
- Annulations et retours : Il m’est arrivé d’en remarquer
InvoiceNovaleurs commençant par ‘C’et le correspondantQuantityest négatif. Cela confirme que les retours sont inclus et, pour l’analyse des revenus, je devrai les séparer ou les exclure. - Manquant : Je pouvais voir visuellement
NaNvaleurs dans leCustomerIDcolonne, confirmant la masse des transactions des clients. - Texte incohérent : J’ai vérifié la colonne Description pour les espaces. D’après le petit échantillon que j’ai reçu, je ne pouvais pas vraiment le dire. Mais cela ne fait pas de mal d’y répondre lorsque je me lance dans le nettoyage des données. Je pourrais également garder la capitalisation cohérente. C’est toujours une bonne pratique de supprimer tous les espaces de début et de fin de toutes les colonnes de chaînes pour éviter des erreurs subtiles lors du regroupement.
- J’ai également remarqué des incohérences de majuscules dans la colonne Pays. J’ai repéré un pays nommé EIRE. Cela signifie probablement que l’Irlande devra peut-être changer cela.
Quels sont les types de données et les nombres non nuls ? (.info())
La prochaine étape essentielle consiste à vérifier la structure à l’aide du .info() méthode. Cela m’indique le type de données de chaque colonne et, plus important encore, le nombre de valeurs non nulles (non manquantes) dont nous disposons.
Principales conclusions
- Valeurs manquantes : Après avoir pris notre échantillon de 10 %, j’ai pu constater un énorme écart dans
CustomerID. Environ 25 % des identifiants clients sont manquants. J’ai également remarqué quelques centaines de valeurs manquantes dansDescriptionque je devrai aborder. - Types de données : Le
InvoiceDatela colonne est toujours répertoriée commeobject(chaîne). Je dois convertir ceci en un véritable Pandasdatetimeobjet.CustomerIDest également actuellement un flotterprobablement parce qu’il contient ceuxNaNvaleurs! C’est un petit détail dont je devrai me souvenir si jamais je veux l’utiliser comme véritable identifiant entier.
À quoi ressemblent les chiffres ? (.describe())
Ensuite, j’ai utilisé .describe() pour obtenir un résumé statistique rapide de toutes les colonnes numériques (Quantity et UnitPrice).
- Quantité (
Quantity): La quantité minimale est -2472. Cela confirme que les rendements existent, mais l’ampleur de ces points minimum suggère une transaction extrêmement aberrante. Pour une analyse de base des ventes, je devrai peut-être filtrer ces chiffres négatifs et potentiellement les valeurs aberrantes extrêmement positives et négatives. - Prix unitaire (
UnitPrice): Le prix minimum est 0. Cela signifie que certains produits ont été distribués gratuitement ou sont des entrées réservées. Puisqu’un produit doit avoir un prix positif lors d’une vente normale, filtrer toutes les lignes oùUnitPriceest zéro ou moins est toujours la meilleure pratique pour calculer les revenus avec précision.
Sur la base de ces inspections rapides effectuées. Ces données sont loin d’être propres. Nous avons des manquants massifs, des types de données incorrects, des valeurs négatives/zéro très problématiques dans nos colonnes numériques principales et des incohérences de texte à résoudre.
Gestion des valeurs manquantes
Maintenant que nous savons où nos données manquent, nous pouvons commencer à discuter des stratégies de gestion des valeurs nulles. Je suis surtout préoccupé par Description et CustomerID.
Descriptions manquantes
Le Description nous indique quel produit a été vendu, donc le perdre rend la transaction dénuée de sens. Dans notre échantillon, moins de 1 % des lignes comportent des descriptions manquantes. Il est logique de supprimer complètement ces lignes, car elles sont inutiles pour l’analyse au niveau du produit.
# Drop rows where the Description is missing
df.dropna(subset=['Description'], inplace=True)
# checking for null counts
df['Description'].isnull().sum()
Sortir:
np.int64(0)
Très bien, parfait ! Toutes les valeurs nulles ont disparu.
Numéros client manquants
Les disparus CustomerIDLes s (environ 25 % de notre échantillon) représentent une affaire beaucoup plus importante. Si je les supprime tous, je perdrai près d’un quart de nos données de ventes, ce qui donnera à NovaShop une vision dangereusement faussée de son chiffre d’affaires total.
Pour une simple analyse des revenus et des produits (objectif de la première partie), je n’ai pas vraiment besoin du CustomerID. Nous pouvons procéder à l’analyse sur toutes les lignes même sans une pièce d’identité. Nous n’avons besoin de l’ID que si nous prévoyons de segmenter la clientèle (comme l’analyse RFM), que je conserverai pour la partie 2 !
Conversion des types de données et suppression des doublons
Maintenant que nous avons traité les descriptions manquantes, les deux prochains problèmes immédiats concernent les lignes entièrement dupliquées et la correction de nos éléments essentiels. InvoiceDate colonne.
Correction du type de date de facture
Rappelez-vous comment .info() a montré InvoiceDate sous forme de chaîne (object) ? Nous devons résoudre ce problème immédiatement afin que Pandas sache comment trier et regrouper nos données par ordre chronologique.
# Convert InvoiceDate from object (string) to datetime
df[‘InvoiceDate’] = pd.to_datetime(df[‘InvoiceDate’])
Supprimer les doublons
Si deux lignes sont identiques tous colonnes, ce sont des doublons et gonfleront artificiellement nos chiffres de ventes. Vérifions-les et supprimons-les.
# Remove Duplicates
num_duplicates = df.duplicated().sum()
print(f”Found {num_duplicates} fully duplicated rows.”)
Sortir:
Found 62 fully duplicated rows
Lâchons-les
# Remove duplicates, keeping the first instance
df.drop_duplicates(inplace=True)
print(f”Dataframe size after removing duplicates: {len(df)} rows.”)
Sortir:
Dataframe size after removing duplicates: 53980 rows.
Filtrage des retours et des erreurs
Notre .describe() La méthode nous a montré de sérieux signaux d’alarme : quantités négatives (retours) et prix unitaires nuls/négatifs (erreurs/articles gratuits). Pour la partie 1, nous voulons calculer revenu net des ventesnous devons donc filtrer ce bruit.
Gestion des quantités et des prix négatifs
Nous conserverons uniquement les transactions où :
Quantityest strictement supérieur à zéro (filtrage de tous les retours et annulations).UnitPriceest strictement supérieur à zéro (filtrage des erreurs et des gratuités).
# Filter: Keep only transactions where Quantity is positive (i.e., sales, not returns)
df = df[df[‘Quantity’] > 0]
# Filter: Keep only transactions where UnitPrice is positive (i.e., not free or an error)
df = df[df[‘UnitPrice’] > 0]
print(f”Dataframe size after filtering: {len(df)} rows.”)
Sortir
Dataframe size after filtering: 52933 rows.
Nettoyage des colonnes de texte
Enfin, abordons les problèmes de standardisation du texte que nous avons repérés visuellement, comme le EIRE code de pays et tout espace caché potentiel dans Description.
- Supprimer les espaces : Nous utilisons
.str.strip()pour supprimer les espaces de début et de fin des deuxDescriptionetCountry. - Normaliser le pays : Nous mappons manuellement les incohérences telles que « EIRE » à « Irlande ».
# Cleaning Text Columns
# Clean Description and Country columns
df[‘Description’] = df[‘Description’].str.strip()
df[‘Country’] = df[‘Country’].str.strip()
# Handle specific country name inconsistencies
# EIRE is a common inconsistency in this dataset for Ireland
df[‘Country’].replace(‘EIRE’, ‘Ireland’, inplace=True)
print(“Text columns cleaned and standardized.”)
Ingénierie des fonctionnalités et premier aperçu
Les données sont désormais propres. Nous pouvons enfin commencer à poser les questions amusantes. La mesure la plus importante pour tout ensemble de données de ventes est le chiffre d’affaires. Puisque nos données originales n’ont que Quantity et UnitPricenous devons concevoir le Revenu chronique nous-mêmes.
Ingénierie des fonctionnalités : création du Revenue Colonne
Le revenu d’une transaction correspond simplement au nombre d’articles vendus multiplié par le prix de chaque article.
df[‘Revenue’] = df[‘Quantity’] * df[‘UnitPrice’]
Premier aperçu : quels pays génèrent des revenus ?
Utilisons nos données propres pour répondre à la question de NovaShop : « Quels sont les pays qui stimulent nos ventes ? »
Nous utiliserons une puissante combinaison en trois étapes :
- Groupe par le
Countrycolonne. - Agrégat (somme) le
Revenueau sein de chaque groupe. - Trier les résultats du revenu le plus élevé au plus faible.
# Group by Country, sum the Revenue, and sort for the top 10
top_countries = df.groupby(‘Country’)[‘Revenue’].sum().sort_values(ascending=False).head(10)
print(“\n — — Top 10 Countries by Revenue (GBP) — -”)
print(top_countries)
Sortir:
--- Top 10 Countries by Revenue (GBP) ---
Country
United Kingdom 941268.661
Netherlands 27435.830
EIRE 26066.000
France 23645.330
Germany 22389.510
Australia 12429.990
Spain 5600.900
Switzerland 5483.890
Hong Kong 3597.850
Belgium 3593.510
Name: Revenue, dtype: float64
Le résultat montre généralement une domination massive du Royaume-Uni. Ceci est attendu puisque la société est basée au Royaume-Uni. Les prochains pays nous donnent une feuille de route rapide sur l’orientation internationale de NovaShop.
Conclusion
Nous l’avons fait. Nous avons pris un ensemble de données brutes d’un demi-million de lignes, l’avons découpé pour en faciliter la gestion, avons combattu les valeurs manquantes, corrigé les types de données, filtré les erreurs et calculé notre première mesure commerciale clé. Le dur travail de base est terminé.
Voici un bref récapitulatif de ce que nous avons conquis dans la première partie :
- Vérification des données : Nous avons utilisé
.head(),.info()et .describe()pour identifier les problèmes cruciaux tels que les prix/quantités négatifs, les identifiants client manquants et le format date/heure incorrect. - Nettoyage des données : Nous avons systématiquement supprimé les valeurs nulles et les doublons, converti
InvoiceDateà un bondatetimeobjet et filtré les transactions non commerciales (retours et articles gratuits). - Ingénierie des fonctionnalités : Nous avons créé la critique
Revenuecolonne. - Premier aperçu : Nous avons généré les 10 principaux pays par chiffre d’affaires pour NovaShop, leur donnant ainsi leur premier point de données exploitable à partir de l’ensemble propre.
L’ensemble de données épuré est désormais prêt pour une analyse plus sophistiquée. Dans la deuxième partie, nous allons approfondir Analyse de produits et décomposition de séries chronologiques. Nous déterminerons quels articles sont les véritables générateurs d’argent et analyserons l’évolution du volume des ventes d’heure en heure et de mois en mois.
J’ai hâte de continuer ! Si vous avez apprécié cet article. N’hésitez pas à me le faire savoir sur l’une de ces chaînes. Vos commentaires compteront beaucoup pour moi.



