@uptimizr/db 2.0.1 → 2.1.0

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.
Files changed (171) hide show
  1. package/AGENTS.md +228 -3
  2. package/README.md +13 -0
  3. package/dist/duckdb/migrations.d.ts.map +1 -1
  4. package/dist/duckdb/migrations.js +169 -0
  5. package/dist/duckdb/migrations.js.map +1 -1
  6. package/dist/duckdb/panelSpecs.d.ts +29 -0
  7. package/dist/duckdb/panelSpecs.d.ts.map +1 -0
  8. package/dist/duckdb/panelSpecs.js +104 -0
  9. package/dist/duckdb/panelSpecs.js.map +1 -0
  10. package/dist/duckdb/projectMetadata.d.ts +43 -0
  11. package/dist/duckdb/projectMetadata.d.ts.map +1 -0
  12. package/dist/duckdb/projectMetadata.js +222 -0
  13. package/dist/duckdb/projectMetadata.js.map +1 -0
  14. package/dist/duckdb/subscriptions.d.ts +45 -0
  15. package/dist/duckdb/subscriptions.d.ts.map +1 -0
  16. package/dist/duckdb/subscriptions.js +202 -0
  17. package/dist/duckdb/subscriptions.js.map +1 -0
  18. package/dist/index.d.ts +18 -4
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +48 -3
  21. package/dist/index.js.map +1 -1
  22. package/dist/insights/anomalies.d.ts +213 -0
  23. package/dist/insights/anomalies.d.ts.map +1 -0
  24. package/dist/insights/anomalies.js +340 -0
  25. package/dist/insights/anomalies.js.map +1 -0
  26. package/dist/insights/baseline.d.ts +51 -0
  27. package/dist/insights/baseline.d.ts.map +1 -0
  28. package/dist/insights/baseline.js +48 -0
  29. package/dist/insights/baseline.js.map +1 -0
  30. package/dist/insights/buckets.d.ts +114 -0
  31. package/dist/insights/buckets.d.ts.map +1 -0
  32. package/dist/insights/buckets.js +220 -0
  33. package/dist/insights/buckets.js.map +1 -0
  34. package/dist/insights/changepoint.d.ts +130 -0
  35. package/dist/insights/changepoint.d.ts.map +1 -0
  36. package/dist/insights/changepoint.js +209 -0
  37. package/dist/insights/changepoint.js.map +1 -0
  38. package/dist/insights/evaluate.d.ts +63 -0
  39. package/dist/insights/evaluate.d.ts.map +1 -0
  40. package/dist/insights/evaluate.js +158 -0
  41. package/dist/insights/evaluate.js.map +1 -0
  42. package/dist/insights/health.d.ts +219 -0
  43. package/dist/insights/health.d.ts.map +1 -0
  44. package/dist/insights/health.js +349 -0
  45. package/dist/insights/health.js.map +1 -0
  46. package/dist/insights/index.d.ts +45 -0
  47. package/dist/insights/index.d.ts.map +1 -0
  48. package/dist/insights/index.js +40 -0
  49. package/dist/insights/index.js.map +1 -0
  50. package/dist/insights/measures.d.ts +222 -0
  51. package/dist/insights/measures.d.ts.map +1 -0
  52. package/dist/insights/measures.js +431 -0
  53. package/dist/insights/measures.js.map +1 -0
  54. package/dist/insights/movers.d.ts +123 -0
  55. package/dist/insights/movers.d.ts.map +1 -0
  56. package/dist/insights/movers.js +166 -0
  57. package/dist/insights/movers.js.map +1 -0
  58. package/dist/insights/significance.d.ts +221 -0
  59. package/dist/insights/significance.d.ts.map +1 -0
  60. package/dist/insights/significance.js +531 -0
  61. package/dist/insights/significance.js.map +1 -0
  62. package/dist/insights/stats.d.ts +104 -0
  63. package/dist/insights/stats.d.ts.map +1 -0
  64. package/dist/insights/stats.js +181 -0
  65. package/dist/insights/stats.js.map +1 -0
  66. package/dist/insights/windows.d.ts +95 -0
  67. package/dist/insights/windows.d.ts.map +1 -0
  68. package/dist/insights/windows.js +97 -0
  69. package/dist/insights/windows.js.map +1 -0
  70. package/dist/metadata.d.ts +178 -1
  71. package/dist/metadata.d.ts.map +1 -1
  72. package/dist/metadata.js +80 -0
  73. package/dist/metadata.js.map +1 -1
  74. package/dist/narrative/build.d.ts +78 -0
  75. package/dist/narrative/build.d.ts.map +1 -0
  76. package/dist/narrative/build.js +437 -0
  77. package/dist/narrative/build.js.map +1 -0
  78. package/dist/narrative/index.d.ts +14 -0
  79. package/dist/narrative/index.d.ts.map +1 -0
  80. package/dist/narrative/index.js +13 -0
  81. package/dist/narrative/index.js.map +1 -0
  82. package/dist/narrative/text.d.ts +25 -0
  83. package/dist/narrative/text.d.ts.map +1 -0
  84. package/dist/narrative/text.js +41 -0
  85. package/dist/narrative/text.js.map +1 -0
  86. package/dist/parity/cases.d.ts +3 -0
  87. package/dist/parity/cases.d.ts.map +1 -1
  88. package/dist/parity/cases.js +387 -7
  89. package/dist/parity/cases.js.map +1 -1
  90. package/dist/parity/compare.d.ts +19 -0
  91. package/dist/parity/compare.d.ts.map +1 -1
  92. package/dist/parity/compare.js +19 -0
  93. package/dist/parity/compare.js.map +1 -1
  94. package/dist/parity/fixtures.d.ts.map +1 -1
  95. package/dist/parity/fixtures.js +25 -0
  96. package/dist/parity/fixtures.js.map +1 -1
  97. package/dist/query/aggregations.d.ts +46 -1
  98. package/dist/query/aggregations.d.ts.map +1 -1
  99. package/dist/query/aggregations.js +86 -0
  100. package/dist/query/aggregations.js.map +1 -1
  101. package/dist/query/clickhouseDialect.d.ts.map +1 -1
  102. package/dist/query/clickhouseDialect.js +5 -1
  103. package/dist/query/clickhouseDialect.js.map +1 -1
  104. package/dist/query/customEventVocabulary.d.ts +46 -0
  105. package/dist/query/customEventVocabulary.d.ts.map +1 -0
  106. package/dist/query/customEventVocabulary.js +127 -0
  107. package/dist/query/customEventVocabulary.js.map +1 -0
  108. package/dist/query/dsl/builders.d.ts +38 -0
  109. package/dist/query/dsl/builders.d.ts.map +1 -0
  110. package/dist/query/dsl/builders.js +35 -0
  111. package/dist/query/dsl/builders.js.map +1 -0
  112. package/dist/query/dsl/compare.d.ts +167 -0
  113. package/dist/query/dsl/compare.d.ts.map +1 -0
  114. package/dist/query/dsl/compare.js +358 -0
  115. package/dist/query/dsl/compare.js.map +1 -0
  116. package/dist/query/dsl/compile.d.ts +89 -0
  117. package/dist/query/dsl/compile.d.ts.map +1 -0
  118. package/dist/query/dsl/compile.js +174 -0
  119. package/dist/query/dsl/compile.js.map +1 -0
  120. package/dist/query/dsl/explain.d.ts +115 -0
  121. package/dist/query/dsl/explain.d.ts.map +1 -0
  122. package/dist/query/dsl/explain.js +143 -0
  123. package/dist/query/dsl/explain.js.map +1 -0
  124. package/dist/query/dsl/generic.d.ts +127 -0
  125. package/dist/query/dsl/generic.d.ts.map +1 -0
  126. package/dist/query/dsl/generic.js +323 -0
  127. package/dist/query/dsl/generic.js.map +1 -0
  128. package/dist/query/dsl/index.d.ts +30 -0
  129. package/dist/query/dsl/index.d.ts.map +1 -0
  130. package/dist/query/dsl/index.js +23 -0
  131. package/dist/query/dsl/index.js.map +1 -0
  132. package/dist/query/dsl/order.d.ts +41 -0
  133. package/dist/query/dsl/order.d.ts.map +1 -0
  134. package/dist/query/dsl/order.js +55 -0
  135. package/dist/query/dsl/order.js.map +1 -0
  136. package/dist/query/dsl/significance.d.ts +130 -0
  137. package/dist/query/dsl/significance.d.ts.map +1 -0
  138. package/dist/query/dsl/significance.js +224 -0
  139. package/dist/query/dsl/significance.js.map +1 -0
  140. package/dist/query/index.d.ts +2 -0
  141. package/dist/query/index.d.ts.map +1 -1
  142. package/dist/query/index.js +8 -0
  143. package/dist/query/index.js.map +1 -1
  144. package/dist/query/summary/index.d.ts +4 -2
  145. package/dist/query/summary/index.d.ts.map +1 -1
  146. package/dist/query/summary/index.js +2 -1
  147. package/dist/query/summary/index.js.map +1 -1
  148. package/dist/query/summary/labels.d.ts +101 -0
  149. package/dist/query/summary/labels.d.ts.map +1 -0
  150. package/dist/query/summary/labels.js +261 -0
  151. package/dist/query/summary/labels.js.map +1 -0
  152. package/dist/query/summary/reading.d.ts.map +1 -1
  153. package/dist/query/summary/reading.js +21 -2
  154. package/dist/query/summary/reading.js.map +1 -1
  155. package/dist/query/summary/schema.d.ts +250 -20
  156. package/dist/query/summary/schema.d.ts.map +1 -1
  157. package/dist/query/summary/schema.js +135 -123
  158. package/dist/query/summary/schema.js.map +1 -1
  159. package/dist/query/summary/summarize.d.ts.map +1 -1
  160. package/dist/query/summary/summarize.js +91 -5
  161. package/dist/query/summary/summarize.js.map +1 -1
  162. package/dist/query/summary/types.d.ts +77 -2
  163. package/dist/query/summary/types.d.ts.map +1 -1
  164. package/dist/query/types.d.ts +47 -1
  165. package/dist/query/types.d.ts.map +1 -1
  166. package/dist/subscriptions.d.ts +208 -0
  167. package/dist/subscriptions.d.ts.map +1 -0
  168. package/dist/subscriptions.js +153 -0
  169. package/dist/subscriptions.js.map +1 -0
  170. package/llms.txt +9 -0
  171. package/package.json +8 -8
@@ -0,0 +1,531 @@
1
+ /**
2
+ * **`significance`** — "is that difference real?" (ADR 0051 §4, sketch §D).
3
+ *
4
+ * `movers` ranks changes by how unusual they are; this answers the next
5
+ * question, the one a robust z-score deliberately does not: *given how much
6
+ * data is behind each side, could this difference have come from chance alone?*
7
+ *
8
+ * Pure TypeScript over the same bucket series as every other primitive — no
9
+ * SQL, no dialect, no I/O — so DuckDB, ClickHouse, Postgres and SQL Server
10
+ * cannot disagree about a p-value.
11
+ *
12
+ * ## One test per shape of measure, chosen from the catalog, never guessed
13
+ *
14
+ * The right test is a property of what the number *is*, and the registry plus
15
+ * the bucket-measure catalog already say what it is. So the choice is derived,
16
+ * not configured:
17
+ *
18
+ * | The measure is… | Test | Effect | Interval |
19
+ * | --- | --- | --- | --- |
20
+ * | a **rate** — its `comparable.primary` declares `rateOf` and the catalog declares the matching denominator series | two-proportion z (pooled) | difference of proportions | Newcombe hybrid score, built from the two **Wilson** intervals |
21
+ * | a **count** of events with no denominator | Poisson rate test (exact conditional binomial) | difference in events per bucket | normal approximation on the rate difference |
22
+ * | anything else — a level or a summed quantity (`fps`, `ms`, bytes) | Welch's t over the per-bucket values | difference of means | `effect ± t(1−α/2, ν) · SE` |
23
+ *
24
+ * Every special function below is implemented here rather than pulled in as a
25
+ * dependency, for the same reason the statistics are not computed in SQL: a
26
+ * self-hoster's p-value must not depend on a transitive package version. They
27
+ * are the textbook algorithms (Lentz's continued fraction for the incomplete
28
+ * beta, Lanczos for the log-gamma, Abramowitz & Stegun 7.1.26 for the error
29
+ * function) and `src/__tests__/insightSignificance.test.ts` pins each one
30
+ * against published reference values.
31
+ *
32
+ * ## What this deliberately does not do
33
+ *
34
+ * **Two windows, not two segments.** Sketch §D allows either. Comparing two
35
+ * *segments* (`variant=red` vs `variant=blue`) needs the bucket series split by
36
+ * a promoted dimension, which the catalog cannot express in v1 — that is the
37
+ * `splitBy` work that arrives with `anomalies` (#306). A segment comparison is
38
+ * therefore refused at the route with a message naming the window parameters,
39
+ * rather than silently answered with the wrong contrast.
40
+ */
41
+ // The Wilson interval a significance row is built from is the *same* one the
42
+ // `format=summary` envelope already puts on a share (design sketch §B.1), for
43
+ // the same reason `stats.ts` reuses `leastSquaresSlope`: two places in one API
44
+ // must not disagree about the interval around the same proportion. It is
45
+ // imported rather than re-exported — `@uptimizr/db` already exports it once.
46
+ import { Z_95, wilsonInterval } from "../query/summary/stats.js";
47
+ import { round } from "./stats.js";
48
+ // --- special functions ----------------------------------------------------
49
+ /** log Γ(x) for x > 0 — Lanczos approximation, g = 7, n = 9. */
50
+ function logGamma(x) {
51
+ const c = [
52
+ 0.99999999999980993, 676.5203681218851, -1259.1392167224028, 771.32342877765313,
53
+ -176.61502916214059, 12.507343278686905, -0.13857109526572012, 9.9843695780195716e-6,
54
+ 1.5056327351493116e-7,
55
+ ];
56
+ if (x < 0.5) {
57
+ // Reflection: Γ(x)Γ(1−x) = π / sin(πx).
58
+ return Math.log(Math.PI / Math.sin(Math.PI * x)) - logGamma(1 - x);
59
+ }
60
+ const z = x - 1;
61
+ let a = c[0];
62
+ const t = z + 7.5;
63
+ for (let i = 1; i < 9; i += 1)
64
+ a += c[i] / (z + i);
65
+ return 0.5 * Math.log(2 * Math.PI) + (z + 0.5) * Math.log(t) - t + Math.log(a);
66
+ }
67
+ /**
68
+ * The continued fraction for the incomplete beta function (modified Lentz).
69
+ * Converges for `x < (a+1)/(a+b+2)`; {@link regularizedIncompleteBeta} applies
70
+ * the symmetry that guarantees it.
71
+ */
72
+ function betaContinuedFraction(a, b, x) {
73
+ const tiny = 1e-300;
74
+ const qab = a + b;
75
+ const qap = a + 1;
76
+ const qam = a - 1;
77
+ let c = 1;
78
+ let d = 1 - (qab * x) / qap;
79
+ if (Math.abs(d) < tiny)
80
+ d = tiny;
81
+ d = 1 / d;
82
+ let h = d;
83
+ for (let m = 1; m <= 300; m += 1) {
84
+ const m2 = 2 * m;
85
+ let aa = (m * (b - m) * x) / ((qam + m2) * (a + m2));
86
+ d = 1 + aa * d;
87
+ if (Math.abs(d) < tiny)
88
+ d = tiny;
89
+ c = 1 + aa / c;
90
+ if (Math.abs(c) < tiny)
91
+ c = tiny;
92
+ d = 1 / d;
93
+ h *= d * c;
94
+ aa = (-(a + m) * (qab + m) * x) / ((a + m2) * (qap + m2));
95
+ d = 1 + aa * d;
96
+ if (Math.abs(d) < tiny)
97
+ d = tiny;
98
+ c = 1 + aa / c;
99
+ if (Math.abs(c) < tiny)
100
+ c = tiny;
101
+ d = 1 / d;
102
+ const delta = d * c;
103
+ h *= delta;
104
+ if (Math.abs(delta - 1) < 3e-16)
105
+ break;
106
+ }
107
+ return h;
108
+ }
109
+ /** The regularized incomplete beta function `I_x(a, b)`. */
110
+ export function regularizedIncompleteBeta(a, b, x) {
111
+ if (!(x > 0))
112
+ return 0;
113
+ if (!(x < 1))
114
+ return 1;
115
+ const front = Math.exp(logGamma(a + b) - logGamma(a) - logGamma(b) + a * Math.log(x) + b * Math.log(1 - x));
116
+ return x < (a + 1) / (a + b + 2)
117
+ ? (front * betaContinuedFraction(a, b, x)) / a
118
+ : 1 -
119
+ (Math.exp(logGamma(a + b) - logGamma(a) - logGamma(b) + b * Math.log(1 - x) + a * Math.log(x)) *
120
+ betaContinuedFraction(b, a, 1 - x)) /
121
+ b;
122
+ }
123
+ /**
124
+ * The standard normal CDF, Φ(z), to full double precision.
125
+ *
126
+ * Hart’s rational approximation as given by West, *Better Approximations to
127
+ * Cumulative Normal Functions* (2005): a 6/7-degree rational for |z| < 7.07 and
128
+ * a continued fraction beyond it. The familiar Abramowitz & Stegun 7.1.26
129
+ * `erf` is not good enough here — its error is ~1.5e-7 *absolute*, which is
130
+ * larger than the p-values this function is asked to produce.
131
+ */
132
+ export function normalCdf(z) {
133
+ if (!Number.isFinite(z))
134
+ return z > 0 ? 1 : 0;
135
+ const x = Math.abs(z);
136
+ let upper;
137
+ if (x > 37) {
138
+ upper = 0;
139
+ }
140
+ else {
141
+ const e = Math.exp((-x * x) / 2);
142
+ if (x < 7.07106781186547) {
143
+ let numerator = 3.52624965998911e-2 * x + 0.700383064443688;
144
+ numerator = numerator * x + 6.37396220353165;
145
+ numerator = numerator * x + 33.912866078383;
146
+ numerator = numerator * x + 112.079291497871;
147
+ numerator = numerator * x + 221.213596169931;
148
+ numerator = numerator * x + 220.206867912376;
149
+ let denominator = 8.83883476483184e-2 * x + 1.75566716318264;
150
+ denominator = denominator * x + 16.064177579207;
151
+ denominator = denominator * x + 86.7807322029461;
152
+ denominator = denominator * x + 296.564248779674;
153
+ denominator = denominator * x + 637.333633378831;
154
+ denominator = denominator * x + 793.826512519948;
155
+ denominator = denominator * x + 440.413735824752;
156
+ upper = (e * numerator) / denominator;
157
+ }
158
+ else {
159
+ let fraction = x + 0.65;
160
+ fraction = x + 4 / fraction;
161
+ fraction = x + 3 / fraction;
162
+ fraction = x + 2 / fraction;
163
+ fraction = x + 1 / fraction;
164
+ upper = e / fraction / 2.506628274631;
165
+ }
166
+ }
167
+ return z > 0 ? 1 - upper : upper;
168
+ }
169
+ /** Two-sided p-value of a standard normal test statistic. */
170
+ export function normalTwoSidedP(z) {
171
+ if (!Number.isFinite(z))
172
+ return 1;
173
+ return Math.min(1, 2 * (1 - normalCdf(Math.abs(z))));
174
+ }
175
+ /** Two-sided p-value of a Student-t statistic on `df` degrees of freedom. */
176
+ export function studentTwoSidedP(t, df) {
177
+ if (!Number.isFinite(t) || !Number.isFinite(df) || df <= 0)
178
+ return 1;
179
+ return Math.min(1, regularizedIncompleteBeta(df / 2, 0.5, df / (df + t * t)));
180
+ }
181
+ /** The 1−α/2 quantile of Student's t on `df` degrees of freedom, by bisection. */
182
+ export function studentTCritical(df, alpha = 0.05) {
183
+ if (!Number.isFinite(df) || df <= 0)
184
+ return Number.NaN;
185
+ let low = 0;
186
+ let high = 400;
187
+ for (let i = 0; i < 200; i += 1) {
188
+ const mid = (low + high) / 2;
189
+ if (studentTwoSidedP(mid, df) > alpha)
190
+ low = mid;
191
+ else
192
+ high = mid;
193
+ }
194
+ return (low + high) / 2;
195
+ }
196
+ /** `P(X ≤ k)` for `X ~ Binomial(n, p)`, via the incomplete beta identity. */
197
+ export function binomialCdf(k, n, p) {
198
+ if (k < 0)
199
+ return 0;
200
+ if (k >= n)
201
+ return 1;
202
+ return regularizedIncompleteBeta(n - k, k + 1, 1 - p);
203
+ }
204
+ /** The 80th percentile of the standard normal — the z for 80% power. */
205
+ export const Z_80 = 0.8416212335729143;
206
+ // --- Wilson and Newcombe intervals ---------------------------------------
207
+ /**
208
+ * The shared Wilson interval as a plain pair, with the one behaviour this
209
+ * module needs that the summariser’s does not: an answer even when the count
210
+ * is not a proper proportion.
211
+ *
212
+ * `wilsonInterval` returns `null` for `successes > trials` because a share
213
+ * summary must refuse to draw an interval on a number that is not a share. Here
214
+ * that case is already reported to the caller in `powerNote`, so the widest
215
+ * honest interval — the whole unit range — is the right fallback rather than a
216
+ * missing row.
217
+ */
218
+ export function wilsonBounds(successes, trials) {
219
+ const interval = wilsonInterval(successes, trials, Z_95);
220
+ return interval == null ? [0, 1] : [interval.low, interval.high];
221
+ }
222
+ /**
223
+ * **Newcombe's hybrid score interval** (1998, method 10) for the difference
224
+ * between two independent proportions, built from the two Wilson intervals.
225
+ *
226
+ * The natural companion to a Wilson interval per arm: it inherits Wilson's
227
+ * behaviour at the boundaries, so a comparison against a zero-count arm still
228
+ * produces an interval of finite, honest width instead of collapsing.
229
+ */
230
+ export function newcombeDifferenceInterval(successesA, trialsA, successesB, trialsB) {
231
+ const pa = trialsA > 0 ? successesA / trialsA : 0;
232
+ const pb = trialsB > 0 ? successesB / trialsB : 0;
233
+ const [la, ua] = wilsonBounds(successesA, trialsA);
234
+ const [lb, ub] = wilsonBounds(successesB, trialsB);
235
+ const difference = pa - pb;
236
+ const lower = difference - Math.sqrt((pa - la) ** 2 + (ub - pb) ** 2);
237
+ const upper = difference + Math.sqrt((ua - pa) ** 2 + (pb - lb) ** 2);
238
+ return [Math.max(-1, lower), Math.min(1, upper)];
239
+ }
240
+ /** Decimals a significance number is rounded to before it leaves the API. */
241
+ export const SIGNIFICANCE_PRECISION = 6;
242
+ /** Conventional significance level. Fixed: a caller-tunable α is p-hacking with extra steps. */
243
+ export const SIGNIFICANCE_ALPHA = 0.05;
244
+ /** `z(1−α/2) + z(power)` — the multiplier in a minimum-detectable-effect. */
245
+ const MDE_MULTIPLIER = Z_95 + Z_80;
246
+ /** One sentence naming the smallest effect these sample sizes could have found. */
247
+ function mdeNote(standardError, unit, extra) {
248
+ const head = standardError == null || !Number.isFinite(standardError) || standardError <= 0
249
+ ? "There is not enough data on one side to say what this comparison could have detected."
250
+ : `With these sample sizes the smallest difference detectable at 80% power (alpha 0.05) is ` +
251
+ `about ${formatMagnitude(MDE_MULTIPLIER * standardError)} ${unit}; a smaller true ` +
252
+ `difference would usually go unnoticed here.`;
253
+ return extra == null ? head : `${head} ${extra}`;
254
+ }
255
+ /** A magnitude rendered with enough digits to be read, and no more. */
256
+ function formatMagnitude(value) {
257
+ const magnitude = Math.abs(value);
258
+ if (magnitude === 0)
259
+ return "0";
260
+ const decimals = magnitude >= 10 ? 1 : magnitude >= 1 ? 2 : magnitude >= 0.01 ? 4 : 6;
261
+ return String(Number(magnitude.toFixed(decimals)));
262
+ }
263
+ /**
264
+ * **Two-proportion z-test**, pooled, with a Newcombe interval on the difference.
265
+ *
266
+ * `H₀: p_a = p_b`. The pooled proportion is used in the standard error because
267
+ * that is the variance *under the null* — the hypothesis being tested — while
268
+ * the interval is unpooled, because an interval describes the difference that
269
+ * was actually observed. The two therefore answer slightly different questions
270
+ * and can, in a borderline case, disagree about whether zero is excluded. That
271
+ * is a property of the textbook procedure, not a bug, and the interval is the
272
+ * one to believe.
273
+ */
274
+ export function twoProportionTest(successesA, trialsA, successesB, trialsB) {
275
+ if (!(trialsA > 0) || !(trialsB > 0)) {
276
+ return { effect: null, ci95: [null, null], p: null, se: null };
277
+ }
278
+ const pa = successesA / trialsA;
279
+ const pb = successesB / trialsB;
280
+ const pooled = (successesA + successesB) / (trialsA + trialsB);
281
+ const seNull = Math.sqrt(pooled * (1 - pooled) * (1 / trialsA + 1 / trialsB));
282
+ const seObserved = Math.sqrt((pa * (1 - pa)) / trialsA + (pb * (1 - pb)) / trialsB);
283
+ const z = seNull > 0 ? (pa - pb) / seNull : 0;
284
+ return {
285
+ effect: pa - pb,
286
+ ci95: newcombeDifferenceInterval(successesA, trialsA, successesB, trialsB),
287
+ // A pooled proportion of exactly 0 or 1 means neither arm saw a single
288
+ // success (or a single failure). Nothing distinguishes them, so p = 1.
289
+ p: seNull > 0 ? normalTwoSidedP(z) : 1,
290
+ se: seObserved,
291
+ };
292
+ }
293
+ /** Mean and unbiased variance of a sample; `null` variance under two points. */
294
+ function moments(values) {
295
+ const n = values.length;
296
+ if (n === 0)
297
+ return { mean: Number.NaN, variance: null, n: 0 };
298
+ let total = 0;
299
+ for (const value of values)
300
+ total += value;
301
+ const mean = total / n;
302
+ if (n < 2)
303
+ return { mean, variance: null, n };
304
+ let sq = 0;
305
+ for (const value of values)
306
+ sq += (value - mean) ** 2;
307
+ return { mean, variance: sq / (n - 1), n };
308
+ }
309
+ /**
310
+ * **Welch's unequal-variances t-test** over two samples of per-bucket values.
311
+ *
312
+ * Welch rather than Student because the two windows are routinely of different
313
+ * length *and* different volatility — a regressed week is both lower and more
314
+ * erratic than the week before it — and pooling those variances would understate
315
+ * the uncertainty exactly when it matters most.
316
+ *
317
+ * The samples are the **bucket values**, so `n` is the number of days (or
318
+ * hours) compared, not the number of events behind them. That is the honest
319
+ * unit: consecutive frame samples inside one day are anything but independent,
320
+ * and treating them as `n` would produce a p-value of 1e-40 for a difference
321
+ * any observer could see is within normal day-to-day drift.
322
+ */
323
+ export function welchTest(sampleA, sampleB) {
324
+ const a = moments(sampleA);
325
+ const b = moments(sampleB);
326
+ if (a.n < 2 || b.n < 2 || a.variance == null || b.variance == null) {
327
+ const effect = a.n > 0 && b.n > 0 ? a.mean - b.mean : null;
328
+ return { effect, ci95: [null, null], p: null, se: null, df: null };
329
+ }
330
+ const va = a.variance / a.n;
331
+ const vb = b.variance / b.n;
332
+ const se = Math.sqrt(va + vb);
333
+ const effect = a.mean - b.mean;
334
+ if (!(se > 0)) {
335
+ // Both samples are constant. They either agree exactly (no difference to
336
+ // test) or differ by a fixed amount with zero observed variance, which no
337
+ // t-test can attach a probability to.
338
+ return { effect, ci95: [effect, effect], p: effect === 0 ? 1 : null, se: 0, df: null };
339
+ }
340
+ const df = (va + vb) ** 2 / (va ** 2 / (a.n - 1) + vb ** 2 / (b.n - 1));
341
+ const t = effect / se;
342
+ const critical = studentTCritical(df, SIGNIFICANCE_ALPHA);
343
+ return {
344
+ effect,
345
+ ci95: [effect - critical * se, effect + critical * se],
346
+ p: studentTwoSidedP(t, df),
347
+ se,
348
+ df,
349
+ };
350
+ }
351
+ /**
352
+ * **Two-sample Poisson rate test** (Przyborowski & Wilks 1940).
353
+ *
354
+ * Conditional on the total `x_a + x_b`, the split between the two windows is
355
+ * Binomial(total, t_a / (t_a + t_b)) under `H₀: λ_a = λ_b`, where `t` is each
356
+ * window's exposure — here its number of buckets. That makes the exact p-value
357
+ * a binomial tail, which is what this returns: exact whatever the counts, with
358
+ * no normal approximation to fail at the small counts an error metric usually
359
+ * has.
360
+ *
361
+ * The **interval**, by contrast, is the normal approximation on the rate
362
+ * difference (`sqrt(x_a/t_a² + x_b/t_b²)`). There is no closed-form exact
363
+ * interval for a difference of Poisson rates, and an approximate interval
364
+ * reported as approximate is better than none; `powerNote` says so when the
365
+ * counts are small enough for it to matter.
366
+ */
367
+ export function poissonRateTest(countA, exposureA, countB, exposureB) {
368
+ if (!(exposureA > 0) || !(exposureB > 0)) {
369
+ return { effect: null, ci95: [null, null], p: null, se: null };
370
+ }
371
+ const rateA = countA / exposureA;
372
+ const rateB = countB / exposureB;
373
+ const effect = rateA - rateB;
374
+ const se = Math.sqrt(countA / exposureA ** 2 + countB / exposureB ** 2);
375
+ const total = countA + countB;
376
+ if (total <= 0) {
377
+ // Neither window saw a single event. The rates are identical at zero, and
378
+ // that is a real (if uninformative) answer rather than a missing one.
379
+ return { effect: 0, ci95: [0, 0], p: 1, se: 0 };
380
+ }
381
+ const share = exposureA / (exposureA + exposureB);
382
+ const expected = total * share;
383
+ // Two-sided by doubling the smaller tail — the convention `poisson.test`
384
+ // follows for equal exposures, and the only one that stays symmetric here.
385
+ const p = countA <= expected
386
+ ? Math.min(1, 2 * binomialCdf(countA, total, share))
387
+ : Math.min(1, 2 * (1 - binomialCdf(countA - 1, total, share)));
388
+ return { effect, ci95: [effect - Z_95 * se, effect + Z_95 * se], p, se };
389
+ }
390
+ /** The finite bucket values of a window, in bucket order. */
391
+ function valuesOf(rows) {
392
+ return [...rows]
393
+ .sort((a, b) => a.bucket - b.bucket)
394
+ .map((row) => row.value)
395
+ .filter((value) => value != null && Number.isFinite(value));
396
+ }
397
+ /** The sum of a window's bucket values; `0` over an empty window. */
398
+ function totalOf(values) {
399
+ let total = 0;
400
+ for (const value of values)
401
+ total += value;
402
+ return total;
403
+ }
404
+ /**
405
+ * Whether a count series is materially **overdispersed** — its bucket-to-bucket
406
+ * variance far above its mean, which is exactly what a Poisson model assumes it
407
+ * is not.
408
+ *
409
+ * Reported rather than corrected: an overdispersed count series makes the
410
+ * Poisson p-value too small, and a reader who is told so can discount it. The
411
+ * threshold is deliberately loose (variance more than three times the mean) so
412
+ * that the note appears only when the assumption is clearly strained.
413
+ */
414
+ function overdispersed(values) {
415
+ const { mean, variance } = moments(values);
416
+ return variance != null && mean > 0 && variance > 3 * mean;
417
+ }
418
+ /**
419
+ * Compare one metric across two windows and say whether the difference is real.
420
+ *
421
+ * Pure: it takes the bucket series both windows produced (plus, for a rate, the
422
+ * denominator's) and returns the row. No store, no request, no dialect.
423
+ */
424
+ export function computeSignificance(input) {
425
+ const currentValues = valuesOf(input.current);
426
+ const referenceValues = valuesOf(input.reference);
427
+ const scene = input.scene ?? "";
428
+ // --- a declared rate: two proportions ---
429
+ if (input.denominator != null && input.counting) {
430
+ const trialsA = totalOf(valuesOf(input.denominator.current));
431
+ const trialsB = totalOf(valuesOf(input.denominator.reference));
432
+ const successesA = totalOf(currentValues);
433
+ const successesB = totalOf(referenceValues);
434
+ const result = twoProportionTest(successesA, trialsA, successesB, trialsB);
435
+ const clipped = successesA > trialsA || successesB > trialsB
436
+ ? "One side counted more events than its denominator, so the ratio is not a proportion " +
437
+ "and the interval should not be read as one."
438
+ : undefined;
439
+ return finish({
440
+ metric: input.metric,
441
+ scene,
442
+ a: { value: trialsA > 0 ? successesA / trialsA : null, n: trialsA },
443
+ b: { value: trialsB > 0 ? successesB / trialsB : null, n: trialsB },
444
+ effect: result.effect,
445
+ ci95: result.ci95,
446
+ p: result.p,
447
+ test: "two_proportion_z",
448
+ effectUnit: "ratio",
449
+ powerNote: mdeNote(result.se, "in the rate", clipped),
450
+ });
451
+ }
452
+ // --- a bare count: a Poisson rate over buckets ---
453
+ if (input.counting) {
454
+ const exposureA = currentValues.length;
455
+ const exposureB = referenceValues.length;
456
+ const countA = totalOf(currentValues);
457
+ const countB = totalOf(referenceValues);
458
+ const result = poissonRateTest(countA, exposureA, countB, exposureB);
459
+ const strained = overdispersed(currentValues) || overdispersed(referenceValues)
460
+ ? "The bucket counts vary far more than a Poisson process would, so this p-value is " +
461
+ "optimistic — treat it as an upper bound on the evidence, not a measurement of it."
462
+ : undefined;
463
+ return finish({
464
+ metric: input.metric,
465
+ scene,
466
+ a: { value: exposureA > 0 ? countA / exposureA : null, n: exposureA },
467
+ b: { value: exposureB > 0 ? countB / exposureB : null, n: exposureB },
468
+ effect: result.effect,
469
+ ci95: result.ci95,
470
+ p: result.p,
471
+ test: "poisson_rate",
472
+ effectUnit: `${input.unit} per bucket`,
473
+ powerNote: mdeNote(result.se, `${input.unit} per bucket`, strained),
474
+ });
475
+ }
476
+ // --- a level or a summed quantity: Welch's t over the buckets ---
477
+ const result = welchTest(currentValues, referenceValues);
478
+ // Three different reasons a Welch row can be unconvincing, and they call for
479
+ // three different sentences. Collapsing them into one would tell a reader
480
+ // 'not enough data' about a window with a hundred identical buckets.
481
+ const thin = result.se === 0 && currentValues.length >= 2 && referenceValues.length >= 2
482
+ ? "Both windows are perfectly constant, so there is no observed variation to attach a " +
483
+ "probability to. The effect is exact; the absence of a p-value is a statement about " +
484
+ "the data, not about the difference."
485
+ : currentValues.length < 5 || referenceValues.length < 5
486
+ ? `Only ${currentValues.length} and ${referenceValues.length} buckets are being ` +
487
+ "compared; a t-test on that few points has very little power, so a large p-value " +
488
+ "here means 'not enough buckets', not 'no difference'."
489
+ : undefined;
490
+ return finish({
491
+ metric: input.metric,
492
+ scene,
493
+ a: { value: bucketMean(currentValues), n: currentValues.length },
494
+ b: { value: bucketMean(referenceValues), n: referenceValues.length },
495
+ effect: result.effect,
496
+ ci95: result.ci95,
497
+ p: result.p,
498
+ test: "welch_t",
499
+ effectUnit: input.unit,
500
+ // A zero standard error has nothing to say about detectable effects, and
501
+ // `thin` already explains why — so it stands alone rather than being
502
+ // prefixed with a sentence about missing data.
503
+ powerNote: result.se === 0 && thin != null ? thin : mdeNote(result.se, input.unit, thin),
504
+ });
505
+ }
506
+ /**
507
+ * The window value Welch compares: the **mean of the bucket values**.
508
+ *
509
+ * Reported as a mean even for an additive measure, where `movers` would report
510
+ * the window *sum*. A row whose `effect` is a difference of means must not
511
+ * carry a `value` that is a sum, or the two numbers cannot be read together;
512
+ * the caveat on the registry entry says so, and `n` is the bucket count so the
513
+ * sum is one multiplication away.
514
+ */
515
+ function bucketMean(values) {
516
+ return values.length === 0 ? null : totalOf(values) / values.length;
517
+ }
518
+ /** Round every number in a row once, at the edge, and derive `significant`. */
519
+ function finish(row) {
520
+ const p = round(row.p, SIGNIFICANCE_PRECISION);
521
+ return {
522
+ ...row,
523
+ a: { value: round(row.a.value, SIGNIFICANCE_PRECISION), n: row.a.n },
524
+ b: { value: round(row.b.value, SIGNIFICANCE_PRECISION), n: row.b.n },
525
+ effect: round(row.effect, SIGNIFICANCE_PRECISION),
526
+ ci95: [round(row.ci95[0], SIGNIFICANCE_PRECISION), round(row.ci95[1], SIGNIFICANCE_PRECISION)],
527
+ p,
528
+ significant: p != null && p < SIGNIFICANCE_ALPHA,
529
+ };
530
+ }
531
+ //# sourceMappingURL=significance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"significance.js","sourceRoot":"","sources":["../../src/insights/significance.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,104 @@
1
+ /**
2
+ * **Insight statistics** (ADR 0051 §4, design sketch §D).
3
+ *
4
+ * The descriptive statistics the insight primitives are built from, in pure
5
+ * TypeScript: no SQL, no dialect, no I/O. That is the whole design rule of
6
+ * `src/insights/` — the *series* is portable (one dialect-authored bucket query,
7
+ * parity-tested on all four engines) and the *statistics over it* are computed
8
+ * once, here, so DuckDB, ClickHouse, Postgres and SQL Server cannot disagree
9
+ * about what "the median" or "the MAD" means.
10
+ *
11
+ * Every function is total and deterministic:
12
+ *
13
+ * - it takes a plain `readonly number[]` and returns a `number` or `null`;
14
+ * - `null` always means *"not defined for this input"* (an empty series, a
15
+ * single point where a slope needs two), never `0` — a zero mean is a claim
16
+ * about the data, absence is not (the same rule the registry row schemas
17
+ * follow);
18
+ * - non-finite inputs are rejected up front rather than silently poisoning a
19
+ * result with `NaN`.
20
+ *
21
+ * Robust statistics (median, MAD) are used rather than mean/standard deviation
22
+ * wherever a value is ranked or compared, because a scene's bucket series is
23
+ * routinely dominated by one outlier day — a launch, a bot sweep, a broken
24
+ * deploy — and a standard deviation computed across it makes every subsequent
25
+ * change look insignificant.
26
+ */
27
+ export { leastSquaresSlope, trendOf } from "../query/summary/stats.js";
28
+ /**
29
+ * Floor added to the MAD in the denominator of {@link robustZ}.
30
+ *
31
+ * A reference window that never varied has `mad = 0`, and dividing by it would
32
+ * be `Infinity`/`NaN`. Adding a small epsilon keeps the score finite and keeps
33
+ * its *ordering* meaningful: against a perfectly flat reference, any change at
34
+ * all is genuinely unprecedented and should rank above a change of the same size
35
+ * against a noisy one. What stops that from flooding the top of a `movers` list
36
+ * is the `minSample` gate, not the epsilon — a flat-zero series with two events
37
+ * behind it reports `aboveMinSample: false` and is ranked below every gated
38
+ * mover (see `movers.ts`).
39
+ */
40
+ export declare const ROBUST_Z_EPSILON = 1e-9;
41
+ /** Scale factor making the MAD a consistent estimator of σ for normal data. */
42
+ export declare const MAD_TO_SIGMA = 1.4826;
43
+ /** The arithmetic mean of the finite values; `null` when there are none. */
44
+ export declare function mean(values: readonly number[]): number | null;
45
+ /**
46
+ * The `q`-quantile (`0..1`) by **linear interpolation between order statistics**
47
+ * — the "R type 7" / `numpy.percentile` default, and the same definition the
48
+ * dialects' own `quantile()` uses closely enough for the tolerances the parity
49
+ * harness applies.
50
+ *
51
+ * `null` when the series is empty or `q` is outside `0..1`. `q = 0` and `q = 1`
52
+ * return the min and max exactly.
53
+ */
54
+ export declare function quantile(values: readonly number[], q: number): number | null;
55
+ /** The median (the 0.5 quantile); `null` on an empty series. */
56
+ export declare function median(values: readonly number[]): number | null;
57
+ /**
58
+ * The **median absolute deviation**: `median(|x − median(x)|)`.
59
+ *
60
+ * The robust counterpart of the standard deviation, and the spread every
61
+ * insight primitive compares against. Unlike σ it has a breakdown point of 50%,
62
+ * so a single catastrophic day in a 28-day window widens it by nothing at all
63
+ * instead of tripling it. Reported **unscaled** (raw median deviation, not
64
+ * multiplied by {@link MAD_TO_SIGMA}) because it is used as a denominator for a
65
+ * *ranking* score rather than as a σ estimate; a caller that wants σ multiplies.
66
+ *
67
+ * `null` on an empty series; `0` on a constant one, which is a true statement
68
+ * about the data rather than a missing value.
69
+ */
70
+ export declare function medianAbsoluteDeviation(values: readonly number[]): number | null;
71
+ /**
72
+ * The **robust z-score** of a change: `delta / (mad + ε)`.
73
+ *
74
+ * "How many typical bucket-to-bucket deviations is this move?" — the ranking
75
+ * score for `movers` (design sketch §D). Returns `0` for a zero delta whatever
76
+ * the spread (nothing moved, so nothing is surprising), and `null` when `delta`
77
+ * or `mad` is missing or non-finite, so a metric with no reference series is
78
+ * reported as unranked rather than as a confident zero.
79
+ *
80
+ * A negative `mad` is impossible by construction and is treated as missing.
81
+ */
82
+ export declare function robustZ(delta: number | null, mad: number | null, epsilon?: number): number | null;
83
+ /**
84
+ * Relative change from `previous` to `current`, as a ratio (`0.25` is +25%).
85
+ *
86
+ * `null` when either side is missing or when `previous` is zero — a percentage
87
+ * change from nothing is undefined, and reporting `Infinity` (or, worse,
88
+ * `100%`) would be a claim the data does not support. The absolute `delta` is
89
+ * always available alongside it, which is the honest number in that case.
90
+ */
91
+ export declare function relativeChange(current: number | null, previous: number | null): number | null;
92
+ /** The sum of the finite values; `null` when there are none (never `0`). */
93
+ export declare function sum(values: readonly number[]): number | null;
94
+ /**
95
+ * Round a computed statistic to a fixed number of decimals.
96
+ *
97
+ * Applied at the API edge only. Two engines can differ in the last bits of a
98
+ * floating-point division, and an agent comparing a `z` of `3.0000000000000004`
99
+ * with one of `3` should not see a difference — so every derived number the
100
+ * insight endpoints emit is rounded to a declared precision, which also keeps
101
+ * the JSON (and the tokens an agent pays for it) small.
102
+ */
103
+ export declare function round(value: number | null, decimals?: number): number | null;
104
+ //# sourceMappingURL=stats.d.ts.map