Feature engineering is where domain knowledge meets drudgery: you can imagine thirty candidate features but implementing, validating, and leakage-checking them is slow enough that you build five. An assistant inverts the economics — ideation and implementation become cheap, so the binding constraint becomes your review: does each feature respect time, grain, and the target's definition?
Tier: Foundation · Workflow area: Feature engineering
This playbook runs the few-shot ideation → codegen → leakage-review loop on top of a frozen baseline (pb-05).
Skip heavy ideation for pure deep-learning-on-raw-signal problems where learned representations replace manual features.
featuretools for relational aggregation.Few-shot ideation, seeded with your best examples. Show the model what good looks like in your domain:
Task: churn model, snapshot-based (features as of snapshot_date, target =
churn within 90 days). Sources: orders (order-item grain), support_tickets,
app_events (user-day grain).
Two features that already work well:
- days_since_last_order (recency)
- tickets_30d / tenure_months (normalized friction)
Generate 25 candidate features across: recency/frequency/monetary, trends
(30d vs 90d deltas), volatility, lifecycle ratios, cross-source interactions.
For EACH: name, one-line rationale, source tables, and the exact time window
relative to snapshot_date. Exclude anything needing data after snapshot.
Cull on paper. Kill duplicates, uncomputable ones, and features whose rationale is circular ("users who churn have churn-like behavior"). Keep 8–12. Your domain sense is the filter — the model over-generates by design.
Generate implementation with explicit as-of semantics:
Implement these 10 features as a function
build_features(snapshots: df, orders: df, tickets: df) -> df with one row
per (customer_id, snapshot_date). Rules:
- Every aggregation filtered to event_ts < snapshot_date (strict).
- Use merge_asof / windowed groupbys, no row loops.
- Missing history → explicit NaN, not 0 (imputation happens in the pipeline).
- Return also a dict feature_docs: {name: {window, sources, formula}}.
Leakage-review each feature against the timeline doc. For every feature ask three questions: Could any input be recorded after snapshot (late-arriving events, mutable statuses)? Does it embed the target (e.g., account_status that flips when churn is logged)? Does it cross entities (aggregates computed over train+test populations, target encodings fit on full data)? Have the assistant do a first pass — Review each feature for temporal leakage given: orders can be updated up to 7 days after creation; ticket status mutates; app_events arrive with up to 48h delay — then verify its answers yourself.
Handle target encoding with CV hygiene. If using target/mean encodings for high-cardinality categoricals, they must be fit within CV folds (sklearn TargetEncoder inside the pipeline, or out-of-fold manually). Never accept generated code that computes a target mean on the full frame and merges it in.
Evaluate as a batch, then ablate. Add all survivors, re-run the pb-05 evaluation. If the metric improves, ask for an ablation script (drop-one-group importance or permutation importance on the time-based validation split) to see which features carry the lift and which are dead weight — prune the dead weight; every retained feature is a maintenance liability.
Suspicion pass on winners. Any feature producing a large jump gets the pb-05 treatment: does the lift survive on the final held-out period? Does its importance make domain sense? Sudden +0.05 AUC from one feature is a leak until proven otherwise.
The model's 25 ideas are hypotheses about the domain; the as-of timestamp discipline in step 3 is what keeps them from becoming leaks.
featuretools hybrid: let Deep Feature Synthesis enumerate relational aggregates mechanically with a cutoff_time frame, and use the assistant to interpret/prune its output and add the domain-specific features DFS cannot invent.feature_docs with window and formula.<= vs < on snapshot_date, and windows in calendar days vs business days — generated code picks arbitrarily; your spec must pin it and your synthetic-row test must catch it.spend / tenure_days for day-0 users; demand explicit handling, not silent inf → NaN → imputed.feature_docs lets a teammate recompute any feature without reading the code.