149 lines
6.4 KiB
Markdown
149 lines
6.4 KiB
Markdown
# Workshop 3 Feature Engineering: Melbourne Housing Dataset
|
||
|
||
Aufbereitung des **Melbourne Housing Datasets** für Supervised Learning, gemäss
|
||
den in Workshop 2 erarbeiteten und konsolidierten Empfehlungen.
|
||
|
||
- **Input:** `data/melb_data.csv` (Rohdaten)
|
||
- **Output:** `data/melb_data_prep.csv` (aufbereitet)
|
||
- **Pipeline:** `src/prepare.py`
|
||
|
||
Die Empfehlungen stammen aus der EDA in Workshop 2. Dieses README ist die
|
||
einzige Quelle der Wahrheit für die anzuwendenden Transformationen — das
|
||
ursprüngliche `WS_03_Empfehlungen.xlsx` muss nicht geöffnet werden.
|
||
|
||
## Aufgabenstellung
|
||
|
||
Das Dataset wurde in Workshop 2 mit Sicht auf Machine Learning untersucht.
|
||
Die daraus abgeleiteten Empfehlungen (siehe unten) werden hier in einer
|
||
deterministischen Pipeline `CSV rein → CSV raus` implementiert. Es findet
|
||
keine neue Exploration statt — die Entscheidungen sind bereits getroffen.
|
||
|
||
## Drehbuch (Transformationsliste)
|
||
|
||
### 1. Data Frame
|
||
|
||
| # | Transformation | Details |
|
||
|-----|----------------|---------|
|
||
| 1.1 | Beobachtungen nach Bedingung entfernen | `Price >= 8000000`; `YearBuilt == 1196` |
|
||
| 1.2 | Duplikate entfernen | kein Bedarf |
|
||
| 1.3 | Fragwürdige Variablen entfernen | `Unnamed: 0`, `Suburb`, `Address`, `SellerG`, `Postcode`, `Bedroom2` |
|
||
| 1.4 | NAs ersetzen | kategoriale: Modalwert · numerische: Median |
|
||
|
||
### 2. Kategoriale Variablen
|
||
|
||
| # | Transformation | Details |
|
||
|-----|----------------|---------|
|
||
| 2.1 | Kardinalität reduzieren | `Regionname`: `* Victoria` → `Victoria` · `Method`: `SA` → `S` |
|
||
| 2.2 | Faktorisieren | `CouncilArea` |
|
||
| 2.3 | Ordinal encodieren | `Type`: `h, u, t` → `1, 2, 3` |
|
||
| 2.4 | Binär encodieren | kein Bedarf |
|
||
| 2.5 | Nominal encodieren (One-Hot) | alle verbleibenden kategorialen Variablen ausser `Date` |
|
||
|
||
### 3. Numerische Variablen
|
||
|
||
| # | Transformation | Details |
|
||
|-----|----------------|---------|
|
||
| 3.1 | Logarithmieren (+ umbenennen) | `Landsize` → `logLandsize` · `BuildingArea` → `logBuildingArea` |
|
||
| 3.2 | Binär umcodieren | kein Bedarf |
|
||
|
||
### 4. Andere Tätigkeiten
|
||
|
||
| # | Transformation | Details |
|
||
|-----|----------------|---------|
|
||
| 4.1 | Konstruktion | `Date` → `month`, `year`, `day_of_week`; danach `Date` droppen |
|
||
| 4.2 | Variablennamen bereinigen | unerwünschte Zeichen → `_` |
|
||
| 4.3 | Standardisieren | kein Bedarf |
|
||
| 4.4 | Speichern | als `melb_data_prep.csv` |
|
||
|
||
## Reihenfolge — wichtig
|
||
|
||
Die Schritte sind nicht beliebig vertauschbar. Insbesondere:
|
||
|
||
- **1.1 vor 1.4:** erst Ausreisser/fehlerhafte Zeilen raus, dann NAs füllen
|
||
(sonst fliessen Schrottwerte in Median/Modalwert ein).
|
||
- **2.1 vor 2.5:** erst Kardinalität reduzieren, dann One-Hot
|
||
(sonst entstehen Dummy-Spalten für Levels, die man gerade zusammenlegen will).
|
||
- **2.5 nach allen anderen kategorialen Schritten:** One-Hot greift alle
|
||
*übrigen* `object`-Spalten ab — `CouncilArea` und `Type` sind dann schon
|
||
numerisch und werden korrekt übersprungen.
|
||
- **4.1 vor 2.5 ODER `Date` explizit ausnehmen:** `Date` ist `object` und würde
|
||
sonst von One-Hot zerlegt. Lösung: `Date` beim One-Hot ignorieren und erst in
|
||
4.1 zu `month`/`year`/`day_of_week` zerlegen.
|
||
|
||
## Projektstruktur
|
||
|
||
```
|
||
workshop3
|
||
├── data/
|
||
│ ├── melb_data.csv # Rohdaten (Input)
|
||
│ └── melb_data_prep.csv # aufbereitet (Output, generiert)
|
||
├── src/
|
||
│ └── prepare.py # Pipeline
|
||
├── devenv.nix
|
||
└── README.md
|
||
```
|
||
|
||
## Ausführen
|
||
|
||
```sh
|
||
python src/prepare.py
|
||
```
|
||
|
||
## Selbstcheck (geklärt)
|
||
|
||
- [x] `YearBuilt == 1196` verifiziert: nächster Wert ist 1830, Sprung von 634
|
||
Jahren, physikalisch unmöglich (Melbourne ~1835 gegründet) → Datenfehler,
|
||
eine Zeile entfernt.
|
||
- [x] NAs in Target `Price`? Nein — Spalte ist vollständig (18396 non-null).
|
||
Median-Imputation in E4 daher unkritisch.
|
||
- [x] Standardisieren bleibt aus — modellabhängig, gehört ans Training, nicht in
|
||
die Aufbereitung (Bäume brauchen es nie).
|
||
|
||
## Ergebnis
|
||
|
||
- 18'393 Zeilen (von 18'396, −3 in 1.1), 24 Spalten
|
||
- keine `object`-Spalten mehr -> alles numerisch (`int`, `float`, `bool`)
|
||
- Output: `data/melb_data_prep.csv`
|
||
|
||
Spaltenrechnung zur Kontrolle: 16 nach NA-Imputation → +8/−2 durch One-Hot (22)
|
||
→ −1 `Date`/+3 Datumskomponenten (24).
|
||
|
||
## Implementierungs-Notizen (Abweichungen von den Vorlagen)
|
||
|
||
Bewusste Entscheidungen, die von `1.6 Implementation.ipynb` (Bank) bzw. der
|
||
WS-03-Musterlösung abweichen:
|
||
|
||
- **Reine `df -> df`-Funktionen statt `inplace=True`.** Jede Transformation
|
||
gibt einen neuen DataFrame zurück. Vermeidet `SettingWithCopyWarning` auf
|
||
Slices und ist Copy-on-Write-sicher (pandas 3.x). Pipeline-Struktur:
|
||
`df = e1(df); df = e2(df); …`.
|
||
- **`.map({...})` statt `.replace([...], [...])`** beim Ordinal-Encoding von
|
||
`Type`. Vermeidet die Downcasting-`FutureWarning` und ist lesbarer
|
||
(Kategorie→Zahl direkt nebeneinander statt positionsabhängige Listen).
|
||
- **`.fillna()` statt `SimpleImputer`** für die NA-Imputation. KISS — kein
|
||
sklearn-Objekt nötig, da hier keine spätere Inference auf neuen Daten
|
||
stattfindet (kein Bedarf, die Imputationswerte zu persistieren).
|
||
- **Vektor-Operation `df.columns.str.replace(...)` statt `for`-Loop** beim
|
||
Bereinigen der Variablennamen.
|
||
- **`+ 1` statt `+ min + 1`** beim Logarithmieren. Flächen sind nie negativ
|
||
(Minimum 0), daher reicht `+1`, um `log10(0)` zu vermeiden. `Landsize` hat
|
||
1942 Nullwerte (~10,5 %) — ohne `+1` wären das ebenso viele `-inf`.
|
||
|
||
## Offene Punkte für ein echtes Projekt (hier bewusst nicht gemacht)
|
||
|
||
Über das Drehbuch hinausgehende Überlegungen, fürs nächste Dataset:
|
||
|
||
- **Zyklische Datums-Features.** `month` und `day_of_week` sind linear codiert
|
||
(Dez→Jan sieht als 12→1 wie ein grosser Sprung aus). Sauberer wäre eine
|
||
sin/cos-Zerlegung. Für baum-basierte Modelle irrelevant, für lineare relevant.
|
||
- **NA-Anteil bei `BuildingArea` (58 %) und `YearBuilt` (51 %).** Median-Füllung
|
||
über die halbe Spalte drückt deren Varianz stark. Kandidat für „Variable
|
||
droppen" oder modellbasiertes Imputieren statt pauschalem Median.
|
||
- **`CouncilArea`-Faktorisierung erzeugt Schein-Ordnung.** 33 nominale Levels
|
||
als 0..32 codiert — ein lineares Modell liest daraus eine Rangordnung, die
|
||
nicht existiert. Bewusster Trade-off gegen 33 One-Hot-Spalten. Für Bäume
|
||
unproblematisch.
|
||
- **Produktionsform.** Für eine wiederverwendbare Pipeline (auch auf neuen
|
||
Daten) wäre `sklearn` `Pipeline` + `ColumnTransformer` die nächste Stufe —
|
||
löst zugleich das Persistenz-Problem der Imputations-/Skalierungswerte.
|