feature: update readme, add more output to ml-pipeline
This commit is contained in:
@@ -8,7 +8,7 @@ den in Workshop 2 erarbeiteten und konsolidierten Empfehlungen.
|
||||
- **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
|
||||
einzige Quelle der Wahrheit für die anzuwendenden Transformationen — das
|
||||
ursprüngliche `WS_03_Empfehlungen.xlsx` muss nicht geöffnet werden.
|
||||
|
||||
## Aufgabenstellung
|
||||
@@ -16,7 +16,7 @@ ursprüngliche `WS_03_Empfehlungen.xlsx` muss nicht geöffnet werden.
|
||||
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.
|
||||
keine neue Exploration statt — die Entscheidungen sind bereits getroffen.
|
||||
|
||||
## Drehbuch (Transformationsliste)
|
||||
|
||||
@@ -89,10 +89,60 @@ workshop3
|
||||
python src/prepare.py
|
||||
```
|
||||
|
||||
## Offene Punkte / Selbstcheck
|
||||
## Selbstcheck (geklärt)
|
||||
|
||||
- [ ] `YearBuilt == 1196` verifizieren (vermutlich Tippfehler für 1996)
|
||||
- [ ] NA-Spalten prüfen -> sind NAs in der Target-Variable `Price`?
|
||||
(Falls ja: Zeilen entfernen statt Median einsetzen.)
|
||||
- [ ] Standardisieren bleibt hier aus -> relevant erst beim Training,
|
||||
und modellabhängig (Bäume brauchen es nicht).
|
||||
- [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.
|
||||
|
||||
@@ -27,6 +27,7 @@ def inspect(df: pd.DataFrame) -> None:
|
||||
|
||||
def e1_remove_observations(df: pd.DataFrame) -> pd.DataFrame:
|
||||
"""E1: Ausreisser und fehlerhafte Beobachtungen entfernen."""
|
||||
df.info()
|
||||
before = len(df)
|
||||
df = df[df.Price < 8000000] # nur Werte bis 8000000 berücksichtigen
|
||||
df = df[
|
||||
@@ -134,6 +135,7 @@ def e41_construct(df: pd.DataFrame) -> pd.DataFrame:
|
||||
def e42_clean_names(df: pd.DataFrame) -> pd.DataFrame:
|
||||
"""E4.2: Unerlaubte Zeichen in Spaltennamen durch _ ersetzen."""
|
||||
df.columns = df.columns.str.replace(r"[^a-zA-Z0-9_]", "_", regex=True)
|
||||
df.info()
|
||||
return df
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user