Pandas Drop Duplicates : Comment supprimer les lignes en double en Python
Updated on
Les lignes en double sont l'un des problèmes de qualité de données les plus courants dans les jeux de données réels. Elles s'infiltrent par des appels API répétés, des exportations CSV qui se chevauchent, des pipelines ETL défaillants ou de simples erreurs de copier-coller lors de la saisie manuelle des données. Non contrôlés, les doublons gonflent le nombre de lignes, faussent les moyennes et les sommes, et introduisent des biais dans les modèles de machine learning. Un jeu de données qui semble contenir 10 000 enregistrements clients pourrait en réalité ne compter que 8 200 clients uniques et 1 800 entrées fantômes corrompant silencieusement chaque calcul construit dessus.
La méthode pandas drop_duplicates() est le moyen standard pour détecter et supprimer ces lignes redondantes. Ce guide parcourt chaque paramètre, montre les modèles courants pour la déduplication dans le monde réel et couvre les considérations de performance pour les grands jeux de données. Chaque exemple de code est prêt à copier et inclut sa sortie attendue.
Pourquoi les doublons sont importants
Avant de plonger dans le code, il vaut la peine de comprendre exactement ce que les lignes en double cassent :
| Problème | Ce qui se passe | Exemple |
|---|---|---|
| Comptages gonflés | len(df) et value_counts() surcomptent | Un client apparaît 3 fois, donc le "total clients" est 3x trop élevé |
| Moyennes erronées | mean() pondère davantage les lignes dupliquées | Une commande de grande valeur comptée deux fois biaise la valeur moyenne des commandes vers le haut |
| Jointures cassées | La fusion sur une clé avec des doublons provoque une explosion de lignes | Une fusion many-to-many produit un produit cartésien au lieu d'un mapping 1:1 |
| Mauvais modèles ML | Les données d'entraînement avec des échantillons répétés biaisent le modèle | Le modèle mémorise les exemples dupliqués et surajuste |
| Stockage gaspillé | Les lignes redondantes consomment du disque et de la mémoire | Un jeu de données de 2 Go pourrait faire 1,4 Go après déduplication |
La solution est simple : trouver les doublons, décider quelle copie conserver (le cas échéant) et supprimer le reste.
Syntaxe de base : df.drop_duplicates()
L'appel le plus simple supprime les lignes où chaque valeur de colonne est identique :
import pandas as pd
df = pd.DataFrame({
'name': ['Alice', 'Bob', 'Alice', 'Charlie', 'Bob'],
'age': [30, 25, 30, 35, 25],
'city': ['NYC', 'LA', 'NYC', 'Chicago', 'LA']
})
print("Before:")
print(df)
df_clean = df.drop_duplicates()
print("\nAfter:")
print(df_clean)Sortie :
Before:
name age city
0 Alice 30 NYC
1 Bob 25 LA
2 Alice 30 NYC
3 Charlie 35 Chicago
4 Bob 25 LA
After:
name age city
0 Alice 30 NYC
1 Bob 25 LA
3 Charlie 35 ChicagoLa ligne 2 et la ligne 4 étaient des copies exactes de la ligne 0 et de la ligne 1, elles ont donc été supprimées. Notez que les valeurs d'index originales (0, 1, 3) sont préservées. Si vous voulez un index séquentiel propre, enchaînez .reset_index(drop=True).
Signature complète de la méthode
DataFrame.drop_duplicates(subset=None, keep='first', inplace=False, ignore_index=False)| Paramètre | Type | Par défaut | Description |
|---|---|---|---|
subset | étiquette de colonne ou liste | None (toutes les colonnes) | Ne considérer que ces colonnes lors de l'identification des doublons |
keep | 'first', 'last', False | 'first' | Quel doublon conserver ; False supprime toutes les copies |
inplace | bool | False | Si True, modifie le DataFrame sur place et retourne None |
ignore_index | bool | False | Si True, réinitialise l'index de 0 à n-1 dans le résultat |
Trouver les doublons d'abord avec df.duplicated()
Avant de supprimer les doublons, vous voulez souvent les inspecter. La méthode duplicated() retourne une Series booléenne marquant les lignes en double :
df = pd.DataFrame({
'order_id': [101, 102, 103, 101, 104, 102],
'product': ['Widget', 'Gadget', 'Widget', 'Widget', 'Gizmo', 'Gadget'],
'amount': [29.99, 49.99, 29.99, 29.99, 19.99, 49.99]
})
# Show which rows are duplicates
print(df.duplicated())Sortie :
0 False
1 False
2 False
3 True
4 False
5 True
dtype: boolPour voir les lignes en double réelles :
duplicates = df[df.duplicated(keep=False)]
print(duplicates)Sortie :
order_id product amount
0 101 Widget 29.99
1 102 Gadget 49.99
3 101 Widget 29.99
5 102 Gadget 49.99L'utilisation de keep=False marque toutes les copies comme doublons (pas seulement la deuxième occurrence), ce qui vous permet de voir chaque ligne impliquée dans la duplication.
Compter les doublons
Pour obtenir un décompte rapide du nombre de lignes en double :
num_duplicates = df.duplicated().sum()
print(f"Number of duplicate rows: {num_duplicates}")
total_rows = len(df)
unique_rows = df.drop_duplicates().shape[0]
print(f"Total: {total_rows}, Unique: {unique_rows}, Duplicates: {total_rows - unique_rows}")Sortie :
Number of duplicate rows: 2
Total: 6, Unique: 4, Duplicates: 2C'est une vérification de bon sens utile avant et après le nettoyage.
Le paramètre subset : Vérifier uniquement des colonnes spécifiques
Souvent, vous voudrez dédupliquer sur la base d'une colonne clé plutôt que d'exiger que chaque colonne corresponde. Le paramètre subset vous permet de spécifier quelles colonnes déterminent l'unicité :
df = pd.DataFrame({
'email': ['alice@mail.com', 'bob@mail.com', 'alice@mail.com', 'charlie@mail.com'],
'name': ['Alice', 'Bob', 'Alice Smith', 'Charlie'],
'signup_date': ['2025-01-01', '2025-01-02', '2025-01-15', '2025-01-03']
})
print("Original:")
print(df)
# Deduplicate by email only
df_deduped = df.drop_duplicates(subset=['email'])
print("\nDeduplicated by email:")
print(df_deduped)Sortie :
Original:
email name signup_date
0 alice@mail.com Alice 2025-01-01
1 bob@mail.com Bob 2025-01-02
2 alice@mail.com Alice Smith 2025-01-15
3 charlie@mail.com Charlie 2025-01-03
Deduplicated by email:
email name signup_date
0 alice@mail.com Alice 2025-01-01
1 bob@mail.com Bob 2025-01-02
3 charlie@mail.com Charlie 2025-01-03La ligne 2 a été supprimée car alice@mail.com apparaissait déjà à la ligne 0, même si les valeurs name et signup_date différaient. Vous pouvez aussi passer plusieurs colonnes : subset=['email', 'name'] ne considérerait les lignes comme dupliquées que lorsque les deux colonnes correspondent.
Le paramètre keep : first, last ou False
Le paramètre keep contrôle quelle occurrence survit :
df = pd.DataFrame({
'sensor_id': ['S1', 'S2', 'S1', 'S2', 'S1'],
'reading': [22.5, 18.3, 23.1, 18.3, 24.0],
'timestamp': ['08:00', '08:00', '09:00', '09:00', '10:00']
})
print("keep='first' (default):")
print(df.drop_duplicates(subset=['sensor_id'], keep='first'))
print("\nkeep='last':")
print(df.drop_duplicates(subset=['sensor_id'], keep='last'))
print("\nkeep=False (drop all duplicates):")
print(df.drop_duplicates(subset=['sensor_id'], keep=False))Sortie :
keep='first' (default):
sensor_id reading timestamp
0 S1 22.5 08:00
1 S2 18.3 08:00
keep='last':
sensor_id reading timestamp
3 S2 18.3 09:00
4 S1 24.0 10:00
keep=False (drop all duplicates):
Empty DataFrame
Columns: [sensor_id, reading, timestamp]
Index: []| Valeur keep | Comportement | Cas d'utilisation |
|---|---|---|
'first' | Conserver la première occurrence, supprimer les suivantes | Conserver l'enregistrement le plus ancien |
'last' | Conserver la dernière occurrence, supprimer les précédentes | Conserver l'enregistrement le plus récent |
False | Supprimer toutes les lignes ayant un doublon | Trouver les lignes véritablement uniques (aucune copie) |
Dans l'exemple ci-dessus, keep=False retourne un DataFrame vide car les deux identifiants de capteur apparaissent plus d'une fois.
In-place vs. Retourner un nouveau DataFrame
Par défaut, drop_duplicates() retourne un nouveau DataFrame et laisse l'original inchangé. Définir inplace=True modifie l'original directement :
df = pd.DataFrame({
'id': [1, 2, 2, 3],
'value': ['a', 'b', 'b', 'c']
})
# Returns new DataFrame (original unchanged)
df_new = df.drop_duplicates()
print(f"Original length: {len(df)}, New length: {len(df_new)}")
# Modifies in place (returns None)
df.drop_duplicates(inplace=True)
print(f"After inplace: {len(df)}")Sortie :
Original length: 4, New length: 3
After inplace: 3Le style pandas moderne recommande d'éviter inplace=True et d'utiliser l'assignation à la place (df = df.drop_duplicates()). Cela rend le code plus facile à lire et à déboguer, surtout dans les opérations enchaînées.
Détection de doublons insensible à la casse
Par défaut, pandas traite "Alice" et "alice" comme des valeurs différentes. Pour une déduplication insensible à la casse, normalisez d'abord la colonne :
df = pd.DataFrame({
'name': ['Alice', 'alice', 'ALICE', 'Bob', 'bob'],
'score': [90, 85, 92, 78, 80]
})
# Create a normalized column for comparison
df['name_lower'] = df['name'].str.lower()
# Deduplicate on the normalized column, keep the first original-case entry
df_deduped = df.drop_duplicates(subset=['name_lower']).drop(columns=['name_lower'])
print(df_deduped)Sortie :
name score
0 Alice 90
3 Bob 78Ce pattern préserve la casse originale tout en identifiant correctement les doublons insensibles à la casse.
Exemple concret : Nettoyage d'une base de données clients
Voici un scénario réaliste. Vous recevez un export client d'un CRM où le même client a été saisi plusieurs fois par différents commerciaux :
import pandas as pd
customers = pd.DataFrame({
'customer_id': [1001, 1002, 1001, 1003, 1002, 1004],
'name': ['Acme Corp', 'Beta Inc', 'Acme Corp', 'Gamma LLC', 'Beta Inc.', 'Delta Co'],
'email': ['acme@mail.com', 'beta@mail.com', 'acme@mail.com', 'gamma@mail.com', 'beta@mail.com', 'delta@mail.com'],
'revenue': [50000, 30000, 52000, 45000, 30000, 20000],
'last_contact': ['2025-12-01', '2025-11-15', '2026-01-10', '2025-10-20', '2025-11-15', '2026-01-05']
})
print(f"Rows before cleaning: {len(customers)}")
print(f"Duplicate customer_ids: {customers.duplicated(subset=['customer_id']).sum()}")
# Sort by last_contact descending so the most recent entry is first
customers['last_contact'] = pd.to_datetime(customers['last_contact'])
customers = customers.sort_values('last_contact', ascending=False)
# Keep the most recent record for each customer_id
customers_clean = customers.drop_duplicates(subset=['customer_id'], keep='first')
customers_clean = customers_clean.sort_values('customer_id').reset_index(drop=True)
print(f"\nRows after cleaning: {len(customers_clean)}")
print(customers_clean)Sortie :
Rows before cleaning: 6
Duplicate customer_ids: 2
Rows after cleaning: 4
customer_id name email revenue last_contact
0 1001 Acme Corp acme@mail.com 52000 2026-01-10
1 1002 Beta Inc beta@mail.com 30000 2025-11-15
2 1003 Gamma LLC gamma@mail.com 45000 2025-10-20
3 1004 Delta Co delta@mail.com 20000 2026-01-05Le pattern clé ici est trier d'abord, puis dédupliquer avec keep='first'. En triant par last_contact en ordre décroissant avant la déduplication, l'enregistrement le plus récent pour chaque client survit.
Exemple concret : Déduplication de résultats de web scraping
Les web scrapers produisent couramment des doublons lorsque les pages sont crawlées plusieurs fois ou que la pagination se chevauche :
import pandas as pd
scraped = pd.DataFrame({
'url': [
'https://shop.com/item/101',
'https://shop.com/item/102',
'https://shop.com/item/101',
'https://shop.com/item/103',
'https://shop.com/item/102',
'https://shop.com/item/104',
],
'title': ['Blue Widget', 'Red Gadget', 'Blue Widget', 'Green Gizmo', 'Red Gadget', 'Yellow Thing'],
'price': [19.99, 29.99, 19.99, 39.99, 31.99, 14.99],
'scraped_at': ['2026-02-01', '2026-02-01', '2026-02-02', '2026-02-02', '2026-02-02', '2026-02-02']
})
print(f"Total scraped rows: {len(scraped)}")
print(f"Unique URLs: {scraped['url'].nunique()}")
# Keep the latest scrape for each URL (prices may have changed)
scraped['scraped_at'] = pd.to_datetime(scraped['scraped_at'])
scraped = scraped.sort_values('scraped_at', ascending=False)
products = scraped.drop_duplicates(subset=['url'], keep='first').reset_index(drop=True)
print(f"\nCleaned rows: {len(products)}")
print(products)Sortie :
Total scraped rows: 6
Unique URLs: 4
Cleaned rows: 4
url title price scraped_at
0 https://shop.com/item/101 Blue Widget 19.99 2026-02-02
1 https://shop.com/item/102 Red Gadget 31.99 2026-02-02
2 https://shop.com/item/103 Green Gizmo 39.99 2026-02-02
3 https://shop.com/item/104 Yellow Thing 14.99 2026-02-02Notez que le prix du Red Gadget a été mis à jour de 29,99 à 31,99 entre les crawls. En conservant l'entrée la plus récente, nous capturons le prix le plus actuel.
drop_duplicates() vs groupby().first() : Quand utiliser lequel
Les deux approches peuvent dédupliquer les données, mais elles fonctionnent différemment :
import pandas as pd
df = pd.DataFrame({
'user_id': [1, 1, 2, 2, 3],
'action': ['login', 'purchase', 'login', 'login', 'purchase'],
'timestamp': ['2026-01-01', '2026-01-02', '2026-01-01', '2026-01-03', '2026-01-01']
})
# Method 1: drop_duplicates
result1 = df.drop_duplicates(subset=['user_id'], keep='first')
print("drop_duplicates:")
print(result1)
# Method 2: groupby().first()
result2 = df.groupby('user_id').first().reset_index()
print("\ngroupby().first():")
print(result2)Sortie :
drop_duplicates:
user_id action timestamp
0 1 login 2026-01-01
2 2 login 2026-01-01
4 3 purchase 2026-01-01
groupby().first():
user_id action timestamp
0 1 login 2026-01-01
1 2 login 2026-01-01
2 3 purchase 2026-01-01Les résultats semblent similaires, mais il y a des différences importantes :
| Caractéristique | drop_duplicates() | groupby().first() |
|---|---|---|
| Vitesse | Plus rapide pour la déduplication simple | Plus lent en raison de la surcharge de regroupement |
| Gestion des NaN | Conserve les valeurs NaN telles quelles | first() ignore les NaN par défaut |
| Index | Préserve l'index original | Réinitialisé aux clés de groupe |
| Agrégation | Ne peut pas agréger d'autres colonnes | Peut être combiné avec agg() pour des résumés multi-colonnes |
| Mémoire | Utilisation mémoire plus faible | Crée un objet GroupBy intermédiaire |
| Cas d'utilisation | Supprimer les doublons exacts ou partiels | Dédupliquer tout en calculant des agrégats |
Règle générale : Utilisez drop_duplicates() quand vous voulez simplement supprimer les lignes en double. Utilisez groupby().first() (ou groupby().agg()) quand vous devez aussi agréger des valeurs des lignes en double -- par exemple, additionner le chiffre d'affaires sur les enregistrements clients en double tout en conservant le premier nom.
Astuces de performance pour les grands DataFrames
Lorsque vous travaillez avec des millions de lignes, la déduplication peut devenir un goulot d'étranglement. Voici des moyens pratiques pour l'accélérer :
1. Spécifier les colonnes subset
Vérifier toutes les colonnes est plus lent que vérifier uniquement les colonnes clés :
# Slower: checks every column
df.drop_duplicates()
# Faster: checks only the key column
df.drop_duplicates(subset=['user_id'])2. Utiliser les types de données appropriés
Convertissez les colonnes texte en type category avant la déduplication si elles ont une faible cardinalité :
df['status'] = df['status'].astype('category')
df['country'] = df['country'].astype('category')
df_clean = df.drop_duplicates(subset=['status', 'country'])3. Trier avant la déduplication uniquement si nécessaire
Trier un grand DataFrame juste pour contrôler quelle ligne keep='first' sélectionne ajoute un temps significatif. Si vous ne vous souciez pas de quel doublon survit, sautez le tri.
4. Envisager le traitement par chunks pour les très gros fichiers
Pour les fichiers qui ne tiennent pas en mémoire, traitez par chunks :
chunks = pd.read_csv('large_file.csv', chunksize=100_000)
seen = set()
clean_chunks = []
for chunk in chunks:
chunk = chunk.drop_duplicates(subset=['id'])
new_rows = chunk[~chunk['id'].isin(seen)]
seen.update(new_rows['id'].tolist())
clean_chunks.append(new_rows)
df_clean = pd.concat(clean_chunks, ignore_index=True)5. Benchmark : performance typique
| Lignes | Colonnes vérifiées | Temps (approx) |
|---|---|---|
| 100K | Toutes (10 cols) | ~15 ms |
| 1M | Toutes (10 cols) | ~150 ms |
| 1M | 1 colonne | ~50 ms |
| 10M | 1 colonne | ~500 ms |
Ces chiffres varient selon le matériel et les types de données, mais la conclusion clé est que drop_duplicates() évolue linéairement et traite la plupart des jeux de données en moins d'une seconde.
Explorez vos données nettoyées avec PyGWalker
Après avoir supprimé les doublons, l'étape suivante est généralement d'explorer le jeu de données nettoyé -- vérifier les distributions, repérer les valeurs aberrantes et confirmer que la déduplication a fonctionné comme prévu. Au lieu d'écrire plusieurs appels matplotlib ou seaborn, vous pouvez utiliser PyGWalker (opens in a new tab), une bibliothèque Python open-source qui transforme n'importe quel DataFrame pandas en une interface de visualisation interactive de type Tableau directement dans Jupyter Notebook.
import pandas as pd
import pygwalker as pyg
# Your cleaned customer data
customers_clean = pd.DataFrame({
'customer_id': [1001, 1002, 1003, 1004],
'name': ['Acme Corp', 'Beta Inc', 'Gamma LLC', 'Delta Co'],
'revenue': [52000, 30000, 45000, 20000],
'region': ['East', 'West', 'East', 'South']
})
# Launch interactive visualization
walker = pyg.walk(customers_clean)Avec PyGWalker, vous pouvez glisser region sur l'axe x et revenue sur l'axe y pour voir instantanément la distribution du chiffre d'affaires par région. Vous pouvez créer des diagrammes en barres, des nuages de points, des histogrammes et des cartes de chaleur en glissant-déposant des champs -- aucun code de graphique requis. C'est particulièrement utile après la déduplication quand vous voulez vérifier que votre logique de nettoyage a produit des résultats sensés.
Installez PyGWalker avec
pip install pygwalkerou essayez-le dans Google Colab (opens in a new tab).
FAQ
Est-ce que drop_duplicates() modifie le DataFrame original ?
Non, par défaut drop_duplicates() retourne un nouveau DataFrame et laisse l'original inchangé. Pour modifier sur place, passez inplace=True, mais l'approche recommandée est d'utiliser l'assignation : df = df.drop_duplicates().
Comment supprimer les doublons basés sur une seule colonne ?
Passez le nom de la colonne au paramètre subset : df.drop_duplicates(subset=['email']). Cela conserve la première ligne pour chaque email unique et supprime les lignes suivantes avec le même email, indépendamment des différences dans les autres colonnes.
Quelle est la différence entre duplicated() et drop_duplicates() ?
duplicated() retourne une Series booléenne indiquant quelles lignes sont des doublons (utile pour l'inspection et le comptage). drop_duplicates() retourne un DataFrame avec les lignes en double supprimées. Utilisez duplicated() d'abord pour comprendre vos données, puis drop_duplicates() pour les nettoyer.
Peut-on supprimer des doublons basés sur une condition ?
Pas directement avec drop_duplicates(). À la place, triez votre DataFrame par la colonne de condition d'abord, puis appelez drop_duplicates() avec keep='first'. Par exemple, pour conserver la ligne avec le chiffre d'affaires le plus élevé pour chaque client : df.sort_values('revenue', ascending=False).drop_duplicates(subset=['customer_id'], keep='first').
Comment gérer les doublons insensibles à la casse ?
Créez une colonne temporaire en minuscules, dédupliquez dessus, puis supprimez la colonne auxiliaire : df['key'] = df['name'].str.lower() suivi de df.drop_duplicates(subset=['key']).drop(columns=['key']). Cela préserve la casse originale tout en identifiant correctement les doublons.
Conclusion
La suppression des lignes en double est une étape fondamentale dans tout workflow de nettoyage de données. La méthode pandas drop_duplicates() gère la grande majorité des tâches de déduplication avec seulement quelques paramètres :
- Utilisez
subsetpour dédupliquer sur des colonnes spécifiques plutôt que sur toutes les colonnes. - Utilisez
keep='first'oukeep='last'pour contrôler quelle occurrence survit ; utilisezkeep=Falsepour supprimer toutes les copies. - Utilisez
duplicated()pour inspecter et compter les doublons avant de les supprimer. - Triez avant de dédupliquer quand vous devez conserver une ligne spécifique (par exemple, l'enregistrement le plus récent).
- Pour la déduplication insensible à la casse, normalisez en minuscules dans une colonne temporaire d'abord.
- Pour les grands jeux de données, limitez les colonnes
subsetet utilisez des types de données appropriés pour maintenir les performances.
Une fois vos données nettoyées, des outils comme PyGWalker (opens in a new tab) vous permettent d'explorer visuellement le résultat sans écrire de code de graphique, vous aidant à vérifier que la déduplication a fonctionné correctement et à passer directement à l'analyse.