@mark1russell7/rigor-statistics 0.1.0 → 0.1.2

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
@@ -1,6 +1,6 @@
1
1
  # @mark1russell7/rigor-statistics
2
2
 
3
- The statistics of measured work. It gives quantiles, bootstrap intervals, the sign test, the paired permutation test, Wilson intervals and the Holm correction. It also gives rank correlation, the comparison with a baseline beyond the noise, and the Brier score of predictions.
3
+ The statistics of measured work. It gives quantiles, bootstrap intervals, the sign test, the paired permutation test, Wilson intervals and the Holm correction. It also gives least squares, rank correlation, the comparison with a baseline beyond the noise, and the Brier score of predictions.
4
4
 
5
5
  ```sh
6
6
  npm install @mark1russell7/rigor-statistics
@@ -30,6 +30,10 @@ 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. |
35
+ | `leastSquares(x, y, { intercept })` | The ordinary least-squares fit of `y` on the rows of `x`, with a QR decomposition. It gives the coefficients, `residualSd`, `r2`, `n` and `k`. A singular design gives NaN coefficients. |
36
+ | `bootstrapLeastSquares(x, y, options)` | One bootstrap interval for each coefficient of `leastSquares`, from the same draws of whole rows. A singular draw does not count. |
33
37
  | `signTest(better, worse, sides)`, `signTestPower(n, level)` | The sign test, and the fewest wins out of `n` that the test calls significant. |
34
38
  | `pairedPermutation(candidate, baseline, options)` | The paired permutation test (random sign flips). It uses the size of each difference. |
35
39
  | `wilson(k, n, z)` | The Wilson interval of a proportion. |
package/build/index.d.ts CHANGED
@@ -40,6 +40,40 @@ interface Summary {
40
40
  readonly iqr: number;
41
41
  }
42
42
  export declare function summarize(values: readonly number[]): Summary;
43
+ /**
44
+ * Ordinary least squares with a Householder QR decomposition. The QR decomposition does not square the condition
45
+ * number of the design, as the normal equations do. A design without full rank gives NaN coefficients and does not
46
+ * throw. Thus a bootstrap run with a singular draw is not finite, and it does not count.
47
+ */
48
+ interface LeastSquaresOptions {
49
+ /** When true, the fit adds an intercept as coefficient 0. The default is true. */
50
+ readonly intercept?: boolean;
51
+ }
52
+ interface LeastSquaresFit {
53
+ /** The coefficients. With an intercept, coefficient 0 is the intercept and coefficient `j + 1` is predictor `j`. */
54
+ readonly coefficients: number[];
55
+ /** The residual standard deviation: the square root of the residual sum of squares divided by `n - k`. */
56
+ readonly residualSd: number;
57
+ /**
58
+ * The coefficient of determination: 1 minus the residual sum of squares divided by the total sum of squares. With
59
+ * an intercept, the total sum uses the distances from the mean of `y`. Without an intercept, it uses the values.
60
+ */
61
+ readonly r2: number;
62
+ /** The number of rows. */
63
+ readonly n: number;
64
+ /** The number of coefficients, with the intercept. */
65
+ readonly k: number;
66
+ }
67
+ /**
68
+ * The ordinary least-squares fit of `y` on the rows of `x`, with `k` coefficients. Each row has the same number of
69
+ * predictors. The fit solves the problem with a Householder QR decomposition. When the design does not have full
70
+ * rank, or when `n <= k`, the coefficients, `residualSd` and `r2` are NaN. When the total sum of squares is 0,
71
+ * `r2` is NaN.
72
+ *
73
+ * This function throws a RangeError for an empty `x` and for rows of different lengths. It also throws
74
+ * for `x` and `y` of different lengths, for a value that is not finite, and for a fit with no coefficient.
75
+ */
76
+ export declare function leastSquares(x: readonly (readonly number[])[], y: readonly number[], options?: LeastSquaresOptions): LeastSquaresFit;
43
77
  interface BootstrapOptions {
44
78
  /** The number of samples. The default is 10 000. */
45
79
  readonly runs?: number;
@@ -73,6 +107,27 @@ interface Interval {
73
107
  * infinity) does not count. When no run is finite, low and high are equal to the estimate.
74
108
  */
75
109
  export declare function bootstrap<T>(data: readonly T[], statistic: (sample: readonly T[]) => number, options?: BootstrapOptions): Interval;
110
+ /**
111
+ * A percentile bootstrap interval of `statistic` over two independent samples `a` and `b`. Use it when the two
112
+ * samples are not paired, for example two arms of a benchmark. Each run draws `a.length` items of `a` with
113
+ * replacement, and then `b.length` items of `b` with replacement. All the draws come from one stream of
114
+ * `random(seed)`. The statistic reads the two drawn samples. The estimate is the statistic of all of `a` and `b`. A
115
+ * run whose statistic is not finite does not count. When no run is finite, low and high are equal to the estimate.
116
+ */
117
+ export declare function bootstrapTwo<A, B>(a: readonly A[], b: readonly B[], statistic: (a: readonly A[], b: readonly B[]) => number, options?: BootstrapOptions): Interval;
118
+ /**
119
+ * A bootstrap interval of the ratio `median(numerator) / median(denominator)` of two independent samples. The
120
+ * interval is the uncertainty of the ratio of the two medians. It is not the spread of the samples. A run whose
121
+ * denominator median is 0 is not finite, so it does not count.
122
+ */
123
+ export declare function bootstrapMedianRatio(numerator: readonly number[], denominator: readonly number[], options?: BootstrapOptions): Interval;
124
+ /**
125
+ * A percentile bootstrap interval of each coefficient of the least-squares fit of `y` on the rows of `x`. Each run
126
+ * draws `n` row indexes with replacement, and fits the drawn rows and their `y` values. All the intervals come from
127
+ * the same draws. The estimate of each interval is the coefficient of the fit of all the data. A run with a singular
128
+ * design gives NaN coefficients, so it does not count. The shape errors are the errors of `leastSquares`.
129
+ */
130
+ export declare function bootstrapLeastSquares(x: readonly (readonly number[])[], y: readonly number[], options?: BootstrapOptions & LeastSquaresOptions): Interval[];
76
131
  /** A bootstrap interval of the median. */
77
132
  export declare function bootstrapMedian(values: readonly number[], options?: BootstrapOptions): Interval;
78
133
  /**
@@ -231,5 +286,5 @@ interface CalibrationLevel {
231
286
  }
232
287
  /** The hit rate of each stated level, from the lowest level. */
233
288
  export declare function calibration(forecasts: readonly Forecast[]): CalibrationLevel[];
234
- export type { BootstrapOptions, CalibrationLevel, CompareOptions, Comparison, Corrected, Effect, Forecast, Interval, NoiseOptions, PermutationOptions, PermutationResult, Proportion, QuantileMethod, Summary };
289
+ export type { BootstrapOptions, CalibrationLevel, CompareOptions, Comparison, Corrected, Effect, Forecast, Interval, LeastSquaresFit, LeastSquaresOptions, NoiseOptions, PermutationOptions, PermutationResult, Proportion, QuantileMethod, Summary };
235
290
  //# sourceMappingURL=index.d.ts.map
@@ -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/regression.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;;;;;;UCxFrC;;WAEN;;UAGM;;WAEN;;WAEA;;;;;WAKA;;WAEA;;WAEA;;;;;;;;;;;wBAkBK,aAAa,mCAAmC,sBAAsB,UAAS,sBAA2B;UC/BzG;;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;;;;;;;wBAUpH,sBACd,mCACA,sBACA,UAAS,mBAAmB,sBAC3B;;wBAmFa,gBAAgB,2BAA2B,UAAS,mBAAwB;;;;;;wBAS5E,kBAAkB,GAChC,eAAe,KACf,UAAU,KAAK,cACf,YAAY,eAAe,gBAC3B,UAAS,mBACR;;;;;;wBC1Ka,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
@@ -74,11 +74,123 @@ function summarize(values) {
74
74
  iqr: q3 - q1
75
75
  };
76
76
  }
77
+ // src/regression.ts
78
+ /**
79
+ * The relative tolerance of the rank test. A column is dependent when the part of it that is orthogonal to the
80
+ * columns before it is shorter than this share of its length.
81
+ */
82
+ const RANK_TOLERANCE = 1e-10;
83
+ /**
84
+ * The ordinary least-squares fit of `y` on the rows of `x`, with `k` coefficients. Each row has the same number of
85
+ * predictors. The fit solves the problem with a Householder QR decomposition. When the design does not have full
86
+ * rank, or when `n <= k`, the coefficients, `residualSd` and `r2` are NaN. When the total sum of squares is 0,
87
+ * `r2` is NaN.
88
+ *
89
+ * This function throws a RangeError for an empty `x` and for rows of different lengths. It also throws
90
+ * for `x` and `y` of different lengths, for a value that is not finite, and for a fit with no coefficient.
91
+ */
92
+ function leastSquares(x, y, options = {}) {
93
+ const intercept = options.intercept ?? true;
94
+ return fitRows(x, y, intercept, checkedShape("leastSquares", x, y, intercept));
95
+ }
96
+ /**
97
+ * This function examines the shape and the values of a least-squares problem, and gives the number of
98
+ * coefficients. It throws a RangeError for each fault.
99
+ */
100
+ function checkedShape(name, x, y, intercept) {
101
+ if (x.length === 0) throw new RangeError(`${name}: x is empty.`);
102
+ if (x.length !== y.length) throw new RangeError(`${name}: x has ${x.length} rows and y has ${y.length} values. The two lengths must be equal.`);
103
+ const p = x[0].length;
104
+ for (let i = 0; i < x.length; i++) {
105
+ const row = x[i];
106
+ if (row.length !== p) throw new RangeError(`${name}: row ${i} of x has ${row.length} values, and row 0 has ${p}. Each row must have the same length.`);
107
+ for (const v of row) if (!Number.isFinite(v)) throw new RangeError(`${name}: row ${i} of x has a value that is not finite.`);
108
+ if (!Number.isFinite(y[i])) throw new RangeError(`${name}: value ${i} of y is not finite.`);
109
+ }
110
+ const k = p + (intercept ? 1 : 0);
111
+ if (k === 0) throw new RangeError(`${name}: the fit has no coefficient. Give a predictor or an intercept.`);
112
+ return k;
113
+ }
114
+ /**
115
+ * The fit of rows with a known shape. The caller examined the shape with `checkedShape`. The design is a matrix of
116
+ * `n` rows and `k` columns, kept by column.
117
+ */
118
+ function fitRows(x, y, intercept, k) {
119
+ const n = x.length;
120
+ const failed = () => ({
121
+ coefficients: new Array(k).fill(NaN),
122
+ residualSd: NaN,
123
+ r2: NaN,
124
+ n,
125
+ k
126
+ });
127
+ if (n <= k) return failed();
128
+ const offset = intercept ? 1 : 0;
129
+ const a = new Float64Array(n * k);
130
+ for (let i = 0; i < n; i++) {
131
+ const row = x[i];
132
+ if (intercept) a[i] = 1;
133
+ for (let j = offset; j < k; j++) a[j * n + i] = row[j - offset];
134
+ }
135
+ const qty = Float64Array.from(y);
136
+ const diagonal = new Float64Array(k);
137
+ for (let j = 0; j < k; j++) {
138
+ const c = j * n;
139
+ let head = 0;
140
+ for (let i = 0; i < j; i++) head += a[c + i] ** 2;
141
+ let tail2 = 0;
142
+ for (let i = j; i < n; i++) tail2 += a[c + i] ** 2;
143
+ const tail = Math.sqrt(tail2);
144
+ if (!(tail > RANK_TOLERANCE * Math.sqrt(head + tail2))) return failed();
145
+ const alpha = a[c + j] > 0 ? -tail : tail;
146
+ a[c + j] = a[c + j] - alpha;
147
+ let vv = 0;
148
+ for (let i = j; i < n; i++) vv += a[c + i] ** 2;
149
+ const reflect = (target, start) => {
150
+ let dot = 0;
151
+ for (let i = j; i < n; i++) dot += a[c + i] * target[start + i];
152
+ const f = 2 * dot / vv;
153
+ for (let i = j; i < n; i++) target[start + i] = target[start + i] - f * a[c + i];
154
+ };
155
+ for (let m = j + 1; m < k; m++) reflect(a, m * n);
156
+ reflect(qty, 0);
157
+ diagonal[j] = alpha;
158
+ }
159
+ const b = new Array(k).fill(0);
160
+ for (let j = k - 1; j >= 0; j--) {
161
+ let s = qty[j];
162
+ for (let m = j + 1; m < k; m++) s -= a[m * n + j] * b[m];
163
+ b[j] = s / diagonal[j];
164
+ }
165
+ let center = 0;
166
+ if (intercept) {
167
+ for (const v of y) center += v;
168
+ center /= n;
169
+ }
170
+ let rss = 0;
171
+ let tss = 0;
172
+ for (let i = 0; i < n; i++) {
173
+ const row = x[i];
174
+ let fitted = intercept ? b[0] : 0;
175
+ for (let j = offset; j < k; j++) fitted += b[j] * row[j - offset];
176
+ rss += (y[i] - fitted) ** 2;
177
+ tss += (y[i] - center) ** 2;
178
+ }
179
+ return {
180
+ coefficients: b,
181
+ residualSd: Math.sqrt(rss / (n - k)),
182
+ r2: tss > 0 ? 1 - rss / tss : NaN,
183
+ n,
184
+ k
185
+ };
186
+ }
77
187
  // src/bootstrap.ts
78
188
  /**
79
189
  * The percentile bootstrap. Each run draws n items with replacement (`below(n)` of rigor-generators, which is
80
190
  * `floor(next() * n)`), and computes the statistic of the sample. The interval is two quantiles of the statistics.
81
191
  * With the same seed, the draws are equal to the draws of the copies in highways, packing and layout-contract.
192
+ * `bootstrapTwo` draws two independent samples in each run: first the items of `a`, then the items of `b`.
193
+ * `bootstrapLeastSquares` draws whole rows, and gives one interval for each coefficient from the same draws.
82
194
  */
83
195
  /**
84
196
  * A percentile bootstrap interval of `statistic` over `data`. A run whose statistic is not finite (NaN or an
@@ -86,23 +198,82 @@ function summarize(values) {
86
198
  */
87
199
  function bootstrap(data, statistic, options = {}) {
88
200
  if (data.length === 0) throw new RangeError("bootstrap: the data is empty.");
201
+ const n = data.length;
202
+ const sample = new Array(n);
203
+ return percentile("bootstrap", options, (r) => {
204
+ for (let i = 0; i < n; i++) sample[i] = data[r.below(n)];
205
+ return statistic(sample);
206
+ }, () => statistic(data));
207
+ }
208
+ /**
209
+ * A percentile bootstrap interval of `statistic` over two independent samples `a` and `b`. Use it when the two
210
+ * samples are not paired, for example two arms of a benchmark. Each run draws `a.length` items of `a` with
211
+ * replacement, and then `b.length` items of `b` with replacement. All the draws come from one stream of
212
+ * `random(seed)`. The statistic reads the two drawn samples. The estimate is the statistic of all of `a` and `b`. A
213
+ * run whose statistic is not finite does not count. When no run is finite, low and high are equal to the estimate.
214
+ */
215
+ function bootstrapTwo(a, b, statistic, options = {}) {
216
+ if (a.length === 0) throw new RangeError("bootstrapTwo: the first sample is empty.");
217
+ if (b.length === 0) throw new RangeError("bootstrapTwo: the second sample is empty.");
218
+ const na = a.length;
219
+ const nb = b.length;
220
+ const sampleA = new Array(na);
221
+ const sampleB = new Array(nb);
222
+ return percentile("bootstrapTwo", options, (r) => {
223
+ for (let i = 0; i < na; i++) sampleA[i] = a[r.below(na)];
224
+ for (let i = 0; i < nb; i++) sampleB[i] = b[r.below(nb)];
225
+ return statistic(sampleA, sampleB);
226
+ }, () => statistic(a, b));
227
+ }
228
+ /**
229
+ * A bootstrap interval of the ratio `median(numerator) / median(denominator)` of two independent samples. The
230
+ * interval is the uncertainty of the ratio of the two medians. It is not the spread of the samples. A run whose
231
+ * denominator median is 0 is not finite, so it does not count.
232
+ */
233
+ function bootstrapMedianRatio(numerator, denominator, options = {}) {
234
+ return bootstrapTwo(numerator, denominator, (a, b) => median(a) / median(b), options);
235
+ }
236
+ /**
237
+ * A percentile bootstrap interval of each coefficient of the least-squares fit of `y` on the rows of `x`. Each run
238
+ * draws `n` row indexes with replacement, and fits the drawn rows and their `y` values. All the intervals come from
239
+ * the same draws. The estimate of each interval is the coefficient of the fit of all the data. A run with a singular
240
+ * design gives NaN coefficients, so it does not count. The shape errors are the errors of `leastSquares`.
241
+ */
242
+ function bootstrapLeastSquares(x, y, options = {}) {
243
+ const intercept = options.intercept ?? true;
244
+ const k = checkedShape("bootstrapLeastSquares", x, y, intercept);
245
+ const n = x.length;
246
+ const sampleX = new Array(n);
247
+ const sampleY = new Array(n);
248
+ return percentiles("bootstrapLeastSquares", options, k, (r) => {
249
+ for (let i = 0; i < n; i++) {
250
+ const row = r.below(n);
251
+ sampleX[i] = x[row];
252
+ sampleY[i] = y[row];
253
+ }
254
+ return fitRows(sampleX, sampleY, intercept, k).coefficients;
255
+ }, () => fitRows(x, y, intercept, k).coefficients);
256
+ }
257
+ /** This function examines the options of a bootstrap, and gives the defaults. It throws a RangeError for each fault. */
258
+ function settings(name, options) {
89
259
  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}.`);
260
+ if (!Number.isSafeInteger(runs) || runs < 1) throw new RangeError(`${name}: runs is a whole number of 1 or more, not ${runs}.`);
91
261
  const tail = (1 - (options.level ?? .95)) / 2;
92
262
  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}.`);
263
+ if (!(lowEnd >= 0 && lowEnd < highEnd && highEnd <= 1)) throw new RangeError(`${name}: the ends are 0 <= low < high <= 1, not ${lowEnd} and ${highEnd}.`);
94
264
  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}.`);
96
- const r = random(options.seed ?? 1);
97
- const n = data.length;
98
- const sample = new Array(n);
99
- 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);
103
- if (Number.isFinite(v)) values.push(v);
104
- }
105
- const estimate = statistic(data);
265
+ if (!(level > 0 && level < 1)) throw new RangeError(`${name}: level is more than 0 and less than 1, not ${level}.`);
266
+ return {
267
+ runs,
268
+ lowEnd,
269
+ highEnd,
270
+ level,
271
+ method: options.method ?? "linear"
272
+ };
273
+ }
274
+ /** The interval of the finite statistics of the runs. When no run is finite, low and high are equal to the estimate. */
275
+ function ends(values, estimate, s, seed) {
276
+ const { runs, level } = s;
106
277
  if (values.length === 0) return {
107
278
  estimate,
108
279
  low: estimate,
@@ -110,20 +281,49 @@ function bootstrap(data, statistic, options = {}) {
110
281
  level,
111
282
  runs,
112
283
  finite: 0,
113
- seed: r.seed
284
+ seed
114
285
  };
115
- const s = sorted(values);
116
- const method = options.method ?? "linear";
286
+ const v = sorted(values);
117
287
  return {
118
288
  estimate,
119
- low: quantileSorted(s, lowEnd, method),
120
- high: quantileSorted(s, highEnd, method),
289
+ low: quantileSorted(v, s.lowEnd, s.method),
290
+ high: quantileSorted(v, s.highEnd, s.method),
121
291
  level,
122
292
  runs,
123
- finite: s.length,
124
- seed: r.seed
293
+ finite: v.length,
294
+ seed
125
295
  };
126
296
  }
297
+ /**
298
+ * The shared part of each percentile bootstrap. It examines the options, and starts one stream of `random(seed)`.
299
+ * Each run gives the stream to `run`, which draws the samples and returns their statistic. Then `all` gives the
300
+ * estimate. The order of the draws is the order in `run`.
301
+ */
302
+ function percentile(name, options, run, all) {
303
+ const s = settings(name, options);
304
+ const r = random(options.seed ?? 1);
305
+ const values = [];
306
+ for (let i = 0; i < s.runs; i++) {
307
+ const v = run(r);
308
+ if (Number.isFinite(v)) values.push(v);
309
+ }
310
+ return ends(values, all(), s, r.seed);
311
+ }
312
+ /**
313
+ * The percentile bootstrap of `k` statistics from the same draws. Each run gives `k` statistics. Each statistic
314
+ * has its own count of finite runs.
315
+ */
316
+ function percentiles(name, options, k, run, all) {
317
+ const s = settings(name, options);
318
+ const r = random(options.seed ?? 1);
319
+ const values = Array.from({ length: k }, () => []);
320
+ for (let i = 0; i < s.runs; i++) {
321
+ const v = run(r);
322
+ for (let j = 0; j < k; j++) if (Number.isFinite(v[j])) values[j].push(v[j]);
323
+ }
324
+ const estimate = all();
325
+ return values.map((list, j) => ends(list, estimate[j], s, r.seed));
326
+ }
127
327
  /** A bootstrap interval of the median. */
128
328
  function bootstrapMedian(values, options = {}) {
129
329
  return bootstrap(values, median, options);
@@ -455,6 +655,6 @@ function calibration(forecasts) {
455
655
  interval: wilson(hits, n)
456
656
  }));
457
657
  }
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 };
658
+ export { KENDALL_EXACT, Z95, bootstrap, bootstrapClusters, bootstrapLeastSquares, bootstrapMedian, bootstrapMedianRatio, bootstrapTwo, brier, calibration, compareRatios, holm, kendallTau, leastSquares, mean, median, noiseFloor, pairedPermutation, quantile, quantileSorted, ranks, sd, signTest, signTestPower, sorted, spearman, summarize, wilson };
459
659
 
460
660
  //# 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/regression.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 * Ordinary least squares with a Householder QR decomposition. The QR decomposition does not square the condition\n * number of the design, as the normal equations do. A design without full rank gives NaN coefficients and does not\n * throw. Thus a bootstrap run with a singular draw is not finite, and it does not count.\n */\n\nexport interface LeastSquaresOptions {\n /** When true, the fit adds an intercept as coefficient 0. The default is true. */\n readonly intercept?: boolean;\n}\n\nexport interface LeastSquaresFit {\n /** The coefficients. With an intercept, coefficient 0 is the intercept and coefficient `j + 1` is predictor `j`. */\n readonly coefficients: number[];\n /** The residual standard deviation: the square root of the residual sum of squares divided by `n - k`. */\n readonly residualSd: number;\n /**\n * The coefficient of determination: 1 minus the residual sum of squares divided by the total sum of squares. With\n * an intercept, the total sum uses the distances from the mean of `y`. Without an intercept, it uses the values.\n */\n readonly r2: number;\n /** The number of rows. */\n readonly n: number;\n /** The number of coefficients, with the intercept. */\n readonly k: number;\n}\n\n/**\n * The relative tolerance of the rank test. A column is dependent when the part of it that is orthogonal to the\n * columns before it is shorter than this share of its length.\n */\nconst RANK_TOLERANCE = 1e-10;\n\n/**\n * The ordinary least-squares fit of `y` on the rows of `x`, with `k` coefficients. Each row has the same number of\n * predictors. The fit solves the problem with a Householder QR decomposition. When the design does not have full\n * rank, or when `n <= k`, the coefficients, `residualSd` and `r2` are NaN. When the total sum of squares is 0,\n * `r2` is NaN.\n *\n * This function throws a RangeError for an empty `x` and for rows of different lengths. It also throws\n * for `x` and `y` of different lengths, for a value that is not finite, and for a fit with no coefficient.\n */\nexport function leastSquares(x: readonly (readonly number[])[], y: readonly number[], options: LeastSquaresOptions = {}): LeastSquaresFit {\n const intercept = options.intercept ?? true;\n const k = checkedShape(\"leastSquares\", x, y, intercept);\n return fitRows(x, y, intercept, k);\n}\n\n/**\n * This function examines the shape and the values of a least-squares problem, and gives the number of\n * coefficients. It throws a RangeError for each fault.\n */\nexport function checkedShape(name: string, x: readonly (readonly number[])[], y: readonly number[], intercept: boolean): number {\n if (x.length === 0) throw new RangeError(`${name}: x is empty.`);\n if (x.length !== y.length) throw new RangeError(`${name}: x has ${x.length} rows and y has ${y.length} values. The two lengths must be equal.`);\n const p = (x[0] as readonly number[]).length;\n for (let i = 0; i < x.length; i++) {\n const row = x[i] as readonly number[];\n if (row.length !== p) throw new RangeError(`${name}: row ${i} of x has ${row.length} values, and row 0 has ${p}. Each row must have the same length.`);\n for (const v of row) if (!Number.isFinite(v)) throw new RangeError(`${name}: row ${i} of x has a value that is not finite.`);\n if (!Number.isFinite(y[i] as number)) throw new RangeError(`${name}: value ${i} of y is not finite.`);\n }\n const k = p + (intercept ? 1 : 0);\n if (k === 0) throw new RangeError(`${name}: the fit has no coefficient. Give a predictor or an intercept.`);\n return k;\n}\n\n/**\n * The fit of rows with a known shape. The caller examined the shape with `checkedShape`. The design is a matrix of\n * `n` rows and `k` columns, kept by column.\n */\nexport function fitRows(x: readonly (readonly number[])[], y: readonly number[], intercept: boolean, k: number): LeastSquaresFit {\n const n = x.length;\n const failed = (): LeastSquaresFit => ({ coefficients: new Array<number>(k).fill(Number.NaN), residualSd: Number.NaN, r2: Number.NaN, n, k });\n if (n <= k) return failed();\n const offset = intercept ? 1 : 0;\n const a = new Float64Array(n * k);\n for (let i = 0; i < n; i++) {\n const row = x[i] as readonly number[];\n if (intercept) a[i] = 1;\n for (let j = offset; j < k; j++) a[j * n + i] = row[j - offset] as number;\n }\n const qty = Float64Array.from(y);\n const diagonal = new Float64Array(k);\n for (let j = 0; j < k; j++) {\n const c = j * n;\n // A reflection keeps the length of a column. Thus the length of column j is the length of its first form.\n let head = 0;\n for (let i = 0; i < j; i++) head += (a[c + i] as number) ** 2;\n let tail2 = 0;\n for (let i = j; i < n; i++) tail2 += (a[c + i] as number) ** 2;\n // The tail is the part of column j that is orthogonal to the columns before it.\n const tail = Math.sqrt(tail2);\n if (!(tail > RANK_TOLERANCE * Math.sqrt(head + tail2))) return failed();\n const alpha = (a[c + j] as number) > 0 ? -tail : tail;\n // The Householder vector v is the tail of column j minus alpha times the first unit vector. It is kept in place.\n a[c + j] = (a[c + j] as number) - alpha;\n let vv = 0;\n for (let i = j; i < n; i++) vv += (a[c + i] as number) ** 2;\n const reflect = (target: Float64Array, start: number): void => {\n let dot = 0;\n for (let i = j; i < n; i++) dot += (a[c + i] as number) * (target[start + i] as number);\n const f = (2 * dot) / vv;\n for (let i = j; i < n; i++) target[start + i] = (target[start + i] as number) - f * (a[c + i] as number);\n };\n for (let m = j + 1; m < k; m++) reflect(a, m * n);\n reflect(qty, 0);\n diagonal[j] = alpha;\n }\n // The back substitution of R b = Q'y. Above the diagonal, R is in the columns of a.\n const b = new Array<number>(k).fill(0);\n for (let j = k - 1; j >= 0; j--) {\n let s = qty[j] as number;\n for (let m = j + 1; m < k; m++) s -= (a[m * n + j] as number) * (b[m] as number);\n b[j] = s / (diagonal[j] as number);\n }\n let center = 0;\n if (intercept) {\n for (const v of y) center += v;\n center /= n;\n }\n let rss = 0;\n let tss = 0;\n for (let i = 0; i < n; i++) {\n const row = x[i] as readonly number[];\n let fitted = intercept ? (b[0] as number) : 0;\n for (let j = offset; j < k; j++) fitted += (b[j] as number) * (row[j - offset] as number);\n rss += ((y[i] as number) - fitted) ** 2;\n tss += ((y[i] as number) - center) ** 2;\n }\n return { coefficients: b, residualSd: Math.sqrt(rss / (n - k)), r2: tss > 0 ? 1 - rss / tss : Number.NaN, n, k };\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 * `bootstrapLeastSquares` draws whole rows, and gives one interval for each coefficient from the same draws.\n */\nimport { type Random, random } from \"@mark1russell7/rigor-generators\";\nimport { type QuantileMethod, median, quantileSorted, sorted } from \"./describe.ts\";\nimport { type LeastSquaresOptions, checkedShape, fitRows } from \"./regression.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 * A percentile bootstrap interval of each coefficient of the least-squares fit of `y` on the rows of `x`. Each run\n * draws `n` row indexes with replacement, and fits the drawn rows and their `y` values. All the intervals come from\n * the same draws. The estimate of each interval is the coefficient of the fit of all the data. A run with a singular\n * design gives NaN coefficients, so it does not count. The shape errors are the errors of `leastSquares`.\n */\nexport function bootstrapLeastSquares(\n x: readonly (readonly number[])[],\n y: readonly number[],\n options: BootstrapOptions & LeastSquaresOptions = {},\n): Interval[] {\n const intercept = options.intercept ?? true;\n const k = checkedShape(\"bootstrapLeastSquares\", x, y, intercept);\n const n = x.length;\n const sampleX = new Array<readonly number[]>(n);\n const sampleY = new Array<number>(n);\n return percentiles(\n \"bootstrapLeastSquares\",\n options,\n k,\n (r) => {\n for (let i = 0; i < n; i++) {\n const row = r.below(n);\n sampleX[i] = x[row] as readonly number[];\n sampleY[i] = y[row] as number;\n }\n return fitRows(sampleX, sampleY, intercept, k).coefficients;\n },\n () => fitRows(x, y, intercept, k).coefficients,\n );\n}\n\ninterface Settings {\n readonly runs: number;\n readonly lowEnd: number;\n readonly highEnd: number;\n readonly level: number;\n readonly method: QuantileMethod;\n}\n\n/** This function examines the options of a bootstrap, and gives the defaults. It throws a RangeError for each fault. */\nfunction settings(name: string, options: BootstrapOptions): Settings {\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 return { runs, lowEnd, highEnd, level, method: options.method ?? \"linear\" };\n}\n\n/** The interval of the finite statistics of the runs. When no run is finite, low and high are equal to the estimate. */\nfunction ends(values: number[], estimate: number, s: Settings, seed: number): Interval {\n const { runs, level } = s;\n if (values.length === 0) return { estimate, low: estimate, high: estimate, level, runs, finite: 0, seed };\n const v = sorted(values);\n return { estimate, low: quantileSorted(v, s.lowEnd, s.method), high: quantileSorted(v, s.highEnd, s.method), level, runs, finite: v.length, seed };\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 s = settings(name, options);\n const r = random(options.seed ?? 1);\n const values: number[] = [];\n for (let i = 0; i < s.runs; i++) {\n const v = run(r);\n if (Number.isFinite(v)) values.push(v);\n }\n return ends(values, all(), s, r.seed);\n}\n\n/**\n * The percentile bootstrap of `k` statistics from the same draws. Each run gives `k` statistics. Each statistic\n * has its own count of finite runs.\n */\nfunction percentiles(name: string, options: BootstrapOptions, k: number, run: (r: Random) => readonly number[], all: () => readonly number[]): Interval[] {\n const s = settings(name, options);\n const r = random(options.seed ?? 1);\n const values = Array.from({ length: k }, (): number[] => []);\n for (let i = 0; i < s.runs; i++) {\n const v = run(r);\n for (let j = 0; j < k; j++) if (Number.isFinite(v[j])) (values[j] as number[]).push(v[j] as number);\n }\n const estimate = all();\n return values.map((list, j) => ends(list, estimate[j] as number, s, 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.2",
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",