hack4dev-building-age-winners / tools /build_official60_first.py
Karar-git's picture
Add FINAL-2 pipeline, overnight search scripts and results (11 Nov 2026)
2c2dd6c verified
Raw History Blame Contribute Delete
2.03 kB
"""
Build the 60 official features exactly as the organisers do: the FIRST QA-valid
observation per pixel-year (features.make_flat_view(agg="first")), rather than
the per-year median our feature table uses.
Needed to run a teammate's pipeline, which was built on the organisers'
preprocessed_data.pkl, on the same inputs it was built for. The organisers'
arrays are additionally z-scored with Madrid's scaler; that is one affine map
per feature shared by both cities, which tree models and per-region
standardisation are invariant to, so it is not reproduced here.
python -I tools/build_official60_first.py
writes data/processed/{madrid,amsterdam}_official60_first.parquet (PID + 60 + age_class)
"""
from __future__ import annotations
import os
import sys
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(ROOT / "src"))
os.chdir(ROOT)
import pandas as pd
import config as C
import features as F
for city, path in (("madrid", ROOT / "data/raw/madrid_train.parquet"),
("amsterdam", ROOT / "data/raw/amsterdam_data.parquet")):
t0 = time.time()
df = pd.read_parquet(path)
df = F.assign_age_class(df)
flat = F.make_flat_view(df, agg="first")
del df
indicators = F.period_coverage(flat)
labels = (flat.sort_values(C.PID + ["year"])
.groupby(C.PID, as_index=False)["age_class"].first())
pix, years, mats, observed, sat = F.to_year_matrices(flat)
del flat
off = F.build_official_60(pix, years, mats, indicators)
out = off.merge(labels, on=C.PID, how="left")
out = out[out["age_class"].notna()].reset_index(drop=True)
out["age_class"] = out["age_class"].astype(int)
assert all(c in out.columns for c in C.FEATURE_COLS_60)
dst = C.PROC_DIR / f"{city}_official60_first.parquet"
out[C.PID + list(C.FEATURE_COLS_60) + ["age_class"]].to_parquet(dst, index=False)
print(f"{city}: {len(out):,} labelled pixels -> {dst} ({time.time() - t0:.0f}s)",
flush=True)