Capping and aggregation
Turn per-unit values into test statistics, with percentile caps, CUPED covariates and Bonferroni correction.
These helpers build the inputs the tests need from per-unit values.
// Linear-interpolated percentile; null for empty values; requires 0 < p < 1.
resolvePercentileCap(values: readonly number[], p: number): number | null
// Cap each per-unit value and retain capped/uncapped totals and counts.
capValues(values: readonly number[], threshold: number | null): CapResult
// Left-to-right per-unit n, sum and sumSquares.
summarizeUnits(values: readonly number[]): UnitMoments
// Also summarize equal-length pre-period covariates and cross-products.
summarizeUnitsWithCovariate(values: readonly number[], covariates: readonly number[]): UnitMomentsWithCovariate
// Binomial → proportion (sum = converters); count/revenue → sample mean.
buildStatistic(kind: 'binomial' | 'count' | 'revenue', m: UnitMoments): BaseStatistic
// Build pre/post statistics with theta null for subsequent CUPED adjustment.
buildRegressionAdjustedStatistic(kind: 'binomial' | 'count' | 'revenue', m: UnitMomentsWithCovariate): RegressionAdjustedStatistic
// Divide alpha by an integer number of comparisons k >= 1.
bonferroniAlpha(alpha: number, k: number): number| Type | Fields |
|---|---|
UnitMoments | n, sum, sumSquares |
UnitMomentsWithCovariate | Adds preSum, preSumSquares, postPreSumOfProducts |
CapResult | values, threshold, cappedCount, uncappedTotal, cappedTotal |
The order that matters
Cap before computing moments: capValues → summarizeUnits → buildStatistic.
import { resolvePercentileCap, capValues, summarizeUnits, buildStatistic } from '@delmaredigital/experiments-stats'
const cap = resolvePercentileCap(historyValues, 0.99) // from pre-test history
const capped = capValues(revenuePerUnit, cap)
const statistic = buildStatistic('revenue', summarizeUnits(capped.values))
// capped.uncappedTotal is still available for display- A
nullcap is the identity: values are copied and the capped count is zero. - Values equal to the cap are not counted as capped.
- Apply the same resolved cap to CUPED covariates.
- Binomial values and binomial covariates must be per-unit 0/1 values. The builders require
sumSquares === sumandsum <= nfor both post and pre. Convert repeated events to a per-unit indicator before aggregating.

