feat(backend): B19 paired t-test - #104
Conversation
Adds Welch's two-sample t-test (unequal variances) for cohort
comparison between precomputed datasets.
services/cohort_stats.py: thin wrapper around scipy.stats.ttest_ind
with the equal_var=False default. Cleans None/NaN before testing
and requires n >= 2 per group (Welch's is undefined otherwise).
Returns {t, p, df}. df falls back to a manual Welch-Satterthwaite
calculation when scipy < 1.11 (no result.df attribute).
api/routes/cohorts.py: POST /api/cohorts/compare with body
{a, b, metric}. Loads backend/data/precomputed/<id>.json (M2
artifact), extracts the metric column, runs the t-test, returns
{a, b, metric, n_a, n_b, t, p, df}.
Validation:
- metric is a Pydantic Literal of {muH, hydrophobicity, charge}
- a == b is a 422 (user mistake, not a server error)
- dataset id is alphanumeric + ._- only (path-traversal guard)
- missing precomputed file is a 404 with a helpful "run M2" message
- n < 2 per group is a 422, not a 500
requirements.txt: pin scipy >= 1.14, < 2.0. Adds ~50MB to the
image but unlocks every downstream stats endpoint (rank tests,
distribution comparisons) we'll want for the cohort and ranking
workflows.
Tests: 11 cases — 4 unit tests on welch_t_test (scipy parity,
NaN handling, n<2 rejection, identical-distribution sanity) and
7 route tests (happy path, NULL skipping, missing dataset,
unsupported metric, identical cohorts, path traversal,
too-few-samples).
|
Warning Review limit reached
More reviews will be available in 5 minutes and 23 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (5)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary
POST /api/cohorts/comparerunning Welch's two-sample t-test on a chosen biochemical metric between two precomputed cohorts.backend/services/cohort_stats.pywithwelch_t_test(a, b) → {t, p, df}— thin wrapper aroundscipy.stats.ttest_ind(equal_var=False). CleansNone/ NaN, requiresn >= 2per group, falls back to a manual Welch-Satterthwaite df calc onscipy < 1.11.backend/api/routes/cohorts.pyregistered inapi/main.py. Reads precomputed JSON frombackend/data/precomputed/<id>.json(M2 artifact). Validates: metric is aLiteral{muH, hydrophobicity, charge},a != b, alphanumeric+._-dataset ids only (path-traversal guard), missing precomputed file → 404 with a helpful "run M2" message,n < 2→ 422 (not 500).scipy >= 1.14, < 2.0tobackend/requirements.txt. ~50MB image bump, but unlocks every downstream stats endpoint (rank tests, distribution comparisons) the cohort and ranking workflows will want.Test plan
`curl -X POST http://localhost:8000/api/cohorts/compare -H "Content-Type: application/json" -d '{"a":"peleg_118","b":"uperin_frog","metric":"muH"}'`
Dependency
The route reads from
backend/data/precomputed/<id>.jsonproduced by M2. The route returns a clean 404 when the artifact is missing, so the merge order between B19 and M2 is flexible.Out of scope (per dispatch)
backend/schemas/api_models.pyresponse shapes.process_upload_dataframeinternals.Closes B19 from `docs/internal/WAVE_2_8_DISPATCH_2026_06_18.md` §3.