Files
cas-pml/SL/notizen/L3_Notizen.md
T

182 lines
7.4 KiB
Markdown
Raw 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 3
> Thema: Feature Engineering
> Datum: 28.05.2026
> Dozentin: Violeta Vogel
## Recap
- Sandbox Prinzip
- Daten werden immer als Kopie bearbeitet (`data = ori_data.copy()`), damit Transformationsschritte sich nicht gegenseitig beeinflussen und die Ausgangsdaten nicht neu geladen werden müssen
- hohe Kardinalität
- bezeichnet eine hohe Anzahl an eindeutigen Werten
## Arten von Variablen
- Numerische Variablen
- messbare Zahlen
- **Stetig** (kontinuierlich)
- Können jeden beliebigen Wert annehmen und unendlich fein unterteilt werden.
- Beispiele: Körpergröße, Gewicht, Temperatur, Zeit
- **Diskret**
- Können nur in bestimmten, meist ganzzahligen Schritten gezählt werden.
- Beispiele: Anzahl der Kinder, Autoverkäufe, Besucher eines Konzerts
- Kategoriale Variablen
- Gruppen oder Eigenschaften
- Diese Variablen teilen Beobachtungen in verschiedene Gruppen oder Kategorien ein. Sie geben eine Eigenschaft an.
- **Nominal**
- Die Kategorien haben keine logische Reihenfolge.
- Beispiele: Geschlecht, Haarfarbe, Augenfarbe, Postleitzahlen.
- **Ordinal**
- Die Kategorien haben eine natürliche Rangordnung oder Reihenfolge.
- Beispiele: Zufriedenheitsgrade (sehr zufrieden, zufrieden, unzufrieden), Schulnoten (1 bis 6)
## Transformationen: Numerisieren Kategorialer Variablen
- Numerisieren
- Numerische Darstellung einer Variablen, damit ML damit rechnen kann
- Verschiedene Methoden
- Faktorisieren
- Ordinal Encodieren
- Nominal Encodieren
- Faustregel: nominal → One-Hot, ordinal → Ordinal Encoding, reines Label ohne Bedeutung → Faktorisieren
### Faktorisieren
- Jeder Kategorie einer kategorialen Variablen wird ein Integer-Wert zugeordnet, beginnend bei 0
- `data.job = pd.factorize(data.job)[0]`
- `pd.factorize()` gibt ein Tupel zurück:
- `[0]` → faktorisierte Werte (`numpy.ndarray`), beginnend bei 0
- `[1]` → Index mit der Zuordnung der Werte zu den Ausgangswerten
- Werte werden per Default in **Reihenfolge des Auftretens** im Dataset vergeben (nicht sortiert)
- mit `sort=True` werden sie lexikografisch (bezogen auf die Ausgangswerte) vergeben
- **Schwäche:** die numerische Zuordnung ist im Grunde willkürlich (Reihenfolge im Datensatz) → es wird eine Ordnung impliziert, die inhaltlich keine ist. Taugt sauber nur für nominale Daten, wo die Zahl reines Label ist.
### Ordinal Encodieren
- Behebt die Faktorisier-Schwäche: bei einer **tatsächlich ordinalen** Variable werden die Zahlen gezielt der natürlichen Rangordnung zugeordnet
- Beispiel `education`:
- `illiterate → 0`
- `unknown → 0`
- `basic.4y → 1`
- `basic.6y → 2`
- `basic.9y → 3`
- `professional.course → 4`
- `high.school → 5`
- `university.degree → 6`
- Umsetzung über `.replace()` mit einem (verschachtelten) Dictionary, das vorher definiert wird:
```python
data.replace(replace_nums, inplace=True)
```
- Hinweis: `sklearn.preprocessing.OrdinalEncoder` macht in Wahrheit nur eine Faktorisierung — echtes ordinales Mapping erfordert deutlich aufwändigere Parametrisierung.
#### Spezialfall: 0-1 Encodieren
- Bei nur zwei Kategorien reicht `np.where`:
```python
data['contact'] = np.where(data.contact == 'cellular', 1, 0)
```
- Achtung: alles, was *nicht* `cellular` ist, wird 0 (inkl. NAs)
- Danach ggf. Spalte umbenennen für Transparenz:
```python
data.rename(columns={'contact': 'contact_cellular'}, inplace=True)
```
### Nominal Encodieren (One-Hot)
- Für Variablen **ohne** Rangordnung — hier wäre eine ordinale Zahl irreführend
- `pd.get_dummies()` erstellt pro Kategorie eine neue Dummy-Variable (0/1) **und entfernt die Ausgangsvariable**
- Wichtige Parameter:
- `drop_first=True` → eine Dummy weniger als Kategorien (vermeidet perfekte Multikollinearität / Dummy-Trap)
- `prefix='marital'` → benennt die neuen Spalten mit Präfix (sinnvoll bei mehreren Variablen)
- `columns=[...]` → mehrere Variablen auf einmal
- Trick für alle kategorialen Spalten außer Target:
```python
target = 'y'
sel_vars = data.select_dtypes(include=['object']).columns.drop(target)
data = pd.get_dummies(data, columns=sel_vars, drop_first=True)
```
## Transformationen: Numerische Variablen
> Hinweis: Normalisieren und Standardisieren sind **Unterarten** von Skalieren, nicht drei gleichrangige Methoden.
- Methoden
- Skalieren
- Normalisieren
- Standardisieren
- Binning
### Skalieren
- Bringt numerische Variablen auf vergleichbare Wertebereiche
- Nur sinnvoll, wenn auf **alle** relevanten Variablen gleich angewendet
- Muss ggf. für spätere neue Daten gespeichert werden → daher in der Praxis `sklearn.preprocessing` statt Handformel:
- `MinMaxScaler` → Normalisierung
- `StandardScaler` → Standardisierung
- `.set_output(transform="pandas")` behält den DataFrame, statt ein numpy-Array zurückzugeben:
```python
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler().set_output(transform="pandas")
data = scaler.fit_transform(data)
```
#### Normalisieren
- Skaliert auf festen Bereich [0, 1]
- Formel: `(x - min) / (max - min)`
- min → 0, max → 1
#### Standardisieren
- Zentriert auf Mittelwert 0, Standardabweichung 1 (z-Transformation)
- Formel: `(x - mean) / std`
- danach: mean ≈ 0, std = 1
> **Wichtig:** Keines der beiden Verfahren ändert die *Form* der Verteilung — nur die Skala der x-Achse. Schiefe bleibt schief.
### Binning
- Numerische Variable in Klassen/Bins zusammenfassen → wird quasi (ordinal-)kategorial
- **Equal Binning:** teilt minmax in gleich breite Bereiche
```python
bins = 10
data.age = pd.cut(data.age, bins=bins, labels=list(range(1, bins + 1)))
```
- **Custom Bins:** eigene Grenzen, nützlich bei stark schiefen Verteilungen
```python
data.campaign = pd.cut(
data.campaign,
bins=[0, 1, 2, 3, 4, 5, 10, 1000],
labels=[1, 2, 3, 4, 5, '6-10', '>10'])
```
- Zur optimalen Bin-Anzahl gibt es keinen Konsens (Freedman-Diaconis, Sturges, …) → experimentell ermitteln
## Konstruktion
- Neue Variablen aus bestehenden ableiten
- Ziele: Komplexität reduzieren, Korrelationen vermeiden
- Beispiel **zyklische Daten** (Windrichtung): 359° und 1° sind nah beieinander, numerisch aber weit weg → Zerlegung in sin/cos-Komponenten löst das
```python
data['x'] = np.sin(data.direction * np.pi / 180) * data.speed
data['y'] = np.cos(data.direction * np.pi / 180) * data.speed
```
- allgemein relevant für alles Zyklische (Stunden, Monate, Winkel)
- Beispiel **Datum**: String → `pd.to_datetime()`, dann Komponenten extrahieren
```python
data['date_dt'] = pd.to_datetime(data.Date, format="%d/%m/%Y")
data['year'] = data.date_dt.dt.year
data['month'] = data.date_dt.dt.month
data['day'] = data.date_dt.dt.day
```
- oder Differenzen zu einem Startdatum (`(data.date_dt - start_date).dt.days`)
## Bereinigen von Variablennamen
- Nach One-Hot entstehen Namen mit Leerzeichen / Bindestrichen / Punkten (`job_blue collar`, `emp.var.rate`), die manche ML-Frameworks als Bezeichner ablehnen
- Erlaubte Zeichen: `a-z`, `A-Z`, `0-9`, `_`
- Per Regex unerlaubte Zeichen durch `_` ersetzen:
```python
new_names = old_names.str.replace('[^a-zA-Z0-9_]', '_', regex=True)
```