Files
crdc-demo/src/utils/drawGroups.js
T
jared 70a12cad22 fix: gate the "real posterior draws" claim on per-group coverage
useDrawDistribution's status is an any-group signal -- 'ready' means at least
one group came back with draws -- but both charts used it chart-wide to hide
<ApproxNote /> and print a caption claiming every box/ridge is real draws.
Individual boxes and ridges already fall back per-group, so the note and
caption were the only things over-claiming.

This is reachable with real data, not just in theory: for LEAID 0400311 (AZ,
unified_m4_mod) the estimates API returns all 8 race x sex groups but the
parquet shard contains draws for WH_M only. The chart reported 'ready' and hid
the note while 7 of its 8 ridges were the analytic approximation.

New src/utils/drawGroups.js owns the "RACE_SEX" key format (previously
duplicated across the hook and both charts) and a hasDrawsForAll() coverage
check. Each chart now checks the groups it actually renders -- RACE_ORDER x its
sex panels/columns -- rather than everything the API returned. A null map
(loading, or a fetch that failed outright) fails the check, so the error path
still shows the note.

Also fixes a stale-state hazard in the same hook: the input guard ran before
setStatus('loading')/setDrawsByGroup(null), so switching to a model whose
groups list is empty (that model's upstream fetch failed) left status at
'ready' with the previous model's draws still in state. Verified by
instrumenting the hook: with the old ordering, switching from a ready model to
an empty one kept status 'ready' and all 8 previous draw keys; with the reset
moved above the guard it correctly resets to 'loading' with no draws.
2026-08-11 10:37:15 -04:00

34 lines
1.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Shared key format and coverage check for the per-group posterior draws
* returned by `useDrawDistribution`. Both the hook (which builds the map) and
* the charts (which read it, and decide whether to claim "real draws") go
* through here so the key format lives in exactly one place.
*/
/** Canonical key for one race×sex group in a `drawsByGroup` map. */
export function groupKey(race, sex) {
return `${race}_${sex}`
}
/**
* True only when *every* group in `groups` has a non-empty draw array.
*
* `useDrawDistribution`'s `status` is an any-group signal: it reports 'ready'
* as soon as one group has real draws. Charts fall back per-group, so the
* chart-wide "these are real posterior draws" note/caption must be gated on
* complete coverage instead — a group can be missing because its enrollment is
* falsy or because its (LEAID, RACE, SEX) isn't in the parquet shard.
*
* Returns false for an empty group list (nothing rendered means nothing to
* claim) and for a null map (loading, or the fetch failed outright).
*
* @param {Record<string, number[]> | null | undefined} drawsByGroup
* @param {Array<{race: string, sex: string}>} groups - the groups a chart is
* actually rendering, not everything the API returned.
* @returns {boolean}
*/
export function hasDrawsForAll(drawsByGroup, groups) {
if (!drawsByGroup || !groups?.length) return false
return groups.every((g) => (drawsByGroup[groupKey(g.race, g.sex)]?.length ?? 0) > 0)
}