npx skills add ...
npx skills add aperivue/medsci-skills --skill analyze-stats
Use when data needs statistical analysis. Runs reproducible Python/R code for Table 1, diagnostic accuracy, agreement, regression, survival, propensity score, survey-weighted and repeated-measures models, with publication tables. Sample size is /calc-sample-size; pooling studies is /meta-analysis.
npx skills add aperivue/medsci-skills --skill analyze-stats
Generate reproducible code (Python preferred, R when necessary) for medical research analyses, run it, and produce publication-ready tables and figures from its output.
Before reading any data file, check whether it might contain Protected Health Information (PHI):
*_deidentified.* files exist in the working directory, use those./deidentify./deidentify.${CLAUDE_SKILL_DIR}/references/templates/ — reusable analysis scripts; read the matching one
before generating code.${CLAUDE_SKILL_DIR}/references/analysis_guides/ — methodology guides; the Phase 2 table says
which to read.${CLAUDE_SKILL_DIR}/references/table-standards/ — table-standards.md (universal and AMA
rules, footnote order, gtsummary pipeline), journal-profiles/ (YAML per journal: radiology,
jama, nejm, lancet, european_radiology, ajr), table-types/ (one template per table type),
tool-comparison.md (R/Python table tools).${CLAUDE_SKILL_DIR}/references/style/figure_style.mplstyle — figure style.[VERIFY: variable_name] and ask the user to
confirm it against the data dictionary; never guess a column name or coding.Observational designs (cohort, case-control, cross-sectional, registry, survey): before
planning, look for a variable_operationalization.md from /define-variables (or an equivalent
codebook-backed definition table). If none exists, warn the user and recommend running
/define-variables first, because exposure/outcome/covariate definitions and cutoffs invented
ad hoc from the data dictionary are a common reason reviewers reject observational work. This is
a warning, not a block: proceed on explicit user confirmation and record that the artifact was
not available.
Propose an analysis plan; the user decides the research question and endpoints. Mark any
clinical definition, cutoff, diagnostic criterion or guideline claim you could not confirm
[VERIFY].
Detect the analysis type from the table in Analysis-Specific Guidelines, or accept the user's specification.
List the specific tests to be performed.
Identify primary and secondary endpoints.
State the assumptions that will be checked (normality, homogeneity, independence).
Note data cleaning needed (recoding, outlier handling, missing-data strategy).
Anchor the estimand to the research question.
interactionR / epiR; Knol & VanderWeele reporting), not only a
multiplicative product term, because a non-significant multiplicative interaction is
compatible with a large additive one (and vice versa). Joint categories (high/high vs
low/low) show joint association, not interaction; "stronger in A than in B" from separate
stratum estimates is the difference-in-significance fallacy — report the formal interaction
term.Screen every categorical/binary predictor for separation before fitting anything. A
predictor that perfectly predicts the outcome has no finite MLE, and the failure is silent:
glm returns an odds ratio near 0 (or enormous), p ≈ 0.99, and an AUC that ends up in a
table. Pathognomonic imaging signs (T2-FLAIR mismatch, the string sign, a halo sign) cause
it routinely: 100% specificity means an empty cell by construction.
COMPLETE_SEPARATION (an empty cell) and QUASI_SEPARATION (a cell below the sparsity
floor) both halt the plan. With two or more predictors the script also tests them jointly
(a linear combination can separate the outcome when no single predictor does); that test
needs scipy, and without it the report says the joint check was not run. The remedy is a design decision, made in the plan: Firth's
penalised likelihood keeps one model, while a two-stage rule — classify the sign-positive
cases directly, model only the sign-negative remainder — is usually the clinically meaningful
choice for a pathognomonic sign, because a sign-positive patient is already diagnosed.
Present the plan and wait for user approval before executing.
Generate and run a Python (preferred) or R script. Every number you report MUST come from its executed output, never estimated or hand-typed, because a number no script reproduces cannot be verified.
Start every script with a reproducibility header:
np.random.seed(42) or set.seed(42).Choose the summary and test from the design and the distribution, not a preliminary test:
|skewness| > 1 → median (IQR) with a
rank test, else mean (SD) (table-types/table1_demographics.md). Never gate on a Shapiro-Wilk or
KS P value: it flags trivial departures at large n, misses real ones at small n, and
test-then-test inflates the type I error (Rochon et al. 2012).sum(n per stratum) == unique N and
sum(events per stratum) == total events. A trend test on overlapping or non-exhaustive strata
is invalid. Print the per-stratum N/event table and the reconciliation (the analysis-side
mirror of /self-review check_cohort_arithmetic.py PARTITION_OVERLAP).max(0, lower). A zero-event
Wilson/score interval can print a negative or tiny-exponent bound (e.g. 3.47e-16) that is a
display artifact; report 0 (or 0.0%), and prefer an exact (Clopper-Pearson) interval for
zero/near-zero cells.After all analyses complete, save _analysis_outputs.md in the output directory, in the format
given in references/analysis_run_workflow.md, so
/make-figures and /write-paper can find the outputs without asking.
For prespecified binary predictions on independent units, use the bundled
scripts/run_analysis.py run workflow in that file: it embeds data/configuration/code/output
hashes, exact counts, metric-specific denominators and the reproduction command in the same
manifest (audit checks recorded versions without rewriting them; compare separates declared
context and numeric equality from byte drift). It does not select thresholds or establish study
validity, privacy clearance or reuse rights. Synthetic example:
python3 ${CLAUDE_SKILL_DIR}/scripts/demo_analysis_run.py --out demo-project.
Before reporting any script as final, lint every emitted .py/.R file:
Fix every Major before reporting the script: MISSING_SEED (randomness with no seed),
HARDCODED_DATA_LITERAL (hand-typed, table-shaped data instead of read_csv()/read.csv() +
subset), HARDCODED_ABS_PATH (non-portable and a PII risk), and INPLACE_SOURCE_OVERWRITE
(writing to the path read as input — never modify raw data; write derived outputs to a new
path). Fix the flags DEBUG_LEFTOVER and UNUSED_IMPORT when tidying.
Minor, Python only: API_DEFAULT_STUDENT_T (scipy's ttest_ind without equal_var is Student's
t; state Welch or the reason for Student) and API_DEFAULT_PENALIZED_OR (LogisticRegression
with none of penalty, C, l1_ratio, in a file that exponentiates a coef_). R's t.test()
is Welch by default.
After execution, generate manuscript-ready text from the script's output:
/search-lit confirmed; mark any other
[UNVERIFIED - NEEDS MANUAL CHECK].Report statistical results; leave judgments of clinical significance to the user. For designs this skill does not cover (adaptive or Bayesian trials, complex multilevel or causal-mediation models), say so and recommend biostatistician review before the results are reported.
statsmodels
TTestIndPower or scipy's nct); do not mix a normal approximation for some values with
exact-t for others. Do not report post-hoc (observed) power: it is a function of the P value
and adds nothing (Hoenig & Heisey 2001); report the CI of the effect instead./self-review treats a primary
metric without one as a defect. In the output CSV carry the interval as explicit columns
(estimate, ci_lower, ci_upper) or as one text column in est (lo–hi) form; never a point
estimate with no interval beside it. Round ORs/HRs/sHRs to 2 decimals and AUC/C-statistic
to 3.Whenever a primary result is a correlation, a standardized coefficient, a regression slope, an OR/HR/RR or a Cohen's d, also report it as a plain-language unit shift a non-statistician can act on — in addition to the effect size (rule 3), not instead of it. This applies to continuous exposure–outcome associations (Spearman's rho, Pearson's r, standardized slopes), to relative measures where the audience needs an absolute-risk feel, and to reader or expert-elicitation studies, clinical-utility framing, abstracts and figure captions.
delta_outcome = b * (x_p75 - x_p25). Keep the sign:
a negative b is a decrease.delta_outcome = beta * (x_p75 - x_p25) / SD_x; a
fully standardized beta is also multiplied by SD_outcome.b = r * SD_outcome / SD_x), which is the mean change per unit only if the relation is
linear; Spearman's rho converts to no change in outcome units. Fit the outcome on the
exposure and translate that slope (Theil-Sen if outliers are why Spearman was used); for a
monotonic but curved association, report the fitted outcome at x_p25 and at x_p75 instead.Output contract (clinical utility is a default, not an optional add-on):
references/table-standards/table-types/incremental_value.md and the make-figures
decision_curve exemplar (and render_core_figures.py for the rendered curve).install.packages() and wait for user confirmation.Code, tables, figures and console output are in English.
Before generating any publication table, load:
${CLAUDE_SKILL_DIR}/references/table-standards/journal-profiles/{journal}.yaml if a target
journal is known (it sets footnote markers, P and CI format, title and abbreviation order)${CLAUDE_SKILL_DIR}/references/table-standards/table-types/{type}.md for the table typeOutput formats (always generate all three): a CSV file (downstream use and archival), a
console markdown rendering (user review), and R gtsummary code (Word/LaTeX export). Read
${CLAUDE_SKILL_DIR}/references/table-standards/table-standards.md for the gtsummary pipeline
and the footnote placement order (general note, abbreviations, specific notes, probability
notes).
Validation checklist (run before finalizing any table):
PDF (vector) + PNG (300 DPI), styled with figure_style.mplstyle (Arial, colorblind-safe
palette); width 3.5 inches (single column) or 7.0 inches (double column); axis labels with
units.
Before generating code for any row, read the files listed for it (paths relative to
${CLAUDE_SKILL_DIR}); they carry the method rules this file does not repeat.
| Analysis type | Read before generating code | Template |
|---|---|---|
| Table 1 (demographics) | references/table-standards/table-types/table1_demographics.md | references/templates/table1_demographics.py |
| Diagnostic accuracy (Se, Sp, PPV, NPV, AUC) | references/analysis_guides/diagnostic_accuracy.md | references/templates/diagnostic_accuracy.py |
| Added value beyond a baseline model (ΔAUC, NRI/IDI, net benefit) | references/table-standards/table-types/incremental_value.md | — |
| Reader study (MRMC) | references/table-standards/table-types/reader_study.md | — |
| Prediction model (outputs a risk used for a decision) | references/analysis_guides/calibration.md | — |
| Inter-rater agreement (kappa, ICC, Bland–Altman) | references/analysis_guides/agreement_reliability.md, references/table-standards/table-types/agreement.md | references/templates/agreement_analysis.py |
| Meta-analysis (pairwise, single-arm proportion, DTA) | references/analysis_guides/meta_analysis.md | references/templates/dta_meta_analysis.R (DTA) |
| Network meta-analysis (≥3 interventions) | references/analysis_guides/network_meta_analysis.md | — |
| Health economic evaluation (CEA, CUA, budget impact) | references/analysis_guides/health_economic_evaluation.md | — |
| Survey/Likert (ordinal rating scales) | the Survey/Likert section below | references/templates/likert_summary.py |
| Survival, competing risks, interval-censored events | references/analysis_guides/survival.md, references/table-standards/table-types/survival_results.md | references/templates/survival_analysis.py (--cluster <id> for nested units) |
| Group comparison, correlation | references/analysis_guides/test_selection.md | — |
| Logistic or linear regression | references/analysis_guides/regression.md | references/templates/regression.py (regression_type = "logistic" or "linear") |
| Propensity score (PSM, IPTW, SIPTW, overlap weighting) | references/analysis_guides/propensity_score.md | references/templates/propensity_score.py |
| Survey-weighted (KNHANES, NHANES, KCHS) | references/analysis_guides/survey_weighted.md | references/templates/survey_weighted_analysis.py |
| NHIS claims-based studies (ICD-10 definitions) | references/analysis_guides/nhis_icd10_mapping.md | — |
| Repeated measures (LMM, GEE, RM ANOVA) | references/analysis_guides/repeated_measures.md; for missing data see references/analysis_guides/missing_data.md (an LMM already uses every observed outcome under MAR, so missing outcomes alone do not call for MICE) | references/templates/repeated_measures.py |
| Mediation | references/analysis_guides/mediation.md | — |
| Multiple testing, many-exposure scans (ExWAS/EWAS) | references/analysis_guides/multiplicity.md | — |
| Mendelian randomization | references/analysis_guides/mendelian_randomization.md | — |
| Polygenic risk score | references/analysis_guides/polygenic_risk_score.md | — |
| Burden of disease, decomposition, forecasting | references/analysis_guides/burden_decomposition_forecasting.md | — |
Comparing models: use a paired DeLong test for two separate scores on the same patients. For a nested "baseline + new term" model fitted and evaluated on the same data, DeLong is not the added-value test — test the term (likelihood ratio) and report ΔAUC with its CI.
The rules below have no separate guide.
(min+max) - x before computing the scale total or Cronbach's alpha. An un-recoded reverse
item produces a negative item-rest correlation and can make alpha negative — a coding bug,
not evidence of a multidimensional construct; do not defend it as one. likert_summary.py
prints per-item item-rest correlations, flags negative ones as reverse-code suspects, warns on
a negative alpha, and applies the recode with --reverse-items E3 .... To screen at cleaning
time, run python3 "${CLAUDE_SKILL_DIR}/../clean-data/scripts/check_reverse_coding.py".Applies to any multivariable adjustment (logistic / linear / Cox / propensity score / survey- weighted) with a dose/duration variable anchored to a categorical exposure (pack-years under smoking status, grams/week under alcohol use, cessation duration under former smoker):
pack_years is a structural
zero — known to be 0 by definition of the category, not missing. Imputing it (MICE/MNAR)
fabricates a dose for unexposed subjects and corrupts the exposure contrast. Before imputing a
dose/duration column, set the implied zero explicitly (IF status == 'never' THEN dose = 0)
and impute only the genuinely missing residual among the exposed. /clean-data flags never
rows with a NULL dose (scripts/check_structural_zero.py).Applies to any cross-sectional / single-visit outcome regression (temporal order is not observed). Select covariates causally, not statistically: