feature: add L3 theroy morning notes
This commit is contained in:
@@ -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 min–max 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)
|
||||
```
|
||||
Reference in New Issue
Block a user