vc_004 · Multi-table aggregation
Status: ✓ Passing · Last run: 2026-08-24T17:43:52 · Pipeline: v2
· History: 13/17 runs passed (76%)
What was tested
Two-table synthetic dataset: 500 clients + ~4,000 transactions joined on client_id. Target lives on the clients table. The signal must be recovered by aggregating transactions per client (count, mean amount, total). Verifies that the pipeline handles multi-row-per-entity sources via aggregate features rather than a row-multiplying equi-join.
What we planted in the data
- 500 entities
- Signal strength: 0.50 (sigmoid slope multiplier)
What the system did
- Training rows built: 500
- Features used in model: 10
- Model selected:
LogisticRegression - CV AUC: 0.750
Top features by importance:
avg_txn_amount(0.561)total_txn_amount(0.270)max_txn_amount(0.173)segment_enterprise(0.040)signup_year_zscore_by_segment(0.036)
What we checked — all assertions passed
| Status | Assertion | Detail |
|---|---|---|
| ✓ | row_count_eq |
training rows = 500, expected = 500 |
| ✓ | no_pipeline_errors |
clean run |
| ✓ | auc_in_band |
AUC = 0.750 inside band [0.65, 0.95] |
How this could have gone wrong
- If
row_count_eqfailed: Training df has exactly 500 rows (one per client). A naive multi-table join would inflate this to ~4,000 (one per transaction); the assertion catches that. - If
no_pipeline_errorsfailed: Pipeline runs cleanly with two-table input — no schema, join, or grain mismatches that would halt the run. - If
auc_in_bandfailed: AUC inside a band consistent with the planted signal: clients with higher mean transaction amounts are more likely to be high spenders, and aggregate features should recover that. Below 0.65 means the aggregations didn't fire; above 0.95 means something leaked the target column into features.
Why this case matters
Multi-table is the failure shape the panel-fan-out fix had to handle generically: when a source table has many rows per entity, the pipeline must aggregate it (correlated-subquery features with time bounds), not blindly equi-join it into pred_grid (which would inflate training rows and leak future transactions into the features for any given snapshot). This case anchors that behavior on a clean two-table synthetic.
Reproducing
# from auto_insight_api/
python -m validation.v2 run vc_004 --pipeline v2 -v
- Case config:
validation/v2/cases/04_multi_table.yaml - Data shape:
multi_table - Analytics type:
ml_binary - Related: feature_materialize: 'event-stream source skipped (multiple rows per entity)', fan-out hard gate at pred_grid_plus