feature: move code from python to jupyter notebook, add plots

This commit is contained in:
2026-06-18 15:28:26 +02:00
parent 7e0b684afb
commit 6371ebf1e2
14 changed files with 291 additions and 82 deletions
+74 -28
View File
@@ -1,66 +1,112 @@
# K-Means: Elbow-Methode & Gütemasse
# CAS PML Unsupervised Learning: Clustering-Aufgaben
> Thema: Optimale Clusterzahl $k$ bestimmen und Clusterqualität bewerten
> Skript: Unsupervised Learning (CAS PML)
## Ziel
Auf synthetischen 2D-Daten (drei Gauss-Blobs) K-Means für verschiedene $k$ laufen lassen und die "richtige" Clusterzahl systematisch über Gütemasse bestimmen — statt nur visuell zu schätzen.
> Modul: CAS Practical Machine Learning Unsupervised Learning (Dehmer)
> Inhalt: zwei Clustering-Übungen rund um K-Means und die Wahl der Clusterzahl $k$
## Projektstruktur
```
.
├── data/ # leer: Daten werden synthetisch generiert
├── data/
│ └── data_delivery_fleet.tsv # Übung 1 (tab-separiert)
├── plots/ # generierte PNGs (headless)
├── src/
── k_means2_elbow_measures.py
── uebung1_kmeans_fleet.py # Übung 1: k-Means auf Fleet-Daten
│ └── k_means2_elbow_measures.py # Übung 2: Elbow & Gütemasse (synthetisch)
├── devenv.nix
└── README.md
```
Ausführen (in der devenv-Shell):
> Tipp für konsistente Namen: `k_means2_elbow_measures.py` ggf. zu `uebung2_elbow_measures.py` umbenennen.
## Setup & Ausführen
In der devenv-Shell:
```
python src/uebung1_kmeans_fleet.py
python src/k_means2_elbow_measures.py
```
## Ablauf des Skripts
Beide Skripte laufen **headless**: Plots werden als PNG nach `plots/` geschrieben (kein `plt.show()`, da in der Shell kein Display vorhanden ist Agg-Backend).
1. Daten generieren: je 100 Punkte um die Zentren $(0,0)$, $(6,6)$, $(0,6)$ via `np.random.randn` (Seed fix → reproduzierbar)
2. Für $k = 1 \dots 8$ K-Means fitten und drei Masse sammeln:
- Inertia (`kmeans.inertia_`) — immer
- Silhouette-Score und Davies-Bouldin-Index — erst ab $k \geq 2$ (für $k=1$ nicht definiert → `np.nan`)
3. Bestes $k$ wählen (vereinfacht: maximaler Silhouette-Score via `np.nanargmax`)
4. Finales Modell mit `best_k` fitten → Labels + Zentren
5. Visualisierung in vier Panels: Rohdaten | Elbow (Inertia) | Silhouette | finales Clustering
## Gütemasse zur Wahl von $k$ (gemeinsame Referenz)
## Gütemasse (Kurzreferenz)
- Inertia / WCSS — Summe der quadrierten Abstände zum jeweiligen Zentroid; sinkt monoton mit $k$, hat also kein Optimum, nur einen "Knick" (Elbow)
- Inertia / WCSS Summe der quadrierten Abstände zum Zentroid; sinkt monoton mit $k$, daher nur "Knick" (Elbow), kein echtes Optimum
$$ \text{WCSS} = \sum_{i=1}^{k} \sum_{x \in P_i} \lVert x - \mu_i \rVert^2 $$
- Silhouette-Score — $a_i$ = mittlere Intra-Cluster-Distanz, $b_i$ = mittlere Distanz zum nächsten Cluster; Bereich $[-1, 1]$, höher = besser
- Erklärte Varianz Anteil der Between-Cluster-Varianz an der Gesamtvarianz; nutzt $\text{TSS} = \text{WCSS} + \text{BSS}$, höher = besser (ideal 100 %)
$$ \text{Variance Explained} = \frac{\text{BSS}}{\text{TSS}} \cdot 100\,\% $$
- Silhouette-Score $a_i$ = mittlere Intra-, $b_i$ = mittlere nächste Inter-Cluster-Distanz; Bereich $[-1, 1]$, höher = besser
$$ s_i = \frac{b_i - a_i}{\max(a_i, b_i)} $$
- Davies-Bouldin-Index — mittleres "Worst-Case"-Verhältnis von Cluster-Streuung zu Cluster-Abstand; niedriger = besser (Achtung: umgekehrt zur Silhouette!)
- Davies-Bouldin (nur Übung 2) mittleres Verhältnis Streuung/Abstand; niedriger = besser (Achtung: umgekehrt zur Silhouette)
$$ DB = \frac{1}{k} \sum_{i=1}^{k} \max_{j \neq i} \frac{\sigma_i + \sigma_j}{d(c_i, c_j)} $$
- mit $\sigma_i$ = mittlere Distanz der Punkte in Cluster $i$ zu ihrem Zentroid $c_i$
---
## Verständnisfragen (im Code eingebettet)
## Übung 1 — k-Means: Delivery Fleet
Aufgabenstellung (Skript): Fleet-Daten von Lieferfahrer:innen clustern.
1. Datensatz aus tab-separiertem File laden (Pandas)
2. Daten visualisieren und eine sinnvolle Clusterzahl visuell *und* systematisch finden
3. Daten clustern
Datensatz `data_delivery_fleet.tsv` (3 Spalten):
- `Driver_ID` ID der Fahrer:in
- `Distance_Feature` mittlere gefahrene Meilen pro Tag
- `Speeding_Feature` % der Zeit über dem Tempolimit (>5 mph)
Geclustert wird auf `Distance_Feature` × `Speeding_Feature`.
Ablauf von `uebung1_kmeans_fleet.py` (erzeugt PNGs in `plots/`):
1. Rohdaten-Scatter (`01_rohdaten.png`)
2. K-Means mit frei gewähltem $k=4$ (`02_kmeans_k4.png`)
3. Sweep $k = 1 \dots 8$ als Übersichts-Grid (`03_kmeans_sweep_k1-8.png`)
4. $k$ bestimmen über Inertia/Elbow (`04`), erklärte Varianz (`05`) und Silhouette (`06`)
5. finales Modell mit bestem $k$ (`07_final.png`)
Verständnisfragen:
- [ ] Legen Elbow, erklärte Varianz und Silhouette dasselbe $k$ nahe?
- [ ] Sollte man die zwei Features vor dem Clustern skalieren (Meilen vs. %, sehr unterschiedliche Spannweiten)?
- [ ] Die Hartigan-Regel ist im Lösungs-Notebook enthalten, gilt aber als veraltet warum bevorzugt man Silhouette/Elbow?
> Anpassungen ggü. dem Notebook: `.as_matrix()` → `.to_numpy()` (in aktuellem Pandas entfernt); `plt.show()` → `savefig` (headless); die Hartigan-Funktion ist als optionaler Legacy-Block enthalten (`RUN_HARTIGAN = False`).
---
## Übung 2 — Elbow-Methode & Gütemasse (synthetisch)
Ziel: auf synthetischen 2D-Daten (drei Gauss-Blobs) K-Means für verschiedene $k$ laufen lassen und die "richtige" Clusterzahl systematisch über Gütemasse bestimmen statt nur visuell zu schätzen.
Ablauf von `k_means2_elbow_measures.py` (erzeugt einen 4-Panel-Plot):
1. Daten generieren: je 100 Punkte um $(0,0)$, $(6,6)$, $(0,6)$ (Seed fix → reproduzierbar)
2. Für $k = 1 \dots 8$ K-Means fitten und sammeln: Inertia, Silhouette, Davies-Bouldin (Letztere ab $k \geq 2$)
3. Bestes $k$ wählen (vereinfacht: maximaler Silhouette-Score)
4. Finales Modell fitten → Labels + Zentren
5. Visualisierung: Rohdaten | Elbow (Inertia) | Silhouette | finales Clustering
Verständnisfragen:
- [ ] Warum nimmt man zur $k$-Wahl die Silhouette und nicht die Inertia?
- [ ] Warum sind die Cluster im Ergebnis-Plot farblich klar getrennt?
- [ ] Warum können verschiedene Runs unterschiedliche Ergebnisse liefern?
- [ ] Ist das gefundene $k$ immer "korrekt"?
## Experimente (zum Ausprobieren)
Experimente (zum Ausprobieren):
- [ ] Zentren näher zusammenlegen (Daten stärker überlappen) → wie verändern sich Elbow und Scores?
- [ ] Davies-Bouldin gegen die Silhouette als $k$-Kriterium tauschen → wählt es dasselbe $k$?
- [ ] Davies-Bouldin statt Silhouette als $k$-Kriterium (dann `argmin` statt `argmax`!) → dasselbe $k$?
- [ ] `n_init` reduzieren / Seed variieren → wie stabil ist das Ergebnis?
> Zu allen Verständnisfragen stehen im Skript kurze Musterantworten als Kommentar.
> Zu den Verständnisfragen stehen im Skript kurze Musterantworten als Kommentar.