Kurs
Hinweis der Redaktion: Jean-Nicholas Hould ist Data Scientist bei Intel Security in Montreal und zeigt auf seinem Blog, wie du mit Data Science durchstartest.
Explorative Datenanalyse (EDA) ist ein statistischer Ansatz, um einen Datensatz zu erkunden und zusammenzufassen. In diesem Schritt des Data-Science-Prozesses willst du die Struktur deines Datensatzes, die Variablen und ihre Beziehungen verstehen.
In diesem Beitrag konzentrierst du dich auf einen Aspekt der explorativen Datenanalyse: Datenprofiling.
Beim Datenprofiling geht es darum, deinen Datensatz anhand beschreibender Statistiken zusammenzufassen. Du nutzt eine Reihe von Kennzahlen, um dein Dataset besser zu verstehen. Datentypen, fehlende Werte, Mittelwert, Median und Standardabweichung sind nur einige der Elemente, die du beim Profiling erfassen solltest. Ziel des Datenprofilings ist ein belastbares Verständnis deiner Daten, damit du anschließend gezielt Abfragen stellen und Visualisierungen in verschiedenen Varianten erstellen kannst.
Woher stammen deine Daten?
Bevor du in eine EDA einsteigst, solltest du so viel wie möglich über die Herkunft der analysierten Daten wissen. Verstehe, wie die Daten erhoben und wie sie aufbereitet wurden. Gab es frühere Transformationen, die deine Analyse beeinflussen könnten?
Auf diese Fragen solltest du für deinen Datensatz eine Antwort haben:
- Wie wurden die Daten erhoben?
- Handelt es sich um eine Stichprobe?
- Wurde korrekt gesampelt?
- Wurde der Datensatz in irgendeiner Form transformiert?
- Sind bekannte Probleme mit dem Datensatz dokumentiert?
Wenn du die Herkunft der Daten nicht verstehst, fällt es schwer, belastbare Schlüsse zu ziehen. Außerdem riskierst du gravierende Analysefehler.
Zusätzlich solltest du sicherstellen, dass der Datensatz in einem standardisierten Format strukturiert ist. Empfohlen wird die Third Normal Form, auch tidy data genannt. Ein „tidy“ Datensatz hat folgende Eigenschaften:
- Jede Variable bildet eine Spalte und enthält Werte
- Jede Beobachtung bildet eine Zeile
- Jeder Typ von Beobachtungseinheit bildet eine Tabelle
Dieses Standardformat beschleunigt deine Analyse, da es mit vielen Tools und Bibliotheken kompatibel ist.
Datenprofiling
In diesem Beitrag nutzt du einen Datensatz zu Craft-Beers von der Website CraftCans. Der Datensatz enthält ausschließlich Daten zu dosenbier aus Brauereien in den USA. Aus der Website geht nicht klar hervor, ob wirklich jedes Dosenbier in den USA erfasst wurde. Zur Sicherheit betrachten wir den Datensatz als Stichprobe, die Verzerrungen enthalten kann.
So sind die Datensätze aufgebaut, die du verwendest:
Beers:
ID: Eindeutige Kennung des Biers.Name: Name des Biers.ABV: Alkoholgehalt in Volumenprozent.IBU: International Bitterness Units des Biers.Style: Bierstil.Ounces: Füllmenge in Unzen.
Breweries:
ID: Eindeutige Kennung der Brauerei.Name: Name der Brauerei.City: Stadt, in der die Brauerei liegt.State: Bundesstaat, in dem die Brauerei liegt.
Datentypen
Der erste Schritt ist, die Zusammensetzung deines Datensatzes zu verstehen.
Mit welchen Variablen hast du es zu tun?
In der Regel lassen sich Daten in folgende Kategorien einordnen:
- Numerisch
- Kategorisch
- Text
- Datum
Zunächst importierst du die Datensätze aus diesem Repository mit der pandas-Funktion from_csv. Außerdem verbindest du die Datensätze beers und breweries, um die spätere Analyse zu erleichtern.
import pandas as pd
beers = pd.DataFrame.from_csv("https://raw.githubusercontent.com/nickhould/craft-beers-dataset/master/data/craftcans_beers.csv")
breweries = pd.DataFrame.from_csv("https://raw.githubusercontent.com/nickhould/craft-beers-dataset/master/data/craftcans_breweries.csv")
beers_and_breweries = pd.merge(beers,
breweries,
how='inner',
left_on="brewery_id",
right_on="id",
sort=True,
suffixes=('_beer', '_brewery'))
Mit pandas kannst du die Funktion dtypes ausführen, um jede Spalte und ihren Datentyp aufzulisten.
beers.dtypes
Das liefert dir folgendes Ergebnis:
abv float64
ibu float64
id int64
name object
style object
brewery_id int64
ounces float64
dtype: object
Wie du siehst, gruppiert diese Funktion die verschiedenen Datentypen nicht übersichtlich. Die numerischen Typen (float64 und int64) werden nicht zu einer Kategorie zusammengefasst, wie wir es uns wünschen würden. Außerdem sind einige Spalten als Objekte gelistet – das ist wenig aussagekräftig.
Um das zu umgehen, kannst du eine eigene Funktion schreiben, die die Kategorie jeder Spalte in einem DataFrame bestimmt.
def get_var_category(series):
unique_count = series.nunique(dropna=False)
total_count = len(series)
if pd.api.types.is_numeric_dtype(series):
return 'Numerical'
elif pd.api.types.is_datetime64_dtype(series):
return 'Date'
elif unique_count==total_count:
return 'Text (Unique)'
else:
return 'Categorical'
def print_categories(df):
for column_name in df.columns:
print(column_name, ": ", get_var_category(df[column_name]))
Beers-Variablen
print_categories(beers)
Der Befehl oben liefert dir folgendes Ergebnis:
abv : Numerical
ibu : Numerical
id : Numerical
name : Categorical
style : Categorical
brewery_id : Numerical
ounces : Numerical
Breweries-Variablen
print_categories(breweries)
Das ergibt folgendes:
name : Categorical
city : Categorical
state : Categorical
id : Numerical
Mit diesen Informationen verstehst du den Datensatz bereits besser. Du weißt, dass du nur mit kategorischen und numerischen Daten arbeitest. Numerische Variablen erlauben viele Kennzahlen wie Mittelwert, Standardabweichung usw. Kategorische Variablen eignen sich in der Regel gut zur Segmentierung und Gruppierung der Daten. Du könntest zum Beispiel untersuchen, wie sich die IBU je nach Bierstil unterscheiden.
Beschreibende Statistiken
In diesem Abschnitt gehst du verschiedene beschreibende Statistiken durch, um die Daten besser zu verstehen. Jede Kennzahl für sich ist nur bedingt hilfreich – die Kombination liefert den größten Erkenntnisgewinn.
Du konzentrierst dich auf die Variable IBU, weil sie numerisch ist. Dieser Variablentyp bietet mehr Auswertungsmöglichkeiten als kategorische Variablen. Auch auf Kategorisches kannst du Kennzahlen anwenden, bist dort aber deutlich eingeschränkter.
Länge
Die Funktion len zählt die Anzahl der Beobachtungen in einer Series. Sie zählt alle Beobachtungen, unabhängig von fehlenden bzw. Null-Werten.
length = len(beers["ibu"])
print(length)
Diese Series umfasst insgesamt 2410 Beobachtungen.
Anzahl
Die Funktion count gibt die Anzahl der nicht-NA/nicht-null Beobachtungen in einer Series zurück.
count = beers["ibu"].count()
print(count)
Wie du siehst, gibt es 1405 nicht-leere Beobachtungen in der Series.
Fehlende Werte
Mit Length und Count kannst du nun die Anzahl fehlender Werte berechnen. Sie ist die Differenz aus Length und Count.
number_of_missing_values = length - count
pct_of_missing_values = float(number_of_missing_values / length)
pct_of_missing_values = "{0:.1f}%".format(pct_of_missing_values*100)
print(pct_of_missing_values)
Um fehlende Werte als Prozentsatz auszugeben, teilst du die Anzahl fehlender Werte durch die Gesamtzahl der Beobachtungen, also die Länge. float stellt sicher, dass Dezimalstellen in der Division berücksichtigt werden. Mit format formatierst du den Wert ansprechend als Prozentzahl.
In diesem Fall fehlen fast 42% der IBU-Werte. Das ist wichtig, weil es deine Analyse beeinflusst. Die meisten beschreibenden Statistiken ignorieren fehlende Werte – das führt zwangsläufig zu Verzerrungen.
Minimum/Maximum
Minimal- und Maximalwert eines Datensatzes erhältst du mit den Funktionen min und max auf einer Series.
print("Minimum value: ", beers["ibu"].min())
print("Maximum value: ", beers["ibu"].max())
Min/Max helfen, die Spannweite einer Variable zu verstehen. Hier reicht IBU von 4 bis 138.
Modus
Der Modus ist der am häufigsten auftretende Wert in einem Datensatz. Er lässt sich mit der Funktion mode auf einer Series bestimmen.
print(beers["ibu"].mode())
In einer Normalverteilung entspricht der Modus dem mean und dem median.
Hier ist der Modus der Variable IBU gleich 20. Das ist der häufigste IBU-Wert im Datensatz.
Mittelwert
Der Mittelwert ist ein Maß der zentralen Tendenz. Er ist die Summe aller Werte geteilt durch die Anzahl nicht fehlender Beobachtungen.
Er lässt sich mit der Funktion mean auf einer Series berechnen.
mean = beers["ibu"].mean()
Der Mittelwert ist anfällig für Ausreißer. Einige extreme Werte können ihn stark nach oben oder unten ziehen.
Median
Auch der Median ist ein Maß der zentralen Tendenz. Er ist der Wert genau in der Mitte einer geordneten Liste numerischer Werte.
median = beers["ibu"].median()
Bei schiefen Verteilungen ist der Median oft das bessere Lagemaß als der Mittelwert.
Bei der Verteilung von IBU liegen Mittelwert und Median in derselben Größenordnung.
Standardabweichung
Die Standardabweichung ist ein Streuungsmaß. Eine hohe Standardabweichung zeigt, dass die Daten über einen weiten Wertebereich verteilt sind. Sie hat dieselbe Einheit wie die Werte selbst.
standarddev = beers["ibu"].std()
Hier beträgt die Standardabweichung rund 26 – genau 25.954065911259324. Wäre die Verteilung von IBU normalverteilt, lägen etwa 68% der Beobachtungen innerhalb einer Standardabweichung um den Mittelwert.
Quantile
Quantile sind Schnittpunkte, die eine Verteilung in gleich große Teile teilen. Viele Quantile haben eigene Namen. Teilt man eine Verteilung in vier gleich große Gruppen, spricht man von quartiles. Du kannst Quantile mit der Funktion quantile auf einer Series berechnen. Übergebe dazu ein Array der gewünschten Quantile. Im Beispiel unten teilen wir die Verteilung in vier gleich große Gruppen.
quantile = beers["ibu"].quantile([.25, .5, .75])
| 0.25 | 21.0 |
| 0.50 | 35.0 |
| 0.75 | 64.0 |
| Name: ibu, | dtype: float64 |
Wie du siehst, entspricht das 50%-Quantil dem Median. Es ist der Wert, der den Datensatz halbiert. Außerdem liegen 75% der Beobachtungen bei höchstens 64 IBU. 50% der Verteilung liegen zwischen 21 und 64 IBU. Beachte: Fehlende Werte gehen in diese Kennzahlen nicht ein.
Verteilungsdiagramme
Visualisierungen sind in der explorativen Datenanalyse extrem hilfreich. In diesem Beitrag vertiefen wir das Thema Visualisierung nicht, aber ohne die Bedeutung eines Häufigkeitsdiagramms geht es beim Datenprofiling nicht. Es ist eine der einfachsten und gleichzeitig aussagekräftigsten Visualisierungen und zeigt, wie oft jeder Wert im Datensatz vorkommt.
Für diese Visualisierung nutzen wir die Bibliothek seaborn`` mit der Funktion displot. Diese Funktion erwartet eine Series ohne fehlende Werte.
import seaborn as sns
sns.set(color_codes=True)
sns.set_palette(sns.color_palette("muted"))
sns.distplot(beers["ibu"].dropna());

In diesem Verteilungsdiagramm erkennst du einige der zuvor berechneten Werte deutlich. Der Minimalwert liegt nahe bei 0 IBU, der Maximalwert nahe bei 140 IBU. Der häufigste Wert liegt klar bei rund 20 IBU. Zusätzlich sehen wir einen Peak bei etwa 60 IBU.
Warum gibt es zwei Peaks in dieser Verteilung?
Wodurch lässt sich das erklären? Das ist ein Aspekt für die zweite Phase der explorativen Datenanalyse.
Korrelationen
Korrelationen helfen, Beziehungen zwischen numerischen Variablen aufzudecken. Es gibt verschiedene Verfahren zur Berechnung der Korrelation. Der Pearson-Korrelationskoeffizient ist weit verbreitet und misst die lineare Abhängigkeit zwischen zwei Variablen. Er liegt zwischen -1 und 1: 1 bedeutet perfekte positive, -1 perfekte negative und 0 keine lineare Korrelation. Wir können diese Berechnung mit der Funktion corr auf einer Series durchführen. Standardmäßig wird die Pearson-Korrelation verwendet, du kannst aber auch andere Methoden angeben.
beers[["abv", "ibu", "ounces"]].corr()
| abv | ibu | ounces | |
| abv | 1 | 0.670621 | 0.172529 |
| ibu | 0.670621 | 1 | 0.054691 |
| ounces | 0.172529 | 0.054691 | 1 |
Wie erwartet, ist die Korrelation von IBU mit sich selbst 1. Interessanter ist, dass die Korrelation zwischen ABV und IBU 0.670621 beträgt. Das ist zwar keine perfekte, aber eine starke positive Korrelation – ein spannender Punkt für eine vertiefte Analyse.
Ein paar Hinweise zu nicht-numerischen Variablen
Die bisher betrachteten Kennzahlen gelten vor allem für numerische Werte. Bei anderen Datentypen wie kategorischen Daten kannst du dennoch interessante Größen berechnen, zum Beispiel die Häufigkeit einzelner Ausprägungen.
DataFrame-Objekte haben eine Funktion describe, die den Datensatz zusammenfasst. Besteht dein DataFrame nur aus kategorischen oder Textwerten, passt sich die Zusammenfassung entsprechend an.
beers[["name", "style"]].describe()
| name | style | |
| count | 2410 | 2405 |
| unique | 2305 | 99 |
| top | Nonstop Hef Hop | American IPA |
| freq | 12 | 424 |
Profiling-Bibliotheken
Wie du gesehen hast, kann das Sammeln beschreibender Statistiken mühsam sein. Zum Glück gibt es Bibliotheken, die diese Arbeit abnehmen und ein klares Datenprofil ausgeben. pandas-profiling ist eine davon. Diese Bibliothek bietet statistisches Profiling deines Datensatzes out of the box. Da unser Datensatz tidy und standardisiert ist, können wir die Bibliothek direkt darauf anwenden.
import pandas_profiling
pandas_profiling.ProfileReport(beers_and_breweries)
Weitere Fragen
Nach dem Profiling deines Datensatzes hast du in der Regel mehr Fragen als zuvor. Perfekt – genau diese Fragen treiben deine explorative Datenanalyse an.
Hier sind einige Fragen, die beim Profiling aufgekommen sind:
- 41,7% der
IBU-Werte fehlen. Warum? Wie beeinflusst das unsere Analyse? - Die
IBU-Verteilung zeigt zwei Peaks. Was erklärt das? - Wodurch erklärt sich die Korrelation zwischen
IBUundABV? Welchen Einfluss hat der Bierstil auf diese Korrelation? - Gibt es Unterschiede bei
IBU,ABVoderStylezwischen Regionen? Und wie sieht es an der Ost- vs. Westküste aus?
Datenprofiling ist kein linearer Prozess. Wenn du deinen Datensatz filterst und segmentierst, wirst du immer wieder darauf zurückkommen und beschreibende Statistiken für Untergruppen ermitteln.
Nächste Schritte
In diesem Beitrag hast du gesehen, wie man einen Datensatz profiliert. Du weißt nun, wie du Variablen bestimmten Datentyp-Gruppen zuordnest. Außerdem hast du verschiedene beschreibende Statistiken berechnet und weißt, wie du sie interpretierst. Und du kennst Bibliotheken, die dir beim Profiling die Rechenarbeit abnehmen. Vor allem aber hast du neue Fragen generiert, die deine explorative Datenanalyse voranbringen.
Sieh dir DataCamps Python Exploratory Data Analysis Tutorial an.