Files
cas-pml/SL/aufgaben/workshop3

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: * VictoriaVictoria · Method: SAS
2.2 Faktorisieren CouncilArea
2.3 Ordinal encodieren Type: h, u, t1, 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) LandsizelogLandsize · BuildingArealogBuildingArea
3.2 Binär umcodieren kein Bedarf

4. Andere Tätigkeiten

# Transformation Details
4.1 Konstruktion Datemonth, 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

python src/prepare.py

Selbstcheck (geklärt)

  • YearBuilt == 1196 verifiziert: 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 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.