# 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) ```