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`
|
- **Pipeline:** `src/prepare.py`
|
||||||
|
|
||||||
Die Empfehlungen stammen aus der EDA in Workshop 2. Dieses README ist die
|
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.
|
ursprüngliche `WS_03_Empfehlungen.xlsx` muss nicht geöffnet werden.
|
||||||
|
|
||||||
## Aufgabenstellung
|
## 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.
|
Das Dataset wurde in Workshop 2 mit Sicht auf Machine Learning untersucht.
|
||||||
Die daraus abgeleiteten Empfehlungen (siehe unten) werden hier in einer
|
Die daraus abgeleiteten Empfehlungen (siehe unten) werden hier in einer
|
||||||
deterministischen Pipeline `CSV rein → CSV raus` implementiert. Es findet
|
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)
|
## Drehbuch (Transformationsliste)
|
||||||
|
|
||||||
@@ -89,10 +89,60 @@ workshop3
|
|||||||
python src/prepare.py
|
python src/prepare.py
|
||||||
```
|
```
|
||||||
|
|
||||||
## Offene Punkte / Selbstcheck
|
## Selbstcheck (geklärt)
|
||||||
|
|
||||||
- [ ] `YearBuilt == 1196` verifizieren (vermutlich Tippfehler für 1996)
|
- [x] `YearBuilt == 1196` verifiziert: nächster Wert ist 1830, Sprung von 634
|
||||||
- [ ] NA-Spalten prüfen -> sind NAs in der Target-Variable `Price`?
|
Jahren, physikalisch unmöglich (Melbourne ~1835 gegründet) → Datenfehler,
|
||||||
(Falls ja: Zeilen entfernen statt Median einsetzen.)
|
eine Zeile entfernt.
|
||||||
- [ ] Standardisieren bleibt hier aus -> relevant erst beim Training,
|
- [x] NAs in Target `Price`? Nein — Spalte ist vollständig (18396 non-null).
|
||||||
und modellabhängig (Bäume brauchen es nicht).
|
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:
|
def e1_remove_observations(df: pd.DataFrame) -> pd.DataFrame:
|
||||||
"""E1: Ausreisser und fehlerhafte Beobachtungen entfernen."""
|
"""E1: Ausreisser und fehlerhafte Beobachtungen entfernen."""
|
||||||
|
df.info()
|
||||||
before = len(df)
|
before = len(df)
|
||||||
df = df[df.Price < 8000000] # nur Werte bis 8000000 berücksichtigen
|
df = df[df.Price < 8000000] # nur Werte bis 8000000 berücksichtigen
|
||||||
df = df[
|
df = df[
|
||||||
@@ -134,6 +135,7 @@ def e41_construct(df: pd.DataFrame) -> pd.DataFrame:
|
|||||||
def e42_clean_names(df: pd.DataFrame) -> pd.DataFrame:
|
def e42_clean_names(df: pd.DataFrame) -> pd.DataFrame:
|
||||||
"""E4.2: Unerlaubte Zeichen in Spaltennamen durch _ ersetzen."""
|
"""E4.2: Unerlaubte Zeichen in Spaltennamen durch _ ersetzen."""
|
||||||
df.columns = df.columns.str.replace(r"[^a-zA-Z0-9_]", "_", regex=True)
|
df.columns = df.columns.str.replace(r"[^a-zA-Z0-9_]", "_", regex=True)
|
||||||
|
df.info()
|
||||||
return df
|
return df
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Reference in New Issue
Block a user