@mark1russell7/rigor-statistics 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -30,6 +30,8 @@ A promotion reads `gains` against `drops` and `effect`. A sign test alone is not
30
30
  | `quantile(values, p, method)`, `quantileSorted` | The `p` quantile, with the `"linear"` method (type 7, the default) or the `"nearest-rank"` method (type 1). |
31
31
  | `bootstrap(data, statistic, options)` | A percentile bootstrap interval of any statistic. The options are `runs`, `level` or `ends`, `seed` and `method`. |
32
32
  | `bootstrapMedian`, `bootstrapClusters` | The interval of the median, and a bootstrap that draws whole clusters (boards, seeds, sites). |
33
+ | `bootstrapTwo(a, b, statistic, options)` | A bootstrap interval of a statistic of two independent samples, for example two arms of a benchmark. Each run draws all of `a`, then all of `b`. |
34
+ | `bootstrapMedianRatio(numerator, denominator, options)` | The interval of `median(numerator) / median(denominator)`. It is the uncertainty of the ratio, not the spread of the samples. |
33
35
  | `signTest(better, worse, sides)`, `signTestPower(n, level)` | The sign test, and the fewest wins out of `n` that the test calls significant. |
34
36
  | `pairedPermutation(candidate, baseline, options)` | The paired permutation test (random sign flips). It uses the size of each difference. |
35
37
  | `wilson(k, n, z)` | The Wilson interval of a proportion. |
package/build/index.d.ts CHANGED
@@ -73,6 +73,20 @@ interface Interval {
73
73
  * infinity) does not count. When no run is finite, low and high are equal to the estimate.
74
74
  */
75
75
  export declare function bootstrap<T>(data: readonly T[], statistic: (sample: readonly T[]) => number, options?: BootstrapOptions): Interval;
76
+ /**
77
+ * A percentile bootstrap interval of `statistic` over two independent samples `a` and `b`. Use it when the two
78
+ * samples are not paired, for example two arms of a benchmark. Each run draws `a.length` items of `a` with
79
+ * replacement, and then `b.length` items of `b` with replacement. All the draws come from one stream of
80
+ * `random(seed)`. The statistic reads the two drawn samples. The estimate is the statistic of all of `a` and `b`. A
81
+ * run whose statistic is not finite does not count. When no run is finite, low and high are equal to the estimate.
82
+ */
83
+ export declare function bootstrapTwo<A, B>(a: readonly A[], b: readonly B[], statistic: (a: readonly A[], b: readonly B[]) => number, options?: BootstrapOptions): Interval;
84
+ /**
85
+ * A bootstrap interval of the ratio `median(numerator) / median(denominator)` of two independent samples. The
86
+ * interval is the uncertainty of the ratio of the two medians. It is not the spread of the samples. A run whose
87
+ * denominator median is 0 is not finite, so it does not count.
88
+ */
89
+ export declare function bootstrapMedianRatio(numerator: readonly number[], denominator: readonly number[], options?: BootstrapOptions): Interval;
76
90
  /** A bootstrap interval of the median. */
77
91
  export declare function bootstrapMedian(values: readonly number[], options?: BootstrapOptions): Interval;
78
92
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/describe.ts","../src/bootstrap.ts","../src/tests.ts","../src/rank.ts","../src/compare.ts"],"mappings":";;;;;;KAOY;;wBAYI,OAAO;wBAIP,KAAK;;wBAQL,GAAG;;;;;;wBAcH,eAAe,iCAAiC,WAAW,SAAQ;;wBAgBnE,SAAS,2BAA2B,WAAW,SAAQ;;;;;wBASvD,OAAO;;UAQN;WACN;WACA;;WAEA;WACA;;WAEA;WACA;;WAEA;WACA;;WAEA;;wBAGK,UAAU,4BAA4B;UCtFrC;;WAEN;;WAEA;;WAEA;;WAEA,SAAS;;;;;;WAMT;;UAGM;;WAEN;WACA;WACA;WACA;WACA;;WAEA;;WAEA;;;;;;wBAOK,UAAU,GAAG,eAAe,KAAK,YAAY,iBAAiB,gBAAgB,UAAS,mBAAwB;;wBA0B/G,gBAAgB,2BAA2B,UAAS,mBAAwB;;;;;;wBAS5E,kBAAkB,GAChC,eAAe,KACf,UAAU,KAAK,cACf,YAAY,eAAe,gBAC3B,UAAS,mBACR;;;;;;wBC7Ca,SAAS,gBAAgB,eAAe;;;;;wBAaxC,cAAc,WAAW,gBAAc;;qBAO1C;;UAGI;WACN;WACA;;;;;;wBAOK,OAAO,WAAW,WAAW,aAAkB;;UAc9C;;WAEN;;WAEA;;WAEA;;;;;;;wBAQK,KAAK,4BAA4B,iBAAe;UAa/C;;WAEN;;WAEA;;WAEA,aAAa;;UAGP;;WAEN;;WAEA;WACA;;;;;;wBAOK,kBAAkB,8BAA8B,6BAA6B,UAAS,qBAA0B;;wBCtHhH,MAAM;;;;;wBAgBN,SAAS,sBAAsB;;qBAmBlC;;;;;;wBAQG,WAAW,sBAAsB,sBAAsB;UC/CtD;;WAEN;;WAEA;;;;;;wBAOK,WAAW,2BAA2B,UAAS;UAM9C;;WAEN;;WAEA;;WAEA;;;UAIM;WACN;WACA;;WAEA;;WAEA;;UAGM;;WAEN;WACA;;WAEA;;WAEA;;WAEA;;WAEA,QAAQ;;WAER;;WAEA;;WAEA;WACA,cAAc;;WAEd;WACA,cAAc;;;;;;;;wBAST,cAAc,2BAA2B,UAAS,iBAAsB;;UA6CvE;WACN;WACA;;;wBASK,MAAM,oBAAoB;;UAQzB;WACN;WACA;WACA;;WAEA;;WAEA,UAAU;;;wBAIL,YAAY,oBAAoB,aAAa"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/describe.ts","../src/bootstrap.ts","../src/tests.ts","../src/rank.ts","../src/compare.ts"],"mappings":";;;;;;KAOY;;wBAYI,OAAO;wBAIP,KAAK;;wBAQL,GAAG;;;;;;wBAcH,eAAe,iCAAiC,WAAW,SAAQ;;wBAgBnE,SAAS,2BAA2B,WAAW,SAAQ;;;;;wBASvD,OAAO;;UAQN;WACN;WACA;;WAEA;WACA;;WAEA;WACA;;WAEA;WACA;;WAEA;;wBAGK,UAAU,4BAA4B;UCrFrC;;WAEN;;WAEA;;WAEA;;WAEA,SAAS;;;;;;WAMT;;UAGM;;WAEN;WACA;WACA;WACA;WACA;;WAEA;;WAEA;;;;;;wBAOK,UAAU,GAAG,eAAe,KAAK,YAAY,iBAAiB,gBAAgB,UAAS,mBAAwB;;;;;;;;wBAsB/G,aAAa,GAAG,GAC9B,YAAY,KACZ,YAAY,KACZ,YAAY,YAAY,KAAK,YAAY,gBACzC,UAAS,mBACR;;;;;;wBAwBa,qBAAqB,8BAA8B,gCAAgC,UAAS,mBAAwB;;wBA+BpH,gBAAgB,2BAA2B,UAAS,mBAAwB;;;;;;wBAS5E,kBAAkB,GAChC,eAAe,KACf,UAAU,KAAK,cACf,YAAY,eAAe,gBAC3B,UAAS,mBACR;;;;;;wBCtGa,SAAS,gBAAgB,eAAe;;;;;wBAaxC,cAAc,WAAW,gBAAc;;qBAO1C;;UAGI;WACN;WACA;;;;;;wBAOK,OAAO,WAAW,WAAW,aAAkB;;UAc9C;;WAEN;;WAEA;;WAEA;;;;;;;wBAQK,KAAK,4BAA4B,iBAAe;UAa/C;;WAEN;;WAEA;;WAEA,aAAa;;UAGP;;WAEN;;WAEA;WACA;;;;;;wBAOK,kBAAkB,8BAA8B,6BAA6B,UAAS,qBAA0B;;wBCtHhH,MAAM;;;;;wBAgBN,SAAS,sBAAsB;;qBAmBlC;;;;;;wBAQG,WAAW,sBAAsB,sBAAsB;UC/CtD;;WAEN;;WAEA;;;;;;wBAOK,WAAW,2BAA2B,UAAS;UAM9C;;WAEN;;WAEA;;WAEA;;;UAIM;WACN;WACA;;WAEA;;WAEA;;UAGM;;WAEN;WACA;;WAEA;;WAEA;;WAEA;;WAEA,QAAQ;;WAER;;WAEA;;WAEA;WACA,cAAc;;WAEd;WACA,cAAc;;;;;;;;wBAST,cAAc,2BAA2B,UAAS,iBAAsB;;UA6CvE;WACN;WACA;;;wBASK,MAAM,oBAAoB;;UAQzB;WACN;WACA;WACA;;WAEA;;WAEA,UAAU;;;wBAIL,YAAY,oBAAoB,aAAa"}
package/build/index.js CHANGED
@@ -79,6 +79,7 @@ function summarize(values) {
79
79
  * The percentile bootstrap. Each run draws n items with replacement (`below(n)` of rigor-generators, which is
80
80
  * `floor(next() * n)`), and computes the statistic of the sample. The interval is two quantiles of the statistics.
81
81
  * With the same seed, the draws are equal to the draws of the copies in highways, packing and layout-contract.
82
+ * `bootstrapTwo` draws two independent samples in each run: first the items of `a`, then the items of `b`.
82
83
  */
83
84
  /**
84
85
  * A percentile bootstrap interval of `statistic` over `data`. A run whose statistic is not finite (NaN or an
@@ -86,23 +87,61 @@ function summarize(values) {
86
87
  */
87
88
  function bootstrap(data, statistic, options = {}) {
88
89
  if (data.length === 0) throw new RangeError("bootstrap: the data is empty.");
90
+ const n = data.length;
91
+ const sample = new Array(n);
92
+ return percentile("bootstrap", options, (r) => {
93
+ for (let i = 0; i < n; i++) sample[i] = data[r.below(n)];
94
+ return statistic(sample);
95
+ }, () => statistic(data));
96
+ }
97
+ /**
98
+ * A percentile bootstrap interval of `statistic` over two independent samples `a` and `b`. Use it when the two
99
+ * samples are not paired, for example two arms of a benchmark. Each run draws `a.length` items of `a` with
100
+ * replacement, and then `b.length` items of `b` with replacement. All the draws come from one stream of
101
+ * `random(seed)`. The statistic reads the two drawn samples. The estimate is the statistic of all of `a` and `b`. A
102
+ * run whose statistic is not finite does not count. When no run is finite, low and high are equal to the estimate.
103
+ */
104
+ function bootstrapTwo(a, b, statistic, options = {}) {
105
+ if (a.length === 0) throw new RangeError("bootstrapTwo: the first sample is empty.");
106
+ if (b.length === 0) throw new RangeError("bootstrapTwo: the second sample is empty.");
107
+ const na = a.length;
108
+ const nb = b.length;
109
+ const sampleA = new Array(na);
110
+ const sampleB = new Array(nb);
111
+ return percentile("bootstrapTwo", options, (r) => {
112
+ for (let i = 0; i < na; i++) sampleA[i] = a[r.below(na)];
113
+ for (let i = 0; i < nb; i++) sampleB[i] = b[r.below(nb)];
114
+ return statistic(sampleA, sampleB);
115
+ }, () => statistic(a, b));
116
+ }
117
+ /**
118
+ * A bootstrap interval of the ratio `median(numerator) / median(denominator)` of two independent samples. The
119
+ * interval is the uncertainty of the ratio of the two medians. It is not the spread of the samples. A run whose
120
+ * denominator median is 0 is not finite, so it does not count.
121
+ */
122
+ function bootstrapMedianRatio(numerator, denominator, options = {}) {
123
+ return bootstrapTwo(numerator, denominator, (a, b) => median(a) / median(b), options);
124
+ }
125
+ /**
126
+ * The shared part of each percentile bootstrap. It examines the options, and starts one stream of `random(seed)`.
127
+ * Each run gives the stream to `run`, which draws the samples and returns their statistic. Then `all` gives the
128
+ * estimate. The order of the draws is the order in `run`.
129
+ */
130
+ function percentile(name, options, run, all) {
89
131
  const runs = options.runs ?? 1e4;
90
- if (!Number.isSafeInteger(runs) || runs < 1) throw new RangeError(`bootstrap: runs is a whole number of 1 or more, not ${runs}.`);
132
+ if (!Number.isSafeInteger(runs) || runs < 1) throw new RangeError(`${name}: runs is a whole number of 1 or more, not ${runs}.`);
91
133
  const tail = (1 - (options.level ?? .95)) / 2;
92
134
  const [lowEnd, highEnd] = options.ends ?? [tail, 1 - tail];
93
- if (!(lowEnd >= 0 && lowEnd < highEnd && highEnd <= 1)) throw new RangeError(`bootstrap: the ends are 0 <= low < high <= 1, not ${lowEnd} and ${highEnd}.`);
135
+ if (!(lowEnd >= 0 && lowEnd < highEnd && highEnd <= 1)) throw new RangeError(`${name}: the ends are 0 <= low < high <= 1, not ${lowEnd} and ${highEnd}.`);
94
136
  const level = options.ends ? highEnd - lowEnd : options.level ?? .95;
95
- if (!(level > 0 && level < 1)) throw new RangeError(`bootstrap: level is more than 0 and less than 1, not ${level}.`);
137
+ if (!(level > 0 && level < 1)) throw new RangeError(`${name}: level is more than 0 and less than 1, not ${level}.`);
96
138
  const r = random(options.seed ?? 1);
97
- const n = data.length;
98
- const sample = new Array(n);
99
139
  const values = [];
100
- for (let run = 0; run < runs; run++) {
101
- for (let i = 0; i < n; i++) sample[i] = data[r.below(n)];
102
- const v = statistic(sample);
140
+ for (let i = 0; i < runs; i++) {
141
+ const v = run(r);
103
142
  if (Number.isFinite(v)) values.push(v);
104
143
  }
105
- const estimate = statistic(data);
144
+ const estimate = all();
106
145
  if (values.length === 0) return {
107
146
  estimate,
108
147
  low: estimate,
@@ -455,6 +494,6 @@ function calibration(forecasts) {
455
494
  interval: wilson(hits, n)
456
495
  }));
457
496
  }
458
- export { KENDALL_EXACT, Z95, bootstrap, bootstrapClusters, bootstrapMedian, brier, calibration, compareRatios, holm, kendallTau, mean, median, noiseFloor, pairedPermutation, quantile, quantileSorted, ranks, sd, signTest, signTestPower, sorted, spearman, summarize, wilson };
497
+ export { KENDALL_EXACT, Z95, bootstrap, bootstrapClusters, bootstrapMedian, bootstrapMedianRatio, bootstrapTwo, brier, calibration, compareRatios, holm, kendallTau, mean, median, noiseFloor, pairedPermutation, quantile, quantileSorted, ranks, sd, signTest, signTestPower, sorted, spearman, summarize, wilson };
459
498
 
460
499
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/describe.ts","../src/bootstrap.ts","../src/tests.ts","../src/rank.ts","../src/compare.ts"],"sourcesContent":["/**\n * The descriptive statistics: the mean, the standard deviation, the median, the quantiles and a summary of a list.\n * Each function throws a RangeError for an empty list or for a value that is NaN. Thus a missing value cannot make a\n * quiet NaN in a record.\n */\n\n/** The two definitions of a quantile that the engines use (Hyndman and Fan, 1996). */\nexport type QuantileMethod =\n /** Type 7: a linear step between the two nearest ranks. The default of R and NumPy. */\n | \"linear\"\n /** Type 1: the smallest value with at least the share `p` of the values at or below it. */\n | \"nearest-rank\";\n\nfunction checked(name: string, values: readonly number[]): void {\n if (values.length === 0) throw new RangeError(`${name}: the list is empty.`);\n for (const v of values) if (Number.isNaN(v)) throw new RangeError(`${name}: the list has a NaN.`);\n}\n\n/** A new list with the values from the smallest to the largest. */\nexport function sorted(values: readonly number[]): number[] {\n return [...values].sort((a, b) => a - b);\n}\n\nexport function mean(values: readonly number[]): number {\n checked(\"mean\", values);\n let sum = 0;\n for (const v of values) sum += v;\n return sum / values.length;\n}\n\n/** The sample standard deviation (the divisor is n - 1). It needs 2 values or more. */\nexport function sd(values: readonly number[]): number {\n checked(\"sd\", values);\n if (values.length < 2) throw new RangeError(\"sd: the list needs 2 values or more.\");\n const m = mean(values);\n let sum = 0;\n for (const v of values) sum += (v - m) ** 2;\n return Math.sqrt(sum / (values.length - 1));\n}\n\n/**\n * The `p` quantile of a list that is already sorted. Use it when you take several quantiles of one list.\n * The linear method gives the same numbers as the copies in highways, packing and layout-contract. The\n * nearest-rank method gives the same numbers as the copies in lag, lag-hud and the backtest of highways.\n */\nexport function quantileSorted(sortedValues: readonly number[], p: number, method: QuantileMethod = \"linear\"): number {\n checked(\"quantile\", sortedValues);\n if (!(p >= 0 && p <= 1)) throw new RangeError(`quantile: p is from 0 to 1, not ${p}.`);\n const n = sortedValues.length;\n if (method === \"nearest-rank\") return sortedValues[Math.min(n - 1, Math.max(0, Math.ceil(p * n) - 1))] as number;\n const at = (n - 1) * p;\n const low = Math.floor(at);\n const part = at - low;\n const a = sortedValues[low] as number;\n if (part === 0) return a;\n const b = sortedValues[low + 1] as number;\n if (a === b) return a;\n return a + part * (b - a);\n}\n\n/** The `p` quantile of a list. */\nexport function quantile(values: readonly number[], p: number, method: QuantileMethod = \"linear\"): number {\n checked(\"quantile\", values);\n return quantileSorted(sorted(values), p, method);\n}\n\n/**\n * The median: the middle value, or the midpoint `(a + b) / 2` of the two middle values. The midpoint is correctly\n * rounded. `quantile(values, 0.5)` computes `a + 0.5 * (b - a)`, which can differ from it in the last bit.\n */\nexport function median(values: readonly number[]): number {\n checked(\"median\", values);\n const s = sorted(values);\n const middle = Math.floor(s.length / 2);\n return s.length % 2 === 1 ? (s[middle] as number) : ((s[middle - 1] as number) + (s[middle] as number)) / 2;\n}\n\n/** The summary of a list. The quartiles use the linear method. */\nexport interface Summary {\n readonly n: number;\n readonly mean: number;\n /** The sample standard deviation, or NaN for one value. */\n readonly sd: number;\n readonly min: number;\n /** The 0.25 quantile. */\n readonly q1: number;\n readonly median: number;\n /** The 0.75 quantile. */\n readonly q3: number;\n readonly max: number;\n /** The interquartile range: q3 - q1. */\n readonly iqr: number;\n}\n\nexport function summarize(values: readonly number[]): Summary {\n checked(\"summarize\", values);\n const s = sorted(values);\n const q1 = quantileSorted(s, 0.25);\n const q3 = quantileSorted(s, 0.75);\n return {\n n: s.length,\n // The mean and sd of the list in its own order, so that they are equal to mean(values) and sd(values).\n mean: mean(values),\n sd: s.length < 2 ? Number.NaN : sd(values),\n min: s[0] as number,\n q1,\n median: median(s),\n q3,\n max: s[s.length - 1] as number,\n iqr: q3 - q1,\n };\n}\n","/**\n * The percentile bootstrap. Each run draws n items with replacement (`below(n)` of rigor-generators, which is\n * `floor(next() * n)`), and computes the statistic of the sample. The interval is two quantiles of the statistics.\n * With the same seed, the draws are equal to the draws of the copies in highways, packing and layout-contract.\n */\nimport { random } from \"@mark1russell7/rigor-generators\";\nimport { type QuantileMethod, median, quantileSorted, sorted } from \"./describe.ts\";\n\nexport interface BootstrapOptions {\n /** The number of samples. The default is 10 000. */\n readonly runs?: number;\n /** The level of the interval. The default is 0.95. */\n readonly level?: number;\n /** The seed of the draws. The default is 1. */\n readonly seed?: number | string;\n /** The quantile method of the two ends of the interval. The default is linear. */\n readonly method?: QuantileMethod;\n /**\n * The quantiles of the two ends, in place of `level`, for example [0.025, 0.975] or an interval with one side.\n * The default is from the level: (1 - level) / 2 and 1 - (1 - level) / 2. In floating point, (1 - 0.95) / 2 is\n * not 0.025, so a copy that writes 0.025 needs this option to give the same numbers.\n */\n readonly ends?: readonly [number, number];\n}\n\nexport interface Interval {\n /** The statistic of all the data. */\n readonly estimate: number;\n readonly low: number;\n readonly high: number;\n readonly level: number;\n readonly runs: number;\n /** The number of runs whose statistic is a finite number. Only these runs make the interval. */\n readonly finite: number;\n /** The seed as a 32-bit number. */\n readonly seed: number;\n}\n\n/**\n * A percentile bootstrap interval of `statistic` over `data`. A run whose statistic is not finite (NaN or an\n * infinity) does not count. When no run is finite, low and high are equal to the estimate.\n */\nexport function bootstrap<T>(data: readonly T[], statistic: (sample: readonly T[]) => number, options: BootstrapOptions = {}): Interval {\n if (data.length === 0) throw new RangeError(\"bootstrap: the data is empty.\");\n const runs = options.runs ?? 10_000;\n if (!Number.isSafeInteger(runs) || runs < 1) throw new RangeError(`bootstrap: runs is a whole number of 1 or more, not ${runs}.`);\n const tail = (1 - (options.level ?? 0.95)) / 2;\n const [lowEnd, highEnd] = options.ends ?? [tail, 1 - tail];\n if (!(lowEnd >= 0 && lowEnd < highEnd && highEnd <= 1)) throw new RangeError(`bootstrap: the ends are 0 <= low < high <= 1, not ${lowEnd} and ${highEnd}.`);\n const level = options.ends ? highEnd - lowEnd : (options.level ?? 0.95);\n if (!(level > 0 && level < 1)) throw new RangeError(`bootstrap: level is more than 0 and less than 1, not ${level}.`);\n const r = random(options.seed ?? 1);\n const n = data.length;\n const sample = new Array<T>(n);\n const values: number[] = [];\n for (let run = 0; run < runs; run++) {\n for (let i = 0; i < n; i++) sample[i] = data[r.below(n)] as T;\n const v = statistic(sample);\n if (Number.isFinite(v)) values.push(v);\n }\n const estimate = statistic(data);\n if (values.length === 0) return { estimate, low: estimate, high: estimate, level, runs, finite: 0, seed: r.seed };\n const s = sorted(values);\n const method = options.method ?? \"linear\";\n return { estimate, low: quantileSorted(s, lowEnd, method), high: quantileSorted(s, highEnd, method), level, runs, finite: s.length, seed: r.seed };\n}\n\n/** A bootstrap interval of the median. */\nexport function bootstrapMedian(values: readonly number[], options: BootstrapOptions = {}): Interval {\n return bootstrap(values, median, options);\n}\n\n/**\n * A cluster bootstrap: each run draws whole clusters with replacement, and the statistic reads the rows of the drawn\n * clusters. Use it when the rows of one cluster (one board, one seed, one site) are not independent. The clusters\n * are in the order of their first row.\n */\nexport function bootstrapClusters<T>(\n rows: readonly T[],\n cluster: (row: T) => string,\n statistic: (rows: readonly T[]) => number,\n options: BootstrapOptions = {},\n): Interval {\n const groups = new Map<string, T[]>();\n for (const row of rows) {\n const key = cluster(row);\n const list = groups.get(key);\n if (list) list.push(row);\n else groups.set(key, [row]);\n }\n const clusters = [...groups.values()];\n const result = bootstrap(clusters, (drawn) => statistic(drawn.flat()), options);\n // The estimate of all the rows in their own order, not in the order of the clusters.\n const estimate = statistic(rows);\n return result.finite === 0 ? { ...result, estimate, low: estimate, high: estimate } : { ...result, estimate };\n}\n","/**\n * The tests of a difference: the sign test, the paired permutation test, the Wilson interval of a proportion and\n * the Holm correction for several tests.\n */\nimport { random } from \"@mark1russell7/rigor-generators\";\nimport { mean } from \"./describe.ts\";\n\nfunction whole(name: string, value: number): void {\n if (!Number.isSafeInteger(value) || value < 0) throw new RangeError(`${name}: ${value} is not a whole number of 0 or more.`);\n}\n\n/** The log of each binomial term C(n, k) / 2^n, for each k from 0 to n. */\nfunction halfBinomialLogs(n: number): number[] {\n const logs: number[] = [];\n let logChoose = 0;\n for (let i = 0; i <= n; i++) {\n if (i > 0) logChoose += Math.log((n - i + 1) / i);\n logs.push(logChoose - n * Math.LN2);\n }\n return logs;\n}\n\n/** P(X >= k) for X ~ Binomial(n, 1/2), summed from the largest term so that a small tail keeps its digits. */\nfunction upperTail(k: number, n: number): number {\n if (k <= 0) return 1;\n if (k > n) return 0;\n const logs = halfBinomialLogs(n).slice(k);\n const top = Math.max(...logs);\n const sum = logs.reduce((s, l) => s + Math.exp(l - top), 0);\n return Math.min(1, Math.exp(top) * sum);\n}\n\n/**\n * The sign test of paired results: `better` inputs where the candidate wins and `worse` where it loses. The ties\n * do not count. One-sided (the default), it is the p of `better` wins or more if the two are equal. Two-sided, it is\n * two times the tail of the larger count, at most 1. The one-sided p is equal to `signTestP` of packing.\n */\nexport function signTest(better: number, worse: number, sides: 1 | 2 = 1): number {\n whole(\"signTest\", better);\n whole(\"signTest\", worse);\n const n = better + worse;\n if (n === 0) return 1;\n if (sides === 1) return upperTail(better, n);\n return Math.min(1, 2 * upperTail(Math.max(better, worse), n));\n}\n\n/**\n * The smallest number of wins out of `n` that the sign test calls significant at `level`. It is undefined when even\n * n wins out of n are not enough. Use it before a run, to know how many inputs a claim needs.\n */\nexport function signTestPower(n: number, level = 0.05, sides: 1 | 2 = 2): number | undefined {\n whole(\"signTestPower\", n);\n for (let k = Math.ceil(n / 2); k <= n; k++) if (signTest(k, n - k, sides) < level) return k;\n return undefined;\n}\n\n/** The z of a two-sided 95% interval. */\nexport const Z95 = 1.959963984540054;\n\n/** An interval of a proportion. */\nexport interface Proportion {\n readonly low: number;\n readonly high: number;\n}\n\n/**\n * The Wilson score interval of `k` successes in `n` trials (Wilson, 1927). It keeps its level for a small n and for\n * a proportion near 0 or 1, where the normal interval does not. For n = 0 the interval is 0 to 1.\n */\nexport function wilson(k: number, n: number, z: number = Z95): Proportion {\n whole(\"wilson\", k);\n whole(\"wilson\", n);\n if (k > n) throw new RangeError(`wilson: k ${k} is more than n ${n}.`);\n if (n === 0) return { low: 0, high: 1 };\n const p = k / n;\n const z2 = z * z;\n const denominator = 1 + z2 / n;\n const center = (p + z2 / (2 * n)) / denominator;\n const half = (z / denominator) * Math.sqrt((p * (1 - p)) / n + z2 / (4 * n * n));\n return { low: Math.max(0, center - half), high: Math.min(1, center + half) };\n}\n\n/** One test after the Holm correction. */\nexport interface Corrected {\n /** The p of the test. */\n readonly p: number;\n /** The p after the correction: compare it with the level of the whole family. */\n readonly adjusted: number;\n /** The adjusted p is less than the level. */\n readonly reject: boolean;\n}\n\n/**\n * The Holm step-down correction of a family of tests (Holm, 1979). It keeps the chance of one false claim or more\n * in the family at most `level`. It has no condition on how the tests depend on each other. The result is in the\n * order of the input.\n */\nexport function holm(pValues: readonly number[], level = 0.05): Corrected[] {\n for (const p of pValues) if (!(p >= 0 && p <= 1)) throw new RangeError(`holm: each p is from 0 to 1, not ${p}.`);\n const m = pValues.length;\n const order = pValues.map((p, i) => ({ p, i })).sort((a, b) => a.p - b.p || a.i - b.i);\n const adjusted = new Array<number>(m);\n let running = 0;\n order.forEach(({ p, i }, rank) => {\n running = Math.max(running, Math.min(1, (m - rank) * p));\n adjusted[i] = running;\n });\n return pValues.map((p, i) => ({ p, adjusted: adjusted[i] as number, reject: (adjusted[i] as number) < level }));\n}\n\nexport interface PermutationOptions {\n /** The number of random sign flips. The default is 10 000. */\n readonly runs?: number;\n /** The default is 1. */\n readonly seed?: number | string;\n /** The statistic of the differences. The default is the mean. */\n readonly statistic?: (differences: readonly number[]) => number;\n}\n\nexport interface PermutationResult {\n /** The statistic of the observed differences `candidate[i] - baseline[i]`. */\n readonly observed: number;\n /** The two-sided p: (1 + the flips with a statistic as far from 0 or farther) / (1 + runs). */\n readonly p: number;\n readonly runs: number;\n}\n\n/**\n * The paired permutation test (a random sign flip of each difference). It needs no normal distribution and uses\n * the size of each difference, which the sign test does not. A pair with a difference of 0 adds nothing.\n */\nexport function pairedPermutation(candidate: readonly number[], baseline: readonly number[], options: PermutationOptions = {}): PermutationResult {\n if (candidate.length !== baseline.length) throw new RangeError(`pairedPermutation: the lists have ${candidate.length} and ${baseline.length} values.`);\n const differences = candidate.map((c, i) => c - (baseline[i] as number));\n const statistic = options.statistic ?? mean;\n const runs = options.runs ?? 10_000;\n if (!Number.isSafeInteger(runs) || runs < 1) throw new RangeError(`pairedPermutation: runs is a whole number of 1 or more, not ${runs}.`);\n const observed = statistic(differences);\n const r = random(options.seed ?? 1);\n const flipped = new Array<number>(differences.length);\n let extreme = 0;\n for (let run = 0; run < runs; run++) {\n for (let i = 0; i < differences.length; i++) flipped[i] = r.next() < 0.5 ? -(differences[i] as number) : (differences[i] as number);\n // A small tolerance, so that a flip with the same statistic up to rounding counts as extreme.\n if (Math.abs(statistic(flipped)) >= Math.abs(observed) * (1 - 1e-12)) extreme++;\n }\n return { observed, p: (1 + extreme) / (1 + runs), runs };\n}\n","/**\n * Rank correlation: do two measures put the same items in the same order? Use it to test that a cheap proxy (a\n * predicted cost, a fast measure) orders the items as the real measure does.\n */\nimport { random } from \"@mark1russell7/rigor-generators\";\n\nfunction samePairs(name: string, a: readonly number[], b: readonly number[]): void {\n if (a.length !== b.length) throw new RangeError(`${name}: the lists have ${a.length} and ${b.length} values.`);\n for (const v of a) if (Number.isNaN(v)) throw new RangeError(`${name}: the first list has a NaN.`);\n for (const v of b) if (Number.isNaN(v)) throw new RangeError(`${name}: the second list has a NaN.`);\n}\n\n/** The ranks of the values, from 1. Equal values get the mean of their ranks. */\nexport function ranks(values: readonly number[]): number[] {\n const order = values.map((x, i) => [x, i] as const).sort((p, q) => p[0] - q[0]);\n const out = new Array<number>(values.length);\n for (let i = 0; i < order.length; ) {\n let j = i;\n while (j + 1 < order.length && (order[j + 1] as readonly [number, number])[0] === (order[i] as readonly [number, number])[0]) j++;\n for (let k = i; k <= j; k++) out[(order[k] as readonly [number, number])[1]] = (i + j) / 2 + 1;\n i = j + 1;\n }\n return out;\n}\n\n/**\n * The Spearman rank correlation: the Pearson correlation of the ranks, with mean ranks for ties. The value is from\n * -1 to 1. When one list has no spread, the value is 0. It is equal to the copy in the backtest of highways.\n */\nexport function spearman(a: readonly number[], b: readonly number[]): number {\n samePairs(\"spearman\", a, b);\n const ra = ranks(a);\n const rb = ranks(b);\n const mean = (xs: readonly number[]): number => xs.reduce((s, x) => s + x, 0) / xs.length;\n const ma = mean(ra);\n const mb = mean(rb);\n let num = 0;\n let da = 0;\n let db = 0;\n for (let i = 0; i < ra.length; i++) {\n num += ((ra[i] as number) - ma) * ((rb[i] as number) - mb);\n da += ((ra[i] as number) - ma) ** 2;\n db += ((rb[i] as number) - mb) ** 2;\n }\n return da === 0 || db === 0 ? 0 : num / Math.sqrt(da * db);\n}\n\n/** The largest number of values for which `kendallTau` examines each pair. Above it, a seeded sample of pairs. */\nexport const KENDALL_EXACT = 1500;\nconst KENDALL_SAMPLE = 200_000;\n\n/**\n * The Kendall tau-b of two lists (Kendall, 1938). The value is from -1 to 1. A pair with a tie in both lists does\n * not count. When the lists have fewer than 2 values, or no pair counts, the value is 1. Above `KENDALL_EXACT`\n * values, a seeded sample of 200 000 pairs gives an estimate. It is equal to the copy in highways.\n */\nexport function kendallTau(a: readonly number[], b: readonly number[], seed: number | string = 1): number {\n samePairs(\"kendallTau\", a, b);\n const n = a.length;\n if (n < 2) return 1;\n let concordant = 0;\n let discordant = 0;\n let tieA = 0;\n let tieB = 0;\n const visit = (i: number, j: number): void => {\n const da = Math.sign((a[i] as number) - (a[j] as number));\n const db = Math.sign((b[i] as number) - (b[j] as number));\n if (da === 0 && db === 0) return;\n if (da === 0) tieA++;\n else if (db === 0) tieB++;\n else if (da === db) concordant++;\n else discordant++;\n };\n if (n <= KENDALL_EXACT) {\n for (let i = 0; i < n; i++) for (let j = i + 1; j < n; j++) visit(i, j);\n } else {\n const r = random(seed);\n for (let k = 0; k < KENDALL_SAMPLE; k++) {\n const i = r.below(n);\n const j = r.below(n);\n if (i !== j) visit(i, j);\n }\n }\n const den = Math.sqrt((concordant + discordant + tieA) * (concordant + discordant + tieB));\n return den === 0 ? 1 : (concordant - discordant) / den;\n}\n","/**\n * The comparison of a candidate with a baseline, and the calibration of predictions. These functions make two fleet\n * rules into code. First, a change of a default is judged against the current default. The measure is the inputs that\n * gain and the inputs that lose beyond the noise, with the effect size. A sign test alone is not enough. Second, each\n * prediction states a probability, and the Brier score measures how good the stated probabilities were.\n */\nimport { median, quantileSorted, sd, sorted } from \"./describe.ts\";\nimport { type Proportion, signTest, wilson } from \"./tests.ts\";\n\nexport interface NoiseOptions {\n /** The number of standard deviations. The default is 2. */\n readonly k?: number;\n /** The fewest values (seeds or folds) that give a floor. The default is 5, the 5-seed rule. */\n readonly minimum?: number;\n}\n\n/**\n * The noise floor of a measure: k sample standard deviations of its value over independent seeds or folds. A\n * difference smaller than the floor is not a result.\n */\nexport function noiseFloor(values: readonly number[], options: NoiseOptions = {}): number {\n const minimum = options.minimum ?? 5;\n if (values.length < minimum) throw new RangeError(`noiseFloor: ${values.length} values; the rule needs ${minimum} or more seeds or folds.`);\n return (options.k ?? 2) * sd(values);\n}\n\nexport interface CompareOptions {\n /** \"higher\" (the default): a ratio above 1 is a gain. \"lower\": a ratio below 1 is a gain, as for a time or a cost. */\n readonly better?: \"higher\" | \"lower\";\n /** A ratio at this distance from 1 or nearer is a tie. The default is 0: only a ratio of exactly 1 is a tie. */\n readonly tie?: number;\n /** A gain or a loss counts only beyond this distance from 1. The default is 0. Use noiseFloor. */\n readonly noise?: number;\n}\n\n/** The effect on the inputs that are not ties. */\nexport interface Effect {\n readonly n: number;\n readonly median: number;\n /** The 0.1 quantile (linear). */\n readonly p10: number;\n /** The 0.9 quantile (linear). */\n readonly p90: number;\n}\n\nexport interface Comparison {\n /** The number of inputs. */\n readonly n: number;\n readonly ties: number;\n /** The inputs where the candidate is better, by any amount. */\n readonly wins: number;\n /** The inputs where the candidate is worse, by any amount. */\n readonly losses: number;\n /** The one-sided sign test of wins against losses. */\n readonly signP: number;\n /** The ratios of the inputs that are not ties, or undefined when each input is a tie. */\n readonly effect: Effect | undefined;\n /** The inputs that gain beyond the noise. */\n readonly gains: number;\n /** The inputs that lose beyond the noise. */\n readonly drops: number;\n /** gains / n, with its Wilson interval. */\n readonly gainShare: number;\n readonly gainInterval: Proportion;\n /** drops / n, with its Wilson interval. */\n readonly dropShare: number;\n readonly dropInterval: Proportion;\n}\n\n/**\n * The comparison of a candidate with a baseline, from the ratio candidate / baseline of each input. It answers the\n * three questions of a promotion. How often is the candidate better? By how much? How often is it worse beyond the\n * noise? A promotion reads `gains` against `drops` and `effect`, not `signP` alone, because a sign test counts a\n * win of any size.\n */\nexport function compareRatios(ratios: readonly number[], options: CompareOptions = {}): Comparison {\n if (ratios.length === 0) throw new RangeError(\"compareRatios: the list is empty.\");\n const sign = (options.better ?? \"higher\") === \"higher\" ? 1 : -1;\n const tie = options.tie ?? 0;\n const noise = options.noise ?? 0;\n if (!(tie >= 0) || !(noise >= 0)) throw new RangeError(\"compareRatios: tie and noise are 0 or more.\");\n let ties = 0;\n let wins = 0;\n let losses = 0;\n let gains = 0;\n let drops = 0;\n const changed: number[] = [];\n for (const ratio of ratios) {\n if (!Number.isFinite(ratio)) throw new RangeError(`compareRatios: each ratio is a finite number, not ${ratio}.`);\n const d = sign * (ratio - 1);\n if (Math.abs(ratio - 1) <= tie) {\n ties++;\n continue;\n }\n changed.push(ratio);\n if (d > 0) wins++;\n else losses++;\n if (d > noise) gains++;\n else if (-d > noise) drops++;\n }\n const n = ratios.length;\n const s = sorted(changed);\n const effect = s.length === 0 ? undefined : { n: s.length, median: median(s), p10: quantileSorted(s, 0.1), p90: quantileSorted(s, 0.9) };\n return {\n n,\n ties,\n wins,\n losses,\n signP: signTest(wins, losses),\n effect,\n gains,\n drops,\n gainShare: gains / n,\n gainInterval: wilson(gains, n),\n dropShare: drops / n,\n dropInterval: wilson(drops, n),\n };\n}\n\n/** A prediction: the stated probability that the claim holds, and if it held. */\nexport interface Forecast {\n readonly p: number;\n readonly hit: boolean;\n}\n\nfunction checkedForecasts(name: string, forecasts: readonly Forecast[]): void {\n if (forecasts.length === 0) throw new RangeError(`${name}: the list is empty.`);\n for (const f of forecasts) if (!(f.p >= 0 && f.p <= 1)) throw new RangeError(`${name}: each p is from 0 to 1, not ${f.p}.`);\n}\n\n/** The Brier score: the mean of (p - outcome)^2. 0 is perfect, and 0.25 is a constant 0.5. Lower is better. */\nexport function brier(forecasts: readonly Forecast[]): number {\n checkedForecasts(\"brier\", forecasts);\n let sum = 0;\n for (const f of forecasts) sum += (f.p - (f.hit ? 1 : 0)) ** 2;\n return sum / forecasts.length;\n}\n\n/** The predictions of one stated level. */\nexport interface CalibrationLevel {\n readonly stated: number;\n readonly n: number;\n readonly hits: number;\n /** hits / n. A calibrated level has a rate near `stated`. */\n readonly rate: number;\n /** The Wilson interval of the rate. A level is overconfident when `stated` is above it. */\n readonly interval: Proportion;\n}\n\n/** The hit rate of each stated level, from the lowest level. */\nexport function calibration(forecasts: readonly Forecast[]): CalibrationLevel[] {\n checkedForecasts(\"calibration\", forecasts);\n const levels = new Map<number, { n: number; hits: number }>();\n for (const f of forecasts) {\n const level = levels.get(f.p) ?? { n: 0, hits: 0 };\n level.n++;\n if (f.hit) level.hits++;\n levels.set(f.p, level);\n }\n return [...levels]\n .sort((a, b) => a[0] - b[0])\n .map(([stated, { n, hits }]) => ({ stated, n, hits, rate: hits / n, interval: wilson(hits, n) }));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/describe.ts","../src/bootstrap.ts","../src/tests.ts","../src/rank.ts","../src/compare.ts"],"sourcesContent":["/**\n * The descriptive statistics: the mean, the standard deviation, the median, the quantiles and a summary of a list.\n * Each function throws a RangeError for an empty list or for a value that is NaN. Thus a missing value cannot make a\n * quiet NaN in a record.\n */\n\n/** The two definitions of a quantile that the engines use (Hyndman and Fan, 1996). */\nexport type QuantileMethod =\n /** Type 7: a linear step between the two nearest ranks. The default of R and NumPy. */\n | \"linear\"\n /** Type 1: the smallest value with at least the share `p` of the values at or below it. */\n | \"nearest-rank\";\n\nfunction checked(name: string, values: readonly number[]): void {\n if (values.length === 0) throw new RangeError(`${name}: the list is empty.`);\n for (const v of values) if (Number.isNaN(v)) throw new RangeError(`${name}: the list has a NaN.`);\n}\n\n/** A new list with the values from the smallest to the largest. */\nexport function sorted(values: readonly number[]): number[] {\n return [...values].sort((a, b) => a - b);\n}\n\nexport function mean(values: readonly number[]): number {\n checked(\"mean\", values);\n let sum = 0;\n for (const v of values) sum += v;\n return sum / values.length;\n}\n\n/** The sample standard deviation (the divisor is n - 1). It needs 2 values or more. */\nexport function sd(values: readonly number[]): number {\n checked(\"sd\", values);\n if (values.length < 2) throw new RangeError(\"sd: the list needs 2 values or more.\");\n const m = mean(values);\n let sum = 0;\n for (const v of values) sum += (v - m) ** 2;\n return Math.sqrt(sum / (values.length - 1));\n}\n\n/**\n * The `p` quantile of a list that is already sorted. Use it when you take several quantiles of one list.\n * The linear method gives the same numbers as the copies in highways, packing and layout-contract. The\n * nearest-rank method gives the same numbers as the copies in lag, lag-hud and the backtest of highways.\n */\nexport function quantileSorted(sortedValues: readonly number[], p: number, method: QuantileMethod = \"linear\"): number {\n checked(\"quantile\", sortedValues);\n if (!(p >= 0 && p <= 1)) throw new RangeError(`quantile: p is from 0 to 1, not ${p}.`);\n const n = sortedValues.length;\n if (method === \"nearest-rank\") return sortedValues[Math.min(n - 1, Math.max(0, Math.ceil(p * n) - 1))] as number;\n const at = (n - 1) * p;\n const low = Math.floor(at);\n const part = at - low;\n const a = sortedValues[low] as number;\n if (part === 0) return a;\n const b = sortedValues[low + 1] as number;\n if (a === b) return a;\n return a + part * (b - a);\n}\n\n/** The `p` quantile of a list. */\nexport function quantile(values: readonly number[], p: number, method: QuantileMethod = \"linear\"): number {\n checked(\"quantile\", values);\n return quantileSorted(sorted(values), p, method);\n}\n\n/**\n * The median: the middle value, or the midpoint `(a + b) / 2` of the two middle values. The midpoint is correctly\n * rounded. `quantile(values, 0.5)` computes `a + 0.5 * (b - a)`, which can differ from it in the last bit.\n */\nexport function median(values: readonly number[]): number {\n checked(\"median\", values);\n const s = sorted(values);\n const middle = Math.floor(s.length / 2);\n return s.length % 2 === 1 ? (s[middle] as number) : ((s[middle - 1] as number) + (s[middle] as number)) / 2;\n}\n\n/** The summary of a list. The quartiles use the linear method. */\nexport interface Summary {\n readonly n: number;\n readonly mean: number;\n /** The sample standard deviation, or NaN for one value. */\n readonly sd: number;\n readonly min: number;\n /** The 0.25 quantile. */\n readonly q1: number;\n readonly median: number;\n /** The 0.75 quantile. */\n readonly q3: number;\n readonly max: number;\n /** The interquartile range: q3 - q1. */\n readonly iqr: number;\n}\n\nexport function summarize(values: readonly number[]): Summary {\n checked(\"summarize\", values);\n const s = sorted(values);\n const q1 = quantileSorted(s, 0.25);\n const q3 = quantileSorted(s, 0.75);\n return {\n n: s.length,\n // The mean and sd of the list in its own order, so that they are equal to mean(values) and sd(values).\n mean: mean(values),\n sd: s.length < 2 ? Number.NaN : sd(values),\n min: s[0] as number,\n q1,\n median: median(s),\n q3,\n max: s[s.length - 1] as number,\n iqr: q3 - q1,\n };\n}\n","/**\n * The percentile bootstrap. Each run draws n items with replacement (`below(n)` of rigor-generators, which is\n * `floor(next() * n)`), and computes the statistic of the sample. The interval is two quantiles of the statistics.\n * With the same seed, the draws are equal to the draws of the copies in highways, packing and layout-contract.\n * `bootstrapTwo` draws two independent samples in each run: first the items of `a`, then the items of `b`.\n */\nimport { type Random, random } from \"@mark1russell7/rigor-generators\";\nimport { type QuantileMethod, median, quantileSorted, sorted } from \"./describe.ts\";\n\nexport interface BootstrapOptions {\n /** The number of samples. The default is 10 000. */\n readonly runs?: number;\n /** The level of the interval. The default is 0.95. */\n readonly level?: number;\n /** The seed of the draws. The default is 1. */\n readonly seed?: number | string;\n /** The quantile method of the two ends of the interval. The default is linear. */\n readonly method?: QuantileMethod;\n /**\n * The quantiles of the two ends, in place of `level`, for example [0.025, 0.975] or an interval with one side.\n * The default is from the level: (1 - level) / 2 and 1 - (1 - level) / 2. In floating point, (1 - 0.95) / 2 is\n * not 0.025, so a copy that writes 0.025 needs this option to give the same numbers.\n */\n readonly ends?: readonly [number, number];\n}\n\nexport interface Interval {\n /** The statistic of all the data. */\n readonly estimate: number;\n readonly low: number;\n readonly high: number;\n readonly level: number;\n readonly runs: number;\n /** The number of runs whose statistic is a finite number. Only these runs make the interval. */\n readonly finite: number;\n /** The seed as a 32-bit number. */\n readonly seed: number;\n}\n\n/**\n * A percentile bootstrap interval of `statistic` over `data`. A run whose statistic is not finite (NaN or an\n * infinity) does not count. When no run is finite, low and high are equal to the estimate.\n */\nexport function bootstrap<T>(data: readonly T[], statistic: (sample: readonly T[]) => number, options: BootstrapOptions = {}): Interval {\n if (data.length === 0) throw new RangeError(\"bootstrap: the data is empty.\");\n const n = data.length;\n const sample = new Array<T>(n);\n return percentile(\n \"bootstrap\",\n options,\n (r) => {\n for (let i = 0; i < n; i++) sample[i] = data[r.below(n)] as T;\n return statistic(sample);\n },\n () => statistic(data),\n );\n}\n\n/**\n * A percentile bootstrap interval of `statistic` over two independent samples `a` and `b`. Use it when the two\n * samples are not paired, for example two arms of a benchmark. Each run draws `a.length` items of `a` with\n * replacement, and then `b.length` items of `b` with replacement. All the draws come from one stream of\n * `random(seed)`. The statistic reads the two drawn samples. The estimate is the statistic of all of `a` and `b`. A\n * run whose statistic is not finite does not count. When no run is finite, low and high are equal to the estimate.\n */\nexport function bootstrapTwo<A, B>(\n a: readonly A[],\n b: readonly B[],\n statistic: (a: readonly A[], b: readonly B[]) => number,\n options: BootstrapOptions = {},\n): Interval {\n if (a.length === 0) throw new RangeError(\"bootstrapTwo: the first sample is empty.\");\n if (b.length === 0) throw new RangeError(\"bootstrapTwo: the second sample is empty.\");\n const na = a.length;\n const nb = b.length;\n const sampleA = new Array<A>(na);\n const sampleB = new Array<B>(nb);\n return percentile(\n \"bootstrapTwo\",\n options,\n (r) => {\n for (let i = 0; i < na; i++) sampleA[i] = a[r.below(na)] as A;\n for (let i = 0; i < nb; i++) sampleB[i] = b[r.below(nb)] as B;\n return statistic(sampleA, sampleB);\n },\n () => statistic(a, b),\n );\n}\n\n/**\n * A bootstrap interval of the ratio `median(numerator) / median(denominator)` of two independent samples. The\n * interval is the uncertainty of the ratio of the two medians. It is not the spread of the samples. A run whose\n * denominator median is 0 is not finite, so it does not count.\n */\nexport function bootstrapMedianRatio(numerator: readonly number[], denominator: readonly number[], options: BootstrapOptions = {}): Interval {\n return bootstrapTwo(numerator, denominator, (a, b) => median(a) / median(b), options);\n}\n\n/**\n * The shared part of each percentile bootstrap. It examines the options, and starts one stream of `random(seed)`.\n * Each run gives the stream to `run`, which draws the samples and returns their statistic. Then `all` gives the\n * estimate. The order of the draws is the order in `run`.\n */\nfunction percentile(name: string, options: BootstrapOptions, run: (r: Random) => number, all: () => number): Interval {\n const runs = options.runs ?? 10_000;\n if (!Number.isSafeInteger(runs) || runs < 1) throw new RangeError(`${name}: runs is a whole number of 1 or more, not ${runs}.`);\n const tail = (1 - (options.level ?? 0.95)) / 2;\n const [lowEnd, highEnd] = options.ends ?? [tail, 1 - tail];\n if (!(lowEnd >= 0 && lowEnd < highEnd && highEnd <= 1)) throw new RangeError(`${name}: the ends are 0 <= low < high <= 1, not ${lowEnd} and ${highEnd}.`);\n const level = options.ends ? highEnd - lowEnd : (options.level ?? 0.95);\n if (!(level > 0 && level < 1)) throw new RangeError(`${name}: level is more than 0 and less than 1, not ${level}.`);\n const r = random(options.seed ?? 1);\n const values: number[] = [];\n for (let i = 0; i < runs; i++) {\n const v = run(r);\n if (Number.isFinite(v)) values.push(v);\n }\n const estimate = all();\n if (values.length === 0) return { estimate, low: estimate, high: estimate, level, runs, finite: 0, seed: r.seed };\n const s = sorted(values);\n const method = options.method ?? \"linear\";\n return { estimate, low: quantileSorted(s, lowEnd, method), high: quantileSorted(s, highEnd, method), level, runs, finite: s.length, seed: r.seed };\n}\n\n/** A bootstrap interval of the median. */\nexport function bootstrapMedian(values: readonly number[], options: BootstrapOptions = {}): Interval {\n return bootstrap(values, median, options);\n}\n\n/**\n * A cluster bootstrap: each run draws whole clusters with replacement, and the statistic reads the rows of the drawn\n * clusters. Use it when the rows of one cluster (one board, one seed, one site) are not independent. The clusters\n * are in the order of their first row.\n */\nexport function bootstrapClusters<T>(\n rows: readonly T[],\n cluster: (row: T) => string,\n statistic: (rows: readonly T[]) => number,\n options: BootstrapOptions = {},\n): Interval {\n const groups = new Map<string, T[]>();\n for (const row of rows) {\n const key = cluster(row);\n const list = groups.get(key);\n if (list) list.push(row);\n else groups.set(key, [row]);\n }\n const clusters = [...groups.values()];\n const result = bootstrap(clusters, (drawn) => statistic(drawn.flat()), options);\n // The estimate of all the rows in their own order, not in the order of the clusters.\n const estimate = statistic(rows);\n return result.finite === 0 ? { ...result, estimate, low: estimate, high: estimate } : { ...result, estimate };\n}\n","/**\n * The tests of a difference: the sign test, the paired permutation test, the Wilson interval of a proportion and\n * the Holm correction for several tests.\n */\nimport { random } from \"@mark1russell7/rigor-generators\";\nimport { mean } from \"./describe.ts\";\n\nfunction whole(name: string, value: number): void {\n if (!Number.isSafeInteger(value) || value < 0) throw new RangeError(`${name}: ${value} is not a whole number of 0 or more.`);\n}\n\n/** The log of each binomial term C(n, k) / 2^n, for each k from 0 to n. */\nfunction halfBinomialLogs(n: number): number[] {\n const logs: number[] = [];\n let logChoose = 0;\n for (let i = 0; i <= n; i++) {\n if (i > 0) logChoose += Math.log((n - i + 1) / i);\n logs.push(logChoose - n * Math.LN2);\n }\n return logs;\n}\n\n/** P(X >= k) for X ~ Binomial(n, 1/2), summed from the largest term so that a small tail keeps its digits. */\nfunction upperTail(k: number, n: number): number {\n if (k <= 0) return 1;\n if (k > n) return 0;\n const logs = halfBinomialLogs(n).slice(k);\n const top = Math.max(...logs);\n const sum = logs.reduce((s, l) => s + Math.exp(l - top), 0);\n return Math.min(1, Math.exp(top) * sum);\n}\n\n/**\n * The sign test of paired results: `better` inputs where the candidate wins and `worse` where it loses. The ties\n * do not count. One-sided (the default), it is the p of `better` wins or more if the two are equal. Two-sided, it is\n * two times the tail of the larger count, at most 1. The one-sided p is equal to `signTestP` of packing.\n */\nexport function signTest(better: number, worse: number, sides: 1 | 2 = 1): number {\n whole(\"signTest\", better);\n whole(\"signTest\", worse);\n const n = better + worse;\n if (n === 0) return 1;\n if (sides === 1) return upperTail(better, n);\n return Math.min(1, 2 * upperTail(Math.max(better, worse), n));\n}\n\n/**\n * The smallest number of wins out of `n` that the sign test calls significant at `level`. It is undefined when even\n * n wins out of n are not enough. Use it before a run, to know how many inputs a claim needs.\n */\nexport function signTestPower(n: number, level = 0.05, sides: 1 | 2 = 2): number | undefined {\n whole(\"signTestPower\", n);\n for (let k = Math.ceil(n / 2); k <= n; k++) if (signTest(k, n - k, sides) < level) return k;\n return undefined;\n}\n\n/** The z of a two-sided 95% interval. */\nexport const Z95 = 1.959963984540054;\n\n/** An interval of a proportion. */\nexport interface Proportion {\n readonly low: number;\n readonly high: number;\n}\n\n/**\n * The Wilson score interval of `k` successes in `n` trials (Wilson, 1927). It keeps its level for a small n and for\n * a proportion near 0 or 1, where the normal interval does not. For n = 0 the interval is 0 to 1.\n */\nexport function wilson(k: number, n: number, z: number = Z95): Proportion {\n whole(\"wilson\", k);\n whole(\"wilson\", n);\n if (k > n) throw new RangeError(`wilson: k ${k} is more than n ${n}.`);\n if (n === 0) return { low: 0, high: 1 };\n const p = k / n;\n const z2 = z * z;\n const denominator = 1 + z2 / n;\n const center = (p + z2 / (2 * n)) / denominator;\n const half = (z / denominator) * Math.sqrt((p * (1 - p)) / n + z2 / (4 * n * n));\n return { low: Math.max(0, center - half), high: Math.min(1, center + half) };\n}\n\n/** One test after the Holm correction. */\nexport interface Corrected {\n /** The p of the test. */\n readonly p: number;\n /** The p after the correction: compare it with the level of the whole family. */\n readonly adjusted: number;\n /** The adjusted p is less than the level. */\n readonly reject: boolean;\n}\n\n/**\n * The Holm step-down correction of a family of tests (Holm, 1979). It keeps the chance of one false claim or more\n * in the family at most `level`. It has no condition on how the tests depend on each other. The result is in the\n * order of the input.\n */\nexport function holm(pValues: readonly number[], level = 0.05): Corrected[] {\n for (const p of pValues) if (!(p >= 0 && p <= 1)) throw new RangeError(`holm: each p is from 0 to 1, not ${p}.`);\n const m = pValues.length;\n const order = pValues.map((p, i) => ({ p, i })).sort((a, b) => a.p - b.p || a.i - b.i);\n const adjusted = new Array<number>(m);\n let running = 0;\n order.forEach(({ p, i }, rank) => {\n running = Math.max(running, Math.min(1, (m - rank) * p));\n adjusted[i] = running;\n });\n return pValues.map((p, i) => ({ p, adjusted: adjusted[i] as number, reject: (adjusted[i] as number) < level }));\n}\n\nexport interface PermutationOptions {\n /** The number of random sign flips. The default is 10 000. */\n readonly runs?: number;\n /** The default is 1. */\n readonly seed?: number | string;\n /** The statistic of the differences. The default is the mean. */\n readonly statistic?: (differences: readonly number[]) => number;\n}\n\nexport interface PermutationResult {\n /** The statistic of the observed differences `candidate[i] - baseline[i]`. */\n readonly observed: number;\n /** The two-sided p: (1 + the flips with a statistic as far from 0 or farther) / (1 + runs). */\n readonly p: number;\n readonly runs: number;\n}\n\n/**\n * The paired permutation test (a random sign flip of each difference). It needs no normal distribution and uses\n * the size of each difference, which the sign test does not. A pair with a difference of 0 adds nothing.\n */\nexport function pairedPermutation(candidate: readonly number[], baseline: readonly number[], options: PermutationOptions = {}): PermutationResult {\n if (candidate.length !== baseline.length) throw new RangeError(`pairedPermutation: the lists have ${candidate.length} and ${baseline.length} values.`);\n const differences = candidate.map((c, i) => c - (baseline[i] as number));\n const statistic = options.statistic ?? mean;\n const runs = options.runs ?? 10_000;\n if (!Number.isSafeInteger(runs) || runs < 1) throw new RangeError(`pairedPermutation: runs is a whole number of 1 or more, not ${runs}.`);\n const observed = statistic(differences);\n const r = random(options.seed ?? 1);\n const flipped = new Array<number>(differences.length);\n let extreme = 0;\n for (let run = 0; run < runs; run++) {\n for (let i = 0; i < differences.length; i++) flipped[i] = r.next() < 0.5 ? -(differences[i] as number) : (differences[i] as number);\n // A small tolerance, so that a flip with the same statistic up to rounding counts as extreme.\n if (Math.abs(statistic(flipped)) >= Math.abs(observed) * (1 - 1e-12)) extreme++;\n }\n return { observed, p: (1 + extreme) / (1 + runs), runs };\n}\n","/**\n * Rank correlation: do two measures put the same items in the same order? Use it to test that a cheap proxy (a\n * predicted cost, a fast measure) orders the items as the real measure does.\n */\nimport { random } from \"@mark1russell7/rigor-generators\";\n\nfunction samePairs(name: string, a: readonly number[], b: readonly number[]): void {\n if (a.length !== b.length) throw new RangeError(`${name}: the lists have ${a.length} and ${b.length} values.`);\n for (const v of a) if (Number.isNaN(v)) throw new RangeError(`${name}: the first list has a NaN.`);\n for (const v of b) if (Number.isNaN(v)) throw new RangeError(`${name}: the second list has a NaN.`);\n}\n\n/** The ranks of the values, from 1. Equal values get the mean of their ranks. */\nexport function ranks(values: readonly number[]): number[] {\n const order = values.map((x, i) => [x, i] as const).sort((p, q) => p[0] - q[0]);\n const out = new Array<number>(values.length);\n for (let i = 0; i < order.length; ) {\n let j = i;\n while (j + 1 < order.length && (order[j + 1] as readonly [number, number])[0] === (order[i] as readonly [number, number])[0]) j++;\n for (let k = i; k <= j; k++) out[(order[k] as readonly [number, number])[1]] = (i + j) / 2 + 1;\n i = j + 1;\n }\n return out;\n}\n\n/**\n * The Spearman rank correlation: the Pearson correlation of the ranks, with mean ranks for ties. The value is from\n * -1 to 1. When one list has no spread, the value is 0. It is equal to the copy in the backtest of highways.\n */\nexport function spearman(a: readonly number[], b: readonly number[]): number {\n samePairs(\"spearman\", a, b);\n const ra = ranks(a);\n const rb = ranks(b);\n const mean = (xs: readonly number[]): number => xs.reduce((s, x) => s + x, 0) / xs.length;\n const ma = mean(ra);\n const mb = mean(rb);\n let num = 0;\n let da = 0;\n let db = 0;\n for (let i = 0; i < ra.length; i++) {\n num += ((ra[i] as number) - ma) * ((rb[i] as number) - mb);\n da += ((ra[i] as number) - ma) ** 2;\n db += ((rb[i] as number) - mb) ** 2;\n }\n return da === 0 || db === 0 ? 0 : num / Math.sqrt(da * db);\n}\n\n/** The largest number of values for which `kendallTau` examines each pair. Above it, a seeded sample of pairs. */\nexport const KENDALL_EXACT = 1500;\nconst KENDALL_SAMPLE = 200_000;\n\n/**\n * The Kendall tau-b of two lists (Kendall, 1938). The value is from -1 to 1. A pair with a tie in both lists does\n * not count. When the lists have fewer than 2 values, or no pair counts, the value is 1. Above `KENDALL_EXACT`\n * values, a seeded sample of 200 000 pairs gives an estimate. It is equal to the copy in highways.\n */\nexport function kendallTau(a: readonly number[], b: readonly number[], seed: number | string = 1): number {\n samePairs(\"kendallTau\", a, b);\n const n = a.length;\n if (n < 2) return 1;\n let concordant = 0;\n let discordant = 0;\n let tieA = 0;\n let tieB = 0;\n const visit = (i: number, j: number): void => {\n const da = Math.sign((a[i] as number) - (a[j] as number));\n const db = Math.sign((b[i] as number) - (b[j] as number));\n if (da === 0 && db === 0) return;\n if (da === 0) tieA++;\n else if (db === 0) tieB++;\n else if (da === db) concordant++;\n else discordant++;\n };\n if (n <= KENDALL_EXACT) {\n for (let i = 0; i < n; i++) for (let j = i + 1; j < n; j++) visit(i, j);\n } else {\n const r = random(seed);\n for (let k = 0; k < KENDALL_SAMPLE; k++) {\n const i = r.below(n);\n const j = r.below(n);\n if (i !== j) visit(i, j);\n }\n }\n const den = Math.sqrt((concordant + discordant + tieA) * (concordant + discordant + tieB));\n return den === 0 ? 1 : (concordant - discordant) / den;\n}\n","/**\n * The comparison of a candidate with a baseline, and the calibration of predictions. These functions make two fleet\n * rules into code. First, a change of a default is judged against the current default. The measure is the inputs that\n * gain and the inputs that lose beyond the noise, with the effect size. A sign test alone is not enough. Second, each\n * prediction states a probability, and the Brier score measures how good the stated probabilities were.\n */\nimport { median, quantileSorted, sd, sorted } from \"./describe.ts\";\nimport { type Proportion, signTest, wilson } from \"./tests.ts\";\n\nexport interface NoiseOptions {\n /** The number of standard deviations. The default is 2. */\n readonly k?: number;\n /** The fewest values (seeds or folds) that give a floor. The default is 5, the 5-seed rule. */\n readonly minimum?: number;\n}\n\n/**\n * The noise floor of a measure: k sample standard deviations of its value over independent seeds or folds. A\n * difference smaller than the floor is not a result.\n */\nexport function noiseFloor(values: readonly number[], options: NoiseOptions = {}): number {\n const minimum = options.minimum ?? 5;\n if (values.length < minimum) throw new RangeError(`noiseFloor: ${values.length} values; the rule needs ${minimum} or more seeds or folds.`);\n return (options.k ?? 2) * sd(values);\n}\n\nexport interface CompareOptions {\n /** \"higher\" (the default): a ratio above 1 is a gain. \"lower\": a ratio below 1 is a gain, as for a time or a cost. */\n readonly better?: \"higher\" | \"lower\";\n /** A ratio at this distance from 1 or nearer is a tie. The default is 0: only a ratio of exactly 1 is a tie. */\n readonly tie?: number;\n /** A gain or a loss counts only beyond this distance from 1. The default is 0. Use noiseFloor. */\n readonly noise?: number;\n}\n\n/** The effect on the inputs that are not ties. */\nexport interface Effect {\n readonly n: number;\n readonly median: number;\n /** The 0.1 quantile (linear). */\n readonly p10: number;\n /** The 0.9 quantile (linear). */\n readonly p90: number;\n}\n\nexport interface Comparison {\n /** The number of inputs. */\n readonly n: number;\n readonly ties: number;\n /** The inputs where the candidate is better, by any amount. */\n readonly wins: number;\n /** The inputs where the candidate is worse, by any amount. */\n readonly losses: number;\n /** The one-sided sign test of wins against losses. */\n readonly signP: number;\n /** The ratios of the inputs that are not ties, or undefined when each input is a tie. */\n readonly effect: Effect | undefined;\n /** The inputs that gain beyond the noise. */\n readonly gains: number;\n /** The inputs that lose beyond the noise. */\n readonly drops: number;\n /** gains / n, with its Wilson interval. */\n readonly gainShare: number;\n readonly gainInterval: Proportion;\n /** drops / n, with its Wilson interval. */\n readonly dropShare: number;\n readonly dropInterval: Proportion;\n}\n\n/**\n * The comparison of a candidate with a baseline, from the ratio candidate / baseline of each input. It answers the\n * three questions of a promotion. How often is the candidate better? By how much? How often is it worse beyond the\n * noise? A promotion reads `gains` against `drops` and `effect`, not `signP` alone, because a sign test counts a\n * win of any size.\n */\nexport function compareRatios(ratios: readonly number[], options: CompareOptions = {}): Comparison {\n if (ratios.length === 0) throw new RangeError(\"compareRatios: the list is empty.\");\n const sign = (options.better ?? \"higher\") === \"higher\" ? 1 : -1;\n const tie = options.tie ?? 0;\n const noise = options.noise ?? 0;\n if (!(tie >= 0) || !(noise >= 0)) throw new RangeError(\"compareRatios: tie and noise are 0 or more.\");\n let ties = 0;\n let wins = 0;\n let losses = 0;\n let gains = 0;\n let drops = 0;\n const changed: number[] = [];\n for (const ratio of ratios) {\n if (!Number.isFinite(ratio)) throw new RangeError(`compareRatios: each ratio is a finite number, not ${ratio}.`);\n const d = sign * (ratio - 1);\n if (Math.abs(ratio - 1) <= tie) {\n ties++;\n continue;\n }\n changed.push(ratio);\n if (d > 0) wins++;\n else losses++;\n if (d > noise) gains++;\n else if (-d > noise) drops++;\n }\n const n = ratios.length;\n const s = sorted(changed);\n const effect = s.length === 0 ? undefined : { n: s.length, median: median(s), p10: quantileSorted(s, 0.1), p90: quantileSorted(s, 0.9) };\n return {\n n,\n ties,\n wins,\n losses,\n signP: signTest(wins, losses),\n effect,\n gains,\n drops,\n gainShare: gains / n,\n gainInterval: wilson(gains, n),\n dropShare: drops / n,\n dropInterval: wilson(drops, n),\n };\n}\n\n/** A prediction: the stated probability that the claim holds, and if it held. */\nexport interface Forecast {\n readonly p: number;\n readonly hit: boolean;\n}\n\nfunction checkedForecasts(name: string, forecasts: readonly Forecast[]): void {\n if (forecasts.length === 0) throw new RangeError(`${name}: the list is empty.`);\n for (const f of forecasts) if (!(f.p >= 0 && f.p <= 1)) throw new RangeError(`${name}: each p is from 0 to 1, not ${f.p}.`);\n}\n\n/** The Brier score: the mean of (p - outcome)^2. 0 is perfect, and 0.25 is a constant 0.5. Lower is better. */\nexport function brier(forecasts: readonly Forecast[]): number {\n checkedForecasts(\"brier\", forecasts);\n let sum = 0;\n for (const f of forecasts) sum += (f.p - (f.hit ? 1 : 0)) ** 2;\n return sum / forecasts.length;\n}\n\n/** The predictions of one stated level. */\nexport interface CalibrationLevel {\n readonly stated: number;\n readonly n: number;\n readonly hits: number;\n /** hits / n. A calibrated level has a rate near `stated`. */\n readonly rate: number;\n /** The Wilson interval of the rate. A level is overconfident when `stated` is above it. */\n readonly interval: Proportion;\n}\n\n/** The hit rate of each stated level, from the lowest level. */\nexport function calibration(forecasts: readonly Forecast[]): CalibrationLevel[] {\n checkedForecasts(\"calibration\", forecasts);\n const levels = new Map<number, { n: number; hits: number }>();\n for (const f of forecasts) {\n const level = levels.get(f.p) ?? { n: 0, hits: 0 };\n level.n++;\n if (f.hit) level.hits++;\n levels.set(f.p, level);\n }\n return [...levels]\n .sort((a, b) => a[0] - b[0])\n .map(([stated, { n, hits }]) => ({ stated, n, hits, rate: hits / n, interval: wilson(hits, n) }));\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package",
3
3
  "name": "@mark1russell7/rigor-statistics",
4
- "version": "0.1.0",
4
+ "version": "0.1.1",
5
5
  "description": "The statistics of measured work: quantiles, bootstrap intervals, the sign test, the paired permutation test, Wilson intervals, the Holm correction, rank correlation, the comparison with a baseline beyond the noise, and the Brier score.",
6
6
  "keywords": [
7
7
  "statistics",