Files

357 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Notizen SL Lektion 2
> Thema: Datenverständnis, Explorative Datenanalyse, Feature Engineering
> Datum: 21.05.2026
> Dozentin: Violeta Vogel
## Überwachtes Lernen vs. Unüberwachtes Lernen
- beim Überwachten Lernen kennt das Modell die richtigen Antworten (Labels) und das Modell lernt diese vorherzusagen
- beim unüberwachten Lernen gibt es keine Labels, das Modell sucht selber Strukturen und Muster in den Daten
## Datenverständnis
1. Sammeln der Daten
- Beschaffen der in den Projektressourcen aufgeführten Daten
- Ersten Datenerfassungsbericht erstellen (Datenkatalog bilden)
2. Daten beschreiben
- Erkennen wie jedes einzelne Feature aussieht
- In welchem Format sind die Daten?
- Wie viele Daten habe ich, wie gross ist der Datensatz?
3. Daten erkunden
- Wie sind die Daten verteilt?
- Gibt es Beziehungen zwischen den Daten?
- Müssen eventuell Bereinigungen oder Aggregationen gemacht werden?
- Datenexplorationsbericht erstellen
4. Datenqualität prüfen
- Sind die Daten vollständig?
- Datenqualitätsbericht erstellen
## EDA: Ziele und Methoden
- Mustererkennung
- Datenbereinigung
- Visualisierung
- Hypothesengenerierung
## EDA: Explorative Datenanalyse
- Anomalien
- Ausreisser, Datenpunkte die stark von der Norm abweichen
- Mögliche Anomalien
- Ausreisser → einzelne Datenpunkte die signifikant von Rest abweichen
- Kontextbezogene Anomalien → Daten die nur in einem bestimmten Kontext ungewöhnlich sind
- Kollektive Anomalien → eine Gruppe von Datenpunkten die gemeinsam abweichen, auch wenn sie einzeln normal wirken
- mögliche Anomalien nach Variablenart
- nicht numerische Daten
- fehlende Werte
- Duplikate
- Kategorien Variablen
- hohe Kardinalität (viele eindeutige Werte)
- nicht balancierte Daten
- numerische Variablen
- schiefe Verteilung
- Ausreisser
- Korrelationen
- diskrete Werte mit geringer Kardinalität (wenig eindeutige Werte)
## Klassierung
> Die Klassierung in der deskriptiven Statistik ordnet viele, unterschiedliche Rohdaten in wenige, überschaubare Klassen (Intervalle) ein.
- Zweck: Reduzierung der Datenkomplexität (Muster und Trends erkennen)
- Vorgehen: Festlegung von Klassengrenzen
- Darstellung: Histogramme
- Nachteile: Durch Gruppierungen geht die exakte Messgenauigkeit verloren, da Einzelwerte nicht mehr erkennbar sind
# Aufbau eines Data Frames
- Objekte / Beobachtungen sind in den Zeilen (rows)
- Merkmale / Attribute sind in den Spalten (columns) angegeben
- Spalten enthalten sprechende Namen, über welche sie angesprochen werden können
- pro Spalte ist ein Datentyp festgelegt, unterschiedliche Spalten können aber unterschiedliche Typen aufweisen
## Skalenniveaus (Workshop 1)
### Nominal
> Das ist eine reine Kategorisierung. Werte sind nur Labels ohne jede Ordnung. Du kannst sagen "Rot ≠ Blau", aber nicht "Rot > Blau".
- Was geht:
- Gleichheit prüfen (= oder ≠)
- Was nicht geht:
- Reihenfolge
- Abstände
- Rechnen
- Beispiele:
- Geschlecht
- Postleitzahl
- Häusertyp (h/u/t im Melbourne-Dataset)
- Programmiersprache
- MAC-Adresse
- Sinnvolle Statistik:
- Modus
- Häufigkeiten
- Chi-Quadrat
- Mittelwert ist Unsinn ("durchschnittliche Postleitzahl"...)
- Stolperfalle:
- Wenn Kategorien als Zahlen codiert sind (z.B. Postcode = 3000), sieht's numerisch aus, ist aber nominal. Pandas wird's als int einlesen die Klassifikation musst du selbst machen.
### Ordinal
> Ordnung ohne definierte Abstände. Du kannst Werte in eine sinnvolle Reihenfolge bringen, aber die Abstände dazwischen sind nicht definiert oder nicht gleich.
- Was geht:
- Gleichheit + Reihenfolge (<, >)
- Was nicht geht:
- Abstände interpretieren
- Rechnen
- Beispiele:
- Schulnoten (ist der Abstand zwischen 4 und 5 derselbe wie zwischen 5 und 6? Nicht wirklich)
- Likert-Skalen ("stimme zu" bis "stimme nicht zu")
- Militärränge
- T-Shirt-Grössen (S/M/L/XL)
- Bildungsabschluss
- Sinnvolle Statistik:
- Median
- Quantile
- Rangkorrelationen (Spearman)
- Stolperfalle:
- Likert-Skalen werden in der Praxis ständig wie metrische Daten behandelt (Mittelwert von "3.7 auf 5er-Skala") formal falsch, aber pragmatisch verbreitet. Eine Dauerdebatte in der Sozialforschung.
### Metrisch
> Echte Zahlen mit definierten Abständen
- Was geht:
- Alles bisherige
- Abstände und Verhältnisse berechnen
- Hier wird's manchmal weiter unterteilt:
- **Intervall**: gleiche Abstände, aber kein echter Nullpunkt. Verhältnisse sind sinnlos. Beispiel: Temperatur in °C 20°C ist nicht "doppelt so warm" wie 10°C, weil der Nullpunkt willkürlich gesetzt ist. Andere Beispiele: Kalenderjahre, IQ.
- **Ratio (Verhältnis)**: gleiche Abstände plus echter Nullpunkt. Verhältnisse sind sinnvoll. Beispiel: Preis (0 € heisst tatsächlich "nichts"), Länge, Gewicht, Anzahl Zimmer.
- Sinnvolle Statistik:
- Mittelwert
- Standardabweichung
- Pearson-Korrelation
- alle parametrischen Tests
### Ergebnisse Workshop 1 Melbourne Housing Dataset
| Nr. | Column | Dtype | nominal | ordinal | metrisch |
|----:|--------|-------|:-------:|:-------:|:--------:|
| 0 | Unnamed: 0 | int64 | | | x |
| 1 | Suburb | object | x | | |
| 2 | Address | object | x | | |
| 3 | Rooms | int64 | | | x |
| 4 | Type | object | x | | |
| 5 | Price | float64 | | | x |
| 6 | Method | object | x | | |
| 7 | SellerG | object | x | | |
| 8 | Date | object | | x | x |
| 9 | Distance | float64 | | | x |
| 10 | Postcode | float64 | x | | |
| 11 | Bedroom2 | float64 | | | x |
| 12 | Bathroom | float64 | | | x |
| 13 | Car | float64 | | | x |
| 14 | Landsize | float64 | | | x |
| 15 | BuildingArea | float64 | | | x |
| 16 | YearBuilt | float64 | | x | x |
| 17 | CouncilArea | object | x | | |
| 18 | Lattitude | float64 | | | x |
| 19 | Longtitude | float64 | | | x |
| 20 | Regionname | object | x | | |
| 21 | Propertycount | float64 | | | x |
> Anmerkung zu `Date` und `YearBuilt`: Beide sind als ordinal **und** metrisch markiert. Das ist je nach Lesart vertretbar — ein Datum/Jahr hat eine klare Reihenfolge (ordinal) und gleiche Abstände ohne echten Nullpunkt (Intervall-metrisch). `Unnamed: 0` ist formal ein Index/Label und für ML eigentlich wertlos, auch wenn es als int64 "metrisch" aussieht (vgl. Nominal-Stolperfalle).
---
# EDA in der Praxis (pandas)
## Umgebung vorbereiten
```python
import pandas as pd
import numpy as np
data = pd.read_csv('bank_data.csv', sep=';')
# Indizes der kategorialen bzw. numerischen Variablen für späteren Gebrauch
cat_vars = data.select_dtypes(include=['object']).columns
num_vars = data.select_dtypes(exclude=['object']).columns
```
- Merksatz Dtypes: `float64`, `int64` → numerisch; `object` → nicht numerisch (kategorial)
## Übersicht über den Data Frame
| Befehl | Zweck |
|--------|-------|
| `type(data)` | Objekttyp (`pandas.core.frame.DataFrame`) |
| `data.shape` | Dimensionen `(rows, columns)` |
| `data.info()` | Typ, Index, Dtypes, Non-Null-Count, Speicherbedarf |
| `data.iloc[0:6, 0:6]` | erste paar rows/columns (Position-basiert) |
| `data.head()` / `data.tail()` | erste / letzte rows |
## Missing Values (NAs)
```python
data.isna().sum().sum() # Anzahl NAs insgesamt
data.shape[0] - data.dropna().shape[0] # Anzahl rows mit mind. 1 NA
s = data.isna().sum(); print(s[s > 0]) # NAs pro column
```
- Wichtig: Anzahl NAs insgesamt ≠ Anzahl betroffener rows (eine row kann mehrere NAs haben)
## Duplikate
- Duplikate = Beobachtungen, die in **allen** Variablen denselben Wert aufweisen
- `data.duplicated().sum()` → Anzahl
- `.duplicated()` gibt eine boolesche Serie zurück → zum Anzeigen oder Entfernen nutzbar
- ob Duplikate als Fehler gelten, ist eine **fachliche** Entscheidung
## Kategoriale Variablen Kennzahlen
- `data.education.describe()` liefert bei kategorialen Variablen:
- `count`: Anzahl nicht-Missing Values
- `unique`: Anzahl unterschiedliche Werte (Kategorien / Levels)
- `top`: häufigster Wert (→ Modalwert)
- `freq`: Häufigkeit des Modalwertes
- `data[cat_vars].describe()` → für alle kategorialen Variablen auf einmal
- `data.education.value_counts()` → Frequenztabelle, sortiert nach **abnehmender** Häufigkeit (Modalwert zuerst); NAs werden per Default nicht ausgewiesen
- `data[cat_vars].nunique()` → Anzahl Kategorien je Variable
- sehr hohe Werte (nahe `count`) deuten auf ID-/Label-Charakter → kein Mehrwert für ML, und bei nominaler Umcodierung drohen sehr viele Dummy-Variablen
### Zero / Low Variance
- **Zero Variance**: alle Beobachtungen haben denselben Wert → kein Informationsgehalt, für ML wertlos
- **Low Variance**: ein Wert dominiert die anderen stark → meist wenig nützlich, fachlich abzuklären
- Konzept stammt aus der Analyse numerischer Variablen, lässt sich aber auch auf kategoriale anwenden
### Visualisierung kategorialer Variablen
- Standard: Barchart — `data.education.value_counts().plot.bar()` (Kategorien nach abnehmender Frequenz)
- horizontaler Barplot: gut bei vielen Kategorien
- Pie Plot: in der Data Analytics eher unbeliebt (Flächen/Winkel schwer vergleichbar)
## Numerische Variablen Kennzahlen
- `data.age.describe()` liefert:
| Kennzahl | Bedeutung |
|----------|-----------|
| `count` | Anzahl (nicht-NA) Werte |
| `mean` | Mittelwert (arithmetisches Mittel) |
| `std` | Standardabweichung |
| `min` | Minimum (→ 0. Quartil) |
| `25%` | trennt kleinste 25 % ab → 1. Quartil |
| `50%` | Median → 2. Quartil |
| `75%` | trennt grösste 25 % ab → 3. Quartil |
| `max` | Maximum (→ 4. Quartil) |
- `mean` und `std` heissen **parametrische** Kennzahlen (parametrisieren Normalverteilungsmodelle, werden auch zum Skalieren verwendet)
- die übrigen (Quartile/Median) sind **nichtparametrische** Kennzahlen (vgl. Boxplot)
- `describe()` gibt bei numerischen Variablen **keine** unique-Zahl aus → bei Bedarf `data[num_vars].nunique()` (sinnvoll v.a. bei Integer-Werten, um getarnte Kategorien zu finden)
### Stolperfalle Variablennamen
- Zugriff `data.<variable>.<funktion>()` klappt nur, wenn der Name den Python-Namenskonventionen entspricht
- bei Namen wie `nr.employed` (Punkt!) → traditioneller Zugriff über Spaltenindex nötig:
```python
print(data.age.mean()) # ok
# print(data.nr.employed.mean()) # error
print(data['nr.employed'].mean()) # ok
```
### Visualisierung numerischer Variablen
- **Histogramm**: `data.age.plot.hist()` — Default 10 bins, via `bins=20` überschreibbar; zeigt Verteilungsform
- **Densityplot (KDE)**: `data.age.plot.kde()` — Kernel Density Estimator: pro Beobachtung eine Normalverteilung der Fläche 1/n, additiv überlagert; mehr Detail als Histogramm, aber ressourcenhungrig
- **Boxplot**: `data.age.plot.box(vert=False)` — zeigt nichtparametrische Kennzahlen + ausreisserverdächtige Werte
### Boxplot / Box-Whisker-Plot
- Box begrenzt durch 1. und 3. Quartil; Median innerhalb der Box markiert
- Whiskers ("Schnauzhaare") = Werte ausserhalb der Box bis zur Ausreissergrenze
- **IQR** (Interquartile Range) = 3. Quartil 1. Quartil
- als **Ausreisser** markiert werden Werte:
- unterhalb von `Q1 1.5 × IQR`
- oberhalb von `Q3 + 1.5 × IQR`
- gruppierte Boxplots zum Vergleich: `data.boxplot(column=['age'], by='education', vert=False)`
### Ausreisser prüfen
- sortierte Ausgabe hilft einzuschätzen, ob Randwerte echte Extremwerte sind:
```python
print(data.age.sort_values().head(10)) # kleinste
print(data.age.sort_values(na_position="first").tail(10)) # grösste
```
- Beispiel `age`: kleinster Wert (17) kommt mehrfach vor → plausibel; grösster Wert (116) setzt sich deutlich ab und ist fachlich unglaubwürdig → Ausreisser-Verdacht
### Schiefe Verteilungen
- Benennung (rechts-/linksschief) richtet sich nach der Position der **Extremwerte** (der "Schwanz")
- rechtsschiefe Verteilungen sind in der Praxis häufig (z.B. Einkommen, hier `age` leicht, `duration` stark)
- können oft mittels **Logarithmieren** in weniger schiefe Verteilungen transformiert werden
## Interaktionen zwischen Variablen
### Kategorial × kategorial
- **Kreuztabelle** (Mehrweg-Frequenztabelle): `pd.crosstab(data['job'], data['education'])`
- farblich formatieren: `ct.style.background_gradient(axis=None)`
- **Heatmap** (seaborn): `sns.heatmap(ct, annot=True, fmt='d')` — erleichtert Interpretation
- mit Chi-Quadrat liessen sich Zusammenhänge quantifizieren, Vergleiche sind aber schwierig
> Toolwechsel: ab hier werden Visualisierungen mit **seaborn** (`sns`) gemacht — High-Level-Interface auf matplotlib, einfacher zu bedienen. matplotlib = mächtiges Low-Level-Interface mit steiler Lernkurve.
> ```python
> import matplotlib.pyplot as plt
> import seaborn as sns
> sns.set()
> ```
### Numerisch × numerisch Korrelationskoeffizient
- misst **Stärke und Richtung** des (linearen) Zusammenhangs zweier Variablen
- `r` liegt zwischen 1 und +1:
- +1 → perfekter positiver Zusammenhang (beide steigen; z.B. Körpergrösse/Gewicht)
- 0 → kein linearer Zusammenhang (z.B. Schuhgrösse/Intelligenz)
- 1 → perfekter negativer Zusammenhang (eine steigt, andere sinkt; z.B. Geschwindigkeit/Reisezeit)
- Anwendung: Feature Selection (Zusammenhang mit Target), EDA (Muster/Beziehungen), Modellinterpretation
- Achtung: misst nur **lineare** Zusammenhänge — Korrelation ≠ Kausalität
```python
corr = data[['age', 'duration']].corr()
```
- Diagonale immer 1.0 (Identität); Matrix ist symmetrisch, da `cor(x,y) = cor(y,x)` (kommutativ)
- **Korrelogramm** (Heatmap) für die ganze Matrix:
```python
plt.figure(figsize=(7, 6))
ax = sns.heatmap(corr, annot=True, fmt='.2f',
xticklabels=corr.columns, yticklabels=corr.columns,
cmap=sns.diverging_palette(10, 220, as_cmap=True),
vmin=-1, vmax=1)
```
- **Scatterplot** für zwei Variablen, optional Gruppen einfärben:
```python
sns.scatterplot(x='age', y='duration', data=data, hue='y')
```
### Maskieren starker Zusammenhänge
- `.corr()` gibt selbst einen DataFrame → mit `.where()` lassen sich Werte maskieren, z.B. um in einem Korrelogramm nur stark korrelierte Paare zu zeigen:
```python
mask = corr.where(abs(corr) >= 0.95)
# danach mask in sns.heatmap(...) statt corr verwenden
```
- nützlich, um redundante (hoch korrelierte) Features zu identifizieren → Kandidaten zum Entfernen