Cas pratique · SQL · Nettoyage de données

Cas pratique : Nettoyage d'un fichier CSV avec SQL

Dataset brut de 63 000 ventes → doublons, valeurs manquantes, dates incohérentes, paiements non standardisés. Découvrez étape par étape comment SQL permet de nettoyer et préparer vos données pour l'analyse.

Niveau : intermédiaire | Temps de lecture : 15 min | Outil : SQLite + DB Browser | Dataset : 63 000 lignes

1. Introduction

Les données brutes sont rarement exploitables directement. Avant toute analyse ou modélisation, une étape de nettoyage est indispensable : suppression des doublons, traitement des valeurs manquantes, standardisation des formats, correction des incohérences.

Dans ce cas pratique, nous allons nettoyer un dataset de ventes de 63 000 lignes généré aléatoirement avec Faker. Les problèmes sont variés et réalistes : doublons, valeurs manquantes, dates mal formatées, paiements non standardisés, quantités aberrantes, prix à zéro, et incohérences de calcul.

Objectif pédagogique

À l'issue de ce cas pratique, vous saurez :
- Importer un CSV dans SQLite
- Inspecter la qualité des données avec SQL
- Nettoyer les problèmes courants (doublons, NULL, formats)
- Exporter un dataset propre

Comptage initial des lignes du dataset

Figure 1 : Le dataset brut contient 63 000 lignes.

2. Présentation du dataset

Le dataset raw_sales_data_56k.csv contient des ventes simulées avec des erreurs volontaires pour illustrer les problèmes récurrents de qualité des données.

 Métadonnées description.txt
 Licence CC BY-NC 4.0
 Structure 63 000 lignes × 11 colonnes

Colonnes du dataset :

  • Transaction_ID : identifiant unique de la transaction
  • Sale_Date : date de la vente (formats variés)
  • Customer_ID : identifiant client
  • Customer_Email : email client (avec valeurs manquantes)
  • Product_Category : catégorie produit (casse variable, espaces)
  • Product_Name : nom du produit
  • Quantity : quantité achetée (TEXT, valeurs aberrantes)
  • Unit_Price : prix unitaire (NULL, zéro)
  • Total_Amount : montant total (incohérent avec Quantity × Unit_Price)
  • Store_Location : localisation du magasin (casse variable)
  • Payment_Method : moyen de paiement (non standardisé)

Aperçu des 10 premières lignes du dataset

Figure 2 : Aperçu du dataset brut – on observe déjà des NULL et des formats incohérents.

Structure des colonnes après import

Figure 3 : Structure des colonnes – on note que Quantity est en TEXT au lieu de INTEGER.

3. Environnement technique

Pour ce cas pratique, nous utilisons :

  • SQLite : base de données légère, sans installation serveur
  • DB Browser for SQLite : interface graphique pour exécuter les requêtes
  • Fichier unique : `sales_cleaning.db` – portable et reproductible
Pourquoi SQLite ?

SQLite est parfait pour ce type d'exercice : zéro configuration, un seul fichier, et le langage SQL standard (sauf quelques fonctions spécifiques). Les requêtes présentées sont adaptables à PostgreSQL, MySQL ou SQL Server.

Import du CSV dans DB Browser

Figure 4 : Import du fichier CSV dans DB Browser.

 Erreur fréquente à l'import

Lors de l'import, la ligne d'en-tête a été importée comme une ligne de données. Nous avons dû la supprimer manuellement. Voici la requête utilisée :

DELETE FROM raw_sales WHERE Transaction_ID = 'Transaction_ID';

Suppression de la ligne d'en-tête importée par erreur

4. Inspection initiale des données

4.1 Comptage des lignes

Nombre total de transactions
SELECT COUNT(*) FROM raw_sales;

Résultat : 63 000 lignes

4.2 Structure des colonnes

Types de données
PRAGMA table_info(raw_sales);

Observation : La colonne `Quantity` est au format TEXT au lieu de INTEGER – à corriger.

4.3 Identification des problèmes

Comptage des NULL sur Customer_Email

Valeurs manquantes :

  • Customer_Email : 2 845 NULL (~4,5%)
  • Product_Category : 908 NULL (~1,4%)
  • Quantity : 1 711 NULL (~2,7%)
  • Unit_Price : 2 915 NULL (~4,6%)

Formats de date incohérents

Dates incohérentes : mélange de YYYY-MM-DD, YYYY/MM/DD, MM-DD-YYYY, et même du texte ("Jan 2024 (approx)").

Méthodes de paiement non standardisées

Payment_Method : 9 valeurs distinctes ("Credit Card", "cc", "PayPal", "ppal", etc.) – à standardiser.

Casse variable sur Product_Category et Store_Location

Casse et espaces : "HOME GOODS", "Home Goods", "home goods" – à uniformiser.

Quantités aberrantes

Quantités aberrantes : valeurs négatives (-1) et absurdes (9999).

Prix unitaire à zéro

Prix à zéro : 20 lignes avec Unit_Price = 0 (à corriger).

Incohérence Total_Amount

Incohérence Total_Amount : 11 059 lignes où Total_Amount ≠ Quantity × Unit_Price.

5. Nettoyage des données

5.1 Création d'une table de travail

Table de travail
CREATE TABLE clean_sales AS SELECT * FROM raw_sales;

5.2 Correction des types de données

Quantity en INTEGER
CREATE TABLE clean_sales_fixed AS
SELECT 
    Transaction_ID,
    Sale_Date,
    Customer_ID,
    Customer_Email,
    Product_Category,
    Product_Name,
    CAST(Quantity AS INTEGER) AS Quantity,
    Unit_Price,
    Total_Amount,
    Store_Location,
    Payment_Method
FROM clean_sales;

DROP TABLE clean_sales;
ALTER TABLE clean_sales_fixed RENAME TO clean_sales;

5.3 Suppression des doublons

Suppression des doublons exacts
DELETE FROM clean_sales
WHERE rowid NOT IN (
    SELECT MIN(rowid) 
    FROM clean_sales 
    GROUP BY Transaction_ID, Sale_Date, Customer_ID, Customer_Email, 
             Product_Category, Product_Name, Quantity, Unit_Price, 
             Total_Amount, Store_Location, Payment_Method
);

Résultat : 167 doublons supprimés.

Suppression des doublons

5.4 Standardisation des dates

Création d'une colonne date propre
ALTER TABLE clean_sales ADD COLUMN Sale_Date_Clean TEXT;

UPDATE clean_sales 
SET Sale_Date_Clean = SUBSTR(Sale_Date, 1, 10)
WHERE Sale_Date GLOB '*-*-*' AND LENGTH(Sale_Date) = 10;

UPDATE clean_sales 
SET Sale_Date_Clean = Sale_Date
WHERE Sale_Date_Clean IS NULL;

Standardisation des dates

5.5 Nettoyage du texte (UPPER + TRIM)

Product_Category et Store_Location
UPDATE clean_sales SET Product_Category = UPPER(TRIM(Product_Category));
UPDATE clean_sales SET Store_Location = UPPER(TRIM(Store_Location));

Nettoyage Product_Category

Nettoyage Store_Location

5.6 Standardisation des méthodes de paiement

Payment_Method uniformisée
UPDATE clean_sales 
SET Payment_Method = CASE 
    WHEN Payment_Method IN ('Credit Card', 'cc') THEN 'Credit Card'
    WHEN Payment_Method IN ('PayPal', 'ppal') THEN 'PayPal'
    WHEN Payment_Method IN ('Debit Card', 'debit') THEN 'Debit Card'
    WHEN Payment_Method IS NULL THEN 'Unknown'
    ELSE Payment_Method
END;

Standardisation Payment_Method

5.7 Correction des quantités aberrantes

Mise à NULL des valeurs aberrantes
UPDATE clean_sales SET Quantity = NULL WHERE Quantity < 0 OR Quantity > 1000;

Correction des quantités aberrantes

5.8 Correction des prix à zéro

Mise à NULL des Unit_Price = 0
UPDATE clean_sales SET Unit_Price = NULL WHERE Unit_Price = 0;

Résultat : 20 lignes corrigées.

Correction des prix à zéro

5.9 Imputation des valeurs manquantes

Imputation de Quantity (médiane)

Imputation par la médiane
UPDATE clean_sales 
SET Quantity = (
    SELECT AVG(Quantity) 
    FROM (
        SELECT Quantity 
        FROM clean_sales 
        WHERE Quantity IS NOT NULL 
        ORDER BY Quantity 
        LIMIT 2 - (SELECT COUNT(*) FROM clean_sales WHERE Quantity IS NOT NULL) % 2 
        OFFSET (SELECT (COUNT(*) - 1) / 2 FROM clean_sales WHERE Quantity IS NOT NULL)
    )
) 
WHERE Quantity IS NULL;

Imputation des quantités manquantes

Imputation de Unit_Price (moyenne)

Imputation par la moyenne
UPDATE clean_sales 
SET Unit_Price = (SELECT AVG(Unit_Price) FROM clean_sales WHERE Unit_Price IS NOT NULL)
WHERE Unit_Price IS NULL;

Résultat : 2 935 lignes imputées.

Imputation des prix unitaires manquants

5.10 Recalcul de Total_Amount

Recalculer là où c'est incohérent
UPDATE clean_sales 
SET Total_Amount = Quantity * Unit_Price 
WHERE Total_Amount != Quantity * Unit_Price;

Résultat : 15 445 lignes recalculées.

Recalcul de Total_Amount

6. Vérification finale

62 833
lignes après nettoyage
-167 doublons
0
valeurs manquantes sur colonnes clés
0
incohérences Total_Amount
Vérification des NULL
SELECT 
    COUNT(CASE WHEN Customer_Email IS NULL THEN 1 END) AS missing_email,
    COUNT(CASE WHEN Quantity IS NULL THEN 1 END) AS missing_quantity,
    COUNT(CASE WHEN Unit_Price IS NULL THEN 1 END) AS missing_price
FROM clean_sales;
Résultat de la vérification :
  • ✅ Plus aucun doublon
  • ✅ Plus de valeurs manquantes sur les colonnes critiques
  • ✅ Tous les Total_Amount sont cohérents
  • ✅ Les catégories et localisations sont uniformisées (UPPER CASE)
  • ✅ Les méthodes de paiement sont standardisées

7. Export du dataset propre

Export en CSV
-- Dans DB Browser : Fichier → Exporter → Table(s) CSV
-- Ou via SQLite CLI :
.headers on
.mode csv
.output clean_sales_data.csv
SELECT * FROM clean_sales;
 Télécharger le dataset nettoyé clean_sales_data.csv (7.64 Mo)

8. Bonnes pratiques à retenir

1. Toujours travailler sur une copie

Créez une table de travail avant toute modification destructive.

2. Vérifier les types de données à l'import

Assurez-vous que les colonnes numériques sont bien importées en INTEGER/REAL.

3. Documenter les transformations

Gardez une trace des requêtes exécutées pour reproduire le nettoyage.

4. Utiliser des transactions pour les opérations critiques

BEGIN; ... COMMIT; permet d'annuler en cas d'erreur.

9. FAQ — Nettoyage de données avec SQL

Pourquoi utiliser SQL plutôt qu'Excel pour nettoyer des données ?

SQL est plus rapide sur les gros volumes (63 000 lignes, Excel commence à ramer), plus reproductible (les requêtes peuvent être réexécutées), et plus puissant pour les transformations complexes (jointures, sous-requêtes, window functions).

Les mêmes requêtes fonctionnent-elles sur PostgreSQL/MySQL ?

Globalement oui, avec quelques adaptations : la fonction GLOB est spécifique à SQLite (remplacer par LIKE ou REGEXP). L'imputation par médiane sera plus simple avec PERCENTILE_CONT (PostgreSQL) ou NTILE.

Comment gérer les dates plus proprement ?

SQLite n'a pas de type DATE natif. Pour un usage professionnel, préférez PostgreSQL ou MySQL, et utilisez DATE() pour normaliser. Dans notre cas, nous avons simplifié en extrayant les 10 premiers caractères des formats standard.

Que faire si mon dataset contient des millions de lignes ?

SQLite peut gérer des millions de lignes, mais les performances peuvent diminuer. Passez alors à PostgreSQL ou utilisez des index sur les colonnes de filtrage. Évitez les SELECT DISTINCT * et préférez les GROUP BY ciblés.

Pourquoi utiliser la médiane pour Quantity et la moyenne pour Unit_Price ?

La quantité a une distribution asymétrique (petites valeurs fréquentes, quelques outliers). La médiane est plus robuste. Le prix unitaire a une distribution plus symétrique, la moyenne est acceptable.

10. Conclusion

Ce cas pratique montre comment SQL permet de nettoyer efficacement un dataset de taille réelle (63 000 lignes) avec des problèmes variés. En une vingtaine de requêtes, nous avons :

  • Supprimé 167 doublons
  • Corrigé les types de données
  • Traité 2 845 emails manquants
  • Imputé 1 711 quantités et 2 915 prix unitaires
  • Standardisé 62 833 catégories et localisations
  • Unifié 9 méthodes de paiement en 4 catégories
  • Recalculé 15 445 montants totaux incohérents

À retenir

  • Le nettoyage de données représente 60 à 80% du temps d'un projet data
  • SQL est un outil puissant pour le nettoyage à grande échelle
  • Les étapes clés : inspection, correction des types, doublons, NULL, standardisation, vérification
  • Toujours travailler sur une copie et documenter les transformations
Pour aller plus loin : Découvrez notre guide complet sur le nettoyage de données avec SQL pour des techniques avancées.
 

Recevez la veille IA & Data qui compte vraiment

 

    Analyses claires, outils concrets et tendances IA sans bruit.     Rejoignez les lecteurs de IANA Data.