feature: add L3 theroy morning notes

This commit is contained in:
2026-05-28 10:04:13 +02:00
parent 3c62074697
commit 26081fdf44
+181
View File
@@ -0,0 +1,181 @@
# 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)
```