diff --git a/SL/notizen/L2_Notizen.md b/SL/notizen/L2_Notizen.md index 3fff89e..0a2067f 100644 --- a/SL/notizen/L2_Notizen.md +++ b/SL/notizen/L2_Notizen.md @@ -1,28 +1,14 @@ # Notizen SL Lektion 2 ->Thema: Datenverständnis, Explorative Datenanalyse, Feature Engineering ->Datum: 21.05.2026 ->Dozentin: Violeta Vogel +> 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 -## Übungsaufgabe 1 - -> Alle genannten Modelle nutzen primär Self-Supervised Learning (im Tabellenschema am ehesten "unüberwacht", da keine händischen Labels) -> für das Pretraining, gefolgt von überwachtem Fine-Tuning und Reinforcement Learning für das Alignment. - -1. Pretraining - - Self-Supervised (Next-Token-Prediction auf riesigen Text) - - "Unsupervised" -2. Supervised Fine-Tuning (SFT) - - Überwacht (Mensch schreibt ideale Antworten auf Prompts) -3. RLHF / RLAIF / DPO - - Reinforcement Learning aus menschlichem (oder KI-) Feedback - - DPO: Direct Preference Optimization - ## Datenverständnis 1. Sammeln der Daten @@ -41,7 +27,6 @@ - Sind die Daten vollständig? - Datenqualitätsbericht erstellen - ## EDA: Ziele und Methoden - Mustererkennung @@ -54,17 +39,17 @@ - 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 - - Kollekive Anomalien -> eine Gruppe von Datenpunkten die gemeinsam abweichen auch wenn sie einzeln normal wirken + - 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 numberische Daten + - nicht numerische Daten - fehlende Werte - Duplikate - Kategorien Variablen - hohe Kardinalität (viele eindeutige Werte) - nicht balancierte Daten - - numberische Variablen + - numerische Variablen - schiefe Verteilung - Ausreisser - Korrelationen @@ -72,25 +57,21 @@ ## Klassierung -> Die Klassierung in der deskriptiven Statistik ordnet viele, unterschiedliche -Rohdaten in wenige, überschaubare Klassen (Intervalle) ein. +> 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 -- Datstellung: Histogramme -- Nachteile: Durch Gruppierungen geht die exakte Messgenauigkeit verloren, da einzelwerte nicht mehr erkennbar sind - +- 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 +- 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 -## Workshop 1 +## Skalenniveaus (Workshop 1) ### Nominal @@ -98,7 +79,7 @@ unterschiedliche Typen aufweisen - Was geht: - Gleichheit prüfen (= oder ≠) -- Was nicht geht: +- Was nicht geht: - Reihenfolge - Abstände - Rechnen @@ -113,8 +94,8 @@ unterschiedliche Typen aufweisen - 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. +- 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 @@ -125,12 +106,12 @@ unterschiedliche Typen aufweisen - Was nicht geht: - Abstände interpretieren - Rechnen -- Beispiele: +- 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. + - Bildungsabschluss - Sinnvolle Statistik: - Median - Quantile @@ -146,36 +127,230 @@ unterschiedliche Typen aufweisen - 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. + - **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 +### Ergebnisse Workshop 1 – Melbourne Housing Dataset -Nr. Cholumn 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 +| 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..()` 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