import { useMemo } from 'react'
import { area, curveMonotoneX, curveStep } from 'd3-shape'
import { scaleLinear } from 'd3-scale'
import { interpolateRgb } from 'd3-interpolate'
import { rateProfile } from '../utils/densityProfile.js'
import { differenceRates, differenceSummary } from '../utils/groupDifference.js'
import { displayDraws } from '../utils/districtGroups.js'
import { niceTicks } from '../utils/niceTicks.js'
/**
* Chart B — "Model estimated differences".
*
* Port of the white paper's Fig 7 (`wp_fig_group_difference`): the posterior
* distribution of Δ = rate(A) − rate(B), per 1,000 students, computed at each
* draw index.
*
* Two deliberate departures from the R figure:
*
* - The fill is a **diverging** ramp centred at zero (navy below, paper at
* zero, ember above) rather than the paper's sequential YlOrRd. Δ is a signed
* quantity; a sequential ramp encodes "more" where the data means "which
* direction", and would make a large negative difference read as a small one.
* - The readout is spelled out in a sentence. `Pr(Δ > 0) = 94.4%` is the number
* a reader is most likely to misread as "94.4% more arrests".
*/
const NEGATIVE_COLOR = '#22406A' // --cv-navy-600
const ZERO_COLOR = '#F2EDE4' // --cv-paper-2
const POSITIVE_COLOR = '#C25311' // --cv-accent
const GRADIENT_STOPS = 24
const WIDTH = 760
const HEIGHT = 250
const MARGIN = { top: 18, right: 22, bottom: 46, left: 22 }
export default function GroupDifference({
groups,
enrollByGroup,
byModel,
selectedModel,
status,
pooled,
pair,
onPairChange,
}) {
const { counts, enroll } = useMemo(
() => displayDraws(byModel?.[selectedModel]?.counts, enrollByGroup, pooled),
[byModel, selectedModel, enrollByGroup, pooled],
)
// Only groups with a usable draw set can be differenced at all — offering the
// others in the picker would produce an empty chart with no explanation.
const comparable = useMemo(
() => groups.filter((g) => counts?.[g.key]?.length > 0 && enroll?.[g.key] > 0),
[groups, counts, enroll],
)
const [keyA, keyB] = pair || []
const groupA = comparable.find((g) => g.key === keyA)
const groupB = comparable.find((g) => g.key === keyB)
const deltas = useMemo(
() =>
groupA && groupB
? differenceRates(counts[groupA.key], enroll[groupA.key], counts[groupB.key], enroll[groupB.key])
: [],
[groupA, groupB, counts, enroll],
)
const summary = useMemo(() => differenceSummary(deltas), [deltas])
return (
Model estimated differences
How much higher is one group’s modelled arrest rate than another’s, and how
sure is the model of the direction?
Δ is computed at each of {summary.n.toLocaleString()} posterior predictive draws as{' '}
{groupA.label} minus {groupB.label}, per 1,000 students. The dashed line marks no
difference. Bars beneath the curve are the 80% and 95% intervals around the median Δ.
In {formatPercent(share)} of posterior predictive draws, the {higher.sentenceLabel} arrest
rate exceeds the {lower.sentenceLabel} rate. The median difference is{' '}
{formatDelta(summary.median)} per 1,000 students.