6.4 KiB
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 —CouncilAreaundTypesind dann schon numerisch und werden korrekt übersprungen. - 4.1 vor 2.5 ODER
Dateexplizit ausnehmen:Dateistobjectund würde sonst von One-Hot zerlegt. Lösung:Datebeim One-Hot ignorieren und erst in 4.1 zumonth/year/day_of_weekzerlegen.
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
python src/prepare.py
Selbstcheck (geklärt)
YearBuilt == 1196verifiziert: nächster Wert ist 1830, Sprung von 634 Jahren, physikalisch unmöglich (Melbourne ~1835 gegründet) → Datenfehler, eine Zeile entfernt.- NAs in Target
Price? Nein — Spalte ist vollständig (18396 non-null). Median-Imputation in E4 daher unkritisch. - 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 stattinplace=True. Jede Transformation gibt einen neuen DataFrame zurück. VermeidetSettingWithCopyWarningauf Slices und ist Copy-on-Write-sicher (pandas 3.x). Pipeline-Struktur:df = e1(df); df = e2(df); …. .map({...})statt.replace([...], [...])beim Ordinal-Encoding vonType. Vermeidet die Downcasting-FutureWarningund ist lesbarer (Kategorie→Zahl direkt nebeneinander statt positionsabhängige Listen)..fillna()stattSimpleImputerfü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(...)stattfor-Loop beim Bereinigen der Variablennamen. + 1statt+ min + 1beim Logarithmieren. Flächen sind nie negativ (Minimum 0), daher reicht+1, umlog10(0)zu vermeiden.Landsizehat 1942 Nullwerte (~10,5 %) — ohne+1wä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.
monthundday_of_weeksind 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 %) undYearBuilt(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
sklearnPipeline+ColumnTransformerdie nächste Stufe — löst zugleich das Persistenz-Problem der Imputations-/Skalierungswerte.