skills/tabular/successive-groupby-aggregates/SKILL.md
Build hierarchical features for transaction panels by aggregating twice — first groupby (entity, sub-key) to get a per-(entity, sub-key) summary, then groupby (entity) on those summaries to compute mean/min/max/std across the sub-keys, capturing the *distribution* of per-customer behavior rather than a single flat mean
npx skillsauth add wenmin-wu/ds-skills tabular-successive-groupby-aggregatesInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
A flat df.groupby('card_id').sum() collapses a customer's whole transaction history into one number and throws away how that history is distributed across categories, months, or merchants. Two-step (successive) groupby keeps the distribution. Step 1: group by (card_id, sub_key) and compute a per-bucket statistic (sum, mean, count). Step 2: group the result by card_id alone and compute mean/min/max/std/skew across the buckets. The final feature for a card is no longer "average spend" but "average-of-monthly-spends, std-of-monthly-spends, max-monthly-spend" — features that distinguish a steady spender from a one-off whale even when their totals match. Every winning Elo Merchant solution leaned on this pattern.
import pandas as pd
# Step 1 — per (card, month_lag) summary
per_lag = (
tx.groupby(['card_id', 'month_lag'])
.agg(sum_amt=('purchase_amount', 'sum'),
cnt=('purchase_amount', 'size'),
nuniq_merch=('merchant_id', 'nunique'))
.reset_index()
)
# Step 2 — distribution of those summaries per card
feat = per_lag.groupby('card_id').agg(
lag_sum_mean=('sum_amt', 'mean'),
lag_sum_std=('sum_amt', 'std'),
lag_sum_max=('sum_amt', 'max'),
lag_cnt_mean=('cnt', 'mean'),
lag_nuniq_merch_mean=('nuniq_merch', 'mean'),
).reset_index()
card_id, user_id) and the sub-key (month_lag, category_2, merchant_id)(entity, sub_key) and compute base aggregates (sum, count, nunique, mean)entity and compute distribution stats (mean, std, min, max, skew) across the sub-key axislag_sum_std, cat_cnt_maxmonth_lag, category_*, merchant_id, weekdaystd in step 2: it is the single most predictive feature — distinguishes consistent vs. spiky behavior.historical_transactions and new_merchant_transactions get their own feature block; never concatenate them before aggregating.nunique is expensive: use it only on the entity-level pass, not in the inner loop.data-ai
Scaled Pinball Loss (SPL) metric for evaluating quantile forecasts, normalized by mean absolute successive differences of training data
data-ai
Walk backward through a time series and multiplicatively rescale segments when jumps exceed a fraction of the running mean to correct data collection anomalies
testing
Transform forecasting target to next/current ratio minus one so that optimizing MAE or squared error implicitly minimizes SMAPE
tools
Convert point forecasts to prediction intervals by scaling with logit-transformed quantile ratios passed through a Normal CDF