Ein richtiges Code in Data Science

In diesem Eintrag schreibe ich darüber, was ich im letzten Eintrag nicht geschrieben habe.

Thema "richtig laufendes Code". Die Frage ist, wie man ein Code schreiben kann, das

  • fehlerlos laufen kann und
  • das richtige Ergebnis berechnet.

Ja, Ihr berechnetes Ergebnis muss richtig sein. Aber können Sie wirklich sagen, dass Ihr Ergebnis keinen logischen Fehler hat?

Es geht um die Daten

In seinem Buch schreibt Herr Grus über ein Beispiel.

You investigate further and discover that the outlier was actually an internal test account that no one ever bothered to remove. So you feel pretty justified in excluding it. ("Data Science from Scratch", Seite 65)

Das ist ein glückliches Beispiel: Test-Daten sind Ausreißer. Aber es passiert oft, dass es in den Daten zu vermeidende Daten gibt. Es ist auch normal, dass niemand im Voraus über solche Daten spricht.

Wenn ein DWH groß ist, gibt es keine Person, die alle Daten ausführlich versteht. Stellen Sie sich vor, dass das DWH einige hunderte Tabelle hat. Viele Entwickler entwickeln sie. Eine Spalte hat einen Namen, durch den man die Bedeutung schnell verstehen kann. Aber eigentlich sind die Werte anderes als man erwartet. Einige Tabellen werden automatisch und regelmäßig aktualisiert, andere nicht. Eine Ingestion-Applikation holt regelmäßig Daten ab und sie in einer Tabelle speichert. Wegen der Änderung der Datenformat hat die Tabelle etwas Anderes als früher.

Das kommt in den Raum, wenn Sie vor allem nötige Daten aus DWH abholen. Sie müssen unter vielen Tabellen die richtige Tabelle auswählen und die richtigen Zeilen und Spalten selektieren. Das ist gar nicht trivial als man denkt.

Das kann passieren, auch wenn die Tabelle klein ist. Die Tabelle hat die Daten von 2016 bis zum Heute. Die Tabelle hat ca 80 000 Zeilen und deshalb haben Sie sich entschieden, die ganzen Daten zu analysieren. Aber die Daten für ersten einigen Monaten sind eigentlich Test-Daten. Sie muss man zur Analyse vermeiden. Aber niemand sagt das im Voraus und Sie erfahren das Faktum, nach Sie etwas Komisches gefunden haben und eine Frage danach gestellt haben. Um etwas Komisches zu finden, muss man die Daten ausführlich untersuchen. Es ist ganz normal, dass Sie keine Zeit dafür haben. Deswegen analysieren Sie die Daten ohne zu wissen, dass es Müll in Ihren Daten gibt.

Äh, warum ich über die Daten spreche?

Ob man richtig die Daten auswertet, kommt darauf an, ob man die Daten richtig versteht.

Ein Beispiel mit Codes

Ihre Aufgabe ist, einige Statistik der Bezahlung der Kunden zu berechnen.

Hier gibt es eine Tabelle, in der alle Bezahlungen gespeichert werden. Die Tabelle hat (zumindest) drei Spalte transaction_id, customer_id und payment. Sie wissen schon, dass eine Zeile einem Transaktion entspricht.

Sie laden die Daten in Jupyter Notebook und führen die folgende Zeile aus.

average_payment_by_customer = df.groupby("customer_id")["payment"].mean()

Oder auf DbVisualizer (oder DataGrip) kriegt man die Datan durch die folgende Abfrage.

SELECT customer_id  AS customer_id,
       avg(payment) AS average_payment 
  FROM payments
 GROUP BY customer_id
;

In R schreibt man folgend.

df %>% 
  group_by(customer_id) %>% 
  summarise(average_payment=mean(payment))

Aber es kann sein, dass die durchschnittliche Bezahlung je nach dem Kunden nicht ergibt.

Angenommen, dass es mehrere Kunden ohne "richtige" customer_id gibt. In solchem Fall weisen wir den Kunden 00000000000 zu. Also die customer_id 00000000000 besteht aus viele Leute, nicht einem Kunden. Deshalb mussten Sie die customer_id bei der Berechnung ignorieren.

Wenn man beispielsweise

df.groupby("customer_id")["payment"].sum()

oder

df.groupby("customer_id")["payment"].count()

überprüft, findet man wahrscheinlich sofort, was es etwas Komisches gibt.

Aber stellen Sie sich vor, dass Sie den Mittelwert der gesamte Bezahlung berechnen müssen. Dann die folgende Zeile fällt Ihnen sofort ein und Sie führt sie sofort aus.

average_total_payment = df.groupby("customer_id")["payment"].sum().mean()

Oder in SQL schreiben Sie folgend

WITH total_payment_per_customer AS (
  SELECT customer_id, 
         sum(payment) AS total_payment
    FROM payments
   GROUP BY customer_id
)
SELECT mean(total_payment) AS average_total_payment
  FROM total_payment_per_customer
;

Dieser Wert ist höchstwahrscheinlich größer als was Sie berichten mussten. Aber Sie finden nicht, dass die Daten etwas haben, was Sie entfernen sollten, weil Sie sofort den Wert berechnet haben, ohne die Daten zu überprüfen.

Viele Data Scientists schreiben Codes, aber sie sind kaum kompliziert. (Ja, natürlich weiß ich, dass es viele SQL-Skripten mit mehreren hunderten Zeilen gibt.) Keine Klasse oder keine Funktion wird definiert. Es ist oft genug, die bekannten Bibliotheken zu importieren und die API der Bibliotheken zu verwenden.

Die Codes, die ich oben geschrieben habe, sind sinnvoll, einfach zu verstehen und gar nicht kompliziert. Aber meine Berechnung basiert darauf, dass die Daten etwas sind, was wir erwarten. Wenn die Daten-Qualität schlecht ist, oder wenn man keine richtige ausführliche Information über die Daten hat, kann man das richtige Ergebnis nicht bekommen.

Glückspilz! Die Dokumentation liegt vor!

Es ist nicht ungewöhnlich, dass es keine Dokumente für die Daten gibt. Kein Wunder. Angenommen, dass es mehrere Hunderte Tabelle in einer Datenbank gibt. Dann es ist gar nicht realistisch, dass jede Tabelle eine ausführliche Dokumentation hat. Es kann auch sein, dass die Datenbank, die Sie benutzen wollten, noch in Arbeit ist. Dann natürlich steht keine Dokumentation zur Verfügung und das Schema kann sich irgendwann sogar verändern.

Zum Glück haben Sie die Dokumentation bekommen. Man muss sie auf jeden Fall durchlesen. Sie muss hilfreich sein. Aber Realität ist folgend:

Die Dokumentation ist veraltet

Typisch ist, dass eine Spalte in der Tabelle hinzugefügt wurde, aber in der Dokumentation gibt es keine Erklärung der Spalte. Das ist vielleicht nicht harmlos.

Aber die Änderung kann eine große Bedeutung haben. Beispielsweise hatten Sie früher nur die Daten vom Typ A, aber ab einem Zeitpunkt finden Sie die Daten vom neuen Typ B. Um die Typen zu unterscheiden, ist der Spalte zur Verfügung gestellt. Obwohl die Änderung wichtig ist, gibt es keine Information darüber in der Dokumentation.

Keine genaue Erklärung der Werte

Es ist nicht genug, die Bedeutung einer Spalte zu erklären.

Payment: die Bezahlung für die Transaktion in EUR.

Ist das klar? Vielleicht Ja, aber was wäre, wenn eine negative Zahl für einen Umtausch eingetragen wäre? Was bedeutet NULL in diesem Feld? Stornierung? Was bedeutet der Wert 0.00 in der Spalte?

Zu viele Begriffe

Data Scientists müssen auf jeden Fall die relevanten Begriffe verstehen. Aber es ist schwer, sie zu verstehen, wenn es viel zu viele Begriffe gibt. Zum Beispiel: predictive maintenance. Es geht um die verschiedenen technischen Komponenten. Es ist nicht realistisch, die Funktionen aller Komponenten ausführlich zu verstehen.

Was für Maßnahmen kann man treffen?

Dann was wir machen sollten, um einen Fehler zu vermeiden?

Wenn es um die Daten-Qualität geht, spielt ein Unit-Test keine Rolle. Die nötige Datenverarbeitung ist vielleicht einfach. Man definiert keine Funktion, keine Klasse. Dann was kann man machen, um einen Fehler zu vermeiden?

Was wir machen müssen, ist klar: Die Daten verstehen. Dafür gibt es meiner Meinung nach drei Vorgänge:

  1. Den Überblick über die Daten sammeln.
  2. Ein Paar Beispiele überprüfen.
  3. Peer-Review.

1. Den Überblick über die Daten sammeln

Mit anderen Wörtern sollte man die Datenqualität überprüfen. Für jede Spalte sollte man die folgenden Punkte überprüfen.

  • Datentyp
    • Kategorisch oder numerisch?
  • Der Anteil der fehlenden Werte (missing rate)
    • df.isnull().sum() und/oder df.isnull().mean().
    • Was für eine Bedeutung hat NULL?
    • Der fehlende Wert ist möglicherweise nicht NULL. Zum Beispiel sind null (String), ?, unknown, usw. (Solches besondere Zeichen hat möglicherweise eine einige Bedeutung und ist nicht Alternative zu NULL.)
  • Minimum, Maximum, Medien.
    • Liegen Sie in Ihrem erwarteten Bereich?
    • Wenn nicht, was bedeuten die "komischen" Werte?
  • Histogramm
    • Ist die Verteilung komisch?
    • Gibt es Ausreißer?

2. Ein Paar Beispiele überprüfen

Das ist die Grundlage vom Testen. Wähle einen Kunden aus, dann sehe die Daten des Kunden und berechne die KPI für den Kunden. Darüber hinaus sollte man mehrere Beispiele überprüfen, aber wichtig ist, die Daten zu verstehen, indem man die Beispiel genau sieht.

Zum Beispiel gibt es hier die Daten über 5 Kunden. Sie haben die Summe der Bezahlungen: 59.56, 40.42, 144.26, 14.86, 122.85. Der Mittelwert ist 75.542. Was wäre, wenn der Mittelwert aller Kunden 102.76 wäre. Der Wert liegt eigentlich im Konfidenzintervall, wenn wir die Normalverteilung annehmen, aber der Mittelwert aller Kunden ist ziemlich groß, oder?

Wenn es ein Beispiel mit dem komischen Wert gibt, sollte man es nehmen. Zum Beispiel eine Zeile mit einer negativen Bezahlung. Entspricht die Zeile einem Umtausch? Wie sollten Sie damit umgehen? (Das ist nicht trivial.)

3. Peer-Review

Sie sollten unbedingt einen Kollegen darum bitten, Ihr Skript zu überprüfen. Wichtig ist, die Logik überprüfen zu lassen. (Natürlich muss das Ergebnis auch überprüft werden.) Das ist nicht einfach, das wird oft einfach ignoriert.

Ein Beispiel von einem falschen Peer-Review:

A: Ich habe die Summe der Bezahlung jedes Kunden berechnet. Diese Tabelle ist das Ergebnis meiner SQL-Anfrage.

(A zeigt die Tabelle. B sieht die Anfrage nicht. Die Werte sehen nicht komisch aus.)

B: Passt. Danke!

Was falsch ist, ist selbstverständlich. Aber das kann wirklich einfach passieren. Das liegt daran, dass es aufwändig ist, das Code zu lesen und zu verstehen.

Ein automatischer Test ist (in einigen Fällen) möglich

Diese Maßnahmen sind manuell. Das liegt daran, dass das Ziel ist, die Daten zu verstehen. Diesen Teil kann man nicht automatisieren.

Aber ich würde sagen, dass es einige Fälle (in Data Science) gibt, wo man seine Codes automatisch testen kann. Darüber schreibe ich irgendwann.

Share this page on        
Categories: #data-mining