5. Deskriptive Datenanalyse#

Wofür nutzen wir Python?#

Im Kapitel “DaLi Thema 1: Grundlagen der Datenauswertung” wurden die wichtigsten Visualisierungsformen und Kenngrößen vorgestellt, die für eine Datenauswertung notwendig sind.

Sobald wir es mit der Verarbeitung realer (oft sehr umfangreicher) Datensätze zu tun haben, kann Software helfen, bestimmte Kenngrößen und Visualisierungsformen zu erstellen. Dieses DaLi-Thema beschäftigt sich daher mit der Analyse und Visualisierung solcher Datensätze mittels Software (Python oder R).

Um einen ersten Eindruck davon zu bekommen, wie wir Python in diesem Zusammenhang nutzen können, verwenden wir den aus “DaLi Thema 1: Grundlagen der Datenanalyse” bekannten Datensatz “Tips”.

Kurzbeschreibung des Datensatzes “Tips”#

Ein Kellner hat über einen Zeitraum von mehreren Monaten Informationen zu jedem Trinkgeld, das er in einem Restaurant erhalten hat, aufgezeichnet. Dabei wurden mehrere Variablen erfasst:

  • Rechnungssumme in Dollar (total_bill)

  • Trinkgeld in Dollar (tip)

  • Geschlecht der zahlenden Person (sex)

  • Raucher unter den Gästen (smoker)

  • Wochentag (day)

  • Tageszeit (time)

  • Gruppengröße (size)

Im Folgenden erklären wir kurz, was der jeweilige Code bewirkt, bevor der Code und die entsprechenden Ausgaben dargestellt werden.

Erste Informationen über einen Datensatz erhalten#

Die folgenden zwei Funktionen sind in Python über die pandas-Bibliothek integriert. Sie eignen sich gut, um einen ersten Überblick über einen Datensatz und die Eigenschaften seiner Merkmale zu erhalten:

  • info() liefert Details zur Datenstruktur, wie Spaltennamen, Datentypen und Anzahl der nicht-fehlenden Werte.

  • describe(include='all') gibt zusammenfassende Statistiken für jede Spalte aus — inklusive Minimum, Maximum, Mittelwert, Quartilen usw. Bei include='all' werden auch kategoriale Variablen eingeschlossen.

Diese Funktionen sind ideal, um schnell potenzielle Probleme (z. B. fehlende Werte oder falsche Datentypen) zu erkennen und ein grundlegendes Gefühl für die Verteilung der Daten zu bekommen.

import pandas as pd
tips = pd.read_csv("tips.csv")

# Informationen über Struktur und Merkmale
tips.info()
tips.describe(include='all')
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 244 entries, 0 to 243
Data columns (total 7 columns):
 #   Column      Non-Null Count  Dtype  
---  ------      --------------  -----  
 0   total_bill  244 non-null    float64
 1   tip         244 non-null    float64
 2   sex         244 non-null    object 
 3   smoker      244 non-null    object 
 4   day         244 non-null    object 
 5   time        244 non-null    object 
 6   size        244 non-null    int64  
dtypes: float64(2), int64(1), object(4)
memory usage: 13.5+ KB
total_bill tip sex smoker day time size
count 244.000000 244.000000 244 244 244 244 244.000000
unique NaN NaN 2 2 4 2 NaN
top NaN NaN Male No Sat Dinner NaN
freq NaN NaN 157 151 87 176 NaN
mean 19.785943 2.998279 NaN NaN NaN NaN 2.569672
std 8.902412 1.383638 NaN NaN NaN NaN 0.951100
min 3.070000 1.000000 NaN NaN NaN NaN 1.000000
25% 13.347500 2.000000 NaN NaN NaN NaN 2.000000
50% 17.795000 2.900000 NaN NaN NaN NaN 2.000000
75% 24.127500 3.562500 NaN NaN NaN NaN 3.000000
max 50.810000 10.000000 NaN NaN NaN NaN 6.000000

Ähnliche Informationen erhält man auch mit der Funktion skim() aus der Bibliothek skimpy:

from skimpy import skim
import pandas as pd
tips = pd.read_csv("tips.csv")

# Detaillierte Übersicht über den Datensatz
skim(tips)
╭──────────────────────────────────────────────── skimpy summary ─────────────────────────────────────────────────╮
│          Data Summary                Data Types                                                                 │
│ ┏━━━━━━━━━━━━━━━━━━━┳━━━━━━━━┓ ┏━━━━━━━━━━━━━┳━━━━━━━┓                                                          │
│ ┃ Dataframe          Values ┃ ┃ Column Type  Count ┃                                                          │
│ ┡━━━━━━━━━━━━━━━━━━━╇━━━━━━━━┩ ┡━━━━━━━━━━━━━╇━━━━━━━┩                                                          │
│ │ Number of rows    │ 244    │ │ string      │ 4     │                                                          │
│ │ Number of columns │ 7      │ │ float64     │ 2     │                                                          │
│ └───────────────────┴────────┘ │ int64       │ 1     │                                                          │
│                                └─────────────┴───────┘                                                          │
│                                                     number                                                      │
│ ┏━━━━━━━━━━━━━━━━┳━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┓  │
│ ┃ column          NA   NA %    mean     sd        p0      p25      p50     p75     p100    hist    ┃  │
│ ┡━━━━━━━━━━━━━━━━╇━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━━━╇━━━━━━━━━┩  │
│ │ total_bill      0     0  19.79   8.902  3.07  13.35  17.8 24.13 50.81▂█▄▂▁▁  │  │
│ │ tip             0     0  2.998   1.384     1      2   2.9 3.562    10 ██▃▁   │  │
│ │ size            0     0   2.57  0.9511     1      2     2     3     6  █▂▂   │  │
│ └────────────────┴─────┴────────┴─────────┴──────────┴────────┴─────────┴────────┴────────┴────────┴─────────┘  │
│                                                     string                                                      │
│ ┏━━━━━━━━━┳━━━━━┳━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┳━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┓  │
│ ┃ column   NA   NA %   shortest   longest  min     max    chars per row  words per row  total words ┃  │
│ ┡━━━━━━━━━╇━━━━━╇━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━╇━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━┩  │
│ │ sex      0    0Male     Female FemaleMale          4.71            1        244 │  │
│ │ smoker   0    0No       Yes    No    Yes           2.38            1        244 │  │
│ │ day      0    0Sun      Thur   Fri   Thur          3.25            1        244 │  │
│ │ time     0    0Lunch    Dinner DinnerLunch         5.72            1        244 │  │
│ └─────────┴─────┴───────┴───────────┴─────────┴────────┴───────┴───────────────┴───────────────┴─────────────┘  │
╰────────────────────────────────────────────────────── End ──────────────────────────────────────────────────────╯

Übung 1#

🧠 Wie hoch war das höchste, vom Kellner dokumentierte Trinkgeld (in Dollar)?

Balkendiagramm erstellen#

Unten siehst du eine Variante zur Erstellung eines Balkendiagramms. Die Funktion bar() aus der matplotlib-Bibliothek wird verwendet, um die Häufigkeiten der Kategorien „Female“ und „Male“ in der Spalte sex des Datensatzes darzustellen.

Zunächst wird die Pandas-Bibliothek verwendet, um den Datensatz tips.csv zu laden. Anschließend wird die Funktion value_counts() auf die Spalte sex angewendet, um zu zählen, wie oft jedes Geschlecht vorkommt. Diese Zählwerte werden in der Variablen sex_counts gespeichert; die zugehörigen Kategorienamen und Werte werden extrahiert.

Schließlich wird ein einfaches vertikales Balkendiagramm mit plt.bar() erstellt. Das Diagramm enthält einen Titel sowie Achsenbeschriftungen, um die dargestellten Informationen besser interpretieren zu können.

import pandas as pd
import matplotlib.pyplot as plt
tips = pd.read_csv("tips.csv")

sex_counts = tips["sex"].value_counts()
labels = sex_counts.index
values = sex_counts.values

plt.bar(labels, values)

plt.title("Anzahl der Gäste nach Geschlecht")
plt.xlabel("Geschlecht")
plt.ylabel("Anzahl")
plt.show()
_images/e1f5c1fb259925cc8a03bc36a349d7258c5a65a2a014aaf0ff918fae0943fc47.png

Gruppiertes Balkendiagramm#

Der folgende Code erstellt ein gruppiertes Balkendiagramm, das die Anzahl der Gäste nach Geschlecht zeigt, getrennt nach Tageszeit (Lunch vs. Dinner). Dies wird mit der catplot()-Funktion von seaborn umgesetzt, die es ermöglicht, mehrere Diagramme basierend auf den Werten einer kategorialen Variable zu erstellen.

Das Argument col="time" gibt an, dass für jeden Wert in der Spalte time ein eigenes Diagramm erstellt werden soll. Das Ergebnis sind zwei nebeneinander angeordnete Balkendiagramme, die die Geschlechterverteilung für Mittag- und Abendessen separat darstellen.

Diese Visualisierung eignet sich gut, um zu vergleichen, wie sich die Geschlechterverteilung der Gäste zwischen den beiden Tageszeiten unterscheidet.

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
tips = pd.read_csv("tips.csv")
tips = sns.load_dataset("tips")

g = sns.catplot(
    data=tips,
    x="sex",
    kind="count",
    col="time",
)

g.set_titles("Zeit: {col_name}")
g.set_axis_labels("Geschlecht", "Anzahl")
plt.show()
_images/91688f0d90b8d466d079391923b549ab62b50a1cca528014aa59c05359d64dc6.png

Übung 2#

🧠 Beim Mittagessen war die Anzahl weiblicher und männlicher Gäste, die bezahlt haben, ungefähr gleich, aber beim Abendessen haben männliche Gäste deutlich häufiger bezahlt (mehr als doppelt so oft). Wahr oder falsch?

Histogramm erstellen#

Der folgende Code erstellt ein Histogramm, das die Häufigkeitsverteilung der Rechnungsbeträge im Datensatz visualisiert. Die Funktion plt.hist() aus der matplotlib-Bibliothek wird verwendet, um darzustellen, wie oft Rechnungsbeträge in bestimmten Wertebereichen auftreten.

edgecolor="black" fügt jedem Balken klare Umrandungen hinzu.

Die x-Achse zeigt die Rechnungsbeträge in Dollar, während die y-Achse die Anzahl der Rechnungen in jedem Intervall angibt.

import pandas as pd
import matplotlib.pyplot as plt
tips = pd.read_csv("tips.csv")

plt.hist(
    tips["total_bill"],
    edgecolor="black"
)
plt.title("Häufigkeitsverteilung der Rechnungsbeträge")
plt.xlabel("Rechnungsbetrag in $")
plt.ylabel("Anzahl")
plt.show()
_images/566984793597a67a201f141924f4b2ed4d2c5a0c25fe4d8dfe4bfb97d6a68f4d.png

Streudiagramm erstellen#

Der folgende Code erstellt ein Streudiagramm, um die Beziehung zwischen dem Gesamtbetrag der Rechnung und der Trinkgeldhöhe zu visualisieren. Die Funktion plt.scatter() aus der matplotlib-Bibliothek wird verwendet, um jede Beobachtung als Punkt zu zeichnen, wobei:

  • die x-Achse den Gesamtbetrag der Rechnung in Dollar darstellt

  • die y-Achse den entsprechenden Trinkgeldbetrag zeigt Jeder Punkt im Diagramm entspricht einer Zeile im Datensatz. Diese Art der Visualisierung ist nützlich, um Muster oder Trends zu erkennen – beispielsweise, ob höhere Rechnungen mit höheren Trinkgeldern verbunden sind.

import pandas as pd
import matplotlib.pyplot as plt
tips = pd.read_csv("tips.csv")

plt.scatter(
    tips["total_bill"],
    tips["tip"]
)

plt.title("Trinkgeld vs. Rechnungsbetrag")
plt.xlabel("Rechnungsbetrag ($)")
plt.ylabel("Trinkgeld ($)")
plt.show()
_images/87679f3a1b2d7bd77d7ff3335c2ec383aeb2166425f4bba03dde312c2afd1750.png

Übung 3#

🧠 Wie hoch war das Trinkgeld bei einer Rechnung von ungefähr 48 $? (Eine ungefähre Schätzung reicht aus)

Streudiagramm mit Regressionslinie erstellen#

Der folgende Code erstellt ein Streudiagramm, das die Beziehung zwischen der Gesamtrechnung und dem Trinkgeldbetrag zeigt. Zusätzlich zu den einzelnen Datenpunkten wird eine Regressionslinie hinzugefügt, die die lineare Beziehung zwischen den beiden Variablen modelliert.

Dies wird mit der Funktion lmplot() aus der seaborn-Bibliothek gemacht.

  • Der Parameter x="total_bill" definiert die Variable auf der x-Achse,

  • y="tip" definiert die Variable auf der y-Achse,

  • und seaborn passt automatisch eine lineare Regressionslinie an und zeichnet sie durch die Daten.

import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
tips = pd.read_csv("tips.csv")

sns.lmplot(
    data=tips,
    x="total_bill",
    y="tip"
)

plt.title("Trinkgeld vs. Gesamtrechnung mit Regressionslinie")
plt.xlabel("Gesamtrechnung ($)")
plt.ylabel("Trinkgeld ($)")
plt.show()
_images/6e71821da50e1d0101813632b76fe578663679bcb39f457ec1cb856230b07ce6.png

Mosaikdiagramm erstellen#

Der folgende Code erstellt ein Mosaikdiagramm, um die Beziehung zwischen den kategorialen Variablen Geschlecht (sex) und Tageszeit (time) im Datensatz zu visualisieren.

Ein Mosaikdiagramm zeigt die relativen Häufigkeiten von Kombinationen kategorialer Werte durch Rechtecke, deren Flächen proportional zur Anzahl der Beobachtungen sind. In diesem Beispiel:

Die x-Achse ist aufgeteilt nach den Werten von sex (weiblich / männlich), und jeder Abschnitt ist weiter unterteilt nach time (Mittagessen / Abendessen). So kann man leicht erkennen, ob zum Beispiel ein höherer Anteil von Männern oder Frauen zu einer bestimmten Tageszeit das Restaurant besucht hat.

Das Diagramm wird mit der Funktion mosaic() aus der Bibliothek statsmodels erstellt, die speziell für diese Art der kategorialen Visualisierung gedacht ist.

import pandas as pd
from statsmodels.graphics.mosaicplot import mosaic
import matplotlib.pyplot as plt
tips = pd.read_csv("tips.csv")

mosaic(tips, ['sex', 'time'])

plt.title("Mosaikdiagramm: Geschlecht vs. Tageszeit")
plt.xlabel("Geschlecht")
plt.ylabel("Tageszeit")
plt.show()
_images/615450983d6a11cb7a36f51edfa918b0097a34efddcc87314a22b660bbf94055.png

Boxplot erstellen#

Dieser Code verwendet die Funktion boxplot() aus der seaborn-Bibliothek (importiert als sns), um ein Box-and-Whisker-Diagramm zu erstellen, das die Verteilung der Gesamtrechnungen für die beiden Zeitkategorien Mittagessen und Abendessen vergleicht.

Jede Box repräsentiert die Streuung der Daten für eine Gruppe und enthält:

  • den Median (horizontale Linie innerhalb der Box),

  • die Interquartilsdifferenz (die Box selbst),

  • und mögliche Ausreißer (einzelne Punkte).

Die x-Achse zeigt die beiden Zeitkategorien (time), während die y-Achse die entsprechenden Rechnungsbeträge (total_bill) anzeigt. Diese Visualisierung erleichtert den Vergleich, ob die Rechnungen abends tendenziell höher sind als mittags.

import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
tips = pd.read_csv("tips.csv")

sns.boxplot(
    data=tips,
    x="time",
    y="total_bill",
)

plt.title("Boxplot der Gesamtrechnung nach Tageszeit")
plt.xlabel("Tageszeit")
plt.ylabel("Gesamtrechnung ($)")
plt.show()
_images/0cb1c6bd3fc1abf125ba88dcda3f57ed1d6640544449c9ae8d4204ce1d8c4f5d.png

Verteilungsfunktion bestimmen#

Dieser Code verwendet die Klasse ECDF() aus dem Modul statsmodels.distributions.empirical_distribution, um die empirische Verteilungsfunktion der Werte von total_bill zu berechnen.

Eine ECDF zeigt für jeden Wert auf der x-Achse den Anteil der Beobachtungen, die kleiner oder gleich diesem Wert sind. Das Ergebnis ist eine treppenförmige Kurve, die von 0 bis 1 ansteigt. Diese Art von Diagramm ist nützlich, um zu verstehen, wie Werte im Datensatz verteilt sind – zum Beispiel, um abzuschätzen, welcher Anteil der Rechnungen unter einem bestimmten Betrag liegt.

Die Funktion plt.step() wird verwendet, um die ECDF als Stufenfunktion zu zeichnen, und Rasterlinien werden hinzugefügt, um die Lesbarkeit zu verbessern.

import pandas as pd
import matplotlib.pyplot as plt
from statsmodels.distributions.empirical_distribution import ECDF
tips = pd.read_csv("tips.csv")

ecdf = ECDF(tips["total_bill"])

plt.step(ecdf.x, ecdf.y, where="post")
plt.title("Empirische Verteilungsfunktion der Gesamtrechnung")
plt.xlabel("Gesamtrechnung ($)")
plt.ylabel("Kumulative Wahrscheinlichkeit")
plt.grid(True)
plt.show()
_images/fb6c3d6916e070b9336d9fd751b41fd5d51065a9bfaa079be0afa3f7adf6e85d.png

Übung 4#

🧠 Wieviel Prozent der Rechnungen lagen unter $20?