Data Science Skill
Version: 9.0 | Updated: 01-July-2026 | Architect: Karim Bhalwani |
Unified reference for exploratory data analysis, statistical inference, predictive modeling, time series forecasting, and experimentation. Covers the full DS workflow: from raw dataset to validated model and reproducible artifact.
Behavioral Directives
- Problem before model: never train a model before the business question, success metric, and data quality are confirmed. A 99% accurate model on the wrong question is worthless.
- EDA before modeling: always profile distributions, missingness, and outliers before feature engineering. Skipping EDA leaks bugs into every downstream step.
- Validation over fit: report cross-validated metrics, never just train-set scores. Use CV mean score as the iteration signal; hold out a test set untouched until the final evaluation (evaluated exactly once, after the keep/discard loop is complete).
- One clear approach: recommend a single algorithm path with rationale. Present an alternative to the user only when both conditions are true: (a) the primary evaluation metric (e.g., AUC-ROC for classification, RMSE for regression - use whatever metric was agreed for the task) differs by less than 5 percentage points in absolute terms (this governs whether to surface an alternative model to the user; it is separate from the 0.002 tie tolerance used for iteration keep/discard decisions in the Fixed Evaluation Harness), and (b) one model is natively interpretable (linear/logistic/decision tree) while the other is not (ensemble/neural net).
- One change per iteration: when iterating to improve a model, change exactly ONE category at a time (features OR algorithm OR hyperparameters OR preprocessing). Multi-variable changes destroy the learning signal: you cannot tell which change moved the score.
- Simplicity tie-breaker: when two model candidates produce equal scores within tolerance, the simpler one wins. See the Simplicity tie-breaker rules in the Fixed Evaluation Harness section for exact thresholds and examples.
- Notebook for exploration, script for production: use Jupyter cells for EDA and model experimentation; convert to clean
.py modules for reproducible artifacts.
Workflow Routing Guide
Start here: use the quick-reference table for the common case. Only read the numbered steps below if your situation doesn't match any table row exactly.
How to use the table: match your task and target type to find the workflow. Size and interpretability only matter for predictive modeling rows.
| Task |
Target |
Size |
Interpretability |
→ Workflow |
| Explore data |
- |
any |
any |
EDA |
| Test hypothesis |
- |
any |
any |
Statistical Testing |
| Predict numeric |
continuous |
any |
any |
Regression |
| Predict class |
binary |
any |
regulated |
Logistic + SHAP |
| Predict class |
binary |
any |
internal/research |
XGBoost |
| Predict class |
multi-class |
any |
any |
Multinomial |
| Forecast |
time series |
any |
any |
Time Series |
| A/B test |
- |
any |
any |
Experiment Design |
Edge-case fallback only - if no table row matches, answer each step independently before moving to the next:
Step 1: Task Type
What are you doing?
Step 2: Target Type (predictive modeling only)
What does the model predict?
- Continuous numeric → Regression (LinearRegression baseline → XGBoost / LightGBM for nonlinear)
- Binary categorical → Binary classification (LogisticRegression baseline → XGBoost; check class balance)
- Multi-class categorical → Multinomial classification (one-vs-rest LogisticRegression → XGBoost with
objective='multi:softprob')
- Ordinal categorical → Ordinal regression or binned regression (avoid naive multi-class; ordering matters)
Step 3: Dataset Size
How many rows?
- < 10K rows → Pandas + scikit-learn; use stratified k-fold CV
- 10K - 1M rows → Pandas + scikit-learn / XGBoost; consider feature subsampling
- > 1M rows → Sample for development, then scale via PySpark MLlib or Dask, OR delegate ETL to
data-engineer
Step 4: Interpretability Requirement
Who will use the output and in what domain?
- Regulated domain (credit, healthcare, hiring) → Linear/logistic models or tree models with SHAP explanations
- Internal tool → Any model; provide SHAP summary for top features
- Research / exploratory → Any model; document assumptions and limitations
EDA Workflow
The non-negotiable profiling sequence before any modeling:
- Shape and types:
df.shape, df.dtypes, sample 10 rows
- Missing data: per-column null counts and percentages; flag columns > 30% missing
- Distributions: histograms for numerics, value counts for categoricals; flag skew and rare categories
- Outliers: IQR or z-score on numerics; visualize with boxplots
- Correlations: pairwise correlations for numerics; flag |r| > 0.9 for multicollinearity
- Target leakage check: any feature suspiciously correlated with target (|r| > 0.95) is likely leaked
- Class balance (classification only): document target distribution; flag minority class < 10%
If the dataset has already been split or partially processed, explicitly audit what transformations have been applied, whether split happened before or after those transformations, and whether EDA was performed on the full dataset or only the training fold. Document findings before proceeding.
Full EDA checklist with code patterns and visualization recipes: references/eda-reference.md
Statistical Testing
Test selection by question type:
- "Is mean of A different from B?" (2 groups, numeric) → Welch's t-test (do not assume equal variance)
- "Are groups A, B, C different?" (3+ groups, numeric) → One-way ANOVA → Tukey HSD post-hoc
- "Is proportion A different from B?" (2 groups, binary) → Two-proportion z-test or chi-square
- "Are categorical variables independent?" (categorical x categorical) → Chi-square test of independence
- "Non-normal distributions or small n?" → Mann-Whitney U (2 groups), Kruskal-Wallis (3+ groups)
- "Paired observations?" (before/after) → Paired t-test or Wilcoxon signed-rank
Always report effect size (Cohen's d, odds ratio, eta-squared), not just p-value. Run power analysis before declaring "no significant difference."
Full test selection tree, power analysis code, and assumption checks: references/statistical-testing-reference.md
Predictive Modeling
Standard workflow:
# 1. Split BEFORE feature engineering (avoid leakage)
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42, stratify=y # stratify for classification
)
# 2. Pipeline: impute -> encode -> scale -> model (single object, fit once)
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from xgboost import XGBClassifier
preprocessor = ColumnTransformer([
("num", Pipeline([("imp", SimpleImputer(strategy="median")), ("sc", StandardScaler())]), num_cols),
("cat", Pipeline([("imp", SimpleImputer(strategy="most_frequent")), ("oh", OneHotEncoder(handle_unknown="ignore"))]), cat_cols),
])
model = Pipeline([
("prep", preprocessor),
("clf", XGBClassifier(n_estimators=200, max_depth=6, learning_rate=0.1, random_state=42)),
])
# 3. Cross-validate on train; fit on full train; evaluate ONCE on test
from sklearn.model_selection import cross_val_score
cv_scores = cross_val_score(model, X_train, y_train, cv=5, scoring="roc_auc")
print(f"CV AUC: {cv_scores.mean():.3f} +/- {cv_scores.std():.3f}")
model.fit(X_train, y_train)
test_score = model.score(X_test, y_test)
Key rules: split before feature engineering, use Pipeline objects (no leakage from scaling/imputing on full data), cross-validate on train only, evaluate on test exactly once.
Full guide: algorithm selection, hyperparameter tuning, SHAP explainability, calibration, imbalanced classes: references/modeling-reference.md
Time Series
Forecasting decision tree:
- Strong seasonality + holidays + business calendar → Prophet (
pip install prophet)
- Stationary, single seasonality, no exogenous → SARIMA via statsmodels
- Multiple related series, exogenous variables, large data → Gradient-boosted regression on lag features (XGBoost / LightGBM)
- Hierarchical (store-level + region-level) → Reconcile with hts library or hierarchical Prophet
Always: decompose into trend + seasonality + residual first; check stationarity (ADF test) for ARIMA family; use time-based train/test split (NEVER random shuffle).
Full reference: ARIMA parameter selection, Prophet tuning, residual diagnostics, forecast evaluation metrics (MAPE, sMAPE, MASE): references/time-series-reference.md
Experiment Design (A/B Testing)
Pre-experiment checklist:
- Define primary metric: one metric, pre-registered. Secondary metrics are descriptive only.
- Calculate sample size: power analysis with realistic minimum detectable effect (MDE). Use the
validate_experiment.py script.
- Define guardrails: secondary metrics that must not regress (latency, error rate, revenue per user)
- Define randomization unit: user, session, or request? Must be the same as the analysis unit.
- Define test duration: minimum 1 week (capture day-of-week effects); never stop early on positive results (peeking inflates false positives)
Post-experiment validation:
- SRM check: chi-square test on observed vs expected traffic split. p < 0.001 means randomization is broken; results are invalid.
- Novelty effect: compare week 1 vs week 2 results; large drops indicate novelty effect, not real impact.
- Heterogeneous effects: segment by user cohort but treat as exploratory (multiple comparison problem).
Full reference: power analysis formulas, SRM detection, sequential testing, multi-armed bandits: references/experiment-design-reference.md
Fixed Evaluation Harness (Hill-Climbing Protocol)
When iterating to improve a model, treat the project as a hill-climbing loop with a strict separation between fixed and editable surfaces.
The Boundary
| Surface |
Status |
Examples |
| FIXED (do not change while iterating) |
Frozen at problem-definition time |
Test set rows, primary metric definition, target definition, business question, evaluation script |
| EDITABLE (the optimization surface) |
Changes one category at a time |
Feature engineering, algorithm choice, hyperparameters, preprocessing, sampling strategy |
If the test set or metric changes, all prior scores become incomparable. Treat the harness as immutable for the duration of the iteration loop. If the metric must change (e.g., business pivot), restart the ledger with a new file and a documented reason.
Keep / Discard Rules
After every iteration:
| Outcome |
Decision |
| CV mean score improves beyond tie tolerance (default 0.002) |
Keep |
| CV mean score equal within tolerance AND candidate is simpler |
Keep (simplification win) |
| CV mean score equal within tolerance, no simplification |
Discard |
| CV mean score degrades beyond tie tolerance |
Discard |
The held-out test set is evaluated exactly once after the keep/discard loop is complete, consistent with the Validation over fit directive.
"Simpler" means: fewer features, fewer preprocessing steps, smaller hyperparameter footprint, or removing custom code. A 0.001 drop from deleting 50 lines of preprocessing is a clear keep. A 0.001 gain from adding 50 lines is a clear discard.
One Change Per Iteration
Every iteration changes ONE category from the editable surface:
- features (added, removed, transformed)
- algorithm (e.g., LogisticRegression -> XGBoost)
- hyperparameters
- preprocessing (encoder swap, imputation strategy)
- data (more rows, different sampling)
- evaluation (CV strategy change - rare; usually counts as a harness change)
Multi-variable changes destroy the learning signal. If you change features AND swap the algorithm in the same iteration, you cannot tell which moved the score.
Generalization Gate
Before keeping any change, ask:
"If this exact dataset disappeared and was replaced with a refresh, would this change still be a worthwhile improvement?"
If no, the change is overfit to a quirk of the current data and should be discarded even if the score moved up. Common red flags: a feature that mirrors a single anomalous date, a hyperparameter tuned to a specific fold split, a preprocessing step that only helps because of a known data error upstream.
The Ledger
Every iteration is logged via scripts/experiment_log.py with:
- algorithm, primary metric, CV mean/std, test score
- change category (features | algorithm | hyperparameters | preprocessing | data | evaluation)
- Keep / Discard decision and rationale
- description (what changed, why, what was expected)
The ledger is append-only; never edit past entries. Discarded runs stay logged because they carry signal: which features did NOT help is as valuable as which did.
One ledger file = one fixed harness. A ledger represents a single (test set + primary metric + target definition) tuple. If any of those change, start a new ledger file with a new path (e.g., model_log_v2_pr_auc.jsonl). The script prints a warning to stderr when a logged entry's metric does not match the most recent entry in the file. Mixing metrics or test sets in one ledger destroys the comparability that the keep/discard rules depend on.
Failure Diagnosis Before Fixing
When a model underperforms, classify the failure before changing anything. The 6 categories are documented in references/modeling-reference.md under "Model Failure Taxonomy". Fix the class of failure, not the specific symptom.
Code Standards
- Reproducibility: set
random_state / random_seed in every randomized operation
- No leakage: split data before any fit, transform, or imputation
- Pipelines over loose steps: scikit-learn
Pipeline and ColumnTransformer for any multi-step preprocessing
- Type hints on production scripts:
def train(X: pd.DataFrame, y: pd.Series) -> Pipeline:
- Notebook hygiene: each notebook starts with imports + config; clear all outputs before commit (or use
nbstripout)
- Version data and models: log dataset hash, model artifact path, and metric scores together
Definition of Done
When to Load This Skill
- Profiling a new dataset (EDA)
- Running hypothesis tests on business data
- Building classification or regression models
- Forecasting time series (sales, demand, KPIs)
- Designing or analyzing A/B experiments
- Reviewing a colleague's statistical methodology, model choices, or experiment design for correctness (not code style - use guardian for code review)
Constraints
- Does NOT design data ingestion pipelines (use
data-engineering skill)
- Does NOT write SQL against production databases (use
data-analyst skill)
- Does NOT serve models behind APIs or LLM agents (use
llm-app-patterns skill or ai-engineer agent)
- Does NOT manage MLOps infrastructure or CI/CD (use
ops skill)
- Does NOT skip EDA, even on a familiar dataset (data drifts; assumptions break)
Common Traps
| Trap |
Why It Fails |
Correct Approach |
| Fitting scaler/imputer on full data before split |
Test set statistics leak into training; CV scores are optimistic |
Fit preprocessing inside a Pipeline; train/test split first |
| Random shuffle on time series |
Future data leaks into training; forecast appears unrealistically accurate |
Use TimeSeriesSplit or chronological split |
| Reporting only training accuracy |
Hides overfitting; no signal on generalization |
Always report cross-validated and held-out test metrics |
| Stopping A/B test early on a positive result ("peeking") |
Inflates false positive rate well above 5% |
Pre-register sample size; only stop at planned duration or via sequential test correction |
| Using accuracy on imbalanced classes |
95% accuracy meaningless when 95% is majority class |
Use ROC-AUC, PR-AUC, F1, or class-weighted metrics |
| Treating p-value as effect size |
Statistically significant != practically significant |
Always report effect size (Cohen's d, lift %, odds ratio) alongside p-value |
| Multiple comparisons without correction |
Family-wise error rate explodes; spurious "discoveries" |
Apply Bonferroni or Benjamini-Hochberg when testing > 1 hypothesis |
| One-hot encoding high-cardinality categoricals |
Curse of dimensionality; sparse, slow models |
Use target encoding, frequency encoding, or embeddings |
| Tuning hyperparameters on test set |
Test set becomes a second train set; final score is optimistic |
Tune on validation (or via CV); test set is touched once |
| Imputing target leakage features |
Future-only signals (e.g., total_spend) imputed for past records corrupt training |
Audit each feature: would this value be available at prediction time? |
| Ignoring SRM in A/B tests |
Broken randomization makes results invalid; you can't fix it post-hoc |
Run SRM check (chi-square) before reporting any A/B result |
| Production model with no monitoring plan |
Silent drift; model decays without anyone noticing |
Define data-drift and performance-drift alerts before handoff to deployment |
Integration Points
- architect: Provides ML system design specs (input/output contracts, latency budgets, retraining cadence)
- data-engineer: Builds the upstream pipeline that feeds clean data to the model
- ai-engineer: Wraps trained models behind RAG / agent / API surfaces; takes over at deployment boundary
- data-analyst: Sources curated SQL datasets for analysis; receives DS findings for business reporting
- guardian: Reviews modeling code for leakage, reproducibility, and statistical validity
- ops: Owns MLOps infrastructure (CI/CD, model registry, monitoring) once the model artifact is signed off
References
Load these on demand for deep-dive guidance. Read the relevant reference before writing code for that domain.
Reference Guides
- EDA Reference - profiling checklist, distribution analysis, missingness patterns, outlier detection, leakage diagnostics
- Statistical Testing Reference - test selection tree, power analysis, assumption checks, effect sizes, multiple comparison correction
- Modeling Reference - algorithm selection, hyperparameter tuning, SHAP explainability, calibration, imbalanced classes
- Time Series Reference - decomposition, ARIMA vs Prophet, exogenous variables, forecast evaluation, residual diagnostics
- Experiment Design Reference - power analysis, SRM detection, sequential testing, novelty effects, multi-armed bandits
Scripts
- validate_experiment.py - SRM (Sample Ratio Mismatch) detector and minimum-sample-size calculator. Run before launching an A/B test (sample size) and before reading results (SRM).
- model_eval_report.py - generates a structured model evaluation report (CV scores, test metrics, feature importance, calibration). Produces agent-legible findings.
- experiment_log.py - append-only model iteration ledger (JSON Lines). Records every model candidate with metrics, change category, and Keep/Discard decision. Implements the fixed-harness keep/discard rules so iteration history is auditable.
1---2name: data-science3description: Comprehensive data science reference covering exploratory data analysis, statistical hypothesis testing, predictive modeling with scikit-learn/XGBoost, time series forecasting, and A/B experiment design. Use when profiling datasets, running hypothesis tests, building classification/regression models, forecasting time series, designing experiments, or interpreting model results. DO NOT USE FOR: ad-hoc SQL querying (use data-analyst), ETL/ELT pipeline construction (use data-engineering), LLM/RAG application design (use llm-app-patterns), MLOps and deployment automation (use ops), or pure code review (use guardian).4license: MIT5---67# Data Science Skill89> Version: 9.0 | Updated: 01-July-2026 | Architect: Karim Bhalwani |1011Unified reference for exploratory data analysis, statistical inference, predictive modeling, time series forecasting, and experimentation. Covers the full DS workflow: from raw dataset to validated model and reproducible artifact.1213## Behavioral Directives1415- **Problem before model**: never train a model before the business question, success metric, and data quality are confirmed. A 99% accurate model on the wrong question is worthless.16- **EDA before modeling**: always profile distributions, missingness, and outliers before feature engineering. Skipping EDA leaks bugs into every downstream step.17- **Validation over fit**: report cross-validated metrics, never just train-set scores. Use CV mean score as the iteration signal; hold out a test set untouched until the final evaluation (evaluated exactly once, after the keep/discard loop is complete).18- **One clear approach**: recommend a single algorithm path with rationale. Present an alternative to the user only when both conditions are true: (a) the primary evaluation metric (e.g., AUC-ROC for classification, RMSE for regression - use whatever metric was agreed for the task) differs by less than 5 percentage points in absolute terms (this governs whether to surface an alternative model to the user; it is separate from the 0.002 tie tolerance used for iteration keep/discard decisions in the Fixed Evaluation Harness), and (b) one model is natively interpretable (linear/logistic/decision tree) while the other is not (ensemble/neural net).19- **One change per iteration**: when iterating to improve a model, change exactly ONE category at a time (features OR algorithm OR hyperparameters OR preprocessing). Multi-variable changes destroy the learning signal: you cannot tell which change moved the score.20- **Simplicity tie-breaker**: when two model candidates produce equal scores within tolerance, the simpler one wins. See the Simplicity tie-breaker rules in the Fixed Evaluation Harness section for exact thresholds and examples.21- **Notebook for exploration, script for production**: use Jupyter cells for EDA and model experimentation; convert to clean `.py` modules for reproducible artifacts.2223## Workflow Routing Guide2425**Start here**: use the quick-reference table for the common case. Only read the numbered steps below if your situation doesn't match any table row exactly.2627> **How to use the table**: match your task and target type to find the workflow. Size and interpretability only matter for predictive modeling rows.2829| Task | Target | Size | Interpretability | → Workflow |30| --------------- | ----------- | ---- | ----------------- | ------------------- |31| Explore data | - | any | any | EDA |32| Test hypothesis | - | any | any | Statistical Testing |33| Predict numeric | continuous | any | any | Regression |34| Predict class | binary | any | regulated | Logistic + SHAP |35| Predict class | binary | any | internal/research | XGBoost |36| Predict class | multi-class | any | any | Multinomial |37| Forecast | time series | any | any | Time Series |38| A/B test | - | any | any | Experiment Design |3940**Edge-case fallback only** - if no table row matches, answer each step independently before moving to the next:4142### Step 1: Task Type4344What are you doing?4546- **Understand the data** (no model yet) → Use **EDA Workflow** below + [eda-reference.md](./references/eda-reference.md)47- **Test a hypothesis** (is A different from B?) → Use **Statistical Testing** below + [statistical-testing-reference.md](./references/statistical-testing-reference.md)48- **Predict a value or class** → Use **Predictive Modeling** below + [modeling-reference.md](./references/modeling-reference.md)49- **Forecast a future time series** → Use **Time Series** below + [time-series-reference.md](./references/time-series-reference.md)50- **Design or analyze an A/B test** → Use **Experiment Design** below + [experiment-design-reference.md](./references/experiment-design-reference.md)5152### Step 2: Target Type (predictive modeling only)5354What does the model predict?5556- **Continuous numeric** → Regression (LinearRegression baseline → XGBoost / LightGBM for nonlinear)57- **Binary categorical** → Binary classification (LogisticRegression baseline → XGBoost; check class balance)58- **Multi-class categorical** → Multinomial classification (one-vs-rest LogisticRegression → XGBoost with `objective='multi:softprob'`)59- **Ordinal categorical** → Ordinal regression or binned regression (avoid naive multi-class; ordering matters)6061### Step 3: Dataset Size6263How many rows?6465- **< 10K rows** → Pandas + scikit-learn; use stratified k-fold CV66- **10K - 1M rows** → Pandas + scikit-learn / XGBoost; consider feature subsampling67- **> 1M rows** → Sample for development, then scale via PySpark MLlib or Dask, OR delegate ETL to `data-engineer`6869### Step 4: Interpretability Requirement7071Who will use the output and in what domain?7273- **Regulated domain** (credit, healthcare, hiring) → Linear/logistic models or tree models with SHAP explanations74- **Internal tool** → Any model; provide SHAP summary for top features75- **Research / exploratory** → Any model; document assumptions and limitations7677## EDA Workflow7879The non-negotiable profiling sequence before any modeling:80811. **Shape and types**: `df.shape`, `df.dtypes`, sample 10 rows822. **Missing data**: per-column null counts and percentages; flag columns > 30% missing833. **Distributions**: histograms for numerics, value counts for categoricals; flag skew and rare categories844. **Outliers**: IQR or z-score on numerics; visualize with boxplots855. **Correlations**: pairwise correlations for numerics; flag |r| > 0.9 for multicollinearity866. **Target leakage check**: any feature suspiciously correlated with target (|r| > 0.95) is likely leaked877. **Class balance** (classification only): document target distribution; flag minority class < 10%8889> If the dataset has already been split or partially processed, explicitly audit what transformations have been applied, whether split happened before or after those transformations, and whether EDA was performed on the full dataset or only the training fold. Document findings before proceeding.9091> Full EDA checklist with code patterns and visualization recipes: [references/eda-reference.md](./references/eda-reference.md)9293## Statistical Testing9495Test selection by question type:9697- **"Is mean of A different from B?"** (2 groups, numeric) → Welch's t-test (do not assume equal variance)98- **"Are groups A, B, C different?"** (3+ groups, numeric) → One-way ANOVA → Tukey HSD post-hoc99- **"Is proportion A different from B?"** (2 groups, binary) → Two-proportion z-test or chi-square100- **"Are categorical variables independent?"** (categorical x categorical) → Chi-square test of independence101- **"Non-normal distributions or small n?"** → Mann-Whitney U (2 groups), Kruskal-Wallis (3+ groups)102- **"Paired observations?"** (before/after) → Paired t-test or Wilcoxon signed-rank103104Always report effect size (Cohen's d, odds ratio, eta-squared), not just p-value. Run power analysis before declaring "no significant difference."105106> Full test selection tree, power analysis code, and assumption checks: [references/statistical-testing-reference.md](./references/statistical-testing-reference.md)107108## Predictive Modeling109110Standard workflow:111112```python113# 1. Split BEFORE feature engineering (avoid leakage)114from sklearn.model_selection import train_test_split115X_train, X_test, y_train, y_test = train_test_split(116 X, y, test_size=0.2, random_state=42, stratify=y # stratify for classification117)118119# 2. Pipeline: impute -> encode -> scale -> model (single object, fit once)120from sklearn.pipeline import Pipeline121from sklearn.preprocessing import StandardScaler, OneHotEncoder122from sklearn.compose import ColumnTransformer123from sklearn.impute import SimpleImputer124from xgboost import XGBClassifier125126preprocessor = ColumnTransformer([127 ("num", Pipeline([("imp", SimpleImputer(strategy="median")), ("sc", StandardScaler())]), num_cols),128 ("cat", Pipeline([("imp", SimpleImputer(strategy="most_frequent")), ("oh", OneHotEncoder(handle_unknown="ignore"))]), cat_cols),129])130131model = Pipeline([132 ("prep", preprocessor),133 ("clf", XGBClassifier(n_estimators=200, max_depth=6, learning_rate=0.1, random_state=42)),134])135136# 3. Cross-validate on train; fit on full train; evaluate ONCE on test137from sklearn.model_selection import cross_val_score138cv_scores = cross_val_score(model, X_train, y_train, cv=5, scoring="roc_auc")139print(f"CV AUC: {cv_scores.mean():.3f} +/- {cv_scores.std():.3f}")140141model.fit(X_train, y_train)142test_score = model.score(X_test, y_test)143```144145Key rules: split before feature engineering, use Pipeline objects (no leakage from scaling/imputing on full data), cross-validate on train only, evaluate on test exactly once.146147> Full guide: algorithm selection, hyperparameter tuning, SHAP explainability, calibration, imbalanced classes: [references/modeling-reference.md](./references/modeling-reference.md)148149## Time Series150151Forecasting decision tree:152153- **Strong seasonality + holidays + business calendar** → Prophet (`pip install prophet`)154- **Stationary, single seasonality, no exogenous** → SARIMA via statsmodels155- **Multiple related series, exogenous variables, large data** → Gradient-boosted regression on lag features (XGBoost / LightGBM)156- **Hierarchical (store-level + region-level)** → Reconcile with hts library or hierarchical Prophet157158Always: decompose into trend + seasonality + residual first; check stationarity (ADF test) for ARIMA family; use time-based train/test split (NEVER random shuffle).159160> Full reference: ARIMA parameter selection, Prophet tuning, residual diagnostics, forecast evaluation metrics (MAPE, sMAPE, MASE): [references/time-series-reference.md](./references/time-series-reference.md)161162## Experiment Design (A/B Testing)163164Pre-experiment checklist:1651661. **Define primary metric**: one metric, pre-registered. Secondary metrics are descriptive only.1672. **Calculate sample size**: power analysis with realistic minimum detectable effect (MDE). Use the `validate_experiment.py` script.1683. **Define guardrails**: secondary metrics that must not regress (latency, error rate, revenue per user)1694. **Define randomization unit**: user, session, or request? Must be the same as the analysis unit.1705. **Define test duration**: minimum 1 week (capture day-of-week effects); never stop early on positive results (peeking inflates false positives)171172Post-experiment validation:173174- **SRM check**: chi-square test on observed vs expected traffic split. p < 0.001 means randomization is broken; results are invalid.175- **Novelty effect**: compare week 1 vs week 2 results; large drops indicate novelty effect, not real impact.176- **Heterogeneous effects**: segment by user cohort but treat as exploratory (multiple comparison problem).177178> Full reference: power analysis formulas, SRM detection, sequential testing, multi-armed bandits: [references/experiment-design-reference.md](./references/experiment-design-reference.md)179180## Fixed Evaluation Harness (Hill-Climbing Protocol)181182When iterating to improve a model, treat the project as a hill-climbing loop with a strict separation between fixed and editable surfaces.183184### The Boundary185186| Surface | Status | Examples |187| ----------------------------------------- | --------------------------------- | ------------------------------------------------------------------------------------------------- |188| **FIXED** (do not change while iterating) | Frozen at problem-definition time | Test set rows, primary metric definition, target definition, business question, evaluation script |189| **EDITABLE** (the optimization surface) | Changes one category at a time | Feature engineering, algorithm choice, hyperparameters, preprocessing, sampling strategy |190191If the test set or metric changes, all prior scores become incomparable. Treat the harness as immutable for the duration of the iteration loop. If the metric must change (e.g., business pivot), restart the ledger with a new file and a documented reason.192193### Keep / Discard Rules194195After every iteration:196197| Outcome | Decision |198| ------------------------------------------------------------- | ----------------------------- |199| CV mean score improves beyond tie tolerance (default 0.002) | **Keep** |200| CV mean score equal within tolerance AND candidate is simpler | **Keep** (simplification win) |201| CV mean score equal within tolerance, no simplification | **Discard** |202| CV mean score degrades beyond tie tolerance | **Discard** |203204> The held-out test set is evaluated exactly once after the keep/discard loop is complete, consistent with the Validation over fit directive.205206"Simpler" means: fewer features, fewer preprocessing steps, smaller hyperparameter footprint, or removing custom code. A 0.001 drop from deleting 50 lines of preprocessing is a clear keep. A 0.001 gain from adding 50 lines is a clear discard.207208### One Change Per Iteration209210Every iteration changes ONE category from the editable surface:211212- features (added, removed, transformed)213- algorithm (e.g., LogisticRegression -> XGBoost)214- hyperparameters215- preprocessing (encoder swap, imputation strategy)216- data (more rows, different sampling)217- evaluation (CV strategy change - rare; usually counts as a harness change)218219Multi-variable changes destroy the learning signal. If you change features AND swap the algorithm in the same iteration, you cannot tell which moved the score.220221### Generalization Gate222223Before keeping any change, ask:224225> "If this exact dataset disappeared and was replaced with a refresh, would this change still be a worthwhile improvement?"226227If no, the change is overfit to a quirk of the current data and should be discarded even if the score moved up. Common red flags: a feature that mirrors a single anomalous date, a hyperparameter tuned to a specific fold split, a preprocessing step that only helps because of a known data error upstream.228229### The Ledger230231Every iteration is logged via [scripts/experiment_log.py](./scripts/experiment_log.py) with:232233- algorithm, primary metric, CV mean/std, test score234- change category (features | algorithm | hyperparameters | preprocessing | data | evaluation)235- Keep / Discard decision and rationale236- description (what changed, why, what was expected)237238The ledger is append-only; never edit past entries. Discarded runs stay logged because they carry signal: which features did NOT help is as valuable as which did.239240**One ledger file = one fixed harness.** A ledger represents a single (test set + primary metric + target definition) tuple. If any of those change, **start a new ledger file with a new path** (e.g., `model_log_v2_pr_auc.jsonl`). The script prints a warning to stderr when a logged entry's metric does not match the most recent entry in the file. Mixing metrics or test sets in one ledger destroys the comparability that the keep/discard rules depend on.241242### Failure Diagnosis Before Fixing243244When a model underperforms, classify the failure before changing anything. The 6 categories are documented in [references/modeling-reference.md](./references/modeling-reference.md) under "Model Failure Taxonomy". Fix the class of failure, not the specific symptom.245246## Code Standards247248- **Reproducibility**: set `random_state` / `random_seed` in every randomized operation249- **No leakage**: split data before any fit, transform, or imputation250- **Pipelines over loose steps**: scikit-learn `Pipeline` and `ColumnTransformer` for any multi-step preprocessing251- **Type hints on production scripts**: `def train(X: pd.DataFrame, y: pd.Series) -> Pipeline:`252- **Notebook hygiene**: each notebook starts with imports + config; clear all outputs before commit (or use `nbstripout`)253- **Version data and models**: log dataset hash, model artifact path, and metric scores together254255## Definition of Done256257- [ ] Business question, success metric, and data inventory documented before modeling started258- [ ] EDA artifact saved (notebook or report) covering shape, missingness, distributions, correlations259- [ ] Train/validation/test split applied BEFORE feature engineering (no leakage)260- [ ] Cross-validation scores reported with mean and standard deviation261- [ ] Test set evaluated exactly once, after model selection262- [ ] Feature importance / SHAP summary produced for the chosen model263- [ ] Reproducibility: `random_state` set everywhere; environment captured (`requirements.txt` or `uv.lock`)264- [ ] Limitations and assumptions documented in the final report265- [ ] When iterating to improve a model: every iteration (Keep AND Discard) logged to the experiment ledger via `scripts/experiment_log.py`, with change category and decision rationale266267## When to Load This Skill268269- Profiling a new dataset (EDA)270- Running hypothesis tests on business data271- Building classification or regression models272- Forecasting time series (sales, demand, KPIs)273- Designing or analyzing A/B experiments274- Reviewing a colleague's statistical methodology, model choices, or experiment design for correctness (not code style - use guardian for code review)275276## Constraints277278- Does NOT design data ingestion pipelines (use `data-engineering` skill)279- Does NOT write SQL against production databases (use `data-analyst` skill)280- Does NOT serve models behind APIs or LLM agents (use `llm-app-patterns` skill or `ai-engineer` agent)281- Does NOT manage MLOps infrastructure or CI/CD (use `ops` skill)282- Does NOT skip EDA, even on a familiar dataset (data drifts; assumptions break)283284## Common Traps285286| Trap | Why It Fails | Correct Approach |287| -------------------------------------------------------- | ----------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |288| Fitting scaler/imputer on full data before split | Test set statistics leak into training; CV scores are optimistic | Fit preprocessing inside a `Pipeline`; train/test split first |289| Random shuffle on time series | Future data leaks into training; forecast appears unrealistically accurate | Use `TimeSeriesSplit` or chronological split |290| Reporting only training accuracy | Hides overfitting; no signal on generalization | Always report cross-validated and held-out test metrics |291| Stopping A/B test early on a positive result ("peeking") | Inflates false positive rate well above 5% | Pre-register sample size; only stop at planned duration or via sequential test correction |292| Using accuracy on imbalanced classes | 95% accuracy meaningless when 95% is majority class | Use ROC-AUC, PR-AUC, F1, or class-weighted metrics |293| Treating p-value as effect size | Statistically significant != practically significant | Always report effect size (Cohen's d, lift %, odds ratio) alongside p-value |294| Multiple comparisons without correction | Family-wise error rate explodes; spurious "discoveries" | Apply Bonferroni or Benjamini-Hochberg when testing > 1 hypothesis |295| One-hot encoding high-cardinality categoricals | Curse of dimensionality; sparse, slow models | Use target encoding, frequency encoding, or embeddings |296| Tuning hyperparameters on test set | Test set becomes a second train set; final score is optimistic | Tune on validation (or via CV); test set is touched once |297| Imputing target leakage features | Future-only signals (e.g., `total_spend`) imputed for past records corrupt training | Audit each feature: would this value be available at prediction time? |298| Ignoring SRM in A/B tests | Broken randomization makes results invalid; you can't fix it post-hoc | Run SRM check (chi-square) before reporting any A/B result |299| Production model with no monitoring plan | Silent drift; model decays without anyone noticing | Define data-drift and performance-drift alerts before handoff to deployment |300301## Integration Points302303- **architect**: Provides ML system design specs (input/output contracts, latency budgets, retraining cadence)304- **data-engineer**: Builds the upstream pipeline that feeds clean data to the model305- **ai-engineer**: Wraps trained models behind RAG / agent / API surfaces; takes over at deployment boundary306- **data-analyst**: Sources curated SQL datasets for analysis; receives DS findings for business reporting307- **guardian**: Reviews modeling code for leakage, reproducibility, and statistical validity308- **ops**: Owns MLOps infrastructure (CI/CD, model registry, monitoring) once the model artifact is signed off309310## References311312Load these on demand for deep-dive guidance. Read the relevant reference before writing code for that domain.313314### Reference Guides315316- [EDA Reference](./references/eda-reference.md) - profiling checklist, distribution analysis, missingness patterns, outlier detection, leakage diagnostics317- [Statistical Testing Reference](./references/statistical-testing-reference.md) - test selection tree, power analysis, assumption checks, effect sizes, multiple comparison correction318- [Modeling Reference](./references/modeling-reference.md) - algorithm selection, hyperparameter tuning, SHAP explainability, calibration, imbalanced classes319- [Time Series Reference](./references/time-series-reference.md) - decomposition, ARIMA vs Prophet, exogenous variables, forecast evaluation, residual diagnostics320- [Experiment Design Reference](./references/experiment-design-reference.md) - power analysis, SRM detection, sequential testing, novelty effects, multi-armed bandits321322### Scripts323324- [validate_experiment.py](./scripts/validate_experiment.py) - SRM (Sample Ratio Mismatch) detector and minimum-sample-size calculator. Run before launching an A/B test (sample size) and before reading results (SRM).325- [model_eval_report.py](./scripts/model_eval_report.py) - generates a structured model evaluation report (CV scores, test metrics, feature importance, calibration). Produces agent-legible findings.326- [experiment_log.py](./scripts/experiment_log.py) - append-only model iteration ledger (JSON Lines). Records every model candidate with metrics, change category, and Keep/Discard decision. Implements the fixed-harness keep/discard rules so iteration history is auditable.