Daten deduplizieren#
1. Beispieldaten laden#
[1]:
import pandas as pd
[2]:
customers = pd.read_csv('https://raw.githubusercontent.com/kjam/data-cleaning-101/master/data/customer_data_duped.csv',
encoding='utf-8')
2. Deduplizieren mit pandas#
2.1 Überblick#
[3]:
customers
[3]:
name | job | company | street_address | city | state | user_name | ||
---|---|---|---|---|---|---|---|---|
0 | Patricia Schaefer | Programmer, systems | Estrada-Best | 398 Paul Drive | Christianview | Delaware | lambdavid@gmail.com | ndavidson |
1 | Olivie Dubois | Ingénieur recherche et développement en agroal... | Moreno | rue Lucas Benard | Saint Anastasie-les-Bains | AR | berthelotjacqueline@mahe.fr | manonallain |
2 | Mary Davies-Kirk | Public affairs consultant | Baker Ltd | Flat 3\nPugh mews | Stanleyfurt | ZA | middletonconor@hotmail.com | colemanmichael |
3 | Miroslawa Eckbauer | Dispensing optician | Ladeck GmbH | Mijo-Lübs-Straße 12 | Neubrandenburg | Berlin | sophia01@yahoo.de | romanjunitz |
4 | Richard Bauer | Accountant, chartered certified | Hoffman-Rocha | 6541 Rodriguez Wall | Carlosmouth | Texas | tross@jensen-ware.org | adam78 |
... | ... | ... | ... | ... | ... | ... | ... | ... |
2075 | Maurice Stey | Systems developer | Linke Margraf GmbH & Co. OHG | Laila-Scheibe-Allee 2/0 | Luckenwalde | Hamburg | gutknechtevelyn@niemeier.com | dkreusel |
2076 | Linda Alexander | Commrcil horiculuri | Webb, Ballald and Vasquel | 5594 Persn Ciff | Mooneybury | Maryland | ahleythoa@ail.co | kennethrchn |
2077 | Diane Bailly | Pharmacien | Voisin | 527, rue Dijoux | Duval-les-Bains | CH | aruiz@reynaud.fr | dorothee41 |
2078 | Jorge Riba Cerdán | Hotel manager | Amador-Diego | Rambla de Adriana Barceló 854 Puerta 3 | Huesca | Asturias | manuelamosquera@yahoo.com | eugenia17 |
2079 | Ryan Thompson | Brewing technologist | Smith-Sullivan | 136 Rodriguez Point | Bradfordborough | North Dakota | lcruz@gmail.com | cnewton |
2080 rows × 8 columns
2.2 Datentypen anzeigen#
Hierfür verwenden wir pandas.DataFrame.dtypes:
[4]:
customers.dtypes
[4]:
name object
job object
company object
street_address object
city object
state object
email object
user_name object
dtype: object
2.3 Fehlende Werte ermitteln#
pandas.isnull zeigt für ein array-ähnliches Objekt an, ob Werte fehlen:
NaN
in numerischen ArraysNone
oderNaN
in Objekt-ArraysNaT
in datetimelike
Siehe auch:
notna für die boolesche Umkehrung von pandas.isna
Series.isna für die fehlenden Werte in einer Serie
DataFrame.isna für die fehlenden Werte in einem DataFrame
Index.isna für die fehlenden Werte in einem Index
[5]:
for col in customers.columns:
print(col, customers[col].isnull().sum())
name 0
job 0
company 0
street_address 0
city 0
state 0
email 0
user_name 0
2.4 Duplizierte Datensätze ermitteln#
[6]:
customers.duplicated()
[6]:
0 False
1 False
2 False
3 False
4 False
...
2075 False
2076 False
2077 False
2078 False
2079 False
Length: 2080, dtype: bool
customers.duplicated()
gibt uns noch nicht den gewünschten Hinweis, ob es doppelte Datensätze gibt. Im Folgenden lassen wir uns alle Datensätze ausgeben, für die True
zurückgegeben wird:
[7]:
customers[customers.duplicated()]
[7]:
name | job | company | street_address | city | state | user_name |
---|
Offenbar gibt es keine duplizierten Datensätze.
2.5 Duplizierte Daten löschen#
Das Löschen duplizierter Datensätze mit drop_duplicates
sollte demnach nichts ändern und die Anzahl der Datensätze bei 2080 belassen:
[7]:
customers.drop_duplicates()
[7]:
name | job | company | street_address | city | state | user_name | ||
---|---|---|---|---|---|---|---|---|
0 | Patricia Schaefer | Programmer, systems | Estrada-Best | 398 Paul Drive | Christianview | Delaware | lambdavid@gmail.com | ndavidson |
1 | Olivie Dubois | Ingénieur recherche et développement en agroal... | Moreno | rue Lucas Benard | Saint Anastasie-les-Bains | AR | berthelotjacqueline@mahe.fr | manonallain |
2 | Mary Davies-Kirk | Public affairs consultant | Baker Ltd | Flat 3\nPugh mews | Stanleyfurt | ZA | middletonconor@hotmail.com | colemanmichael |
3 | Miroslawa Eckbauer | Dispensing optician | Ladeck GmbH | Mijo-Lübs-Straße 12 | Neubrandenburg | Berlin | sophia01@yahoo.de | romanjunitz |
4 | Richard Bauer | Accountant, chartered certified | Hoffman-Rocha | 6541 Rodriguez Wall | Carlosmouth | Texas | tross@jensen-ware.org | adam78 |
... | ... | ... | ... | ... | ... | ... | ... | ... |
2075 | Maurice Stey | Systems developer | Linke Margraf GmbH & Co. OHG | Laila-Scheibe-Allee 2/0 | Luckenwalde | Hamburg | gutknechtevelyn@niemeier.com | dkreusel |
2076 | Linda Alexander | Commrcil horiculuri | Webb, Ballald and Vasquel | 5594 Persn Ciff | Mooneybury | Maryland | ahleythoa@ail.co | kennethrchn |
2077 | Diane Bailly | Pharmacien | Voisin | 527, rue Dijoux | Duval-les-Bains | CH | aruiz@reynaud.fr | dorothee41 |
2078 | Jorge Riba Cerdán | Hotel manager | Amador-Diego | Rambla de Adriana Barceló 854 Puerta 3 | Huesca | Asturias | manuelamosquera@yahoo.com | eugenia17 |
2079 | Ryan Thompson | Brewing technologist | Smith-Sullivan | 136 Rodriguez Point | Bradfordborough | North Dakota | lcruz@gmail.com | cnewton |
2080 rows × 8 columns
Nun wollen wir nur diejenigen Datensätze löschen, deren user_name
identisch ist:
[8]:
customers.drop_duplicates(['user_name'])
[8]:
name | job | company | street_address | city | state | user_name | ||
---|---|---|---|---|---|---|---|---|
0 | Patricia Schaefer | Programmer, systems | Estrada-Best | 398 Paul Drive | Christianview | Delaware | lambdavid@gmail.com | ndavidson |
1 | Olivie Dubois | Ingénieur recherche et développement en agroal... | Moreno | rue Lucas Benard | Saint Anastasie-les-Bains | AR | berthelotjacqueline@mahe.fr | manonallain |
2 | Mary Davies-Kirk | Public affairs consultant | Baker Ltd | Flat 3\nPugh mews | Stanleyfurt | ZA | middletonconor@hotmail.com | colemanmichael |
3 | Miroslawa Eckbauer | Dispensing optician | Ladeck GmbH | Mijo-Lübs-Straße 12 | Neubrandenburg | Berlin | sophia01@yahoo.de | romanjunitz |
4 | Richard Bauer | Accountant, chartered certified | Hoffman-Rocha | 6541 Rodriguez Wall | Carlosmouth | Texas | tross@jensen-ware.org | adam78 |
... | ... | ... | ... | ... | ... | ... | ... | ... |
2074 | Rhonda James | Recruitment consultant | Turner, Bradley and Scott | 28382 Stokes Expressway | Port Gabrielaport | New Hampshire | zroberts@hotmail.com | heathscott |
2076 | Linda Alexander | Commrcil horiculuri | Webb, Ballald and Vasquel | 5594 Persn Ciff | Mooneybury | Maryland | ahleythoa@ail.co | kennethrchn |
2077 | Diane Bailly | Pharmacien | Voisin | 527, rue Dijoux | Duval-les-Bains | CH | aruiz@reynaud.fr | dorothee41 |
2078 | Jorge Riba Cerdán | Hotel manager | Amador-Diego | Rambla de Adriana Barceló 854 Puerta 3 | Huesca | Asturias | manuelamosquera@yahoo.com | eugenia17 |
2079 | Ryan Thompson | Brewing technologist | Smith-Sullivan | 136 Rodriguez Point | Bradfordborough | North Dakota | lcruz@gmail.com | cnewton |
2029 rows × 8 columns
Dies löschte 51 Datensätze.
3. Dedupe#
Alternativ können wir die duplizierte Daten mit der Dedupe-Bibliothek erkennen, die ein flaches neuronales Netzwerk verwendet, um aus einem kleinen Training zu lernen.
Siehe auch:
csvdedupe bietet ein Kommandozeilenwerkzeug für Dedupe
Zudem haben dieselben Entwickler*innen parserator erstellt, mit dem ihr Textfunktionen extrahieren und eure eigenen Textextraktion trainieren könnt.
3.1 Dedupe konfigurieren#
Nun definieren wir die Felder, auf die bei der Deduplizierung geachtet werden soll und erstellen ein neues deduper
-Objekt:
[9]:
import dedupe
import os
customers = pd.read_csv('https://raw.githubusercontent.com/kjam/data-cleaning-101/master/data/customer_data_duped.csv',
encoding='utf-8')
[10]:
variables = [
{'field': 'name', 'type': 'String'},
{'field': 'job', 'type': 'String'},
{'field': 'company', 'type': 'String'},
{'field': 'street_address','type': 'String'},
{'field': 'city','type': 'String'},
{'field': 'state', 'type': 'String', 'has_missing': True},
{'field': 'email', 'type': 'String', 'has_missing': True},
{'field': 'user_name', 'type': 'String'},
]
deduper = dedupe.Dedupe(variables)
Wenn der Wert eines Feldes fehlt, sollte dieser fehlende Wert als None
-Objekt dargestellt werden. Durch 'has_missing': True
wird jedoch ein neues, zusätzliches Feld erstellt, das angibt, ob die Daten vorhanden waren oder nicht, und die fehlenden Daten werden mit Null versehen.
Siehe auch:
[11]:
deduper
[11]:
<dedupe.api.Dedupe at 0x7f03b6d92610>
[12]:
customers.shape
[12]:
(2080, 8)
4. Trainingsdaten erstellen#
[13]:
deduper.prepare_training(customers.T.to_dict())
prepare_training initialisiert das aktive Lernen mit unseren Daten und, optional, mit vorhandenen Trainingsdaten.
T
spiegelt den DataFrame über seine Diagonale, indem Zeilen als Spalten geschrieben werden und umgekehrt. Hierfür wird pandas.DataFrame.transpose verwendet.
5. Aktives Lernen#
Mit dedupe.console_label könnt ihr eure Dedupe-Instanz trainieren. Wenn Dedupe ein Datensatzpaar findet, werdet ihr gebeten, es als Duplikat zu kennzeichnen. Ihr könnt hierfür die Tasten y
, n
und u
, um Duplikate zu kennzeichnen. Drückt f
, wenn ihr fertig seid.
[14]:
dedupe.console_label(deduper)
name : Kenneth Moore
job : Magazine journalist
company : Cross, Bell and Diaz
street_address : 75443 Lindsey Pine
city : Thompsonshire
state : Colorado
email : ashley28@rice.com
user_name : todd72
name : Kenneth Moore
job : Magazine journalist
company : Cross, Bfll anf Diaz
street_address : 753 Lindsey Pine
city : Thompsonshe
state : Colorao
email : ashey28@rice.co
user_name : todd72
0/10 positive, 0/10 negative
Do these records refer to the same thing?
(y)es / (n)o / (u)nsure / (f)inished
y
name : Frédérique Lejeune-Daniel
job : Technicien chimiste
company : Schmitt
street_address : chemin Denise Ferrand
city : Saint CharlotteVille
state : IE
email : jchretien@costa.com
user_name : joseph60
name : Frédérique Lejeune-Daniel
job : Tecce cse
company : Sctmitt
street_address : chemin Denise Ferrand
city : Saint ChalotteVille
state : IE
email : jchretien@costacom
user_name : joseph60
1/10 positive, 0/10 negative
Do these records refer to the same thing?
(y)es / (n)o / (u)nsure / (f)inished / (p)revious
y
name : Herr Johann Eigenwillig
job : Immigrtion officer
company : Süßebuer Hänel GmbH
street_address : Lanernplatz 0
city : Stadtsteinach
state : Thürinen
email : hemieluie@nock.com
user_name : istoll
name : Herr Johann Eigenwillig
job : Immigration officer
company : Süßebier Hänel GmbH
street_address : Langernplatz 0
city : Stadtsteinach
state : Thüringen
email : haasemarieluise@noack.com
user_name : istoll
2/10 positive, 0/10 negative
Do these records refer to the same thing?
(y)es / (n)o / (u)nsure / (f)inished / (p)revious
y
name : Dr. Catherine Sutton
job : Engineer, maintenance
company : Ross LLC
street_address : 13689 Morales Centers
city : North Sarah
state : New Mexico
email : lewisnicole@yahoo.com
user_name : clittle
name : Dr. Catherine Sutton
job : Enginee maintenance
company : Ross LLC
street_address : 13689 Morales Centers
city : North Sarah
state : New Mexico
email : ewinicoe@yaoo.com
user_name : little
3/10 positive, 0/10 negative
Do these records refer to the same thing?
(y)es / (n)o / (u)nsure / (f)inished / (p)revious
y
name : Andrés Franco Bravo
job : Photographer
company : Pareja-Fábregas
street_address : Cuesta Margarita Robledo 251 Piso 1
city : Granada
state : Alicante
email : fátimazamora@batlle.com
user_name : losasebastian
name : Andrés Franco Bravo
job : Photographer
company : Pare8a8Fábre8as
street_address : Cuesta Magaita Robledo 251 Piso 1
city : Granada
state : Alicante
email : fáimazamra@balle.cm
user_name : lsasebastian
4/10 positive, 0/10 negative
Do these records refer to the same thing?
(y)es / (n)o / (u)nsure / (f)inished / (p)revious
f
Finished labeling
Die letzten beiden verglichenen Trainingsdatensätze machen deutlich, dass wir dieses Duplikat mit unserem obigen drop_duplicates
-Beispiel nicht gelöscht haben – clittle
und little
wurden als unterschiedlich erkannt.
Mit Dedupe.train werden die von euch markierten Datensatzpaare zu den Trainingsdaten hinzugefügt und das Matching-Modell aktualisiert.
Mit index_predicates=True
berücksichtigt die Deduplizierung auch Prädikate, die auf der Indizierung der Daten beruhen.
Wenn ihr fertig seid, speichert eure Trainingsdaten mit Dedupe.write_settings.
[15]:
settings_file = 'csv_example_learned_settings'
if os.path.exists(settings_file):
print('reading from', settings_file)
with open(settings_file, 'rb') as f:
deduper = dedupe.StaticDedupe(f)
else:
deduper.train(index_predicates=True)
with open(settings_file, 'wb') as sf:
deduper.write_settings(sf)
reading from csv_example_learned_settings
Mit dedupe.Dedupe.partition werden Datensätze identifiziert, die sich alle auf dieselbe Entität beziehen, und als Tupel zurückgegeben, die eine Folge von Datensatz-IDs und Konfidenzwerten sind. Weitere Einzelheiten zum Konfidenzwert findet ihr unter dedupe.Dedupe.cluster.
[16]:
dupes = deduper.partition(customers.T.to_dict())
[17]:
dupes
[17]:
[((136, 1360), (1.0, 1.0)),
((298, 1026), (1.0, 1.0)),
((354, 858), (1.0, 1.0)),
((478, 1119), (1.0, 1.0)),
((938, 1890), (1.0, 1.0)),
((1785, 1939), (1.0, 1.0)),
((0,), (1.0,)),
((1,), (1.0,)),
((2,), (1.0,)),
((3,), (1.0,)),
((4,), (1.0,)),
...]
Wir können uns auch nur einzelne Einträge ausgeben lassen:
[18]:
dupes[0]
[18]:
((136, 1360), (1.0, 1.0))
Diese können wir uns dann mit pandas.DataFrame.iloc anzeigen lassen:
[19]:
customers.iloc[[136,1360]]
[19]:
name | job | company | street_address | city | state | user_name | ||
---|---|---|---|---|---|---|---|---|
136 | Frédérique Lejeune-Daniel | Technicien chimiste | Schmitt | chemin Denise Ferrand | Saint CharlotteVille | IE | jchretien@costa.com | joseph60 |
1360 | Frédérique Lejeune-Daniel | Tecce cse | Sctmitt | chemin Denise Ferrand | Saint ChalotteVille | IE | jchretien@costacom | joseph60 |