@compstats/core 0.2.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.
- package/CHANGELOG.md +110 -0
- package/LICENSE +21 -0
- package/README.md +310 -0
- package/dist/3d.d.ts +33 -0
- package/dist/3d.d.ts.map +1 -0
- package/dist/3d.js +1984 -0
- package/dist/3d.js.map +22 -0
- package/dist/core/arith.d.ts +133 -0
- package/dist/core/arith.d.ts.map +1 -0
- package/dist/core/frame.d.ts +98 -0
- package/dist/core/frame.d.ts.map +1 -0
- package/dist/core/histogram.d.ts +68 -0
- package/dist/core/histogram.d.ts.map +1 -0
- package/dist/core/kde.d.ts +106 -0
- package/dist/core/kde.d.ts.map +1 -0
- package/dist/core/logit.d.ts +109 -0
- package/dist/core/logit.d.ts.map +1 -0
- package/dist/core/matrix.d.ts +135 -0
- package/dist/core/matrix.d.ts.map +1 -0
- package/dist/core/moderation.d.ts +127 -0
- package/dist/core/moderation.d.ts.map +1 -0
- package/dist/core/ols.d.ts +76 -0
- package/dist/core/ols.d.ts.map +1 -0
- package/dist/core/pca.d.ts +99 -0
- package/dist/core/pca.d.ts.map +1 -0
- package/dist/core/precision.d.ts +10 -0
- package/dist/core/precision.d.ts.map +1 -0
- package/dist/core/pretty.d.ts +51 -0
- package/dist/core/pretty.d.ts.map +1 -0
- package/dist/core/regression.d.ts +68 -0
- package/dist/core/regression.d.ts.map +1 -0
- package/dist/core/rng.d.ts +206 -0
- package/dist/core/rng.d.ts.map +1 -0
- package/dist/core/sampling.d.ts +157 -0
- package/dist/core/sampling.d.ts.map +1 -0
- package/dist/core/special.d.ts +92 -0
- package/dist/core/special.d.ts.map +1 -0
- package/dist/core/tdist.d.ts +53 -0
- package/dist/core/tdist.d.ts.map +1 -0
- package/dist/core/ttest.d.ts +146 -0
- package/dist/core/ttest.d.ts.map +1 -0
- package/dist/data/moderationData.d.ts +40 -0
- package/dist/data/moderationData.d.ts.map +1 -0
- package/dist/data/pcaDegenerate.d.ts +18 -0
- package/dist/data/pcaDegenerate.d.ts.map +1 -0
- package/dist/index.d.ts +66 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4195 -0
- package/dist/index.js.map +47 -0
- package/dist/interactive/controls.d.ts +97 -0
- package/dist/interactive/controls.d.ts.map +1 -0
- package/dist/interactive/logit.d.ts +57 -0
- package/dist/interactive/logit.d.ts.map +1 -0
- package/dist/interactive/matrixInverse.d.ts +86 -0
- package/dist/interactive/matrixInverse.d.ts.map +1 -0
- package/dist/interactive/moderation3d.d.ts +103 -0
- package/dist/interactive/moderation3d.d.ts.map +1 -0
- package/dist/interactive/pca.d.ts +90 -0
- package/dist/interactive/pca.d.ts.map +1 -0
- package/dist/interactive/regression.d.ts +57 -0
- package/dist/interactive/regression.d.ts.map +1 -0
- package/dist/interactive/sampling.d.ts +78 -0
- package/dist/interactive/sampling.d.ts.map +1 -0
- package/dist/interactive/scatter3d.d.ts +124 -0
- package/dist/interactive/scatter3d.d.ts.map +1 -0
- package/dist/interactive/tTest.d.ts +69 -0
- package/dist/interactive/tTest.d.ts.map +1 -0
- package/dist/interactive/target.d.ts +132 -0
- package/dist/interactive/target.d.ts.map +1 -0
- package/dist/plot/axes.d.ts +128 -0
- package/dist/plot/axes.d.ts.map +1 -0
- package/dist/plot/draw.d.ts +46 -0
- package/dist/plot/draw.d.ts.map +1 -0
- package/dist/plot/format.d.ts +33 -0
- package/dist/plot/format.d.ts.map +1 -0
- package/dist/plot/logit.d.ts +68 -0
- package/dist/plot/logit.d.ts.map +1 -0
- package/dist/plot/matrixInverse.d.ts +62 -0
- package/dist/plot/matrixInverse.d.ts.map +1 -0
- package/dist/plot/moderation3d.d.ts +135 -0
- package/dist/plot/moderation3d.d.ts.map +1 -0
- package/dist/plot/pca.d.ts +79 -0
- package/dist/plot/pca.d.ts.map +1 -0
- package/dist/plot/plotly.d.ts +209 -0
- package/dist/plot/plotly.d.ts.map +1 -0
- package/dist/plot/regression.d.ts +52 -0
- package/dist/plot/regression.d.ts.map +1 -0
- package/dist/plot/sampleCi.d.ts +82 -0
- package/dist/plot/sampleCi.d.ts.map +1 -0
- package/dist/plot/sampling.d.ts +159 -0
- package/dist/plot/sampling.d.ts.map +1 -0
- package/dist/plot/scatter3d.d.ts +164 -0
- package/dist/plot/scatter3d.d.ts.map +1 -0
- package/dist/plot/tTest.d.ts +49 -0
- package/dist/plot/tTest.d.ts.map +1 -0
- package/dist/plot/target.d.ts +55 -0
- package/dist/plot/target.d.ts.map +1 -0
- package/package.json +71 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/core/precision.ts", "../src/core/arith.ts", "../src/core/kde.ts", "../src/core/pretty.ts", "../src/core/histogram.ts", "../src/core/rng.ts", "../src/core/sampling.ts", "../src/core/frame.ts", "../src/core/ols.ts", "../src/core/moderation.ts", "../src/core/regression.ts", "../src/core/matrix.ts", "../src/core/pca.ts", "../src/core/logit.ts", "../src/core/special.ts", "../src/core/tdist.ts", "../src/core/ttest.ts", "../src/data/moderationData.ts", "../src/data/pcaDegenerate.ts", "../src/plot/target.ts", "../src/plot/axes.ts", "../src/plot/draw.ts", "../src/plot/matrixInverse.ts", "../src/plot/format.ts", "../src/plot/logit.ts", "../src/plot/sampleCi.ts", "../src/plot/sampling.ts", "../src/plot/pca.ts", "../src/plot/regression.ts", "../src/plot/tTest.ts", "../src/interactive/target.ts", "../src/interactive/controls.ts", "../src/interactive/matrixInverse.ts", "../src/interactive/sampling.ts", "../src/interactive/tTest.ts", "../src/interactive/logit.ts", "../src/interactive/pca.ts", "../src/interactive/regression.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * Report the machine precision of the runtime.\n *\n * The value is the smallest number x such that 1 + x is not equal to 1.\n * It is the equivalent of `.Machine$double.eps` in R.\n *\n * @returns The double-precision epsilon.\n */\nexport function machinePrecision(): number {\n return Number.EPSILON;\n}\n",
|
|
6
|
+
"/**\n * Shared array arithmetic for the core modules.\n *\n * Use these helpers with `map` instead of index-based loops. See the\n * iteration convention in CLAUDE.md.\n */\n\n/** Add all values. Return 0 for an empty array. */\nexport function sum(values: readonly number[]): number {\n return values.reduce((total, value) => total + value, 0);\n}\n\n/** Return the arithmetic mean. Return NaN for an empty array. */\nexport function mean(values: readonly number[]): number {\n return sum(values) / values.length;\n}\n\n/**\n * Return the smallest and the largest value, as R's `range()` does.\n *\n * Written as a fold rather than `Math.min(...values)`, which overflows the\n * call stack on a long column.\n *\n * @param values The observations.\n * @returns The lowest and the highest value. An empty array returns\n * [Infinity, -Infinity], the shape of R's `range(numeric(0))`.\n *\n * A comparison keeps the accumulator when it is false, so a NaN entry is\n * skipped rather than carried through. No caller supports NaN input: the\n * modules that call this either drop the non-finite values first or state\n * that they do not handle R's NA.\n */\nexport function extent(values: readonly number[]): [number, number] {\n return values.reduce<[number, number]>(\n ([low, high], value) => [\n value < low ? value : low,\n value > high ? value : high,\n ],\n [Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY],\n );\n}\n\n/**\n * Map a negative zero to a positive one.\n *\n * A computed zero can come out of an algorithm with a sign that R's own\n * zeros never carry: of 1498 zero entries of a matrix inverse over a sweep of\n * 20000 slider settings, R gives a positive zero every time.\n */\nexport function withoutNegativeZero(value: number): number {\n return value === 0 ? 0 : value;\n}\n\n/** Reject a count that is not a non-negative integer. */\nexport function requireCount(value: number, name: string): void {\n if (!Number.isInteger(value) || value < 0) {\n throw new RangeError(`${name} must be a non-negative integer, got ${value}`);\n }\n}\n\n/**\n * Combine two arrays element by element.\n *\n * Stop at the end of the shorter array.\n */\nexport function zipWith<A, B, C>(\n as: readonly A[],\n bs: readonly B[],\n combine: (a: A, b: B) => C,\n): C[] {\n const length = Math.min(as.length, bs.length);\n // The slice bounds the index, so the access cannot be undefined.\n return as.slice(0, length).map((a, index) => combine(a, bs[index] as B));\n}\n\n/**\n * Return the standard deviation, with the n − 1 denominator of R's `sd()`.\n *\n * @param values The observations.\n * @returns The standard deviation, or NaN below two values. R returns NA\n * there, with a warning that a library cannot give.\n */\nexport function sd(values: readonly number[]): number {\n if (values.length < 2) {\n return Number.NaN;\n }\n\n const center = mean(values);\n const squares = values.map((value) => (value - center) * (value - center));\n return Math.sqrt(sum(squares) / (values.length - 1));\n}\n\n/**\n * Return the mean absolute deviation, `mean(|x - mean(x)|)`.\n *\n * R base has no function for this. R's `mad()` is the *median* absolute\n * deviation from the median, scaled by 1.4826, which is a different\n * statistic. A caller that shows either one to a reader must write the name\n * in full, because the abbreviation covers both.\n *\n * The sampling demonstration offers this as one of its choices of statistic.\n * It measures spread in the units of the data, as the standard deviation\n * does, but a value far from the mean moves it less, because the distance is\n * not squared.\n *\n * @param values The observations.\n * @returns The mean distance from the mean. One value gives 0, because it\n * sits at its own mean. No values gives NaN, as `mean` does.\n */\nexport function meanAbsoluteDeviation(values: readonly number[]): number {\n const center = mean(values);\n return mean(values.map((value) => Math.abs(value - center)));\n}\n\n/**\n * Return `a * b + c`, rounded once.\n *\n * Written as `a * b + c`, JavaScript rounds twice — once for the product,\n * once for the sum — and the two roundings show. R's `seq.int` rounds once\n * and lands one unit in the last place away often enough to change a tick.\n * The linear algebra in `matrix.ts` needs the same: the compiler of the R\n * install the fixtures come from contracts a multiply and an add into one\n * instruction, so a matrix near singular differs in every digit without it.\n *\n * The two helpers below split each operation into the value a double can\n * hold plus the part it drops, so the dropped parts can be added back before\n * the one rounding that remains. Both are the standard error-free\n * transformations (Dekker 1971, Knuth). Both need every intermediate to stay\n * in range, which fails only within a factor of 2^28 of the largest double.\n * There the plain form is used: the result is already dominated by its own\n * overflow.\n */\nexport function fusedMultiplyAdd(a: number, b: number, c: number): number {\n const [product, productError] = twoProduct(a, b);\n const [sum, sumError] = twoSum(c, product);\n const rounded = sum + (sumError + productError);\n return Number.isFinite(rounded) ? rounded : a * b + c;\n}\n\n/** Split a double into two halves whose product is exact. Dekker's method. */\nfunction split(value: number): [number, number] {\n const scaled = 134217729 * value;\n const high = scaled - (scaled - value);\n return [high, value - high];\n}\n\n/** Return the product and the part of it the product cannot hold. */\nfunction twoProduct(a: number, b: number): [number, number] {\n const product = a * b;\n const [aHigh, aLow] = split(a);\n const [bHigh, bLow] = split(b);\n const error =\n aLow * bLow - (product - aHigh * bHigh - aLow * bHigh - aHigh * bLow);\n return [product, error];\n}\n\n/** Return the sum and the part of it the sum cannot hold. */\nfunction twoSum(a: number, b: number): [number, number] {\n const sum = a + b;\n const carried = sum - a;\n return [sum, a - (sum - carried) + (b - carried)];\n}\n\n/**\n * Return a sample quantile, by the rule of R's `quantile(type = 7)`.\n *\n * Type 7 is the default of R's `quantile()`, and so the rule behind the\n * `IQR()` that `bw.nrd0()` uses to pick a bandwidth. It reads the sorted\n * values at position `1 + (n - 1) * p` and interpolates between the two\n * neighbors of a fractional position.\n *\n * @param values The observations. The function does not modify them.\n * @param p The probability. It must be in [0, 1].\n * @returns The quantile, or NaN for no values. R returns NA there.\n * @throws RangeError If p is outside [0, 1], as R does.\n */\nexport function quantile(values: readonly number[], p: number): number {\n return quantiles(values, [p])[0] as number;\n}\n\n/**\n * Return several sample quantiles, by the rule of R's `quantile(type = 7)`.\n *\n * R's `quantile()` also takes a vector of probabilities, and for one reason:\n * it sorts once and reads every position off the one sorted copy. Asking for\n * the two quartiles together instead of one at a time halves the work, which\n * is what the bandwidth rule does over a pooled sample.\n *\n * @param values The observations. The function does not modify them.\n * @param probs The probabilities. Each must be in [0, 1].\n * @returns One quantile per probability, in the order given. Each is NaN for\n * no values, where R returns NA.\n * @throws RangeError If a probability is outside [0, 1], as R does.\n */\nexport function quantiles(\n values: readonly number[],\n probs: readonly number[],\n): number[] {\n if (probs.some((p) => !(p >= 0 && p <= 1))) {\n throw new RangeError(`every probability must be in [0, 1], got ${probs}`);\n }\n if (values.length === 0) {\n return probs.map(() => Number.NaN);\n }\n\n // A Float64Array sorts by value with no comparator to call, which is about\n // three times faster than sorting a copy of the array over a pooled sample.\n const sorted = Float64Array.from(values).sort();\n\n return probs.map((p) => type7(sorted, p));\n}\n\n/**\n * Return the median, the value with half the observations on each side.\n *\n * R's `median()` sorts the values and, on an even count, averages the two in\n * the middle. That is the type-7 quantile at 0.5, which weights those same two\n * values by a half each, so this delegates rather than repeat the rule. The\n * two forms land on the same double, the halves included.\n *\n * The sampling demonstration offers this as one of its choices of statistic.\n * It marks the center in the units of the data, as the mean does, but a value\n * far from the center moves it very little, because only the ordering counts.\n *\n * @param values The observations. The function does not modify them.\n * @returns The median, or NaN for no values. R returns NA there.\n */\nexport function median(values: readonly number[]): number {\n return quantile(values, 0.5);\n}\n\n/** Read one type-7 quantile off already sorted values. */\nfunction type7(sorted: Float64Array, p: number): number {\n const position = 1 + (sorted.length - 1) * p;\n const below = Math.floor(position);\n const above = Math.ceil(position);\n // Both indices are inside the array: p in [0, 1] bounds the position by\n // 1 and by the length.\n const low = sorted[below - 1] as number;\n const high = sorted[above - 1] as number;\n\n if (position > below && high !== low) {\n // R's own form. The algebraically equal low + h * (high - low) rounds\n // differently, and this keeps the last bits with R.\n const h = position - below;\n return (1 - h) * low + h * high;\n }\n return low;\n}\n",
|
|
7
|
+
"/**\n * Gaussian kernel density estimate — R's `density()` with its defaults.\n *\n * `plot_sampling()` in the R package draws two of these panels: one over the\n * population and one over the pooled samples. Both calls are `density(x)` with\n * no other argument, so this module ports that default path: the Gaussian\n * kernel, the `nrd0` bandwidth, a 512-point output grid, `cut = 3`, and\n * `ext = 4`. It also ports R's `from` and `to` arguments, which freeze the\n * ends of the reported window — the sampling plot uses them to keep one axis\n * across redraws. Verified against R 4.5.3 in `kde.test.ts`.\n *\n * The estimate is a convolution, and R computes it with an FFT rather than by\n * summing a kernel over every point. This port copies that: the cost is\n * linear in the count of values plus a fixed 1024-point transform, so pooling\n * a million sampled values stays fast. The steps, in R's own order:\n *\n * 1. Pick the bandwidth with `bwNrd0`.\n * 2. Set the reported window to `range(x) ± 3 * bw` (`cut = 3`), and the\n * wider working window to `range(x) ± 7 * bw` (a further `ext = 4`).\n * 3. Spread the mass of the values over 512 equally spaced points of the\n * working window, splitting each value between its two neighbors.\n * 4. Convolve that with a Gaussian of standard deviation `bw`, using a\n * 1024-point FFT, and clamp negative rounding noise to zero.\n * 5. Interpolate the result back onto the reported window.\n *\n * The two windows are different widths and must not be confused. A port that\n * runs the FFT over the reported window gives a visibly narrower curve.\n */\n\nimport { extent, quantiles, sd } from \"./arith\";\n\n/** Points in the reported grid. R's `density(n = 512)` default. */\nconst GRID_SIZE = 512;\n\n/** Points in the FFT grid. R pads the binned mass to twice its length. */\nconst FFT_SIZE = 2 * GRID_SIZE;\n\n/** Bandwidths of reported window beyond the data. R's `cut = 3`. */\nconst CUT = 3;\n\n/** Further bandwidths of working window beyond that. R's `ext = 4`. */\nconst EXT = 4;\n\n/** `M_1_SQRT_2PI` of R's C sources. */\nconst INV_SQRT_2PI = 0.398942280401432677939946059934;\n\n/** What the caller may change. R's other arguments are not ported. */\nexport interface KernelDensityOptions {\n /**\n * The bandwidth, the standard deviation of the kernel.\n *\n * The default is `bwNrd0(values)`, R's `bw = \"nrd0\"`. Giving the bandwidth\n * skips that selection, and with it the rule that the selection needs two\n * values — R does the same.\n */\n readonly bw?: number;\n /**\n * The low end of the reported window, R's `from`.\n *\n * The default is `min(x) - 3 * bw`, R's `cut = 3`. The working window still\n * reaches 4 bandwidths further out, and `binDist` drops the mass beyond it,\n * as R's `BinDist` does. So a window narrower than the data cuts the tails\n * off, and a wider one pads the curve with near-zero density — the frozen\n * axis the sampling plot needs across redraws.\n */\n readonly from?: number;\n /** The high end of the reported window, R's `to`. Same rules as `from`. */\n readonly to?: number;\n}\n\n/** The curve, in the shape of R's `density` object. */\nexport interface KernelDensityEstimate {\n /**\n * The 512 grid points. They run from `min(x) - 3 * bw` to\n * `max(x) + 3 * bw`, unless the caller froze an end with `from` or `to`.\n */\n readonly x: readonly number[];\n /** The density at each grid point. Never negative. */\n readonly y: readonly number[];\n /** The bandwidth the estimate used. */\n readonly bw: number;\n /**\n * The count of values the caller gave, before any were dropped. R reports\n * the same count, and scales the curve by the share that it kept.\n */\n readonly n: number;\n}\n\n/**\n * Pick a bandwidth by Silverman's rule, R's `bw.nrd0()`.\n *\n * The rule is `0.9 * min(sd, IQR / 1.34) * n^(-1/5)`. When that smaller of\n * the two spreads is 0 — every value the same, or every value repeated past\n * the quartiles — R walks a chain of fallbacks and takes the first that is\n * not 0: the standard deviation, then the size of the first value, then 1.\n * This keeps the rule from returning a bandwidth of 0, which would leave the\n * estimate undrawable.\n *\n * @param values The observations. All must be finite.\n * @returns The bandwidth. Always positive.\n * @throws RangeError Below two values. R stops with \"need at least 2 data\n * points\", since the spread of one value has no meaning.\n */\nexport function bwNrd0(values: readonly number[]): number {\n if (values.length < 2) {\n throw new RangeError(`need at least 2 data points, got ${values.length}`);\n }\n\n const spread = sd(values);\n const [lowerQuartile, upperQuartile] = quantiles(values, [0.25, 0.75]);\n const iqrSpread = (upperQuartile - lowerQuartile) / 1.34;\n const scale = fallbackScale(spread, iqrSpread, values[0]);\n\n return 0.9 * scale * Math.pow(values.length, -0.2);\n}\n\n/** Take the first candidate of R's chain that is not 0. */\nfunction fallbackScale(\n spread: number,\n iqrSpread: number,\n first: number,\n): number {\n const smaller = Math.min(spread, iqrSpread);\n if (smaller !== 0) {\n return smaller;\n }\n if (spread !== 0) {\n return spread;\n }\n return Math.abs(first) !== 0 ? Math.abs(first) : 1;\n}\n\n/**\n * Estimate the density of the values.\n *\n * R takes a matrix here and flattens it. This port takes the flat array, so a\n * caller that pools several samples flattens them first. R flattens a matrix\n * column by column, which for `plot_sampling()` means one whole sample after\n * another.\n *\n * Values that are infinite are dropped, and the curve is scaled by the share\n * of values that remain, as in R. A value that is NaN is refused: R reads it\n * as a missing value and stops.\n *\n * @param values The observations.\n * @param options The bandwidth and the window ends, if the caller sets them.\n * @returns The grid, the density on it, the bandwidth, and the count of\n * values given.\n * @throws RangeError If the bandwidth has to be selected from fewer than two\n * values, if a given bandwidth is not positive and finite, if a given\n * window end is not finite, if a value is NaN, or if no value is finite.\n */\nexport function kernelDensity(\n values: readonly number[],\n options: KernelDensityOptions = {},\n): KernelDensityEstimate {\n const finite = finiteValuesOf(values);\n const bw = options.bw ?? bwNrd0(finite);\n\n if (!Number.isFinite(bw) || bw <= 0) {\n throw new RangeError(`bw must be positive and finite, got ${bw}`);\n }\n if (finite.length === 0) {\n throw new RangeError(\"need at least 1 finite value, got none\");\n }\n\n if (options.from !== undefined && !Number.isFinite(options.from)) {\n throw new RangeError(`non-finite 'from': ${options.from}`);\n }\n if (options.to !== undefined && !Number.isFinite(options.to)) {\n throw new RangeError(`non-finite 'to': ${options.to}`);\n }\n\n const [lowest, highest] = extent(finite);\n const from = options.from ?? lowest - CUT * bw;\n const to = options.to ?? highest + CUT * bw;\n const lo = from - EXT * bw;\n const up = to + EXT * bw;\n\n const binned = binDist(finite, lo, up, finite.length / values.length);\n const kernel = gaussianKernel(lo, up, bw);\n const density = convolve(binned, kernel);\n\n const working = gridOf(lo, up);\n const x = gridOf(from, to);\n const y = x.map((point) => interpolate(working, density, point));\n\n return { x, y, bw, n: values.length };\n}\n\n/** Drop the values that are not finite. Refuse a value that is NaN. */\nfunction finiteValuesOf(values: readonly number[]): readonly number[] {\n if (values.every((value) => Number.isFinite(value))) {\n return values;\n }\n if (values.some((value) => Number.isNaN(value))) {\n throw new RangeError(\"values contain a missing value\");\n }\n return values.filter((value) => Number.isFinite(value));\n}\n\n/**\n * Spread the mass of the values over the working grid — R's `C_BinDist`.\n *\n * Each value carries mass `1 / count` and lands between two grid points. The\n * split is proportional to the distance to each: a value three quarters of\n * the way from one point to the next gives a quarter of its mass to the point\n * behind it and three quarters to the point ahead. Rounding each value to its\n * nearest point instead would step the curve.\n *\n * @param values The finite observations.\n * @param lo The low end of the working window.\n * @param up The high end of the working window.\n * @param keptShare The share of the caller's values that reached here, R's\n * `totMass`. It is 1 unless infinite values were dropped.\n * @returns The mass on the first 512 points, zero-padded to 1024 for the FFT.\n */\nfunction binDist(\n values: readonly number[],\n lo: number,\n up: number,\n keptShare: number,\n): Float64Array {\n const bins = new Float64Array(FFT_SIZE);\n const step = (up - lo) / (GRID_SIZE - 1);\n const weight = keptShare / values.length;\n const lastPair = GRID_SIZE - 2;\n\n // An index loop, against the map convention of CLAUDE.md: every value adds\n // to two entries of one shared accumulator, which a map cannot express.\n // This is also the only step whose cost grows with the count of values, so\n // it stays a single pass with no allocation.\n for (let i = 0; i < values.length; i += 1) {\n const position = (values[i] - lo) / step;\n const behind = Math.floor(position);\n const ahead = position - behind;\n\n if (behind >= 0 && behind <= lastPair) {\n bins[behind] += (1 - ahead) * weight;\n bins[behind + 1] += ahead * weight;\n } else if (behind === -1) {\n bins[0] += ahead * weight;\n } else if (behind === lastPair + 1) {\n bins[behind] += (1 - ahead) * weight;\n }\n }\n\n return bins;\n}\n\n/**\n * Build the kernel over the lags of the working grid — R's `kords`.\n *\n * The grid runs from lag 0 up to twice the width of the window, and then the\n * second half is replaced by the negative lags in reverse. That folding is\n * what makes the circular convolution of the FFT behave like the ordinary one\n * over a finite window.\n */\nfunction gaussianKernel(lo: number, up: number, bw: number): Float64Array {\n const span = ((FFT_SIZE - 1) / (GRID_SIZE - 1)) * (up - lo);\n const step = span / (FFT_SIZE - 1);\n const lags = Array.from({ length: FFT_SIZE }, (_, index) => index * step);\n const folded = lags.map((lag, index) =>\n index > GRID_SIZE ? -lags[FFT_SIZE - index] : lag,\n );\n\n return Float64Array.from(folded, (lag) => dnorm(lag, bw));\n}\n\n/**\n * The density of a normal distribution at `at`, centered on 0 — R's `dnorm`.\n *\n * Beyond five standard deviations R splits the value in two and exponentiates\n * each part, which keeps the far tail of the kernel accurate to the last bits.\n * This copies that split so the tails of the curve track R's.\n */\nfunction dnorm(at: number, sd: number): number {\n const z = Math.abs(at) / sd;\n\n if (z < 5) {\n return (INV_SQRT_2PI * Math.exp(-0.5 * z * z)) / sd;\n }\n // Beyond this the density is below the smallest normal double.\n if (z > Math.sqrt(-2 * Math.LN2 * (-1021 + 1 - 53))) {\n return 0;\n }\n\n const head = Math.round(z * 65536) / 65536;\n const tail = z - head;\n return (\n (INV_SQRT_2PI / sd) *\n (Math.exp(-0.5 * head * head) * Math.exp((-0.5 * tail - head) * tail))\n );\n}\n\n/**\n * Convolve the binned mass with the kernel through the frequency domain.\n *\n * The transform of a convolution is the product of the transforms, so one\n * inverse transform of that product gives the whole curve. R does exactly\n * this, and clamps the result at 0 because the rounding of the transform can\n * leave a density slightly below 0 where it should be flat.\n *\n * @returns The density on the 512 working grid points.\n */\nfunction convolve(binned: Float64Array, kernel: Float64Array): Float64Array {\n const massReal = Float64Array.from(binned);\n const massImaginary = new Float64Array(FFT_SIZE);\n const kernelReal = Float64Array.from(kernel);\n const kernelImaginary = new Float64Array(FFT_SIZE);\n\n fftInPlace(massReal, massImaginary, false);\n fftInPlace(kernelReal, kernelImaginary, false);\n\n // An index loop, against the map convention of CLAUDE.md: this multiplies\n // two arrays in place, one complex pair at a time, to avoid four more\n // 1024-point allocations per redraw.\n for (let i = 0; i < FFT_SIZE; i += 1) {\n const real =\n massReal[i] * kernelReal[i] +\n massImaginary[i] * kernelImaginary[i];\n const imaginary =\n massImaginary[i] * kernelReal[i] -\n massReal[i] * kernelImaginary[i];\n massReal[i] = real;\n massImaginary[i] = imaginary;\n }\n\n fftInPlace(massReal, massImaginary, true);\n\n return Float64Array.from(massReal.subarray(0, GRID_SIZE), (value) =>\n Math.max(0, value / FFT_SIZE),\n );\n}\n\n/**\n * Transform in place, by the radix-2 Cooley-Tukey algorithm.\n *\n * The length must be a power of two; here it is always 1024. The forward\n * transform matches R's `fft(z)` and the inverse matches\n * `fft(z, inverse = TRUE)`, which R leaves unscaled — the caller divides.\n *\n * @param real The real parts. Replaced by the result.\n * @param imaginary The imaginary parts. Replaced by the result.\n * @param inverse Whether to run the inverse transform.\n */\nfunction fftInPlace(\n real: Float64Array,\n imaginary: Float64Array,\n inverse: boolean,\n): void {\n const size = real.length;\n const half = size >> 1;\n\n // Index loops throughout, against the map convention of CLAUDE.md: the\n // algorithm is a fixed pattern of in-place swaps and butterflies over pairs\n // of indices, which is what makes it O(n log n) instead of O(n^2).\n for (let i = 1, j = 0; i < size; i += 1) {\n let bit = half;\n for (; (j & bit) !== 0; bit >>= 1) {\n j ^= bit;\n }\n j ^= bit;\n if (i < j) {\n const swappedReal = real[i];\n const swappedImaginary = imaginary[i];\n real[i] = real[j];\n imaginary[i] = imaginary[j];\n real[j] = swappedReal;\n imaginary[j] = swappedImaginary;\n }\n }\n\n const direction = inverse ? 1 : -1;\n const cosines = new Float64Array(half);\n const sines = new Float64Array(half);\n for (let k = 0; k < half; k += 1) {\n const angle = (direction * 2 * Math.PI * k) / size;\n cosines[k] = Math.cos(angle);\n sines[k] = Math.sin(angle);\n }\n\n for (let span = 1; span < size; span <<= 1) {\n const stride = half / span;\n for (let start = 0; start < size; start += span << 1) {\n for (let k = 0; k < span; k += 1) {\n const even = start + k;\n const odd = even + span;\n const cosine = cosines[k * stride];\n const sine = sines[k * stride];\n const oddReal =\n real[odd] * cosine - imaginary[odd] * sine;\n const oddImaginary =\n real[odd] * sine + imaginary[odd] * cosine;\n real[odd] = real[even] - oddReal;\n imaginary[odd] = imaginary[even] - oddImaginary;\n real[even] = real[even] + oddReal;\n imaginary[even] = imaginary[even] + oddImaginary;\n }\n }\n }\n}\n\n/**\n * Build 512 equally spaced points from `from` to `to`, as R's `seq.int` does.\n *\n * R pins both ends and computes each point between them from the start, not\n * by adding up steps. Interior points can still differ from R's by one unit\n * in the last place: R's C loop rounds its multiply-and-add once where this\n * rounds twice. That is a relative difference near 1e-16, far below the\n * tolerance the tests hold the curve to.\n */\nfunction gridOf(from: number, to: number): number[] {\n const step = (to - from) / (GRID_SIZE - 1);\n const points = Array.from(\n { length: GRID_SIZE },\n (_, index) => from + index * step,\n );\n points[GRID_SIZE - 1] = to;\n return points;\n}\n\n/**\n * Read the density at a point between two grid points — R's `approx`.\n *\n * The search is the bisection R uses, including its two tests for landing\n * exactly on a grid point. The reported window always sits inside the working\n * window, so the two guards for a point outside it are unreachable; they hold\n * the ends flat rather than returning R's NA.\n */\nfunction interpolate(\n grid: readonly number[],\n values: Float64Array,\n at: number,\n): number {\n let low = 0;\n let high = grid.length - 1;\n\n if (at < grid[low]) {\n return values[low];\n }\n if (at > grid[high]) {\n return values[high];\n }\n\n while (low < high - 1) {\n const middle = (low + high) >> 1;\n if (at < grid[middle]) {\n high = middle;\n } else {\n low = middle;\n }\n }\n\n if (at === grid[high]) {\n return values[high];\n }\n if (at === grid[low]) {\n return values[low];\n }\n\n const lowValue = values[low];\n const highValue = values[high];\n const lowPoint = grid[low];\n const highPoint = grid[high];\n return (\n lowValue +\n (highValue - lowValue) * ((at - lowPoint) / (highPoint - lowPoint))\n );\n}\n",
|
|
8
|
+
"/**\n * Round numbers covering a range — R's `pretty()`.\n *\n * R places histogram cell edges and axis labels with this. It picks a cell\n * size from the ladder 1, 2, 5, 10 times a power of ten, then rounds the low\n * end down and the high end up to multiples of that size. The result\n * therefore **covers** the range it was given, and usually extends past both\n * ends.\n *\n * That widening is the whole point, and it is what separates this from\n * `prettyTicks` in `src/plot/axes.ts`, which keeps ticks inside a window the\n * caller has already fixed. Do not swap one for the other: a histogram drawn\n * on ticks that stop short of the data loses its outer cells, and a fixed\n * plot window drawn on widened ticks grows past its frame.\n *\n * The port follows R's C routine `R_pretty` step for step, including its\n * rounding guard of 1e-10 and its bias toward 2 and 5 over stepping straight\n * to the next power of ten. Verified against R 4.5.3 in `pretty.test.ts` and\n * across a sweep of 5348 random ranges, every one of which it reproduces\n * edge for edge. R's remaining tuning arguments (`shrink.sml`, `high.u.bias`,\n * `u5.bias`, `eps.correct`, `f.min`) stay at their defaults: nothing in the\n * package changes them, and each one is a branch that could only be guessed\n * at rather than tested.\n */\n\nimport { fusedMultiplyAdd, requireCount } from \"./arith\";\n\n/** R's `rounding_eps`, the slack that keeps a near-multiple from stepping. */\nconst ROUNDING_EPS = 1e-10;\n\n/** R's `shrink.sml`, applied to the cell of a range with no width. */\nconst SHRINK_SMALL = 0.75;\n\n/** R's `high.u.bias`, the pull toward a larger unit. */\nconst HIGH_U_BIAS = 1.5;\n\n/** R's `u5.bias`, the extra pull toward a unit of 5. */\nconst U5_BIAS = 0.5 + 1.5 * HIGH_U_BIAS;\n\n/** What the caller may change. R's tuning arguments are not ported. */\nexport interface RPrettyOptions {\n /**\n * About how many cells to produce. R's `n`, default 5. It is a wish, not a\n * count: the returned array can hold more or fewer edges.\n */\n readonly n?: number;\n /**\n * The fewest cells to accept. R's `min.n`, default `floor(n / 3)`. R's\n * `hist()` passes 1 here rather than taking this default.\n */\n readonly minN?: number;\n}\n\n/**\n * Return round numbers that cover the range from `lo` to `up`.\n *\n * @param lo The low end of the range to cover.\n * @param up The high end. It must not be below `lo`.\n * @param options The wished-for cell count and the fewest cells to accept.\n * @returns The edges, in increasing order. Always at least two, except for\n * the degenerate request of no cells at all.\n * @throws RangeError If an end is not finite, if `up` is below `lo`, if a\n * count is negative or fractional, or if `minN` is above `n` (R stops with\n * \"invalid 'min.n' argument\").\n */\nexport function rPretty(\n lo: number,\n up: number,\n options: RPrettyOptions = {},\n): number[] {\n const { n = 5, minN = Math.floor(n / 3) } = options;\n\n if (!Number.isFinite(lo) || !Number.isFinite(up)) {\n throw new RangeError(`lo and up must be finite, got ${lo} and ${up}`);\n }\n if (up < lo) {\n throw new RangeError(`up must not be below lo, got ${lo} and ${up}`);\n }\n requireCount(n, \"n\");\n requireCount(minN, \"minN\");\n if (minN > n) {\n throw new RangeError(`minN must not be above n, got ${minN} and ${n}`);\n }\n\n const { low, high, cells } = prettyBounds(lo, up, n, minN);\n if (cells === 0) {\n return [low];\n }\n\n const step = (high - low) / cells;\n // R's `seq.int` computes each point to wider precision than a double\n // carries and rounds it a single time. Writing `low + index * step` rounds\n // twice and lands one unit in the last place away often enough to matter:\n // 1081 of the 5348 swept ranges differed on at least one edge before this.\n const edges = Array.from({ length: cells + 1 }, (_, index) =>\n fusedMultiplyAdd(index, step, low),\n );\n edges[cells] = high;\n\n // R zaps an edge that rounding left just off zero, so a range through zero\n // reports a clean 0 rather than 1e-17.\n return edges.map((edge) => (Math.abs(edge) < 1e-14 * step ? 0 : edge));\n}\n\n/**\n * Find the covering bounds and the cell count — R's `R_pretty`.\n *\n * The steps: measure a first guess at the cell size, round that guess to a\n * round number, step the ends out to multiples of it, and widen further if\n * that left fewer cells than `minN`.\n */\nfunction prettyBounds(\n lo: number,\n up: number,\n n: number,\n minN: number,\n): { low: number; high: number; cells: number } {\n const unit = cellUnit(lo, up, n, minN);\n\n let steps = Math.floor(lo / unit + ROUNDING_EPS);\n let stepsUp = Math.ceil(up / unit - ROUNDING_EPS);\n // Index loops, against the map convention of CLAUDE.md: these walk one\n // step at a time until the multiple covers the end, as R's C loops do.\n while (steps * unit > lo + ROUNDING_EPS * unit) {\n steps -= 1;\n }\n while (stepsUp * unit < up - ROUNDING_EPS * unit) {\n stepsUp += 1;\n }\n\n let cells = Math.floor(0.5 + stepsUp - steps);\n if (cells < minN) {\n // Widen toward zero: a range above zero grows downward, one below zero\n // grows upward, and a range with an end exactly at zero grows the only\n // way it can. R splits the extra cells between the two ends when neither\n // end is pinned, giving the odd one to the end it is growing toward. The\n // rule is read off R's own results — 132 of the swept ranges turn on it —\n // rather than from the C source, which is not shipped with this R.\n const missing = minN - cells;\n if (lo === 0 && up > 0) {\n // Anchored at zero from below: every new cell has to go on top.\n stepsUp += missing;\n } else if (up === 0 && lo < 0) {\n steps -= missing;\n } else if (steps >= 0) {\n stepsUp += Math.floor(missing / 2);\n steps -= Math.floor(missing / 2) + (missing % 2);\n } else {\n steps -= Math.floor(missing / 2);\n stepsUp += Math.floor(missing / 2) + (missing % 2);\n }\n cells = minN;\n }\n\n return {\n low: steps * unit < lo ? steps * unit : lo,\n high: stepsUp * unit > up ? stepsUp * unit : up,\n cells,\n };\n}\n\n/**\n * Pick the cell size from the ladder 1, 2, 5, 10 times a power of ten.\n *\n * The tests are R's, and they are one-sided on purpose: a candidate wins when\n * it overshoots the wanted cell size by less than `bias` times the amount the\n * current choice undershoots it. That is what makes 5 beat 2 more readily\n * than 2 beats 1.\n */\nfunction cellUnit(lo: number, up: number, n: number, minN: number): number {\n const width = up - lo;\n let cell: number;\n let noWidth: boolean;\n\n if (width === 0 && up === 0) {\n cell = 1;\n noWidth = true;\n } else {\n cell = Math.max(Math.abs(lo), Math.abs(up));\n const bound =\n 1 +\n (U5_BIAS >= 1.5 * HIGH_U_BIAS + 0.5\n ? 1 / (1 + HIGH_U_BIAS)\n : 1.5 / (1 + U5_BIAS));\n // A range this narrow relative to its distance from zero carries no\n // usable width; R treats it as a point.\n noWidth = width < cell * bound * Math.max(1, n) * Number.EPSILON * 3;\n }\n\n if (noWidth) {\n // A range with no width has only its distance from zero to set a scale\n // by, and that would put a cell of 1e9 around a point at 1e9. R pulls the\n // cell down a decade first, keeping it above 9 so the ladder still has\n // room to choose. Confirmed by bisecting R's unit transitions: they land\n // on 9.333..., 96.666..., and 283.333... to twelve decimals.\n if (cell > 10) {\n cell = 9 + cell / 10;\n }\n cell *= SHRINK_SMALL;\n if (minN > 1) {\n cell /= minN;\n }\n } else {\n cell = width;\n if (n > 1) {\n cell /= n;\n }\n }\n\n const base = powerOfTen(Math.floor(Math.log10(cell)));\n let unit = base;\n if (2 * base - cell < HIGH_U_BIAS * (cell - unit)) {\n unit = 2 * base;\n if (5 * base - cell < U5_BIAS * (cell - unit)) {\n unit = 5 * base;\n if (10 * base - cell < HIGH_U_BIAS * (cell - unit)) {\n unit = 10 * base;\n }\n }\n }\n return unit;\n}\n\n/**\n * Return 10 to the power of an integer, exactly.\n *\n * R's source warns that this step \"relies on exact calculation\", which some C\n * libraries do not deliver from `pow`. Reading the value as a decimal literal\n * does: the language guarantees the nearest double to the decimal written.\n */\nfunction powerOfTen(exponent: number): number {\n const parsed = Number(`1e${exponent}`);\n return Number.isFinite(parsed) && parsed !== 0\n ? parsed\n : Math.pow(10, exponent);\n}\n",
|
|
9
|
+
"/**\n * Counts of values per cell — R's `hist(plot = FALSE)` with its defaults.\n *\n * `plot_sampling()` draws its third panel from this: it accumulates one\n * statistic per sample and calls `hist()` on the running collection, then\n * reads `counts` back to place the panel's label. Verified against R 4.5.3 in\n * `histogram.test.ts`.\n *\n * Three rules carry the behavior, and each one is easy to get subtly wrong:\n *\n * 1. **How many cells.** Sturges' rule, `ceiling(log2(n) + 1)`, is only a\n * suggestion. It goes to `rPretty`, which returns round edges covering the\n * data — often a different count.\n * 2. **Which cell a value belongs to.** Cells are closed on the right,\n * `(edge[k], edge[k+1]]`, so a value sitting on an edge counts into the\n * cell below it. The lowest edge is the exception: nothing sits below it,\n * so it is opened to admit the smallest value.\n * 3. **Values that land on an edge.** Rounding leaves a value that ought to\n * sit on an edge a hair to one side of it. R nudges the edges by a\n * millionth of a cell before counting — outward at the bottom, upward\n * everywhere else — so such a value still lands where it belongs. The\n * reported edges are the unnudged ones.\n */\n\nimport { extent, quantile, sum, zipWith } from \"./arith\";\nimport { rPretty } from \"./pretty\";\n\n/** R's `fuzz` argument of `hist.default`, in cell widths. */\nconst FUZZ = 1e-7;\n\n/** What the caller may change. R's drawing arguments are not ported. */\nexport interface HistogramOptions {\n /**\n * How to place the cell edges.\n *\n * A number asks for about that many cells, which `rPretty` rounds off; an\n * array gives the edges outright. The default is Sturges' rule. R takes the\n * same two forms in one argument, but cannot tell a one-edge array from a\n * request for one cell — this port reads an array as edges, always.\n */\n readonly breaks?: number | readonly number[];\n}\n\n/** The counted histogram, in the shape of R's `histogram` object. */\nexport interface Histogram {\n /** The cell edges, in increasing order. One more than there are cells. */\n readonly breaks: readonly number[];\n /** How many values fell in each cell. */\n readonly counts: readonly number[];\n /** The midpoint of each cell, R's `mids`. */\n readonly mids: readonly number[];\n}\n\n/**\n * Suggest a cell count by Sturges' rule — R's `nclass.Sturges`.\n *\n * @param values The observations. Only how many there are matters.\n * @returns `ceiling(log2(n) + 1)`, or -Infinity for no values. R returns -Inf\n * there too, which is what makes its `hist()` stop.\n */\nexport function nclassSturges(values: readonly number[]): number {\n return Math.ceil(Math.log2(values.length) + 1);\n}\n\n/**\n * Count the values into cells.\n *\n * Values that are not finite are dropped first, as R does — including NaN,\n * which R reads as a missing value and drops here. Note the difference from\n * `kernelDensity`, which refuses NaN, because R's `density()` refuses it.\n *\n * @param values The observations.\n * @param options How to place the cell edges.\n * @returns The edges, the count in each cell, and the cell midpoints.\n * @throws RangeError If no value is finite, if a requested cell count is\n * below one or fractional, if fewer than two edges are given, or if the\n * given edges leave a value uncounted. R stops in all of those cases too.\n */\nexport function histogram(\n values: readonly number[],\n options: HistogramOptions = {},\n): Histogram {\n const finite = values.filter((value) => Number.isFinite(value));\n if (finite.length === 0) {\n throw new RangeError(\"need at least 1 finite value, got none\");\n }\n\n const breaks = resolveBreaks(finite, options.breaks);\n const widths = zipWith(\n breaks.slice(1),\n breaks.slice(0, -1),\n (up, low) => up - low,\n );\n const counts = binCount(finite, fuzzyBreaks(breaks, widths, finite));\n\n if (sum(counts) < finite.length) {\n throw new RangeError(\n \"some values were not counted; the breaks may not span their range\",\n );\n }\n\n return {\n breaks,\n counts,\n mids: zipWith(breaks.slice(1), breaks.slice(0, -1), (up, low) =>\n 0.5 * (up + low),\n ),\n };\n}\n\n/** Place the cell edges, from Sturges' rule, a wish, or the caller's array. */\nfunction resolveBreaks(\n finite: readonly number[],\n requested: number | readonly number[] | undefined,\n): number[] {\n // Narrowing by `typeof`, not Array.isArray: the latter narrows to a mutable\n // array and so leaves a readonly array in the union.\n if (requested !== undefined && typeof requested !== \"number\") {\n if (requested.length < 2) {\n throw new RangeError(\n `breaks given as edges need at least 2 of them, got ${requested.length}`,\n );\n }\n return [...requested].sort((a, b) => a - b);\n }\n\n const cells = requested ?? nclassSturges(finite);\n if (!Number.isInteger(cells) || cells < 1) {\n throw new RangeError(`invalid number of breaks: ${cells}`);\n }\n\n // R's hist() overrides pretty()'s own min.n here, and asks for 1.\n return rPretty(...extent(finite), { n: cells, minN: 1 });\n}\n\n/**\n * Nudge the edges so a value sitting on one lands in the right cell.\n *\n * The size of the nudge follows R: the middle cell width when there are many\n * cells, the narrowest when there are few, and the width of the data itself\n * when there are almost none.\n */\nfunction fuzzyBreaks(\n breaks: readonly number[],\n widths: readonly number[],\n finite: readonly number[],\n): number[] {\n const positive = widths.filter((width) => width > 0);\n const [lowest, highest] = extent(finite);\n const scale =\n breaks.length > 5\n ? quantile(widths, 0.5)\n : breaks.length <= 3\n ? highest - lowest\n : Math.min(...positive);\n const nudge = FUZZ * scale;\n\n // The lowest edge moves outward to admit the smallest value; every other\n // edge moves up, since each one closes the cell below it.\n return breaks.map((edge, index) =>\n index === 0 ? edge - nudge : edge + nudge,\n );\n}\n\n/**\n * Find the cell of each value and count it — R's `C_BinCount`.\n *\n * The search is R's bisection, written the same way: a value above the middle\n * edge belongs to the upper half, and a value exactly on it belongs to the\n * lower half, because cells close on the right.\n */\nfunction binCount(\n values: readonly number[],\n breaks: readonly number[],\n): number[] {\n const counts = new Array<number>(breaks.length - 1).fill(0);\n const last = breaks.length - 1;\n\n // An index loop, against the map convention of CLAUDE.md: each value walks\n // a bisection and then adds to one entry of a shared accumulator.\n for (let i = 0; i < values.length; i += 1) {\n const value = values[i] as number;\n if (value < (breaks[0] as number) || value > (breaks[last] as number)) {\n continue;\n }\n\n let low = 0;\n let high = last;\n while (high - low >= 2) {\n const middle = (high + low) >> 1;\n if (value > (breaks[middle] as number)) {\n low = middle;\n } else {\n high = middle;\n }\n }\n counts[low] += 1;\n }\n\n return counts;\n}\n",
|
|
10
|
+
"/**\n * Seedable random draws for the sampling demonstrations.\n *\n * R gets these from `runif()`, `rnorm()`, and `sample()`, which all read one\n * global Mersenne Twister stream. This port makes the generator an argument\n * instead. A pure core cannot hold a stream, and a demonstration that a\n * student reloads must show the same numbers.\n *\n * The generator does not reproduce R's stream. No JavaScript generator does,\n * and the port plan does not ask for it. The requirement is that one seed\n * always gives one sequence, in this run and in every later run.\n *\n * The generator is mulberry32 (Tommy Ettinger, public domain): 32 bits of\n * state, four integer operations per draw, a period of 2^32, and a published\n * result on the gjrand test suite. It is small enough to read in full below,\n * which matters more here than statistical strength — these draws teach the\n * sampling distribution, they do not protect anything. Do not use this module\n * for cryptography.\n */\n\nimport { requireCount, sum } from \"./arith\";\n\n/** A source of uniform values in the interval [0, 1). */\nexport type Rng = () => number;\n\n/** The parameters of `runif`. The defaults are the defaults of R's `runif`. */\nexport interface RunifOptions {\n /** The low end of the interval. */\n readonly min?: number;\n /** The high end of the interval. The generator never returns it. */\n readonly max?: number;\n}\n\n/** The parameters of `rnorm`. The defaults are the defaults of R's `rnorm`. */\nexport interface RnormOptions {\n /** The center of the distribution. */\n readonly mean?: number;\n /** The standard deviation. A negative value gives NaN, as in R. */\n readonly sd?: number;\n}\n\n/**\n * The parameters of `rlnorm`. The defaults are the defaults of R's `rlnorm`.\n *\n * Both name the normal distribution behind the exponential, not the lognormal\n * distribution itself.\n */\nexport interface RlnormOptions {\n /** The center of the normal behind the exponential. */\n readonly meanlog?: number;\n /**\n * The standard deviation of the normal behind the exponential. A negative\n * value gives NaN, as in R.\n */\n readonly sdlog?: number;\n}\n\n/**\n * The parameters of `rcauchy`. The defaults are the defaults of R's\n * `rcauchy`.\n */\nexport interface RcauchyOptions {\n /** The center of the distribution. It is the median, not a mean. */\n readonly location?: number;\n /** The half-width at half-maximum. A negative value gives NaN, as in R. */\n readonly scale?: number;\n}\n\n/**\n * Make a generator from a seed.\n *\n * Two generators of one seed give the same sequence. The seed keeps only its\n * integer part, and only the low 32 bits of that.\n *\n * @param seed The start state. It must be finite.\n * @returns A generator of uniform values in [0, 1).\n * @throws RangeError If the seed is not finite.\n */\nexport function seededRng(seed: number): Rng {\n if (!Number.isFinite(seed)) {\n throw new RangeError(`seed must be finite, got ${seed}`);\n }\n\n let state = Math.trunc(seed) | 0;\n\n return () => {\n state = (state + 0x6d2b79f5) | 0;\n let mixed = Math.imul(state ^ (state >>> 15), 1 | state);\n mixed = (mixed + Math.imul(mixed ^ (mixed >>> 7), 61 | mixed)) ^ mixed;\n return ((mixed ^ (mixed >>> 14)) >>> 0) / 4294967296;\n };\n}\n\n/**\n * Draw uniform values.\n *\n * The function takes one value from the generator for each result, in order.\n *\n * An interval with `min` above `max`, or with a bound that is not finite,\n * gives NaN for every result and takes nothing from the generator. R does the\n * same, with a warning that a library cannot give.\n *\n * @param rng The source of randomness.\n * @param n How many values to draw. It must be a non-negative integer.\n * @param options The interval. The default is [0, 1).\n * @returns The drawn values.\n * @throws RangeError If n is negative or is not an integer.\n */\nexport function runif(\n rng: Rng,\n n: number,\n options: RunifOptions = {},\n): number[] {\n requireCount(n, \"n\");\n const { min = 0, max = 1 } = options;\n\n if (!Number.isFinite(min) || !Number.isFinite(max) || max < min) {\n return new Array<number>(n).fill(Number.NaN);\n }\n\n return Array.from({ length: n }, () => min + (max - min) * rng());\n}\n\n/**\n * Draw normal values.\n *\n * The function uses the Box-Muller transform, which makes two independent\n * standard normal values from two uniform values. It takes the values in\n * pairs: the first value of a pair gives the radius, the second gives the\n * angle. An odd count discards the second value of the last pair, so the\n * count of draws from the generator is always `2 * ceil(n / 2)`.\n *\n * The radius is `sqrt(-2 * log(1 - u))`, not `sqrt(-2 * log(u))`. The\n * generator can return an exact 0 but never returns 1, and `log(0)` is not\n * finite. The subtraction moves the open end of the interval to the point\n * where the logarithm needs it.\n *\n * A `mean` that is not finite, or an `sd` that is negative or not finite,\n * gives NaN for every result and takes nothing from the generator, as in R.\n *\n * @param rng The source of randomness.\n * @param n How many values to draw. It must be a non-negative integer.\n * @param options The center and the spread. The default is the standard\n * normal distribution.\n * @returns The drawn values.\n * @throws RangeError If n is negative or is not an integer.\n */\nexport function rnorm(\n rng: Rng,\n n: number,\n options: RnormOptions = {},\n): number[] {\n requireCount(n, \"n\");\n const { mean = 0, sd = 1 } = options;\n\n if (!Number.isFinite(mean) || !Number.isFinite(sd) || sd < 0) {\n return new Array<number>(n).fill(Number.NaN);\n }\n\n const pairs = Array.from({ length: Math.ceil(n / 2) }, () =>\n standardNormalPair(rng),\n );\n return pairs\n .flat()\n .slice(0, n)\n .map((z) => mean + sd * z);\n}\n\n/**\n * Draw Student t values.\n *\n * Each value is `z / sqrt(chiSquare / df)`: a standard normal draw over the\n * square root of a scaled chi-square. The chi-square is the sum of `df`\n * squared standard normal draws. The draws come in bulk: one `rnorm` call of\n * length `n` for the numerators, then `df` further calls of length `n`, one\n * per chi-square component. Each `rnorm` call takes `2 * ceil(n / 2)` values\n * from the generator, so `rt` takes `(df + 1) * 2 * ceil(n / 2)`.\n *\n * R's `rt` accepts any `df > 0`, through a gamma sampler. A gamma sampler\n * rejects and redraws, so it cannot state a draw count. This port accepts a\n * positive integer `df` only, which the normal construction covers with a\n * fixed draw count. A `df` that is not a positive finite integer gives NaN\n * for every result and takes nothing from the generator, as `rnorm` does for\n * a bad `sd`.\n *\n * @param rng The source of randomness.\n * @param n How many values to draw. It must be a non-negative integer.\n * @param df The degrees of freedom. It must be a positive integer.\n * @returns The drawn values.\n * @throws RangeError If n is negative or is not an integer.\n */\nexport function rt(rng: Rng, n: number, df: number): number[] {\n requireCount(n, \"n\");\n\n if (!Number.isInteger(df) || df <= 0) {\n return new Array<number>(n).fill(Number.NaN);\n }\n\n const z = rnorm(rng, n);\n const components = Array.from({ length: df }, () => rnorm(rng, n));\n return z.map((numerator, index) => {\n // The index is inside every component: each rnorm call returns n values.\n const chiSquare = sum(\n components.map((draws) => (draws[index] as number) ** 2),\n );\n return numerator / Math.sqrt(chiSquare / df);\n });\n}\n\n/**\n * Draw lognormal values.\n *\n * A lognormal value is the exponential of a normal one, so this draws from\n * `rnorm` at `meanlog` and `sdlog` and exponentiates each value. The draw\n * count is therefore `rnorm`'s, `2 * ceil(n / 2)`.\n *\n * The parameters name the normal distribution behind the exponential, not the\n * distribution the function returns. R names them the same way. Every draw is\n * above zero, and the shape leans right: the median is `exp(meanlog)` and the\n * mean sits above it.\n *\n * A `meanlog` that is not finite, or an `sdlog` that is negative or not\n * finite, gives NaN for every result and takes nothing from the generator, as\n * `rnorm` does.\n *\n * @param rng The source of randomness.\n * @param n How many values to draw. It must be a non-negative integer.\n * @param options The center and the spread of the normal behind the\n * exponential. The default is the standard normal, as in R.\n * @returns The drawn values.\n * @throws RangeError If n is negative or is not an integer.\n */\nexport function rlnorm(\n rng: Rng,\n n: number,\n options: RlnormOptions = {},\n): number[] {\n const { meanlog = 0, sdlog = 1 } = options;\n return rnorm(rng, n, { mean: meanlog, sd: sdlog }).map(Math.exp);\n}\n\n/**\n * Draw Cauchy values.\n *\n * This is R's `rcauchy(n, location, scale)`, by the inverse rule: the Cauchy\n * quantile function is `location + scale * tan(pi * (u - 0.5))`, so applying\n * it to uniform draws gives Cauchy draws. The draws come in one `runif` call\n * of length `n`, so the function takes exactly `n` values from the generator.\n *\n * The Cauchy has no mean and no variance. Its center is the `location`, which\n * is the median, and its spread is the `scale`, the half-width at half of the\n * peak density. The tails are heavy enough that averages of draws do not\n * settle, which is what the sampling demonstrations use it for.\n *\n * A `location` that is not finite, or a `scale` that is negative or not\n * finite, gives NaN for every result and takes nothing from the generator,\n * as in R.\n *\n * @param rng The source of randomness.\n * @param n How many values to draw. It must be a non-negative integer.\n * @param options The center and the spread. The default is the standard\n * Cauchy distribution.\n * @returns The drawn values.\n * @throws RangeError If n is negative or is not an integer.\n */\nexport function rcauchy(\n rng: Rng,\n n: number,\n options: RcauchyOptions = {},\n): number[] {\n requireCount(n, \"n\");\n const { location = 0, scale = 1 } = options;\n\n if (!Number.isFinite(location) || !Number.isFinite(scale) || scale < 0) {\n return new Array<number>(n).fill(Number.NaN);\n }\n\n return runif(rng, n).map(\n (u) => location + scale * Math.tan(Math.PI * (u - 0.5)),\n );\n}\n\n/** Make two standard normal values from two uniform values. */\nfunction standardNormalPair(rng: Rng): [number, number] {\n const radius = Math.sqrt(-2 * Math.log(1 - rng()));\n const angle = 2 * Math.PI * rng();\n return [radius * Math.cos(angle), radius * Math.sin(angle)];\n}\n\n/**\n * Take values from a population, without replacement.\n *\n * This is R's `sample(values, k)`, which also samples without replacement by\n * default. The algorithm is a partial Fisher-Yates shuffle: step `i` takes one\n * value from the generator and swaps position `i` with position\n * `i + floor(u * (length - i))`, so each step selects from the values that no\n * earlier step took. The function takes exactly `k` values from the generator.\n *\n * The function copies the population. It does not modify the input.\n *\n * @param rng The source of randomness.\n * @param values The population. Any element type is permitted.\n * @param k How many values to take. It must be a non-negative integer.\n * @returns The taken values, in the order the algorithm found them.\n * @throws RangeError If k is negative, is not an integer, or is more than the\n * size of the population. R gives the same error: \"cannot take a sample\n * larger than the population when 'replace = FALSE'\".\n */\nexport function sampleWithoutReplacement<T>(\n rng: Rng,\n values: readonly T[],\n k: number,\n): T[] {\n requireCount(k, \"k\");\n if (k > values.length) {\n throw new RangeError(\n `cannot take a sample of ${k} from a population of ${values.length}`,\n );\n }\n\n const pool = values.slice();\n // An index loop, against the map convention of CLAUDE.md: each step swaps\n // two positions of the pool, which a map over the values cannot express.\n for (let i = 0; i < k; i += 1) {\n const chosen = i + Math.floor(rng() * (pool.length - i));\n const held = pool[chosen];\n pool[chosen] = pool[i];\n pool[i] = held;\n }\n\n return pool.slice(0, k);\n}\n",
|
|
11
|
+
"/**\n * Drawing samples and reading confidence intervals off them — the statistics\n * of `plot_sampling()` and `plot_sample_ci()` in `../compstatslib/R/`.\n *\n * Both demonstrations rest on the same idea: a statistic computed from a\n * sample is itself a random quantity, and drawing many samples shows how it\n * scatters. `plot_sampling()` draws repeatedly and accumulates the statistic;\n * `plot_sample_ci()` draws once and asks how often an interval built from a\n * sample covers the mean it was drawn from.\n *\n * Two rules shape this module, both from the port plan:\n *\n * - **The generator is an argument, and one call threads one generator.** R\n * reads a global stream, which a pure core cannot. `drawSamples` takes\n * exactly `reps * sampleSize` values from the generator it is given, in\n * draw order, so a caller who holds the generator can keep drawing where\n * the last call stopped. Restarting the generator per repetition would\n * return the same sample every time.\n * - **Accumulation belongs to the caller.** R's `plot_sampling()` returns a\n * `vars` list holding every statistic drawn so far, and its interactive\n * wrapper hands that back on the next call. This module takes one draw's\n * inputs and returns one draw's outputs; nothing is kept between calls.\n *\n * Verified against R 4.5.3 in `sampling.test.ts` for the interval arithmetic.\n * The drawn values cannot be checked against R and are not meant to be: a\n * seeded JavaScript generator does not reproduce R's Mersenne Twister.\n */\n\nimport { mean, requireCount, sd } from \"./arith\";\nimport { rnorm, sampleWithoutReplacement, type Rng } from \"./rng\";\n\n/** R's hardcoded 95% multiplier in `plot_sample_ci()`. Not `qnorm(0.975)`. */\nconst CI95_MULTIPLIER = 1.96;\n\n/** R's hardcoded 99% multiplier. Not `qnorm(0.995)`, which is 2.5758. */\nconst CI99_MULTIPLIER = 2.58;\n\n/** The defaults of R's `plot_sample_ci()`. */\nconst DEFAULT_POP_SIZE = 10000;\nconst DEFAULT_NUM_SAMPLES = 100;\nconst DEFAULT_SAMPLE_SIZE = 100;\n\n/** What one call to `drawSamples` should do. */\nexport interface DrawSamplesOptions {\n /** How many values to take per sample. R's `sample_size`. */\n readonly sampleSize: number;\n /** How many samples to draw in this call. R's `reps`, default 1. */\n readonly reps?: number;\n /**\n * The statistic to compute from each sample. R's `theta`, default the mean.\n * Any function of a sample will do — the median, a trimmed mean, a range.\n */\n readonly theta?: (sample: readonly number[]) => number;\n}\n\n/** One draw: the samples themselves and the statistic of each. */\nexport interface SampleDraw {\n /**\n * The samples, in draw order. R holds these as the columns of a matrix, so\n * flattening this array gives the same order R's `as.vector()` does — which\n * is what to hand `kernelDensity` when pooling them.\n */\n readonly samples: readonly (readonly number[])[];\n /** The statistic of each sample, in the same order. */\n readonly thetas: readonly number[];\n}\n\n/** A pair of bounds. */\nexport interface Interval {\n readonly low: number;\n readonly high: number;\n}\n\n/** What one sample says about the mean it was drawn from. */\nexport interface SampleInterval {\n /** The sample mean. */\n readonly mean: number;\n /** The sample standard deviation, with R's n − 1 denominator. */\n readonly sd: number;\n /** The standard error, `sd / sqrt(sampleSize)`. */\n readonly standardError: number;\n /** The 95% interval, `mean ± 1.96 * standardError`. */\n readonly ci95: Interval;\n /** The 99% interval, `mean ± 2.58 * standardError`. */\n readonly ci99: Interval;\n /**\n * Whether either interval misses the population mean — R's `bad` set, one\n * sample at a time. R collects the positions where this holds.\n */\n readonly excludesPopulationMean: boolean;\n}\n\n/** How to simulate a population. R's `distr_func` with its `...` bound in. */\nexport type DistributionFn = (rng: Rng, n: number) => number[];\n\n/** What one call to `simulateSampleCi` should do. R's own defaults. */\nexport interface SampleCiOptions {\n /** How many samples to draw. R's `num_samples`, default 100. */\n readonly numSamples?: number;\n /** How many values per sample. R's `sample_size`, default 100. */\n readonly sampleSize?: number;\n /** How many values in the simulated population. R's `pop_size`, 10000. */\n readonly popSize?: number;\n /**\n * How to draw the population. R's `distr_func`, default `rnorm` with its\n * own defaults, a standard normal. A caller who wants R's\n * `plot_sample_ci(distr_func = runif, min = 17, max = 35)` passes\n * `(rng, n) => runif(rng, n, { min: 17, max: 35 })`, which is what R's\n * `...` pass-through amounts to.\n */\n readonly distribution?: DistributionFn;\n}\n\n/** The simulated population's statistics and every sample's interval. */\nexport interface SampleCiSimulation {\n /** The mean of the drawn population. R draws its vertical line here. */\n readonly populationMean: number;\n /** The spread of the drawn population. R's window is this wide, halved. */\n readonly populationSd: number;\n /** One entry per sample, in draw order. */\n readonly intervals: readonly SampleInterval[];\n}\n\n/**\n * Draw samples from a population and compute a statistic from each.\n *\n * This is R's `replicate(reps, sample(population, sample_size))` followed by\n * `apply(samples, FUN = theta, MARGIN = 2)`. Sampling is without replacement,\n * as R's `sample()` is by default, so a value appears at most once within a\n * sample — though the same value can appear in several samples.\n *\n * @param rng The source of randomness. The call takes exactly\n * `reps * sampleSize` values from it.\n * @param population The values to draw from. The function does not modify\n * them.\n * @param options The sample size, how many samples, and the statistic.\n * @returns The samples in draw order and the statistic of each.\n * @throws RangeError If `reps` or `sampleSize` is negative or fractional, or\n * if `sampleSize` is larger than the population — the last from\n * `sampleWithoutReplacement`, which is where R's own refusal lives.\n */\nexport function drawSamples(\n rng: Rng,\n population: readonly number[],\n options: DrawSamplesOptions,\n): SampleDraw {\n const { sampleSize, reps = 1, theta = mean } = options;\n requireCount(reps, \"reps\");\n\n // Array.from calls its builder once per index, in order, so the samples\n // come off the one generator in draw order.\n const samples = Array.from({ length: reps }, () =>\n sampleWithoutReplacement(rng, population, sampleSize),\n );\n\n return { samples, thetas: samples.map((sample) => theta(sample)) };\n}\n\n/**\n * Read a confidence interval off each sample.\n *\n * The multipliers are R's own literal 1.96 and 2.58, not quantiles of any\n * distribution. That matters: 2.58 is not `qnorm(0.995)` to more than three\n * digits, and a port that \"corrected\" it would draw slightly different bars\n * from the R original the demonstration is taught beside.\n *\n * A sample of one gives NaN throughout, because its standard deviation is\n * undefined — R reports NA there and drops it from the `bad` set, and NaN\n * comparisons being false does the same thing here.\n *\n * @param samples The samples, each already drawn.\n * @param populationMean The mean the samples were drawn from, which the\n * intervals are asked to cover.\n * @returns One entry per sample, in the order given.\n */\nexport function sampleConfidenceIntervals(\n samples: readonly (readonly number[])[],\n populationMean: number,\n): SampleInterval[] {\n return samples.map((sample) => {\n const center = mean(sample);\n const spread = sd(sample);\n const standardError = spread / Math.sqrt(sample.length);\n const ci95 = spreadAround(center, standardError, CI95_MULTIPLIER);\n const ci99 = spreadAround(center, standardError, CI99_MULTIPLIER);\n\n return {\n mean: center,\n sd: spread,\n standardError,\n ci95,\n ci99,\n // R's four-way test, kept whole. The 99% interval contains the 95% one,\n // so only the 95% pair can fire, but this is what R asks and reading it\n // back to R's source should not need an argument about which half is\n // redundant.\n excludesPopulationMean:\n ci95.low > populationMean ||\n ci95.high < populationMean ||\n ci99.low > populationMean ||\n ci99.high < populationMean,\n };\n });\n}\n\n/** Build an interval of so many standard errors around a center. */\nfunction spreadAround(\n center: number,\n standardError: number,\n multiplier: number,\n): Interval {\n return {\n low: center - standardError * multiplier,\n high: center + standardError * multiplier,\n };\n}\n\n/**\n * Simulate a population, sample it many times, and interval each sample.\n *\n * This is the whole of `plot_sample_ci()` except the drawing. The population\n * is simulated rather than given, which is the point of the demonstration:\n * the true mean is known, so a student can count how many intervals miss it.\n *\n * The call takes the population's draws from the generator first, then each\n * sample's, so a caller replaying one seed gets one simulation.\n *\n * @param rng The source of randomness, threaded through the whole call.\n * @param options The sizes and the population's distribution.\n * @returns The population's mean and spread, and every sample's interval.\n * @throws RangeError If a count is negative or fractional, or if the sample\n * size is larger than the population.\n */\nexport function simulateSampleCi(\n rng: Rng,\n options: SampleCiOptions = {},\n): SampleCiSimulation {\n const {\n numSamples = DEFAULT_NUM_SAMPLES,\n sampleSize = DEFAULT_SAMPLE_SIZE,\n popSize = DEFAULT_POP_SIZE,\n distribution = standardNormal,\n } = options;\n requireCount(numSamples, \"numSamples\");\n requireCount(popSize, \"popSize\");\n\n const population = distribution(rng, popSize);\n const populationMean = mean(population);\n const { samples } = drawSamples(rng, population, {\n sampleSize,\n reps: numSamples,\n });\n\n return {\n populationMean,\n populationSd: sd(population),\n intervals: sampleConfidenceIntervals(samples, populationMean),\n };\n}\n\n/** R's default `distr_func`, `rnorm` with its own defaults. */\nfunction standardNormal(rng: Rng, n: number): number[] {\n return rnorm(rng, n);\n}\n",
|
|
12
|
+
"/**\n * Column-keyed data frames, and the checks R gets for free.\n *\n * The 3D functions of the R package take an R data frame, which guarantees\n * two things a JavaScript object does not: every column holds one type, and\n * every column has the same length. `moderation.ts` and the scatter3d module\n * both need those guarantees, so the questions are asked in one place and\n * answered the same way for both.\n *\n * The port follows `../compstatslib/R/scatter3d_helpers.R`:\n *\n * ```r\n * scatter3d_numeric_cols <- function(data) {\n * names(data)[vapply(data, is.numeric, logical(1))]\n * }\n * ```\n *\n * with two departures, both forced by the language and both pinned by tests.\n * R reads a vector's declared type, so `is.numeric(numeric(0))` is TRUE and a\n * column of mixed types cannot exist. A JavaScript array declares nothing, so\n * this module reads the values: **a column is numeric when it holds at least\n * one value and every value is a number.** An empty column therefore is not\n * numeric — it offers no evidence either way, and an empty axis draws nothing\n * — and a column of numbers with one string in it is not numeric either,\n * because fitting it would give `NaN` for every coefficient.\n */\n\n/**\n * One column of a data frame.\n *\n * Numeric columns carry the statistics. The other two types are here because\n * `plot_scatter3d()` accepts a categorical column for `color`, which R allows\n * to be a factor, a character vector, or a logical vector.\n */\nexport type Column = readonly number[] | readonly string[] | readonly boolean[];\n\n/**\n * A data frame: named columns of equal length.\n *\n * The equal length is a rule, not a type. `frameRows` enforces it.\n */\nexport type DataFrame = { readonly [name: string]: Column };\n\n/**\n * Report whether a column holds numbers, and narrow it when it does.\n *\n * @param column The column to inspect.\n * @returns True when the column has at least one value and every value is a\n * number. `NaN` counts as a number, as R's missing values do.\n */\nexport function isNumericColumn(column: Column): column is readonly number[] {\n return column.length > 0 && column.every((value) => typeof value === \"number\");\n}\n\n/**\n * Name the numeric columns, in the order the frame declares them.\n *\n * This is R's `scatter3d_numeric_cols()`. The order matters: the scatter3d\n * default takes the first three names this returns.\n *\n * @param data The frame to inspect.\n * @returns The names of the numeric columns, in insertion order.\n */\nexport function numericColumns(data: DataFrame): string[] {\n return Object.keys(data).filter((name) =>\n isNumericColumn(data[name] as Column),\n );\n}\n\n/**\n * Refuse a frame that cannot fill three numeric axes.\n *\n * This is R's `scatter3d_require_3_numeric()`, which both the plot and the\n * gadget call with their own name, so that the message says which function\n * the caller reached.\n *\n * @param numeric The numeric column names, from `numericColumns`.\n * @param caller The name to print, such as `plotScatter3d`.\n * @throws RangeError If fewer than three names were given.\n */\nexport function requireThreeNumericColumns(\n numeric: readonly string[],\n caller: string,\n): void {\n if (numeric.length < 3) {\n throw new RangeError(\n `${caller}() needs at least 3 numeric columns; got ${numeric.length}. ` +\n \"Supply x/y/z explicitly or add numeric columns.\",\n );\n }\n}\n\n/**\n * Return the number of rows, and refuse a frame that has no single answer.\n *\n * @param data The frame to measure.\n * @returns The shared length of the columns. A frame with no columns has no\n * rows.\n * @throws RangeError If two columns have different lengths. An R data frame\n * cannot be built that way, so nothing downstream is written to survive it.\n */\nexport function frameRows(data: DataFrame): number {\n const names = Object.keys(data);\n const first = names[0];\n if (first === undefined) {\n return 0;\n }\n\n const rows = (data[first] as Column).length;\n const ragged = names.find((name) => (data[name] as Column).length !== rows);\n if (ragged !== undefined) {\n throw new RangeError(\n `every column needs the same number of rows: \"${first}\" has ${rows} ` +\n `but \"${ragged}\" has ${(data[ragged] as Column).length}`,\n );\n }\n\n return rows;\n}\n\n/**\n * Read one numeric column, or explain why it cannot be used.\n *\n * The wording follows R's own, which names both the column and the argument\n * it arrived through: `Column \"b\" (passed as \\`x\\`) is not in \\`data\\`.`\n *\n * @param data The frame to read.\n * @param name The column name the caller asked for.\n * @param role The option that carried the name, such as `iv` or `mod`. It\n * appears in the error, so the caller learns which argument is wrong.\n * @returns The column.\n * @throws RangeError If the frame has no such column, or the column is not\n * numeric.\n */\nexport function requireNumericColumn(\n data: DataFrame,\n name: string,\n role: string,\n): readonly number[] {\n const column = data[name];\n if (column === undefined) {\n throw new RangeError(\n `Column \"${name}\" (passed as \\`${role}\\`) is not in the data.`,\n );\n }\n if (!isNumericColumn(column)) {\n throw new RangeError(\n `Column \"${name}\" (passed as \\`${role}\\`) is not numeric; ` +\n \"only numeric columns can carry the statistics.\",\n );\n }\n\n return column;\n}\n",
|
|
13
|
+
"/**\n * Dense (weighted) least squares for small designs.\n *\n * This is the equivalent of R's `lm.wfit()`, and of the solver R runs inside\n * every step of `glm.fit()`'s IRLS loop. R factors the design with `dqrdc2`,\n * a Householder QR with a limited column-pivoting rule: a column whose norm\n * has collapsed against the columns to its left is moved to the right edge\n * and its coefficient is reported as `NA`. The port reproduces that rule,\n * because it is what makes `lm()` and `glm()` report an aliased coefficient\n * instead of dividing by a near-zero pivot. Verified against R in\n * `ols.test.ts`.\n *\n * Designs here are tiny — two columns for logit, four for a moderation\n * surface — so the code follows the LINPACK routine plainly rather than\n * blocking or vectorizing it.\n */\n\nimport { sum, zipWith } from \"./arith\";\n\n/**\n * The result of a fit.\n *\n * A `null` coefficient is the equivalent of R's `NA`: the column carried no\n * information beyond the columns to its left, so R aliases it. `linearRegression`\n * uses the same convention.\n */\nexport interface LeastSquaresFit {\n /**\n * One coefficient per design column, in column order. An aliased column\n * reports null.\n */\n readonly coefficients: readonly (number | null)[];\n /** The fitted response of each row, in input order. */\n readonly fitted: readonly number[];\n /** Response minus fit, of each row, in input order. */\n readonly residuals: readonly number[];\n /** The number of columns the fit could identify. */\n readonly rank: number;\n}\n\nexport interface LeastSquaresOptions {\n /**\n * One weight per row, on R's `lm.wfit()` scale: the solver applies the\n * square root itself.\n *\n * IRLS is the trap here. `glm.fit` carries square-root weights `w` and\n * hands `x * w` to its QR routine, so a caller porting that loop passes\n * `w * w` here, not `w`.\n *\n * A weight of zero drops the row from the fit. The row still gets a fitted\n * value, predicted from the other rows, as it does in R.\n */\n readonly weights?: readonly number[];\n /**\n * How far a column's norm may collapse before the fit aliases it.\n *\n * A column is aliased when its norm, after the columns to its left are\n * projected out, falls below this fraction of its original norm. The\n * default is the value `lm.fit()` uses. `glm.fit()` passes\n * `min(1e-7, epsilon / 1000)`, which is `1e-11` at R's default epsilon.\n */\n readonly tolerance?: number;\n}\n\n/** The rank tolerance of R's `lm.fit()`. */\nexport const DEFAULT_LEAST_SQUARES_TOLERANCE = 1e-7;\n\n/**\n * Fit `y` on the columns of `design` by least squares.\n *\n * @param design One row per observation, each row one value per column. A\n * model with an intercept carries a leading column of ones. The function\n * does not modify it.\n * @param y The response, one value per row.\n * @param options Weights and the rank tolerance.\n * @returns The coefficients, the fit, and the rank.\n * @throws RangeError if there are no rows, if the shapes disagree, or if a\n * weight is negative. R refuses the same inputs: \"0 (non-NA) cases\" and\n * \"missing or negative weights not allowed\".\n */\nexport function leastSquares(\n design: readonly (readonly number[])[],\n y: readonly number[],\n options: LeastSquaresOptions = {},\n): LeastSquaresFit {\n const { weights, tolerance = DEFAULT_LEAST_SQUARES_TOLERANCE } = options;\n const rows = design.length;\n\n if (rows === 0) {\n throw new RangeError(\"least squares needs at least one row\");\n }\n const width = design[0].length;\n if (design.some((row) => row.length !== width)) {\n throw new RangeError(\"every design row needs the same number of columns\");\n }\n if (y.length !== rows) {\n throw new RangeError(\n `the response has ${y.length} values but the design has ${rows} rows`,\n );\n }\n if (weights !== undefined) {\n if (weights.length !== rows) {\n throw new RangeError(\n `there are ${weights.length} weights but ${rows} rows`,\n );\n }\n if (weights.some((weight) => !(weight >= 0))) {\n throw new RangeError(\"weights cannot be negative or missing\");\n }\n }\n\n // Scale each row by the square root of its weight, which turns the weighted\n // problem into an ordinary one. This is what lm.wfit and glm.fit both do.\n const scale = weights?.map((weight) => Math.sqrt(weight));\n const scaled = (value: number, row: number): number =>\n scale === undefined ? value : value * scale[row];\n\n const columns = Array.from({ length: width }, (_, column) =>\n design.map((row, index) => scaled(row[column], index)),\n );\n const projected = y.map(scaled);\n\n const { householders, pivot, rank } = decompose(columns, tolerance, rows);\n applyHouseholders(columns, householders, projected, Math.min(rank, rows - 1));\n const solved = backSubstitute(columns, projected, rank);\n\n // The solve returns the coefficients in pivot order. An aliased column\n // never reaches the solve and keeps its null, the way R reports NA.\n const coefficients = new Array<number | null>(width).fill(null);\n pivot.slice(0, rank).forEach((column, position) => {\n coefficients[column] = solved[position];\n });\n\n const fitted = design.map((row) =>\n sum(\n zipWith(row, coefficients, (value, coefficient) =>\n coefficient === null ? 0 : value * coefficient,\n ),\n ),\n );\n const residuals = zipWith(y, fitted, (value, fit) => value - fit);\n\n return { coefficients, fitted, residuals, rank };\n}\n\n/**\n * Factor the columns in place, R's way.\n *\n * `dqrdc2` walks the columns left to right. A column whose remaining norm has\n * fallen below `tolerance` times its original norm moves to the right edge and\n * the columns behind it shift left, so the columns that survive keep their\n * original order — which is why R can report the coefficients of a rank-\n * deficient fit in their own slots, with `NA` in the slot of the column it\n * dropped.\n *\n * On return each column holds its part of R above the diagonal and the\n * Householder vector below it, as LINPACK stores them.\n *\n * @returns The Householder scalars, the column order, and the rank.\n */\nfunction decompose(\n columns: number[][],\n tolerance: number,\n rows: number,\n): { householders: number[]; pivot: number[]; rank: number } {\n const width = columns.length;\n const pivot = columns.map((_, column) => column);\n // R substitutes 1 for a zero norm, so that an all-zero column compares as\n // negligible rather than dividing by zero.\n const originalNorms = columns.map((column) => norm(column, 0) || 1);\n const householders = new Array<number>(width).fill(0);\n // The count of columns still in play. R keeps this as `k` and rank is\n // min(k, rows) once the walk is over.\n let live = width;\n\n // Index loops throughout: a QR factorization addresses single matrix\n // entries by position, and this one follows LINPACK's dqrdc2 step for step.\n for (let step = 0; step < Math.min(rows, width); step++) {\n while (\n step < live &&\n norm(columns[step], step) < originalNorms[step] * tolerance\n ) {\n cycleToEnd(columns, pivot, originalNorms, step);\n live -= 1;\n }\n\n // The last row leaves nothing to reflect. R skips it and keeps the entry\n // as it stands, which is how a design with more columns than rows still\n // resolves the coefficients it can.\n if (step === rows - 1) {\n continue;\n }\n householders[step] = reflect(columns, step, rows);\n }\n\n return { householders, pivot, rank: Math.min(live, rows) };\n}\n\n/**\n * Build the Householder reflector of one column and apply it to the columns\n * to its right.\n *\n * @returns The leading entry of the reflector, which R keeps in `qraux`.\n */\nfunction reflect(columns: number[][], step: number, rows: number): number {\n const column = columns[step];\n const length = norm(column, step);\n if (length === 0) {\n return 0;\n }\n // Reflect away from the leading entry, so that nothing cancels.\n const pivotNorm = column[step] < 0 ? -length : length;\n\n for (let row = step; row < rows; row++) {\n column[row] = column[row] / pivotNorm;\n }\n const leading = 1 + column[step];\n column[step] = leading;\n\n for (let index = step + 1; index < columns.length; index++) {\n const other = columns[index];\n let inner = 0;\n for (let row = step; row < rows; row++) {\n inner += column[row] * other[row];\n }\n const factor = -inner / leading;\n for (let row = step; row < rows; row++) {\n other[row] = other[row] + factor * column[row];\n }\n }\n\n column[step] = -pivotNorm;\n return leading;\n}\n\n/** Apply the stored reflectors to the response, giving Qᵀy. */\nfunction applyHouseholders(\n columns: readonly number[][],\n householders: readonly number[],\n response: number[],\n count: number,\n): void {\n const rows = response.length;\n\n for (let step = 0; step < count; step++) {\n const leading = householders[step];\n if (leading === 0) {\n continue;\n }\n const column = columns[step];\n // The reflector's leading entry lives outside the column, so the two\n // passes below read it separately from the entries under the diagonal.\n let inner = leading * response[step];\n for (let row = step + 1; row < rows; row++) {\n inner += column[row] * response[row];\n }\n const factor = -inner / leading;\n response[step] = response[step] + factor * leading;\n for (let row = step + 1; row < rows; row++) {\n response[row] = response[row] + factor * column[row];\n }\n }\n}\n\n/** Solve the leading `rank` columns of the triangular system. */\nfunction backSubstitute(\n columns: readonly number[][],\n response: readonly number[],\n rank: number,\n): number[] {\n const solved = new Array<number>(rank).fill(0);\n\n for (let row = rank - 1; row >= 0; row--) {\n let value = response[row];\n for (let column = row + 1; column < rank; column++) {\n value -= columns[column][row] * solved[column];\n }\n solved[row] = value / columns[row][row];\n }\n\n return solved;\n}\n\n/** Move one column to the right edge, sliding the rest left. */\nfunction cycleToEnd(\n columns: number[][],\n pivot: number[],\n originalNorms: number[],\n step: number,\n): void {\n moveToEnd(columns, step);\n moveToEnd(pivot, step);\n moveToEnd(originalNorms, step);\n}\n\n/** Move one entry of an array to its end, sliding the rest left. */\nfunction moveToEnd<T>(track: T[], from: number): void {\n const [moved] = track.splice(from, 1) as [T];\n track.push(moved);\n}\n\n/** The Euclidean length of a column from `from` down. */\nfunction norm(column: readonly number[], from: number): number {\n return Math.hypot(...column.slice(from));\n}\n",
|
|
14
|
+
"/**\n * The fitted surface of a moderated regression.\n *\n * This is the statistics half of `plot_moderation_3d()` in the R package,\n * which fits `lm(formula, data)`, predicts over a 15 by 15 grid of the IV and\n * the moderator, and hands the grid to `lattice::wireframe()`. Verified\n * against R in `moderation.test.ts`.\n *\n * Four things are worth knowing before reading the code.\n *\n * **Columns, not a formula.** R names the model with `y ~ x * z`. TypeScript\n * has no such notation, so the model arrives as column names, per CLAUDE.md:\n * an outcome, an IV, a moderator, an optional list of controls, and a flag\n * for the interaction. The three models the fixtures pin are\n * `{outcome: \"y\", iv: \"x\", mod: \"z\"}` (R's `y ~ x * z`), the same with\n * `interaction: false` (`y ~ x + z`), and the same with `controls: [\"w\"]`\n * (`y ~ x + z + w + x:z`).\n *\n * **The design follows R's model matrix, not the option order.** R's\n * `model.matrix` puts every main effect before any interaction, whatever\n * order the formula was typed in, so `y ~ x + z + w + x:z` gives the columns\n * `(Intercept), x, z, w, x:z`. This module builds them in that order, which\n * is what lets a caller read the coefficients next to R's.\n *\n * **Controls are held at their mean, and only numbers are accepted.** R's\n * `hold_value()` also handles factors (first level), characters (first in\n * sort order) and logicals (always FALSE). The bundled data has no such\n * column and no doc example uses one, so this port supports the numeric\n * branch alone and refuses the rest, rather than shipping three rules nothing\n * exercises. The other rules are recorded in\n * `.claude/plans/moderation-fixtures.md` section 4 if they are ever wanted.\n *\n * **Missing values leave the fit, as R's do.** `lm()` drops incomplete rows\n * through `na.omit` and `hold_value()` averages with `na.rm = TRUE`; this\n * port does the same, with NaN standing in for `NA` and any non-finite value\n * counting as missing. The details — the `na.exclude`-style NaN padding of\n * `fitted` and `residuals`, the one departure on `zlim` — are on\n * `moderationSurface` itself.\n */\n\nimport { extent, mean, sum, zipWith } from \"./arith\";\nimport { frameRows, requireNumericColumn, type DataFrame } from \"./frame\";\nimport { leastSquares } from \"./ols\";\n\n/**\n * The number of steps along each axis of the grid. R's\n * `plot_moderation_3d()` hardcodes `length.out = 15` for both.\n */\nconst GRID_STEPS = 15;\n\n/** Which columns make the model. */\nexport interface ModerationOptions {\n /** The column to predict — the vertical axis of the surface. */\n readonly outcome: string;\n /** The predictor on the first horizontal axis. */\n readonly iv: string;\n /** The predictor on the second horizontal axis. */\n readonly mod: string;\n /**\n * Whether the model carries the IV by moderator product. True by default,\n * which is R's `y ~ x * z`. False gives R's additive `y ~ x + z`, whose\n * surface is a plane with no twist.\n */\n readonly interaction?: boolean;\n /**\n * Further predictors to fit but not to plot. Each is held at its own mean\n * over the grid, R's `hold_value()` for a numeric column. Empty by default.\n */\n readonly controls?: readonly string[];\n}\n\n/** One fitted term, named as R names it. */\nexport interface ModerationTerm {\n /**\n * R's coefficient name: `(Intercept)`, a column name, or `iv:mod` for the\n * product term.\n */\n readonly name: string;\n /**\n * The coefficient, or null where R reports `NA` — a column the fit could\n * not tell apart from the columns before it.\n */\n readonly value: number | null;\n}\n\n/** A fitted model and the surface it predicts. */\nexport interface ModerationSurface {\n /** The fitted terms, in R's model-matrix order. */\n readonly coefficients: readonly ModerationTerm[];\n /**\n * The fitted outcome of each data row, in input order. NaN where the row\n * was dropped for a missing value, as R's `na.exclude` pads.\n */\n readonly fitted: readonly number[];\n /**\n * The outcome minus the fit, of each data row, in input order. NaN where\n * the row was dropped for a missing value.\n */\n readonly residuals: readonly number[];\n /** The 15 IV values of the grid, from the column's minimum to its maximum. */\n readonly ivValues: readonly number[];\n /** The 15 moderator values of the grid, over the same span. */\n readonly modValues: readonly number[];\n /**\n * The 225 predicted outcomes, **with the IV varying fastest**: index\n * `j * 15 + i` holds the prediction at `ivValues[i]` and `modValues[j]`.\n * This is the row order of R's `expand.grid(seq_iv, seq_mod)`.\n */\n readonly predictions: readonly number[];\n /**\n * The vertical range to draw, as `[low, high]`: the range of the observed\n * outcome together with the range of the surface. Which of the two reaches\n * further depends on the model — an interaction usually swings the surface\n * past the data at the corners of the grid, while a plane stays inside it.\n */\n readonly zlim: readonly [number, number];\n /** The value each control is held at over the grid: its mean. */\n readonly holds: Readonly<Record<string, number>>;\n}\n\n/**\n * Fit the model and predict its surface.\n *\n * Rows with a missing (non-finite) value in any model column are dropped\n * before fitting, R's `na.action = na.omit`; their fitted values and\n * residuals report NaN, keeping input order. The grid and zlim span the\n * finite values of their columns, and a control is held at its finite mean —\n * R's `hold_value()` with `na.rm = TRUE`. (R itself computes zlim with no\n * `na.rm` and fails on a missing outcome; the port draws what it can fit, a\n * stated departure.)\n *\n * @param data The frame holding every column the options name.\n * @param options Which column plays which part in the model.\n * @returns The fit, the grid, the surface, and the vertical range.\n * @throws RangeError If a named column is absent, empty, or not numeric, if\n * the IV and the moderator are the same column, if a control repeats\n * another named column, if the frame is ragged, or if no row is complete.\n */\nexport function moderationSurface(\n data: DataFrame,\n options: ModerationOptions,\n): ModerationSurface {\n const { outcome, iv, mod, interaction = true, controls = [] } = options;\n\n if (iv === mod) {\n throw new RangeError(\n `\\`iv\\` and \\`mod\\` must name different columns, both name \"${iv}\"`,\n );\n }\n // A repeated column would enter the design twice and alias itself, and the\n // caller would read a null coefficient with no clue why.\n const named = new Set([outcome, iv, mod]);\n controls.forEach((control) => {\n if (named.has(control)) {\n throw new RangeError(\n `control \"${control}\" already names the outcome, the iv, the mod, ` +\n \"or another control\",\n );\n }\n named.add(control);\n });\n\n const rows = frameRows(data);\n const y = requireNumericColumn(data, outcome, \"outcome\");\n const ivColumn = requireNumericColumn(data, iv, \"iv\");\n const modColumn = requireNumericColumn(data, mod, \"mod\");\n const controlColumns = controls.map((control) =>\n requireNumericColumn(data, control, \"controls\"),\n );\n\n // R's na.omit: a row with a missing value in any model column leaves the\n // fit. NaN is this library's missing value, and an infinity would poison\n // the fit the same way, so \"complete\" means finite everywhere.\n const modelColumns = [y, ivColumn, modColumn, ...controlColumns];\n const completeRows = y\n .map((_, row) => row)\n .filter((row) =>\n modelColumns.every((column) => Number.isFinite(column[row])),\n );\n if (completeRows.length === 0) {\n throw new RangeError(\n \"the model has no complete rows: every row is missing a value in \" +\n \"the outcome, the IV, the moderator, or a control\",\n );\n }\n\n // R's model.matrix order: the intercept, the main effects in the order the\n // model names them, then the interaction.\n const designColumns: readonly {\n readonly name: string;\n readonly values: readonly number[];\n }[] = [\n { name: \"(Intercept)\", values: new Array<number>(rows).fill(1) },\n { name: iv, values: ivColumn },\n { name: mod, values: modColumn },\n ...controls.map((control, index) => ({\n name: control,\n values: controlColumns[index] as readonly number[],\n })),\n ...(interaction\n ? [\n {\n name: `${iv}:${mod}`,\n values: zipWith(ivColumn, modColumn, (a, b) => a * b),\n },\n ]\n : []),\n ];\n\n const design = completeRows.map((row) =>\n designColumns.map((column) => column.values[row] as number),\n );\n const fit = leastSquares(\n design,\n completeRows.map((row) => y[row] as number),\n );\n const coefficients = designColumns.map((column, index) => ({\n name: column.name,\n value: fit.coefficients[index] ?? null,\n }));\n\n // R's na.exclude padding: report the fit in input order, NaN where a row\n // was dropped.\n const fitted = new Array<number>(rows).fill(Number.NaN);\n const residuals = new Array<number>(rows).fill(Number.NaN);\n completeRows.forEach((row, survivor) => {\n fitted[row] = fit.fitted[survivor] as number;\n residuals[row] = fit.residuals[survivor] as number;\n });\n\n // extent() ignores non-finite values, so each axis spans its column's\n // finite range — R's seq over min and max, which R only reaches when the\n // column has no NA. The hold is R's hold_value(): mean with na.rm = TRUE.\n const ivValues = rSeq(...extent(ivColumn), GRID_STEPS);\n const modValues = rSeq(...extent(modColumn), GRID_STEPS);\n const holds = Object.fromEntries(\n controls.map((control, index) => [\n control,\n mean(\n (controlColumns[index] as readonly number[]).filter(Number.isFinite),\n ),\n ]),\n );\n\n // R's predict() drops an aliased term rather than giving up on the row, so\n // a null coefficient contributes nothing here either.\n const weights = coefficients.map((term) => term.value ?? 0);\n const predictions = modValues.flatMap((modValue) =>\n ivValues.map((ivValue) => {\n const gridRow = [\n 1,\n ivValue,\n modValue,\n ...controls.map((control) => holds[control] as number),\n ...(interaction ? [ivValue * modValue] : []),\n ];\n return sum(zipWith(gridRow, weights, (value, weight) => value * weight));\n }),\n );\n\n const [dataLow, dataHigh] = extent(y);\n const [surfaceLow, surfaceHigh] = extent(predictions);\n\n return {\n coefficients,\n fitted,\n residuals,\n ivValues,\n modValues,\n predictions,\n zlim: [Math.min(dataLow, surfaceLow), Math.max(dataHigh, surfaceHigh)],\n holds,\n };\n}\n\n/**\n * Step from one end of a span to the other, the way R's\n * `seq(from, to, length.out = n)` does.\n *\n * R computes `from + i * by` in plain double arithmetic and writes both\n * endpoints in exactly. That last part matters — it is why the grid always\n * reaches the data's own minimum and maximum — and so does the plain\n * arithmetic: rounding the product and the sum together, as `pretty.ts` must\n * for R's other sequence path, moves 14 of the 30 grid coordinates of the\n * bundled dataset off R's values.\n *\n * @param from The first value.\n * @param to The last value.\n * @param length How many values to return.\n * @returns The sequence. A span of no width repeats its one value, as R's\n * `seq(3, 3, length.out = 15)` does.\n */\nfunction rSeq(from: number, to: number, length: number): number[] {\n if (from === to) {\n return new Array<number>(length).fill(from);\n }\n\n const by = (to - from) / (length - 1);\n return Array.from({ length }, (_, index) =>\n index === 0 ? from : index === length - 1 ? to : from + index * by,\n );\n}\n",
|
|
15
|
+
"/**\n * Ordinary least-squares regression of y on x.\n *\n * This is the statistics half of `plot_regression()` in the R package. R gets these\n * numbers from `lm()`, `cor()`, and `summary()$r.squared`. Verified against R\n * in `regression.test.ts`.\n */\n\nimport { mean, sum, zipWith } from \"./arith\";\n\n/** One observation. R holds these as rows of a data frame. */\nexport interface Point {\n readonly x: number;\n readonly y: number;\n}\n\n/**\n * The indices of the rows a fit may use: both coordinates finite.\n *\n * This is R's `na.omit` for a set of points. NaN is this library's missing\n * value, and an infinity would poison a fit the same way, so \"complete\" means\n * finite everywhere — the rule `moderationSurface` set for frames. The point\n * cores (this module, `logit.ts`, `pca.ts`) share the rule through this\n * helper.\n */\nexport function completePointRows(points: readonly Point[]): readonly number[] {\n return points.flatMap((point, row) =>\n Number.isFinite(point.x) && Number.isFinite(point.y) ? [row] : [],\n );\n}\n\n/**\n * The result of a fit.\n *\n * A `null` field is the equivalent of R's `NA`. R drops a singular predictor\n * and reports `NA` for its coefficient. This port reports `null`, which makes\n * strict TypeScript force the caller to handle the degenerate fit.\n */\nexport interface RegressionFit {\n /** The y value where the line crosses x = 0. */\n readonly intercept: number;\n /** The change in y for each unit of x. Null if x has no variation. */\n readonly slope: number | null;\n /** Pearson r. Null if x or y has no variation. */\n readonly correlation: number | null;\n /** Sum of squares regression. */\n readonly ssr: number;\n /** Sum of squares error. */\n readonly sse: number;\n /** Sum of squares total. */\n readonly sst: number;\n /** The part of SST that the fit explains. Null if SST is 0. */\n readonly rSquared: number | null;\n /**\n * The fitted y value of each point, in input order. NaN where the point\n * was dropped for a missing value.\n */\n readonly fitted: readonly number[];\n}\n\n/**\n * Fit a line to the points.\n *\n * With no variation in x, the function fits the mean of y and reports no\n * slope. R does the same: it drops the singular predictor and fits an\n * intercept-only model. A single point is that same case.\n *\n * A point with a non-finite coordinate is dropped before fitting, R's\n * `na.action = na.omit`; its fitted value reports NaN, keeping input order\n * (R's `na.exclude` padding, as `moderationSurface` does). R's `lm()` errors\n * when every row is missing (\"0 (non-NA) cases\"); this port already answers\n * null for \"nothing to fit\", and an all-missing input is that same answer.\n *\n * @param points The observations. The function does not modify them.\n * @returns The fit, or null if no point is complete.\n */\nexport function linearRegression(\n points: readonly Point[],\n): RegressionFit | null {\n const rows = completePointRows(points);\n if (rows.length === 0) {\n return null;\n }\n\n const xs = rows.map((row) => (points[row] as Point).x);\n const ys = rows.map((row) => (points[row] as Point).y);\n const meanX = mean(xs);\n const meanY = mean(ys);\n\n const devX = xs.map((x) => x - meanX);\n const devY = ys.map((y) => y - meanY);\n const sumSquaresX = sum(devX.map((d) => d * d));\n const sumSquaresY = sum(devY.map((d) => d * d));\n const sumProducts = sum(zipWith(devX, devY, (dx, dy) => dx * dy));\n\n const slope = sumSquaresX === 0 ? null : sumProducts / sumSquaresX;\n const intercept = slope === null ? meanY : meanY - slope * meanX;\n const correlation =\n sumSquaresX === 0 || sumSquaresY === 0\n ? null\n : sumProducts / Math.sqrt(sumSquaresX * sumSquaresY);\n\n const fittedComplete = xs.map((x) =>\n slope === null ? intercept : intercept + slope * x,\n );\n\n const ssr = sum(fittedComplete.map((f) => (f - meanY) * (f - meanY)));\n const sse = sum(zipWith(ys, fittedComplete, (y, f) => (y - f) * (y - f)));\n const sst = sumSquaresY;\n const rSquared = sst === 0 ? null : ssr / sst;\n\n // R's na.exclude padding: report the fit in input order, NaN where a\n // point was dropped.\n const fitted = new Array<number>(points.length).fill(Number.NaN);\n rows.forEach((row, survivor) => {\n fitted[row] = fittedComplete[survivor] as number;\n });\n\n return {\n intercept,\n slope,\n correlation,\n ssr,\n sse,\n sst,\n rSquared,\n fitted,\n };\n}\n",
|
|
16
|
+
"/**\n * The determinant and the inverse of a 2x2 matrix.\n *\n * This is the arithmetic half of `plot_matrix_inverse()` in the R package,\n * which builds `A <- matrix(c(x1, y1, x2, y2), nrow = 2)` and calls\n * `solve(A)` before it draws anything. Verified against R in\n * `matrix.test.ts`.\n *\n * Four things are worth knowing before reading the code.\n *\n * **The matrix is two columns.** R fills a matrix column by column, so\n * `(x1, y1)` is column 1 and `(x2, y2)` is column 2. The R plot draws each\n * column as an arrow from the origin, and the parallelogram they span has the\n * determinant as its area. The field names are R's argument names, so nothing\n * has to be transposed on the way in or out.\n *\n * **R's answers come from a factorization, not from the closed form.** Both\n * `det()` and `solve()` factor the matrix first (LAPACK `dgetrf`, partial\n * pivoting), and `det()` then exponentiates a sum of logarithms. The closed\n * forms `x1*y2 - x2*y1` and `[[y2, -x2], [-y1, x1]]/det` give different last\n * bits: for the default matrix of the interactive gadget the closed form gives\n * exactly -3 where R gives -2.9999999999999996. This module follows the\n * factorization, so its numbers are R's.\n *\n * **Two operations there are not the obvious ones.** The factorization scales\n * the column below the pivot by `1 / pivot` instead of dividing by the pivot,\n * and it rounds the rank-one update once (see `fusedMultiplyAdd`). Both\n * change what a near-singular matrix reports: dividing turns R's\n * \"computationally singular\" into \"exactly singular\" for four equal entries at\n * `1e-5`, and rounding twice moves a cancelled determinant by a factor of two.\n *\n * **A singular matrix is data, not an error.** R stops with an error, which\n * ends the R function before it draws. A component that redraws while a slider\n * moves cannot throw, so this module reports the singularity in its result and\n * lets the caller decide. The two kinds R distinguishes are kept apart,\n * because they describe different things: an exactly zero pivot, and a matrix\n * that is invertible on paper but too ill-conditioned to invert in doubles.\n */\n\nimport { fusedMultiplyAdd, sum, withoutNegativeZero } from \"./arith\";\n\n/**\n * The smallest normal double, LAPACK's `dlamch(\"S\")`.\n *\n * Below it, `dgetf2` divides the column by the pivot instead of multiplying by\n * the reciprocal, because the reciprocal would overflow.\n */\nconst SMALLEST_NORMAL = 2.2250738585072014e-308;\n\n/**\n * A 2x2 matrix, held as R's `plot_matrix_inverse` arguments.\n *\n * `(x1, y1)` is the first column and `(x2, y2)` is the second, which is how\n * `matrix(c(x1, y1, x2, y2), nrow = 2)` fills it. Written as a table of rows,\n * the matrix is `[[x1, x2], [y1, y2]]`.\n */\nexport interface Matrix2 {\n /** Row 1 of column 1. R's `A[1,1]`. */\n readonly x1: number;\n /** Row 2 of column 1. R's `A[2,1]`. */\n readonly y1: number;\n /** Row 1 of column 2. R's `A[1,2]`. */\n readonly x2: number;\n /** Row 2 of column 2. R's `A[2,2]`. */\n readonly y2: number;\n}\n\n/**\n * Why a matrix has no inverse, in the two kinds R reports.\n *\n * `\"exact\"` is R's \"Lapack routine dgesv: system is exactly singular:\n * U[i,i] = 0\": the factorization found a pivot that is the literal value zero.\n * `\"computational\"` is R's \"system is computationally singular: reciprocal\n * condition number = ...\": the factorization completed, but the condition\n * number is below R's tolerance of one machine epsilon.\n */\nexport type Singularity = \"exact\" | \"computational\";\n\n/** What `invertMatrix` reports about one matrix. */\nexport interface MatrixInversion {\n /**\n * The determinant, as R's `det()` computes it. It is exactly zero for an\n * exactly singular matrix, and it is not a test for singularity: a matrix\n * with a determinant of `2e-16` can still be too ill-conditioned to invert,\n * and one with a determinant of `0.01` inverts without trouble.\n */\n readonly determinant: number;\n /**\n * The inverse, or null if R's `solve()` would have stopped with an error.\n * The port could report the huge and meaningless numbers that a\n * computationally singular matrix produces, and does not: a null says the\n * same thing as R's error, in a form a caller can branch on.\n */\n readonly inverse: Matrix2 | null;\n /** Which kind of singularity, or null if the matrix inverts. */\n readonly singularity: Singularity | null;\n /**\n * The reciprocal condition number in the one-norm:\n * `1 / (norm(A) * norm(inverse))`. R's bar for a usable matrix is\n * `rcond >= Number.EPSILON`, and this port keeps that bar.\n *\n * R's `solve()` reads an estimate of the same number from LAPACK's\n * `dgecon` rather than computing it. Near the bar the two agree to the last\n * bit — every fixture at the edge of singularity does — while for a well\n * conditioned matrix R's estimate can be up to about twice this value,\n * because the estimator only bounds the norm of the inverse from below. The\n * two never disagreed about a matrix over a sweep of 20000 slider settings.\n *\n * It is 0 for an exactly singular matrix. R computes no condition number\n * there — the factorization has already failed — and zero is the limit.\n */\n readonly rcond: number;\n /**\n * Which pivot was exactly zero: the `i` of R's message `U[i,i] = 0`. It is\n * 1 only when the whole first column is zero, and null unless the\n * singularity is exact.\n */\n readonly zeroPivot: 1 | 2 | null;\n}\n\n/**\n * Return the determinant, as R's `det()` reports it.\n *\n * R factors the matrix and then computes `sign * exp(sum(log(abs(pivot))))`,\n * so an integer determinant does not always come back as an integer. A\n * singular matrix gives a positive zero, whatever its rows: R leaves the sign\n * of the interchange unread on that path.\n *\n * @param matrix The matrix. The function does not modify it.\n * @returns The determinant. It is NaN if an entry is NaN, and it overflows to\n * an infinity for entries large enough, both as R's does.\n */\nexport function determinant(matrix: Matrix2): number {\n return determinantOf(factorize(matrix));\n}\n\n/**\n * Invert the matrix, and report what R's `solve()` would have done with it.\n *\n * The function does not throw. An entry of NaN spreads into every number of\n * the report, which is what R's `solve()` does with it too. An infinite entry\n * gives an infinite norm and so a condition number of zero, and the report\n * says the matrix is computationally singular. R returns an inverse of zeros\n * there instead, but only because its condition test cannot judge a matrix of\n * infinite norm: R's own `rcond()` stops with an error on the same matrix.\n *\n * @param matrix The matrix. The function does not modify it.\n * @returns The inverse and the determinant, or the kind of singularity that\n * stops the matrix from having an inverse.\n */\nexport function invertMatrix(matrix: Matrix2): MatrixInversion {\n const factorization = factorize(matrix);\n const determinant = determinantOf(factorization);\n const zeroPivot = zeroPivotOf(factorization);\n\n if (zeroPivot !== null) {\n return {\n determinant,\n inverse: null,\n singularity: \"exact\",\n rcond: 0,\n zeroPivot,\n };\n }\n\n const inverse = solveForIdentity(factorization);\n // R's `dgecon` estimates the norm of the inverse from the factorization.\n // This module has the inverse itself, so it takes the norm directly. See\n // the note on `rcond` for what that changes and what it does not.\n const rcond = 1 / (oneNorm(matrix) * oneNorm(inverse));\n\n // R's `solve()` takes its tolerance from `.Machine$double.eps`. A NaN fails\n // this test, as it fails R's, and so passes through as an inverse of NaNs.\n if (rcond < Number.EPSILON) {\n return {\n determinant,\n inverse: null,\n singularity: \"computational\",\n rcond,\n zeroPivot: null,\n };\n }\n\n return {\n determinant,\n inverse,\n singularity: null,\n rcond,\n zeroPivot: null,\n };\n}\n\n/**\n * The matrix, factored into a lower and an upper triangle with the larger\n * first-column entry as the leading pivot. LAPACK's `dgetf2`.\n */\ninterface Factorization {\n /** Whether the rows were interchanged to bring up the larger pivot. */\n readonly interchanged: boolean;\n /** The one entry of the lower triangle, below its unit diagonal. */\n readonly multiplier: number;\n /** The diagonal of the upper triangle, in order. */\n readonly pivots: readonly [number, number];\n /** The one entry of the upper triangle above its diagonal. */\n readonly upperRight: number;\n}\n\n/** Factor the matrix, as LAPACK's `dgetf2` does. */\nfunction factorize(matrix: Matrix2): Factorization {\n // Partial pivoting: the larger of the two first-column entries leads.\n const interchanged = Math.abs(matrix.y1) > Math.abs(matrix.x1);\n const leading = interchanged ? matrix.y1 : matrix.x1;\n const upperRight = interchanged ? matrix.y2 : matrix.x2;\n const below = interchanged ? matrix.x1 : matrix.y1;\n const belowRight = interchanged ? matrix.x2 : matrix.y2;\n\n // `dgetf2` scales by the reciprocal of the pivot, and divides only where\n // the reciprocal would overflow. The difference reaches the result: see the\n // note on the module.\n const multiplier =\n Math.abs(leading) >= SMALLEST_NORMAL ? below * (1 / leading) : below / leading;\n const trailing = fusedMultiplyAdd(-multiplier, upperRight, belowRight);\n\n return {\n interchanged,\n multiplier,\n pivots: [leading, trailing],\n upperRight,\n };\n}\n\n/** Return the determinant of a factored matrix, as R's `det()` does. */\nfunction determinantOf(factorization: Factorization): number {\n const { interchanged, pivots } = factorization;\n\n // R's `det_ge_real` reads neither the interchanges nor the diagonal when\n // the factorization reports a zero pivot: it sets the modulus to negative\n // infinity and keeps the sign at 1, so the determinant is a positive zero.\n if (zeroPivotOf(factorization) !== null) {\n return 0;\n }\n\n const modulus = sum(pivots.map((pivot) => Math.log(Math.abs(pivot))));\n const sign = pivots.reduce(\n (carried, pivot) => (pivot < 0 ? -carried : carried),\n interchanged ? -1 : 1,\n );\n\n return sign * Math.exp(modulus);\n}\n\n/** Return which pivot is exactly zero, in the order the factorization finds. */\nfunction zeroPivotOf(factorization: Factorization): 1 | 2 | null {\n const [leading, trailing] = factorization.pivots;\n if (leading === 0) {\n return 1;\n }\n return trailing === 0 ? 2 : null;\n}\n\n/**\n * Solve the factored matrix against the identity, as `dgetrs` does.\n *\n * The right-hand side is the identity with its rows interchanged the same way\n * the factorization interchanged them, and each of its columns then goes\n * forward through the lower triangle and back through the upper one.\n */\nfunction solveForIdentity(factorization: Factorization): Matrix2 {\n const { interchanged } = factorization;\n const [firstTop, firstBottom] = interchanged ? [0, 1] : [1, 0];\n const [secondTop, secondBottom] = interchanged ? [1, 0] : [0, 1];\n\n const [x1, y1] = solveColumn(factorization, firstTop, firstBottom);\n const [x2, y2] = solveColumn(factorization, secondTop, secondBottom);\n\n // The substitution can leave a zero entry with a sign; R's never carry one.\n return {\n x1: withoutNegativeZero(x1),\n y1: withoutNegativeZero(y1),\n x2: withoutNegativeZero(x2),\n y2: withoutNegativeZero(y2),\n };\n}\n\n/** Solve one column of the right-hand side. */\nfunction solveColumn(\n factorization: Factorization,\n top: number,\n bottom: number,\n): [number, number] {\n const { multiplier, pivots, upperRight } = factorization;\n\n const lower = fusedMultiplyAdd(-multiplier, top, bottom) / pivots[1];\n const upper = fusedMultiplyAdd(-upperRight, lower, top) / pivots[0];\n\n return [upper, lower];\n}\n\n/** Return the one-norm: the larger of the two absolute column sums. */\nfunction oneNorm(matrix: Matrix2): number {\n return Math.max(\n Math.abs(matrix.x1) + Math.abs(matrix.y1),\n Math.abs(matrix.x2) + Math.abs(matrix.y2),\n );\n}\n",
|
|
17
|
+
"/**\n * Principal components of a set of two-dimensional points.\n *\n * This is the statistics half of `plot_pca()` in the R package, which calls\n * `prcomp(mc_points, scale. = FALSE)`. Verified against R in `pca.test.ts`.\n *\n * Three things are worth knowing before reading the code.\n *\n * **The data is always centered.** R's `plot_pca()` has a `meancenter`\n * argument, but it does not decide whether the components are computed on\n * centered data: `prcomp()` centers again on its own, so `sdev`, `rotation`\n * and the scores come out identical either way (fixture F7 shows the two runs\n * bit for bit). All `meancenter` changes is where the arrows are anchored on\n * screen, which is a drawing decision. So this module has no such option, and\n * the plot layer owns the anchor.\n *\n * **The method is a closed form, not R's SVD.** R decomposes the centered\n * data matrix; this port takes the eigenvectors of the 2x2 covariance matrix,\n * which for two dimensions is a few lines of algebra with no iteration. The\n * covariance route squares the data and so gives up a few bits that an SVD\n * keeps, but at two dimensions the results agree with R far inside the\n * tolerance the tests demand.\n *\n * **Signs are this port's own.** `?prcomp` states that the signs of the\n * rotation columns are arbitrary and vary between programs and even between\n * builds of R. Rather than chase LAPACK, this module fixes its own rule: in\n * each column, the loading of larger magnitude is non-negative, and a tie\n * goes to a non-negative x. Nothing on screen changes — `plot_pca()` draws\n * each component as a two-headed arrow through the center, which is symmetric\n * under a sign flip — but a caller reading the numbers gets one answer for\n * one input instead of a platform's answer.\n */\n\nimport { mean, sum, withoutNegativeZero, zipWith } from \"./arith\";\nimport { completePointRows } from \"./regression\";\nimport type { Point } from \"./regression\";\n\n/**\n * One component's loading vector, as `[x, y]`.\n *\n * These are the two entries of one *column* of R's `rotation` matrix: R\n * prints that matrix with a row per input variable, so R's `rotation[\"x\",\n * \"PC1\"]` is `rotation[0][0]` here and `rotation[\"y\", \"PC1\"]` is\n * `rotation[0][1]`.\n */\nexport type Loadings = readonly [number, number];\n\n/** The components of a point set, in the shape of R's `prcomp` result. */\nexport interface PcaResult {\n /**\n * The standard deviation along each component, largest first. R's `sdev`,\n * computed with the n − 1 divisor.\n */\n readonly sdev: readonly [number, number];\n /**\n * The two loading vectors: `rotation[0]` is PC1, `rotation[1]` is PC2.\n * They are orthonormal, so `rotation[1]` is `rotation[0]` turned a quarter\n * turn, up to the sign rule described above.\n */\n readonly rotation: readonly [Loadings, Loadings];\n /**\n * The mean of each coordinate — the point the components pass through.\n *\n * R's own `pca$center` reports whatever `prcomp` had left to subtract,\n * which is near zero when `plot_pca()` centered the data first. This field\n * is instead the true column mean, which R prints as `mc_diff`.\n */\n readonly center: Point;\n /**\n * The points in component coordinates, in input order. R's `pca$x`, with\n * `x` holding the PC1 score and `y` the PC2 score. Both coordinates are\n * NaN where the point was dropped for a missing value.\n */\n readonly scores: readonly Point[];\n}\n\n/**\n * Compute the two principal components of the points.\n *\n * A single point is a valid input: it has no spread, so both standard\n * deviations are 0 and the components fall back to the coordinate axes,\n * which is what `prcomp` reports for the one component it returns at that\n * size. (R returns `min(n, p)` components and so gives one column there;\n * this port always returns two, matching its behavior on every other\n * degenerate input — see the note on rank below.) `plot_pca()` never gets\n * that far anyway: it draws points and no arrows below three of them.\n *\n * Rank is never reduced. R's `prcomp` drops a component only when given a\n * `tol`, which `plot_pca()` never passes, so collinear, identical and\n * constant-column inputs all still return two components. This function does\n * the same and lets a near-zero `sdev[1]` say that the second direction\n * carries no spread.\n *\n * A point with a non-finite coordinate is dropped before the components are\n * computed, R's `na.omit`; its scores report NaN in both coordinates,\n * keeping input order (the `na.exclude` padding `moderationSurface` uses).\n * R's own `prcomp()` errors on a missing value, so the R usage this mirrors\n * is `prcomp(na.omit(points))` — the fold-in keeps a spreadsheet with one\n * missing row from blanking the whole picture.\n *\n * @param points The observations. The function does not modify them.\n * @returns The components, or null if no point is complete.\n */\nexport function principalComponents(\n points: readonly Point[],\n): PcaResult | null {\n const rows = completePointRows(points);\n if (rows.length === 0) {\n return null;\n }\n const complete = rows.map((row) => points[row] as Point);\n\n const centerX = mean(complete.map((point) => point.x));\n const centerY = mean(complete.map((point) => point.y));\n const devX = complete.map((point) => point.x - centerX);\n const devY = complete.map((point) => point.y - centerY);\n\n // R's `prcomp` divides the singular values by sqrt(max(1, n - 1)), so the\n // variances carry the n - 1 divisor with the same guard at one point.\n const divisor = Math.max(1, complete.length - 1);\n const varX = sum(devX.map((d) => d * d)) / divisor;\n const varY = sum(devY.map((d) => d * d)) / divisor;\n const covariance = sum(zipWith(devX, devY, (dx, dy) => dx * dy)) / divisor;\n\n const [first, second] = componentsOf(varX, varY, covariance);\n\n // R's na.exclude padding: scores in input order, NaN where a point was\n // dropped.\n const scores = points.map(() => ({ x: Number.NaN, y: Number.NaN }));\n rows.forEach((row, survivor) => {\n const dx = devX[survivor] as number;\n const dy = devY[survivor] as number;\n scores[row] = {\n x: dx * first.loadings[0] + dy * first.loadings[1],\n y: dx * second.loadings[0] + dy * second.loadings[1],\n };\n });\n\n return {\n sdev: [sdevOf(first.variance), sdevOf(second.variance)],\n rotation: [first.loadings, second.loadings],\n center: { x: centerX, y: centerY },\n scores,\n };\n}\n\n/** One eigenpair of the covariance matrix. */\ninterface Component {\n readonly variance: number;\n readonly loadings: Loadings;\n}\n\n/**\n * Eigendecompose the symmetric 2x2 covariance matrix, larger eigenvalue\n * first.\n *\n * The eigenvalues of `[[a, b], [b, d]]` are `(a + d)/2 ± hypot((a - d)/2, b)`\n * and an eigenvector of `λ` solves `b·vy = (λ − a)·vx`.\n *\n * A zero off-diagonal is handled on its own path rather than falling out of\n * the formula. It is the case where the components are the coordinate axes\n * exactly — every point sharing one coordinate, which is a classroom input,\n * not a rarity — and the general formula would return the axes only to\n * within a rounding of `hypot`. Taking the branch keeps the answer exact:\n * an axis-aligned rotation of literal 0 and 1, and a literal zero second\n * standard deviation.\n */\nfunction componentsOf(\n varX: number,\n varY: number,\n covariance: number,\n): readonly [Component, Component] {\n if (covariance === 0) {\n const alongX: Component = { variance: varX, loadings: [1, 0] };\n const alongY: Component = { variance: varY, loadings: [0, 1] };\n return varX >= varY ? [alongX, alongY] : [alongY, alongX];\n }\n\n const middle = (varX + varY) / 2;\n const spread = Math.hypot((varX - varY) / 2, covariance);\n const larger = middle + spread;\n const smaller = middle - spread;\n\n // Both rows of (C − λI) give an eigenvector of the larger eigenvalue. They\n // agree in exact arithmetic; the longer one keeps more digits.\n const fromRowX = larger - varX;\n const fromRowY = larger - varY;\n const direction: Loadings =\n fromRowX >= fromRowY ? [covariance, fromRowX] : [fromRowY, covariance];\n const leading = signed(unit(direction));\n\n // A quarter turn of the leading direction: orthonormal by construction,\n // with no second normalization to drift from it.\n const trailing = signed([negated(leading[1]), leading[0]]);\n\n return [\n { variance: larger, loadings: leading },\n { variance: smaller, loadings: trailing },\n ];\n}\n\n/** Scale a vector to unit length. */\nfunction unit(vector: Loadings): Loadings {\n const length = Math.hypot(vector[0], vector[1]);\n return [vector[0] / length, vector[1] / length];\n}\n\n/**\n * Apply the sign rule: the loading of larger magnitude is non-negative, and a\n * tie goes to a non-negative x.\n */\nfunction signed(vector: Loadings): Loadings {\n const dominant =\n Math.abs(vector[0]) >= Math.abs(vector[1]) ? vector[0] : vector[1];\n return dominant >= 0 ? vector : [negated(vector[0]), negated(vector[1])];\n}\n\n/** Negate, mapping zero to positive zero so that no −0 reaches a caller. */\nfunction negated(value: number): number {\n return withoutNegativeZero(-value);\n}\n\n/**\n * Turn a variance into a standard deviation.\n *\n * Cancellation can push the smaller eigenvalue a hair below zero on data\n * that has no spread in that direction; the clamp reports the zero that is\n * meant rather than a NaN.\n */\nfunction sdevOf(variance: number): number {\n return Math.sqrt(Math.max(0, variance));\n}\n",
|
|
18
|
+
"/**\n * Logistic regression, the statistics half of `plot_logit()` in the R package.\n *\n * R gets these numbers from `glm(formula, family = binomial)`, which fits by\n * iteratively reweighted least squares: each pass builds a working response\n * and a set of weights from the current fit, then solves an ordinary weighted\n * least-squares problem. This module follows `stats::glm.fit` step for step,\n * down to its starting values, its convergence test, and the two clamped link\n * functions R implements in C. Verified against R in `logit.test.ts`.\n *\n * The clamps are not a detail. R's `linkinv` pins a linear predictor beyond\n * ±30 to a probability one machine epsilon away from 0 or 1, which is what\n * keeps a perfectly separated fit — the classroom case where every low x is a\n * 0 and every high x is a 1 — producing finite numbers instead of dividing by\n * zero.\n */\n\nimport { mean, sum, zipWith } from \"./arith\";\nimport { leastSquares } from \"./ols\";\nimport { completePointRows } from \"./regression\";\nimport type { Point } from \"./regression\";\n\n/** The convergence threshold and iteration cap of R's `glm.control()`. */\nexport const DEFAULT_LOGIT_EPSILON = 1e-8;\nexport const DEFAULT_LOGIT_MAX_ITERATIONS = 25;\n\nexport interface LogitOptions {\n /**\n * How small the relative change in deviance must be to stop. R's\n * `glm.control(epsilon =)`.\n */\n readonly epsilon?: number;\n /** How many IRLS passes to allow. R's `glm.control(maxit =)`. */\n readonly maxIterations?: number;\n}\n\n/**\n * The result of a fit.\n *\n * A `null` slope is the equivalent of R's `NA`: with no variation in x the\n * QR aliases the column and R reports \"1 not defined because of\n * singularities\". `linearRegression` uses the same convention.\n */\nexport interface LogitFit {\n /** The linear predictor at x = 0. */\n readonly intercept: number;\n /** The change in the log odds for each unit of x. Null if x is constant. */\n readonly slope: number | null;\n /**\n * The fitted probability of each point, in input order. NaN where the\n * point was dropped for a missing value.\n */\n readonly fitted: readonly number[];\n /**\n * The log odds of each point, in input order. NaN where the point was\n * dropped for a missing value.\n */\n readonly linearPredictors: readonly number[];\n /** Residual deviance of the fitted model. */\n readonly deviance: number;\n /** Deviance of the intercept-only model. */\n readonly nullDeviance: number;\n /** Akaike information criterion, the fit statistic `plot_logit` displays. */\n readonly aic: number;\n /** How many coefficients the fit could identify. */\n readonly rank: number;\n /** How many IRLS passes ran. */\n readonly iterations: number;\n /** Whether the deviance settled before the iteration cap. */\n readonly converged: boolean;\n /**\n * Whether any fitted probability landed within `10 * Number.EPSILON` of 0\n * or 1.\n *\n * This is the condition behind R's \"fitted probabilities numerically 0 or 1\n * occurred\" warning, which `plot_logit` suppresses. It reports separation:\n * the data admit no maximum likelihood estimate and only the iteration cap\n * stopped the coefficients from growing. A browser library cannot warn, so\n * the fit reports the condition and lets the caller decide.\n */\n readonly saturated: boolean;\n}\n\n/** Where R's C link functions stop computing and start clamping. */\nconst LINK_THRESHOLD = 30;\n/** R's warning threshold for a probability that has reached 0 or 1. */\nconst BOUND = 10 * Number.EPSILON;\n\n/**\n * Fit the log odds of y as a straight line in x.\n *\n * With no variation in x the fit reports an intercept and no slope, the way\n * R's aliasing does. A single point is that same case: R fits the intercept\n * and reports the slope as `NA`. `plot_logit` never reaches either — it draws\n * the points and returns before fitting fewer than two of them — so the guard\n * belongs to the plot layer and this function reports the honest degenerate\n * fit.\n *\n * A point with a non-finite coordinate is dropped before fitting, R's\n * `na.action = na.omit`; its fitted probability and linear predictor report\n * NaN, keeping input order (R's `na.exclude` padding, as `moderationSurface`\n * does). The 0-or-1 rule below applies to the rows that remain: R's\n * `na.omit` removes an incomplete row before `glm()` ever sees its outcome.\n *\n * @param points The observations. Each complete y must be 0 or 1. The\n * function does not modify them.\n * @param options The convergence controls.\n * @returns The fit, or null if no point is complete.\n * @throws RangeError if a complete row's y is not 0 or 1. R accepts any y in\n * [0, 1] for the binomial family; this port does not, because the points\n * come from clicks and because the AIC below assumes a 0/1 outcome.\n */\nexport function logisticRegression(\n points: readonly Point[],\n options: LogitOptions = {},\n): LogitFit | null {\n const {\n epsilon = DEFAULT_LOGIT_EPSILON,\n maxIterations = DEFAULT_LOGIT_MAX_ITERATIONS,\n } = options;\n\n const rows = completePointRows(points);\n if (rows.length === 0) {\n return null;\n }\n const complete = rows.map((row) => points[row] as Point);\n if (complete.some((point) => point.y !== 0 && point.y !== 1)) {\n throw new RangeError(\"every outcome must be 0 or 1\");\n }\n\n const outcomes = complete.map((point) => point.y);\n const design = complete.map((point) => [1, point.x]);\n\n // R's binomial starting values: (weights * y + 0.5) / (weights + 1), which\n // at unit weights puts a 0 at 0.25 and a 1 at 0.75, then takes the link.\n let fitted = outcomes.map((outcome) => (outcome + 0.5) / 2);\n let predictors = fitted.map(logit);\n let previousDeviance = totalDeviance(outcomes, fitted);\n let deviance = previousDeviance;\n\n let coefficients: readonly (number | null)[] = [Number.NaN, null];\n let rank = 0;\n let iterations = 0;\n let converged = false;\n\n for (let iteration = 1; iteration <= maxIterations; iteration++) {\n iterations = iteration;\n\n // One pass builds both IRLS quantities from the same row, so they are\n // computed together rather than in two walks over parallel arrays.\n const working = predictors.map((predictor, row) => {\n const probability = fitted[row] as number;\n const slope = linkSlope(predictor);\n return {\n weight: (slope * slope) / (probability * (1 - probability)),\n response: predictor + ((outcomes[row] as number) - probability) / slope,\n };\n });\n\n const step = leastSquares(\n design,\n working.map((row) => row.response),\n {\n weights: working.map((row) => row.weight),\n // R: min(1e-7, control$epsilon / 1000).\n tolerance: Math.min(1e-7, epsilon / 1000),\n },\n );\n\n // R abandons the fit rather than adopting coefficients it cannot use.\n if (\n step.coefficients.some(\n (coefficient) => coefficient !== null && !Number.isFinite(coefficient),\n )\n ) {\n break;\n }\n\n coefficients = step.coefficients;\n rank = step.rank;\n // An aliased coefficient counts as zero here. R's QR returns 0 for it\n // during the loop and only reports NA once the fit is over.\n predictors = design.map((row) =>\n sum(\n zipWith(row, coefficients, (value, coefficient) =>\n coefficient === null ? 0 : value * coefficient,\n ),\n ),\n );\n fitted = predictors.map(linkInverse);\n deviance = totalDeviance(outcomes, fitted);\n\n if (\n Math.abs(deviance - previousDeviance) / (0.1 + Math.abs(deviance)) <\n epsilon\n ) {\n converged = true;\n break;\n }\n previousDeviance = deviance;\n }\n\n const wholeMean = mean(outcomes);\n const nullDeviance = totalDeviance(\n outcomes,\n outcomes.map(() => wholeMean),\n );\n\n // R's na.exclude padding: report the fit in input order, NaN where a\n // point was dropped.\n const paddedFitted = new Array<number>(points.length).fill(Number.NaN);\n const paddedPredictors = new Array<number>(points.length).fill(Number.NaN);\n rows.forEach((row, survivor) => {\n paddedFitted[row] = fitted[survivor] as number;\n paddedPredictors[row] = predictors[survivor] as number;\n });\n\n return {\n // The leading column of ones always carries norm, so the QR cannot alias\n // the intercept.\n intercept: coefficients[0] ?? Number.NaN,\n slope: coefficients[1] ?? null,\n fitted: paddedFitted,\n linearPredictors: paddedPredictors,\n deviance,\n nullDeviance,\n // R evaluates the binomial log likelihood and adds 2 * rank. For a 0/1\n // outcome at unit weights the saturated log likelihood is 0, so that\n // evaluation is the deviance itself, term for term.\n aic: deviance + 2 * rank,\n rank,\n iterations,\n converged,\n saturated: fitted.some(\n (probability) => probability > 1 - BOUND || probability < BOUND,\n ),\n };\n}\n\n/**\n * The fitted probability at one x.\n *\n * `plot_logit` draws its curve from 500 of these. An aliased slope holds the\n * curve flat, which is what R's `predict()` does with an `NA` coefficient\n * dropped from the model.\n */\nexport function predictLogit(fit: LogitFit, x: number): number {\n return linkInverse(fit.intercept + (fit.slope ?? 0) * x);\n}\n\n/** The logit link: log odds of a probability. */\nfunction logit(probability: number): number {\n return Math.log(probability / (1 - probability));\n}\n\n/**\n * The inverse link, with R's clamps.\n *\n * R's `C_logit_linkinv` never lets a probability reach 0 or 1: beyond a linear\n * predictor of ±30 it substitutes odds of `eps` or `1 / eps`. That is what\n * produces the saturated 2.2204460492503126e-16 and 0.99999999999999978 of a\n * separated fit, and what keeps the deviance finite there.\n */\nfunction linkInverse(predictor: number): number {\n const odds =\n predictor < -LINK_THRESHOLD\n ? Number.EPSILON\n : predictor > LINK_THRESHOLD\n ? 1 / Number.EPSILON\n : Math.exp(predictor);\n return odds / (1 + odds);\n}\n\n/**\n * The derivative of the inverse link, with R's clamps.\n *\n * Algebraically this is the binomial variance `mu * (1 - mu)`, but R computes\n * it from the linear predictor as `exp(eta) / (1 + exp(eta))^2` and clamps it\n * to `eps` beyond ±30. The two differ in the last two digits — at\n * `eta = log(3)` R gives 0.18750000000000003 against 0.18749999999999994 —\n * and the IRLS weights carry that difference into every coefficient, so this\n * follows R rather than the tidier identity.\n */\nfunction linkSlope(predictor: number): number {\n if (predictor > LINK_THRESHOLD || predictor < -LINK_THRESHOLD) {\n return Number.EPSILON;\n }\n const odds = Math.exp(predictor);\n return odds / ((1 + odds) * (1 + odds));\n}\n\n/** R's binomial `dev.resids`, summed. */\nfunction totalDeviance(\n outcomes: readonly number[],\n probabilities: readonly number[],\n): number {\n return sum(\n zipWith(\n outcomes,\n probabilities,\n (outcome, probability) =>\n 2 *\n (surprise(outcome, probability) +\n surprise(1 - outcome, 1 - probability)),\n ),\n );\n}\n\n/** One side of a deviance residual. R writes this as `y_log_y`. */\nfunction surprise(outcome: number, probability: number): number {\n return outcome !== 0 ? outcome * Math.log(outcome / probability) : 0;\n}\n",
|
|
19
|
+
"/**\n * Special functions that the statistics in `core/` build on.\n *\n * JavaScript has no `lgamma` and no incomplete beta, so the distribution\n * functions need them here. R gets the same quantities from its own C\n * routines; this module is the port's replacement.\n *\n * Every function is pure. None of them touch the DOM or hold state.\n */\n\nimport { sum } from \"./arith\";\n\n/**\n * Lanczos parameter and coefficients, g = 607/128 with 15 terms.\n *\n * This set holds about 15 correct digits over the whole positive real line,\n * which is what the t quantiles need at 1e-12 relative tolerance.\n */\nconst LANCZOS_G = 607 / 128;\n\nconst LANCZOS_LEAD = 0.99999999999999709182;\n\nconst LANCZOS_TAIL: readonly number[] = [\n 57.156235665862923517, -59.597960355475491248, 14.136097974741747174,\n -0.49191381609762019978, 0.33994649984811888699e-4,\n 0.46523628927048575665e-4, -0.98374475304879564677e-4,\n 0.15808870322491248884e-3, -0.21026444172410488319e-3,\n 0.2174396181152126432e-3, -0.16431810653676389022e-3,\n 0.84418223983852743293e-4, -0.2619083840158140867e-4,\n 0.36899182659531622704e-5,\n];\n\nconst LOG_SQRT_TWO_PI = 0.5 * Math.log(2 * Math.PI);\n\n/**\n * The Lanczos series A(x), the slowly varying part of the approximation\n *\n * Γ(x) = √(2π) · (x + g − ½)^(x − ½) · e^−(x + g − ½) · A(x)\n */\nfunction lanczosSeries(x: number): number {\n return (\n LANCZOS_LEAD +\n sum(LANCZOS_TAIL.map((coefficient, index) => coefficient / (x + index)))\n );\n}\n\n/**\n * The natural log of the gamma function, R's `lgamma()`.\n *\n * @param x A positive number.\n * @returns log Γ(x), or NaN if x is zero or less.\n */\nexport function logGamma(x: number): number {\n if (!(x > 0)) {\n return Number.NaN;\n }\n const shifted = x + LANCZOS_G - 0.5;\n return (\n LOG_SQRT_TWO_PI +\n (x - 0.5) * Math.log(shifted) -\n shifted +\n Math.log(lanczosSeries(x))\n );\n}\n\n/**\n * The natural log of the beta function, R's `lbeta()`.\n *\n * The obvious route, `logGamma(a) + logGamma(b) - logGamma(a + b)`, subtracts\n * numbers near 600 for the degrees of freedom this package plots, and loses\n * about three digits doing so. Expanding the Lanczos form first cancels the\n * large terms by hand: the exponential parts collapse to the constant\n * −(g − ½), and the logarithmic parts become ratios that stay of order one.\n * What is left has no cancellation at all.\n *\n * Both ratios go through `log1p`. Each one sits just below 1, and taking the\n * quotient first would round away the small part that the log then reads —\n * a loss that grows with the larger argument, reaching 1e-13 by b = 2500.\n *\n * @param a A positive number.\n * @param b A positive number.\n * @returns log B(a, b), or NaN if either argument is zero or less.\n */\nexport function logBeta(a: number, b: number): number {\n if (!(a > 0) || !(b > 0)) {\n return Number.NaN;\n }\n const shiftedSum = a + b + LANCZOS_G - 0.5;\n return (\n LOG_SQRT_TWO_PI -\n (LANCZOS_G - 0.5) +\n Math.log(lanczosSeries(a)) +\n Math.log(lanczosSeries(b)) -\n Math.log(lanczosSeries(a + b)) +\n (a - 0.5) * Math.log1p(-b / shiftedSum) +\n (b - 0.5) * Math.log1p(-a / shiftedSum) -\n 0.5 * Math.log(shiftedSum)\n );\n}\n\n/** Iteration caps and guards for the continued fraction. */\nconst FRACTION_MAX_STEPS = 400;\nconst FRACTION_EPSILON = 3e-16;\nconst FRACTION_FLOOR = 1e-300;\n\n/**\n * The continued fraction of the incomplete beta function, by the modified\n * Lentz method.\n *\n * The loop is index based on purpose: it refines one running value step by\n * step and stops on a convergence test, so there is no array to map over.\n */\nfunction betaContinuedFraction(x: number, a: number, b: number): number {\n const total = a + b;\n const aPlus = a + 1;\n const aMinus = a - 1;\n\n let c = 1;\n let d = 1 - (total * x) / aPlus;\n if (Math.abs(d) < FRACTION_FLOOR) {\n d = FRACTION_FLOOR;\n }\n d = 1 / d;\n let value = d;\n\n for (let step = 1; step <= FRACTION_MAX_STEPS; step += 1) {\n const twice = 2 * step;\n\n const even = (step * (b - step) * x) / ((aMinus + twice) * (a + twice));\n d = 1 + even * d;\n if (Math.abs(d) < FRACTION_FLOOR) {\n d = FRACTION_FLOOR;\n }\n c = 1 + even / c;\n if (Math.abs(c) < FRACTION_FLOOR) {\n c = FRACTION_FLOOR;\n }\n d = 1 / d;\n value *= d * c;\n\n const odd = (-(a + step) * (total + step) * x) / ((a + twice) * (aPlus + twice));\n d = 1 + odd * d;\n if (Math.abs(d) < FRACTION_FLOOR) {\n d = FRACTION_FLOOR;\n }\n c = 1 + odd / c;\n if (Math.abs(c) < FRACTION_FLOOR) {\n c = FRACTION_FLOOR;\n }\n d = 1 / d;\n\n const delta = d * c;\n value *= delta;\n if (Math.abs(delta - 1) < FRACTION_EPSILON) {\n break;\n }\n }\n\n return value;\n}\n\n/**\n * The regularized incomplete beta function I_x(a, b), R's `pbeta()`.\n *\n * The continued fraction converges quickly only on one side of the\n * distribution, so the function evaluates the mirrored form when x sits above\n * the switch point and takes the complement.\n *\n * @param x A value between 0 and 1.\n * @param a A positive shape.\n * @param b A positive shape.\n * @returns The share of the beta density below x, or NaN for a bad shape.\n */\nexport function incompleteBeta(x: number, a: number, b: number): number {\n if (Number.isNaN(x)) {\n return Number.NaN;\n }\n if (x <= 0) {\n return 0;\n }\n if (x >= 1) {\n return 1;\n }\n return incompleteBetaSplit(x, 1 - x, a, b);\n}\n\n/**\n * The regularized incomplete beta function, told x and 1 − x separately.\n *\n * A caller that can write down both members of the pair should use this\n * instead of `incompleteBeta`. Once x is within 1e-16 of 1, the double\n * holding it has no room left for 1 − x, and rebuilding the complement by\n * subtraction throws away the very digits the answer rests on. `pt()` reads\n * both straight off t and df, so it gives up nothing.\n *\n * The two are treated as an exact pair, not as one value and a derived one:\n * each logarithm is taken from whichever member still carries its precision.\n *\n * @param x A value between 0 and 1.\n * @param complement The value of 1 − x, computed without subtracting.\n * @param a A positive shape.\n * @param b A positive shape.\n * @returns The share of the beta density below x, or NaN for a bad shape.\n */\nexport function incompleteBetaSplit(\n x: number,\n complement: number,\n a: number,\n b: number,\n): number {\n if (Number.isNaN(x) || Number.isNaN(complement) || !(a > 0) || !(b > 0)) {\n return Number.NaN;\n }\n if (x <= 0) {\n return 0;\n }\n if (complement <= 0) {\n return 1;\n }\n\n const logX = complement < 0.5 ? Math.log1p(-complement) : Math.log(x);\n const logComplement = x < 0.5 ? Math.log1p(-x) : Math.log(complement);\n const front = Math.exp(a * logX + b * logComplement - logBeta(a, b));\n\n if (x < (a + 1) / (a + b + 2)) {\n return (front * betaContinuedFraction(x, a, b)) / a;\n }\n return 1 - (front * betaContinuedFraction(complement, b, a)) / b;\n}\n\n/** Iteration cap and tolerance for the incomplete gamma routines. */\nconst GAMMA_MAX_STEPS = 1000;\nconst GAMMA_EPSILON = 3e-16;\n\n/**\n * P(a, x), the regularized lower incomplete gamma, by its power series.\n *\n * The series converges quickly while x stays below a + 1. Index loop with a\n * stated reason: it sums one term at a time and stops on a size test.\n */\nfunction lowerGammaSeries(a: number, x: number): number {\n let term = 1 / a;\n let total = term;\n for (let step = 1; step <= GAMMA_MAX_STEPS; step += 1) {\n term *= x / (a + step);\n total += term;\n if (Math.abs(term) < Math.abs(total) * GAMMA_EPSILON) {\n break;\n }\n }\n return total * Math.exp(-x + a * Math.log(x) - logGamma(a));\n}\n\n/**\n * Q(a, x), the regularized upper incomplete gamma, by the modified Lentz\n * method on its continued fraction.\n *\n * This is the branch that carries the far normal tail, where the answer runs\n * to 1e-70 and below and must stay accurate relative to itself.\n */\nfunction upperGammaFraction(a: number, x: number): number {\n let b = x + 1 - a;\n let c = 1 / FRACTION_FLOOR;\n let d = 1 / b;\n let value = d;\n\n for (let step = 1; step <= GAMMA_MAX_STEPS; step += 1) {\n const numerator = -step * (step - a);\n b += 2;\n d = numerator * d + b;\n if (Math.abs(d) < FRACTION_FLOOR) {\n d = FRACTION_FLOOR;\n }\n c = b + numerator / c;\n if (Math.abs(c) < FRACTION_FLOOR) {\n c = FRACTION_FLOOR;\n }\n d = 1 / d;\n const delta = d * c;\n value *= delta;\n if (Math.abs(delta - 1) < GAMMA_EPSILON) {\n break;\n }\n }\n\n return value * Math.exp(-x + a * Math.log(x) - logGamma(a));\n}\n\n/** Q(a, x), taking whichever of the two routes converges at this x. */\nfunction upperGamma(a: number, x: number): number {\n if (x <= 0) {\n return 1;\n }\n if (!Number.isFinite(x)) {\n return 0;\n }\n return x < a + 1 ? 1 - lowerGammaSeries(a, x) : upperGammaFraction(a, x);\n}\n\n/**\n * The standard normal distribution function, R's `pnorm()`.\n *\n * Built on the identity Φ(−z) = ½ · Q(½, z²/2), which keeps the far tail\n * accurate relative to itself rather than losing it against 1. The\n * non-central t needs Φ(−ncp) down to 1e-72 at the widest slider settings.\n *\n * @param z Where to evaluate the distribution.\n * @returns A probability between 0 and 1, or NaN for a NaN input.\n */\nexport function normalCdf(z: number): number {\n if (Number.isNaN(z)) {\n return Number.NaN;\n }\n const lower = 0.5 * upperGamma(0.5, 0.5 * z * z);\n return z > 0 ? 1 - lower : lower;\n}\n\n/** The largest double below 1. The inverse never returns 1 itself. */\nconst BELOW_ONE = 1 - Number.EPSILON / 2;\n\n/** Iteration cap for the inverse. Newton needs about 8 steps; 200 is slack. */\nconst INVERSE_MAX_STEPS = 200;\n\n/**\n * A starting point for the inverse, from Numerical Recipes.\n *\n * Both branches are approximations only. The Newton loop that follows carries\n * the value the rest of the way, so the guess needs to be in the right\n * neighborhood, not accurate.\n */\nfunction inverseGuess(p: number, a: number, b: number): number {\n if (a >= 1 && b >= 1) {\n const tail = p < 0.5 ? p : 1 - p;\n const t = Math.sqrt(-2 * Math.log(tail));\n const normal =\n (p < 0.5 ? -1 : 1) *\n ((2.30753 + t * 0.27061) / (1 + t * (0.99229 + t * 0.04481)) - t);\n const scale = (normal * normal - 3) / 6;\n const harmonic = 2 / (1 / (2 * a - 1) + 1 / (2 * b - 1));\n const w =\n (normal * Math.sqrt(scale + harmonic)) / harmonic -\n (1 / (2 * b - 1) - 1 / (2 * a - 1)) *\n (scale + 5 / 6 - 2 / (3 * harmonic));\n return a / (a + b * Math.exp(2 * w));\n }\n\n const lower = Math.exp(a * Math.log(a / (a + b))) / a;\n const upper = Math.exp(b * Math.log(b / (a + b))) / b;\n const total = lower + upper;\n if (p < lower / total) {\n return Math.pow(a * total * p, 1 / a);\n }\n return 1 - Math.pow(b * total * (1 - p), 1 / b);\n}\n\n/**\n * The inverse of the regularized incomplete beta function, R's `qbeta()`.\n *\n * Newton's method on I_x(a, b) − p, with the exact beta density as the\n * derivative. Every step keeps a bracket, and a step that leaves the bracket\n * falls back to bisection, so the loop cannot run away on a poor guess.\n *\n * @param p A probability between 0 and 1.\n * @param a A positive shape.\n * @param b A positive shape.\n * @returns The x where I_x(a, b) equals p, or NaN for a bad shape.\n */\nexport function inverseIncompleteBeta(\n p: number,\n a: number,\n b: number,\n): number {\n if (Number.isNaN(p) || !(a > 0) || !(b > 0)) {\n return Number.NaN;\n }\n if (p <= 0) {\n return 0;\n }\n if (p >= 1) {\n return 1;\n }\n\n const logBetaValue = logBeta(a, b);\n let lower = 0;\n let upper = 1;\n let x = inverseGuess(p, a, b);\n if (!(x > 0) || !(x < 1)) {\n x = 0.5;\n }\n\n // Index loop with a stated reason: this refines a single root and stops on\n // a convergence test.\n for (let step = 0; step < INVERSE_MAX_STEPS; step += 1) {\n const residual = incompleteBeta(x, a, b) - p;\n if (residual < 0) {\n lower = x;\n } else {\n upper = x;\n }\n\n const density = Math.exp(\n (a - 1) * Math.log(x) + (b - 1) * Math.log1p(-x) - logBetaValue,\n );\n let next =\n density > 0 && Number.isFinite(density) ? x - residual / density : Number.NaN;\n if (!(next > lower) || !(next < upper)) {\n next = 0.5 * (lower + upper);\n }\n if (next === x) {\n break;\n }\n\n const moved = Math.abs(next - x);\n x = next;\n if (moved <= Number.EPSILON * x) {\n break;\n }\n }\n\n return Math.min(x, BELOW_ONE);\n}\n",
|
|
20
|
+
"/**\n * The t distribution: density, cumulative probability, and quantile.\n *\n * These are the port of R's `dt()`, `pt()`, and `qt()`. `plot_t_test()` in\n * `../compstatslib/R/t_statistic_plot.R` draws both hypothesis curves from\n * them. Verified against R in `tdist.test.ts`.\n *\n * All three take an optional non-centrality, as in R. Left out or given as 0,\n * they run the central path, which `plot_t_test()` draws the null hypothesis\n * from; given a non-zero value they run the non-central path behind the\n * alternative-hypothesis curve, where the non-centrality is the t statistic\n * itself.\n *\n * A note on how close this comes to R. The non-central routines follow R's\n * own `pnt.c` and `dnt.c` step for step, down to the iteration cap and the\n * error bound. That is on purpose. Both stop the series on an *absolute*\n * bound, so where they stop is part of the answer, and a tidier stopping rule\n * would move the last digits away from R rather than toward the truth. It\n * also means this port inherits R's limits: at a large non-centrality the\n * series terms are built by repeated subtraction and go to noise once they\n * fall below about 1e-16, which is what R's own \"full precision may not have\n * been achieved\" warning reports. Densities near 1e-50 there agree with R in\n * absolute terms only.\n */\n\nimport {\n incompleteBetaSplit,\n inverseIncompleteBeta,\n logBeta,\n normalCdf,\n} from \"./special\";\n\n/** True when a non-centrality was given and is not the central case. */\nfunction isNonCentral(ncp: number | undefined): ncp is number {\n return ncp !== undefined && ncp !== 0;\n}\n\n/** True when any argument rules the answer out before any work starts. */\nfunction isBadArgument(value: number, df: number, ncp: number | undefined): boolean {\n return (\n Number.isNaN(value) ||\n !(df > 0) ||\n (ncp !== undefined && Number.isNaN(ncp))\n );\n}\n\n/**\n * The density of the t distribution, R's `dt()`.\n *\n * @param x Where to evaluate the density.\n * @param df Degrees of freedom. Any positive number, whole or not.\n * @param ncp The non-centrality. Left out, or 0, gives the central density.\n * @returns The density, or NaN if df is zero or less.\n */\nexport function dt(x: number, df: number, ncp?: number): number {\n if (isBadArgument(x, df, ncp)) {\n return Number.NaN;\n }\n return isNonCentral(ncp)\n ? nonCentralDensity(x, df, ncp)\n : centralDensity(x, df);\n}\n\n/**\n * The share of the distribution below x, R's `pt()`.\n *\n * @param x Where to evaluate the distribution.\n * @param df Degrees of freedom. Any positive number, whole or not.\n * @param ncp The non-centrality. Left out, or 0, gives the central case.\n * @returns A probability between 0 and 1, or NaN if df is zero or less.\n */\nexport function pt(x: number, df: number, ncp?: number): number {\n if (isBadArgument(x, df, ncp)) {\n return Number.NaN;\n }\n return isNonCentral(ncp)\n ? nonCentralProbability(x, df, ncp)\n : centralProbability(x, df);\n}\n\n/**\n * The value with probability p below it, R's `qt()`.\n *\n * @param p A probability between 0 and 1. The ends give infinities, as in R.\n * @param df Degrees of freedom. Any positive number, whole or not.\n * @param ncp The non-centrality. Left out, or 0, gives the central case.\n * @returns The quantile, or NaN for a p outside 0 to 1 or a df of zero or\n * less.\n */\nexport function qt(p: number, df: number, ncp?: number): number {\n if (isBadArgument(p, df, ncp) || p < 0 || p > 1) {\n return Number.NaN;\n }\n return isNonCentral(ncp)\n ? nonCentralQuantile(p, df, ncp)\n : centralQuantile(p, df);\n}\n\n/**\n * The density, computed in logs.\n *\n * f(x) = (1 + x²/df)^−(df+1)/2 / (√df · B(½, df/2))\n *\n * `log1p` keeps the small-x end accurate, and the log form keeps the large-df\n * end from overflowing on the way to a modest answer.\n */\nfunction centralDensity(x: number, df: number): number {\n if (!Number.isFinite(x)) {\n return 0;\n }\n const logDensity =\n -0.5 * Math.log(df) -\n logBeta(0.5, df / 2) -\n ((df + 1) / 2) * Math.log1p((x * x) / df);\n return Math.exp(logDensity);\n}\n\n/**\n * The cumulative probability, from the upper tail outward.\n *\n * Working from whichever tail holds the smaller mass avoids subtracting two\n * nearly equal numbers, which is what makes the far tails accurate.\n */\nfunction centralProbability(x: number, df: number): number {\n if (x === 0) {\n return 0.5;\n }\n if (x === Number.POSITIVE_INFINITY) {\n return 1;\n }\n if (x === Number.NEGATIVE_INFINITY) {\n return 0;\n }\n const tail = upperTail(Math.abs(x), df);\n return x < 0 ? tail : 1 - tail;\n}\n\n/**\n * The mass above t, for a t of zero or more.\n *\n * P(T > t) = ½ · I_z(df/2, ½), z = df / (df + t²)\n *\n * This is the incomplete-beta identity R's `pt()` uses. Both z and 1 − z come\n * straight out of t and df, each to full precision, so the pair goes to\n * `incompleteBetaSplit` rather than letting it subtract one from the other. A\n * small t drives z against 1, where a subtracted complement would keep only a\n * handful of digits; a large t makes the answer tiny, where building it as\n * ½ − something would cancel it away to nothing.\n */\nfunction upperTail(t: number, df: number): number {\n const squared = t * t;\n if (!Number.isFinite(squared)) {\n // t is past 1e154. The mass above it is far below the smallest double.\n return 0;\n }\n const total = df + squared;\n return 0.5 * incompleteBetaSplit(df / total, squared / total, df / 2, 0.5);\n}\n\n/** How many Newton steps the quantile takes after the beta inverse. */\nconst POLISH_MAX_STEPS = 4;\n\n/**\n * The quantile, by inverting the incomplete beta and then polishing.\n *\n * The symmetry of the distribution turns a one-sided probability into a\n * two-sided mass, which the beta inverse handles. Which shape goes first\n * depends on which side is small: taking the large side would compute the\n * answer as one minus something near one and throw away digits.\n */\nfunction centralQuantile(p: number, df: number): number {\n if (p === 0.5) {\n return 0;\n }\n if (p <= 0) {\n return Number.NEGATIVE_INFINITY;\n }\n if (p >= 1) {\n return Number.POSITIVE_INFINITY;\n }\n\n const tail = p < 0.5 ? p : 1 - p;\n const sign = p < 0.5 ? -1 : 1;\n const twoSided = 2 * tail;\n\n let squared: number;\n if (twoSided > 0.5) {\n // Near the middle: the answer is small, so solve for x²/(df + x²).\n const near = inverseIncompleteBeta(1 - twoSided, 0.5, df / 2);\n squared = (df * near) / (1 - near);\n } else {\n // Out in a tail: the answer is large, so solve for df/(df + x²).\n const far = inverseIncompleteBeta(twoSided, df / 2, 0.5);\n squared = (df * (1 - far)) / far;\n }\n\n return sign * polish(Math.sqrt(squared), tail, df);\n}\n\n/**\n * Newton steps on the upper tail, to take the last digits home.\n *\n * The beta inverse lands close but loses a little precision on the way\n * through x² and the square root. Solving P(T > t) = tail directly, with the\n * density as the derivative, recovers it. The step is measured against the\n * tail rather than against p, so a p near 1 does not lose its digits to the\n * subtraction.\n */\nfunction polish(start: number, tail: number, df: number): number {\n let t = start;\n\n // Index loop with a stated reason: this refines a single root and stops on\n // a convergence test.\n for (let step = 0; step < POLISH_MAX_STEPS; step += 1) {\n const density = centralDensity(t, df);\n if (!(density > 0) || !Number.isFinite(t)) {\n break;\n }\n\n const move = (upperTail(t, df) - tail) / density;\n const next = t + move;\n if (!(next > 0) || !Number.isFinite(next) || Math.abs(move) > 0.25 * t) {\n break;\n }\n if (next === t) {\n break;\n }\n\n t = next;\n if (Math.abs(move) <= Number.EPSILON * t) {\n break;\n }\n }\n\n return t;\n}\n\n/**\n * Iteration cap and error bound for the AS 243 series.\n *\n * These are R's own values from `pnt.c`. They are part of the answer, not a\n * detail: the series stops on an *absolute* bound, so where it stops decides\n * the last digits. Holding R's numbers here is what makes this port agree\n * with R rather than merely come close to the true value.\n */\nconst SERIES_MAX_STEPS = 1000;\nconst SERIES_ERROR_MAX = 1e-12;\n\n/** Above this non-centrality R leaves the series for a normal fit. */\nconst SERIES_NCP_LIMIT_SQUARED = 2 * Math.LN2 * 1022;\n\n/** Above this many degrees of freedom R does the same. */\nconst SERIES_DF_LIMIT = 4e5;\n\nconst SQRT_TWO_OVER_PI = Math.sqrt(2 / Math.PI);\n\n/**\n * The share of the non-central distribution below x, R's `pt(x, df, ncp)`.\n *\n * The series only runs on values of zero or more, so a negative x is\n * reflected through the origin along with the non-centrality. That names the\n * other tail, which the caller flips back.\n */\nfunction nonCentralProbability(x: number, df: number, ncp: number): number {\n if (x === Number.POSITIVE_INFINITY) {\n return 1;\n }\n if (x === Number.NEGATIVE_INFINITY) {\n return 0;\n }\n\n const reflected = x < 0;\n const t = reflected ? -x : x;\n const delta = reflected ? -ncp : ncp;\n const lower =\n df > SERIES_DF_LIMIT || delta * delta > SERIES_NCP_LIMIT_SQUARED\n ? normalApproximation(t, df, delta)\n : lenthSeries(t, df, delta);\n\n return reflected ? 1 - lower : lower;\n}\n\n/**\n * Abramowitz and Stegun 26.7.10, the fit R falls back on.\n *\n * Past a non-centrality of about 37.6 the leading Poisson weight\n * exp(−ncp²/2) drops below the smallest double and the series has nothing\n * left to sum. The sliders reach that: a difference of 4 with a standard\n * deviation of 1 over 500 observations puts the non-centrality near 89.\n */\nfunction normalApproximation(t: number, df: number, delta: number): number {\n const shrink = 1 / (4 * df);\n const spread = Math.sqrt(1 + t * t * 2 * shrink);\n return normalCdf((t * (1 - shrink) - delta) / spread);\n}\n\n/**\n * The AS 243 twin series of Lenth (1989), as R's `pnt.c` runs it.\n *\n * The distribution is a Poisson mixture of incomplete beta terms. Both\n * families of terms step by recurrence rather than being evaluated afresh,\n * and `remaining` carries the Poisson mass still to come, which bounds the\n * error of stopping early.\n *\n * @param t Where to evaluate, zero or more.\n * @param df Degrees of freedom.\n * @param delta The non-centrality, of either sign.\n */\nfunction lenthSeries(t: number, df: number, delta: number): number {\n const squared = t * t;\n const total = df + squared;\n const x = squared / total;\n const complement = df / total;\n\n let sum = 0;\n if (x > 0) {\n const lambda = delta * delta;\n let oddWeight = 0.5 * Math.exp(-0.5 * lambda);\n let evenWeight = SQRT_TWO_OVER_PI * oddWeight * delta;\n\n let remaining = 0.5 - oddWeight;\n if (remaining < 1e-7) {\n remaining = -0.5 * Math.expm1(-0.5 * lambda);\n }\n\n let a = 0.5;\n const b = 0.5 * df;\n const powered = Math.pow(complement, b);\n const logBetaValue = logBeta(0.5, b);\n\n let oddTerm = incompleteBetaSplit(x, complement, a, b);\n let oddStep = 2 * powered * Math.exp(a * Math.log(x) - logBetaValue);\n let evenTerm = 1 - powered;\n let evenStep = b * x * powered;\n sum = oddWeight * oddTerm + evenWeight * evenTerm;\n\n // Index loop with a stated reason: each pass advances one Poisson term by\n // recurrence and the loop stops on an error bound, not on a data length.\n for (let step = 1; step <= SERIES_MAX_STEPS; step += 1) {\n a += 1;\n oddTerm -= oddStep;\n evenTerm -= evenStep;\n oddStep *= (x * (a + b - 1)) / a;\n evenStep *= (x * (a + b - 0.5)) / (a + 0.5);\n oddWeight *= lambda / (2 * step);\n evenWeight *= lambda / (2 * step + 1);\n remaining -= oddWeight;\n if (remaining <= 0) {\n break;\n }\n sum += oddWeight * oddTerm + evenWeight * evenTerm;\n if (Math.abs(2 * remaining * (oddTerm - oddStep)) < SERIES_ERROR_MAX) {\n break;\n }\n }\n }\n\n return Math.min(Math.max(sum + normalCdf(-delta), 0), 1);\n}\n\n/**\n * The density of the non-central distribution, R's `dt(x, df, ncp)`.\n *\n * Away from zero the density is read off the distribution function, using\n * that its derivative can be written as a difference between two evaluations\n * two degrees of freedom apart. R notes in `dnt.c` that this still cancels,\n * and it does: at x = 1 the two probabilities agree to about two digits\n * before the difference is taken. Following R's route rather than a cleaner\n * one is deliberate, since the fixtures are R's own output.\n */\nfunction nonCentralDensity(x: number, df: number, ncp: number): number {\n if (!Number.isFinite(x)) {\n return 0;\n }\n\n if (Math.abs(x) > Math.sqrt(df * Number.EPSILON)) {\n const stepped = x * Math.sqrt((df + 2) / df);\n const difference =\n nonCentralProbability(stepped, df + 2, ncp) -\n nonCentralProbability(x, df, ncp);\n return (df / Math.abs(x)) * Math.abs(difference);\n }\n\n // At zero that difference is 0/0. The density there is the central one\n // damped by the non-centrality, exp(−ncp²/2).\n return Math.exp(\n -0.5 * Math.log(df) - logBeta(0.5, df / 2) - 0.5 * ncp * ncp,\n );\n}\n\n/** Iteration cap for the non-central quantile search. */\nconst QUANTILE_MAX_STEPS = 200;\n\n/**\n * The non-central quantile, R's `qt(p, df, ncp)`.\n *\n * There is no closed form to invert, so this brackets the root and then works\n * inward. R bisects to a relative 1e-13; Newton steps get there in a handful\n * of passes instead, with the bracket kept so that a step into the flat part\n * of a far tail cannot run away.\n */\nfunction nonCentralQuantile(p: number, df: number, ncp: number): number {\n if (p <= 0) {\n return Number.NEGATIVE_INFINITY;\n }\n if (p >= 1) {\n return Number.POSITIVE_INFINITY;\n }\n\n // Widen outward from the non-centrality until the root is enclosed.\n let upper = Math.max(1, ncp);\n while (\n Number.isFinite(upper) &&\n nonCentralProbability(upper, df, ncp) < p\n ) {\n upper *= 2;\n }\n let lower = Math.min(-1, -ncp);\n while (\n Number.isFinite(lower) &&\n nonCentralProbability(lower, df, ncp) > p\n ) {\n lower *= 2;\n }\n\n let t = 0.5 * (lower + upper);\n\n // Index loop with a stated reason: this refines a single root and stops on\n // a convergence test.\n for (let step = 0; step < QUANTILE_MAX_STEPS; step += 1) {\n const residual = nonCentralProbability(t, df, ncp) - p;\n if (residual < 0) {\n lower = t;\n } else {\n upper = t;\n }\n\n const density = nonCentralDensity(t, df, ncp);\n let next =\n density > 0 && Number.isFinite(density) ? t - residual / density : Number.NaN;\n if (!(next > lower) || !(next < upper)) {\n next = 0.5 * (lower + upper);\n }\n if (next === t) {\n break;\n }\n\n const moved = Math.abs(next - t);\n t = next;\n if (moved <= Number.EPSILON * Math.abs(t)) {\n break;\n }\n }\n\n return t;\n}\n",
|
|
21
|
+
"/**\n * The derived quantities of a one-sided t test.\n *\n * This is the statistics half of `plot_t_test()` in\n * `../compstatslib/R/t_statistic_plot.R`. R computes these while drawing,\n * scattered across `plot_t_test()`, `t_null_plot()`, `t_alt_lines()`, and\n * `plot_error_matrix()`; here they are one pure result that the plot layer\n * reads. Verified against R in `ttest.test.ts`.\n *\n * What the picture shows: a null distribution centerd at 0 and an alternative\n * distribution centerd at the t statistic the given difference produces. The\n * area of the null above the critical value is the significance level; the\n * area of the alternative above that same point is the power, and what falls\n * below it is beta.\n */\n\nimport { dt, pt, qt } from \"./tdist\";\n\n/** The four numbers `plot_t_test()` takes, all optional as in R. */\nexport interface TTestOptions {\n /** The difference the alternative hypothesis claims. */\n readonly diff?: number;\n /** The population standard deviation. */\n readonly sd?: number;\n /** The sample size. */\n readonly n?: number;\n /** The significance level. */\n readonly alpha?: number;\n}\n\n/**\n * R's defaults from the `plot_t_test()` signature.\n *\n * These live here rather than in the plot layer so that the plot and\n * interactive layers read one set of numbers, and so that `tTestStats()` on\n * its own yields the classroom demo, matching R's no-argument behavior.\n */\nexport const DEFAULT_T_TEST_OPTIONS = {\n diff: 0.5,\n sd: 4,\n n: 100,\n alpha: 0.05,\n} as const;\n\n/**\n * A span of the x axis to shade under a curve.\n *\n * `from` can be −Infinity. Once the difference is large enough, beta\n * underflows to exactly 0 and the shading starts at the quantile for 0, which\n * is unbounded — the whole alternative curve lies in the rejection region.\n * About one in thirty of the slider settings reaches this. The value is\n * honest and the plot layer must clamp it to the drawing window; R instead\n * hands the infinity to `seq()` and stops with an error, so this is one place\n * the port has to do better rather than follow.\n */\nexport interface FillRange {\n /** Where the shading starts. May be −Infinity; see above. */\n readonly from: number;\n /** Where the shading stops. R runs every fill out to the 0.999 quantile. */\n readonly to: number;\n}\n\n/**\n * The four cells of the error matrix, and which row R rings.\n *\n * The numbers here are raw. R prints two of the four through `round(x, 2)`\n * and the other two untouched: `plot_error_matrix()` writes `alpha` and\n * `1 - alpha` as they are, but `round(alt_stats[2], 2)` and\n * `round(alt_stats[1], 2)` for the power and beta cells. That asymmetry is\n * R's own. Rounding is a display concern, so it belongs to the plot layer;\n * this type carries full precision and lets the drawing decide.\n */\nexport interface TTestErrorMatrix {\n /** Top left: rejecting a true null. This is alpha. */\n readonly typeOne: number;\n /** Top right: rejecting a false null. This is the power. */\n readonly correctReject: number;\n /** Bottom left: keeping a true null. This is 1 − alpha. */\n readonly correctFailToReject: number;\n /** Bottom right: keeping a false null. This is beta. */\n readonly typeTwo: number;\n /**\n * Whether R rings the top row rather than the bottom one.\n *\n * R's test is `alt_stats[3] < alt_stats[4]`: the point where the alternative\n * fill begins, against the alternative's median. The fill begins at the\n * critical value, so this asks whether the critical value sits below the\n * median — that is, whether the test is more likely than not to reject. When\n * it is, the top row is the likely outcome and gets the ring.\n */\n readonly highlightTopRow: boolean;\n}\n\n/** Everything `plot_t_test()` derives before it draws anything. */\nexport interface TTestStats {\n /** The difference the statistics were computed from. */\n readonly diff: number;\n /** The standard deviation they were computed from. */\n readonly sd: number;\n /** The sample size they were computed from. */\n readonly n: number;\n /** The significance level they were computed from. */\n readonly alpha: number;\n /** Degrees of freedom, n − 1. */\n readonly df: number;\n /**\n * The t statistic, `diff / (sd / √n)`.\n *\n * This doubles as the non-centrality of the alternative distribution, which\n * is what makes the two curves in the picture the same shape shifted.\n */\n readonly t: number;\n /** Where the null distribution starts rejecting, `qt(1 − alpha, df)`. */\n readonly criticalValue: number;\n /** The chance of keeping a false null: the alternative below the critical value. */\n readonly beta: number;\n /** The chance of rejecting a false null, 1 − beta. */\n readonly power: number;\n /** The alternative distribution's midpoint, `qt(0.5, df, ncp)`. */\n readonly altMedian: number;\n /** The height of the alternative curve at its midpoint. */\n readonly altMedianDensity: number;\n /**\n * The shaded span under the alternative curve, the power.\n *\n * `from` is R's `alt_stats[3]`, `qt(beta, df, ncp)`. That inverts the beta\n * it was just built from, so it lands back on the critical value — but only\n * as closely as the two routines invert each other, which is why R's own\n * numbers differ in the last digits from `criticalValue`.\n */\n readonly altFill: FillRange;\n /**\n * The shaded span under the null curve, the significance level.\n *\n * `from` is the critical value, by the same expression.\n */\n readonly nullFill: FillRange;\n /** The four cells R draws beside the curves. */\n readonly errorMatrix: TTestErrorMatrix;\n}\n\n/** How far out R runs a fill. Both tails stop at the same quantile. */\nconst FILL_UPPER_QUANTILE = 0.999;\n\n/**\n * Work out everything the picture needs from the four test parameters.\n *\n * A missing option takes R's default, so calling this with nothing yields the\n * same demo `plot_t_test()` does. Nothing is validated: a sample size of 1\n * leaves no degrees of freedom and the quantiles come back as NaN, which is\n * what R does too.\n *\n * @param options The test parameters. Any left out take R's defaults.\n * @returns The derived quantities. No drawing, no state.\n */\nexport function tTestStats(options: TTestOptions = {}): TTestStats {\n const { diff, sd, n, alpha } = { ...DEFAULT_T_TEST_OPTIONS, ...options };\n\n const df = n - 1;\n const t = diff / (sd / Math.sqrt(n));\n\n // The critical value belongs to the null curve, but beta measures the\n // alternative's mass below it. That shared point is what ties the picture\n // together.\n const criticalValue = qt(1 - alpha, df);\n const beta = pt(criticalValue, df, t);\n const power = 1 - beta;\n\n const altMedian = qt(0.5, df, t);\n\n // Where the alternative fill starts. R reads this back out of beta rather\n // than reusing the critical value, and uses the same number again for the\n // highlight test, so it is computed once here and shared.\n const altFillFrom = qt(beta, df, t);\n\n return {\n diff,\n sd,\n n,\n alpha,\n df,\n t,\n criticalValue,\n beta,\n power,\n altMedian,\n altMedianDensity: dt(altMedian, df, t),\n altFill: {\n from: altFillFrom,\n to: qt(FILL_UPPER_QUANTILE, df, t),\n },\n nullFill: {\n from: criticalValue,\n to: qt(FILL_UPPER_QUANTILE, df),\n },\n errorMatrix: {\n typeOne: alpha,\n correctReject: power,\n correctFailToReject: 1 - alpha,\n typeTwo: beta,\n highlightTopRow: altFillFrom < altMedian,\n },\n };\n}\n",
|
|
22
|
+
"/**\n * The `moderation_data` dataset of the R package.\n *\n * Two hundred rows of a moderated relationship, used as the default data of\n * `plot_moderation_3d()` and `plot_scatter3d()` so that both work with no\n * arguments. R's `data.R` records the recipe: `x`, `z` and `w` are drawn\n * independently from a normal distribution with standard deviation 2, and\n *\n * ```text\n * y = 0.5 x + 0.3 z + 0.8 x z + N(0, 1)\n * ```\n *\n * so the effect of `x` on `y` grows with `z` — the interaction the surface\n * shows as a twist. Column `w` enters no equation: it is noise, and it is\n * there so an example can carry a control variable that means nothing.\n *\n * The values below are the exact doubles of the R data file\n * (`../compstatslib/data/moderation_data.rda`), printed at 17 significant\n * digits, which round-trips an IEEE-754 double. **Exported from R, never\n * regenerated** — the draw used `set.seed(42)` under R's own generator, so a\n * JavaScript regeneration would produce different numbers and silently change\n * every default demo. Source of the printed values:\n * `.claude/plans/moderation-data.tsv`, checked against the column checksums of\n * `.claude/plans/moderation-fixtures.md` in `moderationData.test.ts`.\n *\n * The columns are in the order of the R data frame — `y, x, z, w`, not\n * alphabetical and not `x, y, z`. That order is load-bearing: `plot_scatter3d()`\n * with no axis arguments takes the first three numeric columns, which is why\n * its default plot puts `y` on the horizontal axis.\n */\n\n/** The four columns of the R `moderation_data` data frame. */\nexport type ModerationData = {\n readonly y: readonly number[];\n readonly x: readonly number[];\n readonly z: readonly number[];\n readonly w: readonly number[];\n};\n\n/** The 200 rows of the R `moderation_data` data frame, in file order. */\nexport const moderationData: ModerationData = {\n y: [\n -7.2728972000711964, -1.8368504105536234,\n 2.4825030269210964, 6.0885502472158235,\n -2.7813932586215913, -1.4045477434849749,\n -2.3283702256411951, 0.24770227680217238,\n -1.0630104614416778, -2.0458910688111343,\n -5.1341052959159876, 18.102464141094789,\n -3.4663420296290077, -1.0047522093312424,\n -0.82474666408975483, 0.011971257899126564,\n -2.4321918294924254, -14.110628278307653,\n -1.5077118301445713, -3.3519580636316557,\n 0.34228397854096548, -1.4420566151665186,\n -0.09954101599791107, -4.1717572290618348,\n 5.638370200339164, -0.24209932633994577,\n -0.92942202986679767, -0.14306775813444911,\n -0.75191873199673909, -2.6149345603765624,\n 1.9162030995224288, 2.882308922335612,\n -4.1366719661768885, -1.5262757249783787,\n -0.13241561132928859, 1.0868995782488633,\n -5.4322398208224598, -1.0526454319438507,\n -5.3360807036936473, 0.80370589439224283,\n 0.64566701092436662, -0.58086531051744095,\n 5.9958208137315907, -3.748881331929856,\n 4.7991068639205681, 3.9170147432586608,\n -2.5205797162580499, 1.0052100508228277,\n -1.2592451123609627, -1.4191054380364576,\n -2.7688068774363885, -1.1283556266006958,\n -5.0155719117359876, 0.74436508929657408,\n 1.3457889732609731, -1.5775936354323048,\n 0.24940175331052905, 0.84534536563058338,\n 9.1086395504851989, -1.0895764398669741,\n -0.054834750835499602, 3.4396743692990768,\n 1.82777129315956, -9.0876817903961307,\n -1.6403441299936004, 6.3441120121592336,\n 3.0068415808329494, 1.0402916701960967,\n -10.792485667257104, 0.19774249672127087,\n -2.6230964327485333, -1.5340564759379902,\n 1.7265739813235901, -4.4727441322331023,\n 0.051358701883551766, 3.4112581498484817,\n 1.7155585583877218, -2.4096746494084527,\n -3.6580899543881662, -4.2196299413161089,\n 5.7706115536478979, -4.0368554837030199,\n -1.8534915424952012, -1.4551065653571458,\n -0.57333130613680428, 1.2587331393918402,\n -0.18074955316901531, -1.7541060455623132,\n -7.1816739672594787, 2.2681715762809351,\n -2.2618552849857134, -1.0033432551835315,\n -1.5998101004488254, 13.776336779706227,\n -1.3601934670356379, 0.84377053596043516,\n 0.26923062515768637, -6.1587204803542566,\n 0.28455942391067712, -3.7313390977173673,\n 2.209575611277963, 4.9573155152467479,\n -1.1074416328100625, 6.773590899689121,\n -1.1622855609602678, -0.1468882418607233,\n -1.8138297100034615, -0.88229772478568436,\n -2.5310587963205209, 0.92250684930151283,\n 1.0529999297345707, -1.0226841183124429,\n -0.62826191336833026, 0.047095104558446066,\n -2.1862971987559754, 1.9185432668727294,\n -1.4157898788687031, 3.8559400012867275,\n 4.8196910254616006, 1.1859049624611124,\n 2.1292032810255828, -2.5804694939165822,\n 0.7806460650847441, -0.77177474486716457,\n 4.3023231287427057, 1.1280497997890846,\n -3.3823450205179935, -2.8141768087650334,\n 1.9176969652481777, -1.8022438527307034,\n 0.56769922608595746, -1.1988798479187797,\n -0.81127142895301252, 3.0023694027723717,\n 6.3138141892914694, -0.72091317245731745,\n -0.49342129711845351, -5.3116450695380282,\n 1.5864321371634191, -1.3767448543412033,\n -0.32149297043332475, -2.7378068772633672,\n 0.44512957644982276, 1.0144155909083761,\n -0.33982995595512711, 3.6424411700938926,\n -0.80484964968788497, 0.26491344714041165,\n 1.0255159271802998, -3.4431481857611512,\n -1.2617728430815081, 2.4072718264535986,\n 4.1858481441966102, 0.84449896500128463,\n 0.68906644248057036, -4.9301730292087731,\n 1.3181686730513686, -3.9504745741065488,\n 0.69966539975680719, -7.7331713344467277,\n 0.37966613312994063, 2.0980485157760418,\n 0.58696762292169558, -0.095335203753901337,\n -1.3602473690753563, -1.8363341184552933,\n -5.3271971097587407, -2.4667878836756927,\n -0.84771180433706672, 1.794347325600735,\n -1.1215052328640052, 0.96938089278474238,\n -8.8525095689727635, -10.171192748327044,\n 5.5154793606449468, 0.81334743443491231,\n 1.5442257490953633, -2.3028072866769134,\n 0.68983698216899647, 1.9915198521027817,\n 2.8322478461703762, 0.34764862749885805,\n -0.50342712629907482, 1.1733004427545144,\n 0.18449984894824215, -1.9938893833160367,\n 0.8829064829548916, 0.62667972173052222,\n -3.9311936779928676, -4.6930573223273164,\n 0.5942425542793619, -0.040913228103484439,\n 1.5529759191506414, 0.52558975177612277,\n -0.51812524140199456, 5.7138238288601553,\n 0.80645695222572478, 2.7208395005315182,\n -1.9931388422548042, 2.0982441518090775,\n ],\n x: [\n 2.741916894293337, -1.1293963427921776,\n 0.72625682267467828, 1.265725209922081,\n 0.80853664628199817, -0.21224903218296798,\n 3.0230439948778778, -0.18931807682619511,\n 4.0368474277540827, -0.12542819810484199,\n 2.6097393084469704, 4.5732907854022136,\n -2.7777214022246786, -0.55757753363474283,\n -0.26664267278731602, 1.2719007961401489,\n -0.56850584283214478, -5.3129108418095523,\n -4.8809338571510379, 2.6402266914603838,\n -0.6132771881569492, -3.562616867960001,\n -0.34383471151924272, 2.4293493983451979,\n 3.7903869225299305, -0.86093826321239919,\n -0.51453876553785927, -3.5263261703895603,\n 0.9201947096625428, -1.2799897519202386,\n 0.91090024648243872, 1.4096746744576383,\n 2.0702070439398446, -1.2178527508144228,\n 1.0099102465959404, -3.434017358146686,\n -1.5689180167589927, -1.7018151883530368,\n -4.8284152998932646, 0.07224521378451125,\n 0.41199720040050775, -0.72211459709733239,\n 1.5163264713990341, -1.4534096541531503,\n -2.7365620888385891, 0.86563605177743408,\n -1.6227863523733432, 2.8882025234425055,\n -0.86289240522669086, 1.3112957668044132,\n 0.64385053040789308, -1.5676778817607508,\n 3.1514550395839547, 1.2857986114346329,\n 0.17952129319921128, 0.5531014945829259,\n 1.3585776321105416, 0.17966577315816343,\n -5.9861801663058696, 0.5697659070613188,\n -0.73446928548195067, 0.3704611297312187,\n 1.1636474547310138, 2.799473654585356,\n -1.4545841189489299, 2.6050852640882871,\n 0.67169623950414881, 2.0770121973952422,\n 1.8414571365812926, 1.4417563257337251,\n -2.08623787713571, -0.18037277322141337,\n 1.2470363239990871, -1.907046715544688,\n -1.0856576291477136, 1.1619929953633648,\n 1.536357475669182, 0.92753517708033439,\n -1.7715525948193589, -2.1995617972957109,\n 3.025414019609856, 0.51584287506406168,\n 0.17688045831917268, -0.24179307507817885,\n -2.3886577903210564, 1.2239937960807739,\n -0.43427969149304169, -0.36551341266384341,\n 1.8666926571423201, 1.6435462210164979,\n 2.7842327518685419, -0.95234784610934875,\n 1.3006971214526097, 2.7822209127800015,\n -2.2215777588957977, -1.7215851737556847,\n -2.2634773617075399, -2.9184279990047917,\n 0.15996510648232234, 1.30640867929838,\n 2.4019307511969883, 2.0895021743354505,\n -2.006417293679696, 3.696963803345493,\n -1.3335468175156342, 0.21102762491213886,\n -0.84451176373771208, -0.24470034390994258,\n 0.37638606900299576, 0.23832191599401276,\n -0.050185101734805754, 0.2161454558840652,\n -0.9708704716933354, -1.0084342613758068,\n -3.322198159829624, -0.76466745374763623,\n -1.025300515755601, 5.4037820006895947,\n -2.7242324623794376, 0.27451243711721379,\n -2.987250134632581, -2.9408714828735931,\n 0.2494047723940136, -1.9932782697680742,\n -0.0036452286094163897, -0.85651776285163062,\n -1.2273432128989903, -4.0493556908382153,\n -2.4494959007199721, 0.35903288223587582,\n 1.1352411888470701, -0.98575470710694957,\n 0.00012576813070224816, 2.2457792867599329,\n 2.87971148595238, -2.1942275368116433,\n -0.23463912050035363, 2.4029968018394023,\n -0.93945916113260175, -0.10493896987799263,\n -0.17221459647417905, -1.7753580358128629,\n -0.88936800976947616, -0.058889758176476292,\n -0.82773769811584708, 2.2267720467364067,\n -0.96198568330796386, -0.86633806520145806,\n 1.3937251531042052, -2.1127368263418163,\n -0.081396950302429855, -3.1030896446951792,\n 2.3343390984713639, -0.54729140274816135,\n -0.93569064934450719, -2.4765046559724295,\n -0.015524067554653256, -1.6005643559033189,\n -1.066984659900873, 2.5753504911691776,\n -0.35105174048425475, -2.1435647683013519,\n 0.32641376493476459, -0.72547683125589957,\n 1.1800270959746773, 2.8648438554619791,\n -1.9853850222189862, 0.90930059516056516,\n 0.16979611735697459, 1.7911311645290895,\n -0.45955627789253306, 1.6732381369212268,\n -3.4901117226733867, 3.378917842626747,\n 1.7295559570371559, -0.30155197777149562,\n -2.8980142602783352, 1.2860174000839635,\n 0.96638772762953518, -0.012711252842777425,\n 0.30291178572484884, -1.1682179406996087,\n 0.73761346526048421, 0.5893086794390312,\n -0.55851874668515011, -2.6724733097863078,\n 1.4014976368800685, 1.1083932445480671,\n -1.6726131856028299, -3.189176324012486,\n 0.40991716117526766, -0.69017595594577974,\n 0.50522340672890931, -2.5880049309691064,\n -1.9183408887607261, 2.1715497073598016,\n 0.80754980943142785, 1.1729750734385955,\n 3.6304568923079059, 0.25764285720476621,\n ],\n z: [\n -4.0018584754630222, 0.66755439486714041,\n 2.3426502547175891, 4.1190784845986084,\n -2.7537231964810425, -2.3017111312542111,\n -1.4116427895202421, -2.1081115641543828,\n -1.2914874462849828, -0.37075593535300683,\n -2.4024441014799702, 4.0739443339663,\n 0.21554948977109339, -0.16821620101116128,\n 0.99123928320918786, 0.074830372235930684,\n -0.2641760739118198, 2.9535748471041936,\n -0.43406042018420693, -2.5672044081844505,\n 0.77133578088680466, -0.70302574705818488,\n -1.0435921867125384, -2.1362624013743408,\n 0.85673180653338499, -0.34803646885398981,\n 1.0313354572960587, -0.46873055461184299,\n -1.3170068516435418, 2.500473208157433,\n -0.54352743022279359, 1.8959039917503921,\n -2.4031648602178755, -0.93223219275100444,\n -0.53870279030635992, -0.78193081626172145,\n 2.697414023983427, -0.04552940259682519,\n 0.48845170220690071, -1.8847434157278455,\n -1.4584345530191491, 1.9961378171096964,\n 2.5169633291911206, 2.4977273776201918,\n -2.7612740990506603, 4.0999213872727882,\n 2.0337456595990835, -0.053434928276515886,\n 1.4072155575965211, -1.9427704583037273,\n -2.1923124831177145, 0.098100901875194116,\n -2.3969917131155598, 0.38003799711949598,\n 2.5954117992093324, -2.067747445929502,\n -1.4768815084261278, 0.093127878901726704,\n -2.0351922396522117, -0.76656791977255634,\n 1.7455108233427969, 1.9390900279402341,\n 0.76769333005418883, -3.7031113261349291,\n -0.10799347365233136, 2.1295464286765675,\n 1.6263900748232101, -0.38163294820856347,\n -5.3998596171274054, 0.1219332776022331,\n 1.1475033949671558, 0.091607159450197734,\n 0.31482508035175089, 0.86313074573754156,\n -0.79309947210042442, 2.6199564515030986,\n 0.94078679974788604, -2.4853405411492027,\n 2.7631509127710658, 2.4089178740196484,\n 1.648147927363762, -3.3252588043711886,\n -1.1386126872109616, 1.2710276345790525,\n 0.087444015155715843, 0.69602460732261417,\n 4.9191870977408296, -1.6367606487993389,\n -4.2264002298582737, 0.54739054486082928,\n -1.3751936824868236, 0.89208210592373138,\n -1.6247694475811947, 4.424110960670272,\n -0.24741194315485965, -0.95467101206010463,\n -0.33252298297321642, 1.7251267672528618,\n 0.19468097040563787, -3.2512334784225763,\n -0.00924153565291559, 1.5204843353241098,\n 0.077981825745125244, 1.4701442832288814,\n -0.29294525399773952, -0.11577467076256517,\n 0.96473893220906526, 1.9858872735514685,\n -2.4927909960640844, -0.066975049691219368,\n -0.1419243624999926, -1.517841307461901,\n -2.0687187218783056, -1.2614639079416392,\n 1.1736154400259904, -0.83264531236730377,\n -1.5697756190106404, 0.32683263897219417,\n -2.4734284702186993, 2.0917475524733429,\n -0.96919083243741866, 0.37825762336133084,\n 0.10201266314994641, -0.00048133777288395359,\n 3.6187640848857319, -1.6506559142198896,\n 2.2909409044559705, 0.063146375106486144,\n -1.6704116106134392, -0.13752729803302502,\n 1.4935433740249338, -0.85103746878920816,\n -1.5441644703869135, 0.30552821349578935,\n 1.977193690357598, -0.14691666934434808,\n -2.7740531071194838, -2.6133518087507257,\n -1.5367906501558772, -1.0542162507489161,\n -0.042854130083249252, 1.3409961419925638,\n -0.86923407714481304, -2.2277595666256937,\n 1.2142119897569996, 0.5509139374791826,\n 2.3146941388294735, -3.3649617189565113,\n 0.17463817778974872, 2.7067237878853372,\n 1.4483476013667382, -1.665105651528423,\n 1.4650569735368288, -1.7438537399944254,\n -0.90679502245958543, 2.3750685572581554,\n -0.58029062358015848, 1.6570922899308729,\n -0.58245541755833752, -3.1527248094958122,\n -1.6976313938978438, -2.1770397239143895,\n -0.96858114223434699, -0.67262241801914557,\n -0.30671578132328758, -0.48649445714969192,\n 3.784404083200223, -2.7719966745708398,\n -0.82964860141556074, 0.69816305621366226,\n 3.256884531630813, 0.17704379142723264,\n 2.4783014168011261, -3.2891110715074134,\n 2.8927130507569592, -1.3811203433303656,\n -0.55286217090770406, -2.2188375197988743,\n 0.26773863285890354, 3.5706781034631039,\n 4.8443267105031795, -2.1536578042368233,\n 0.97188222080619802, 2.7770434774885837,\n -0.3913136345690878, -0.43634959541242196,\n -0.60955590909289048, 1.1956654482228859,\n 2.7948588216315193, 1.3752395224799268,\n 0.64037602859393727, -0.60373985155452159,\n 0.99669737111589907, -1.0990738358335126,\n -0.55851300727448538, 2.1930268862234645,\n 0.88402617635762248, 0.48203258805222476,\n -0.51121531071103665, 1.8620658029697648,\n ],\n w: [\n -0.49696586637986573, 0.84464077166478657,\n 1.9753065872793472, 1.6711363440700464,\n -1.321043718091411, 3.1281389865836706,\n -3.2459518705830939, 1.7277927469579759,\n -1.0232055468724002, -3.8347300508908027,\n -3.731627697239897, 0.4903581268793773,\n 4.447068603816132, 0.54675212828893049,\n 2.261569589056565, 1.6773387481201982,\n -1.3092305557564106, 1.9079219569587911,\n 0.70590293097864509, 0.41319858529922687,\n 2.0022264686204969, 1.4949039832805529,\n -1.2531496734462773, 0.79044737187342806,\n -1.7843359080911552, 1.2616368201835455,\n -0.86541036656913495, 0.90427728843703359,\n 0.73599809039269093, -0.54077497932381824,\n 0.9310252354259575, 1.1487124512539382,\n -0.46139998031482182, 2.3444850166049025,\n 2.7854054134662434, -1.3239825114191694,\n -1.5547384134500284, 1.0270771578175601,\n -1.8266244760825443, -0.89884761050060125,\n 1.6058653982072366, -1.1469537019622078,\n -3.8562503356966524, 1.3287816676442965,\n -3.205080447588506, -2.7092005149076992,\n -6.0358653588357924, 1.6624756444601259,\n 0.50219417769468799, 0.92458693188265906,\n 1.6895844464464735, -0.083943048643124199,\n -2.2111518114000552, 1.1275513046617478,\n 2.6067295116393412, -3.0004418755893147,\n -1.2139784702380723, -0.58449013131315453,\n -2.579366686647206, 1.3882116975493977,\n -1.1983638057942381, 2.5138132879432744,\n 0.10701602725472605, 1.4561850353437049,\n 3.1221961364212145, 0.53124950719437969,\n 2.1534525184145346, 0.42139586335849089,\n -3.0233470516447363, 0.044804519827250026,\n 1.4362724120842063, 0.97891403568453961,\n -0.34777669175653925, -2.435398977336801,\n 1.292796737418924, -1.8329120566957797,\n -2.5036468848511326, 1.1898554368110026,\n -2.465621330379653, 0.48872882981227517,\n 0.005544370687190118, -2.65641937205113,\n 2.3593928236655501, -1.1856099982978574,\n 2.3999566326733439, -0.95006735922426966,\n -1.1501141923016327, -0.062452049216998061,\n -0.71611399195691117, -0.7132021552954626,\n -1.7553287668674522, -2.4257942503559256,\n 1.2265732373740676, -1.6124066829249548,\n -2.7529138598177902, -1.0156957983270942,\n -1.6018709734746754, -4.3855713713401352,\n -0.58187429853534967, 0.33434824232103538,\n 0.5893847129713754, 0.7854825308414517,\n -2.0016874260016304, -0.65145423964686777,\n -2.0166976108576851, -1.2708629640675322,\n -2.4196813759746849, -2.2329276013053807,\n 1.2597623251518704, -0.54504314035725121,\n -0.5176823367573421, 3.4591163597829868,\n -0.11678433081474329, -1.0741275692178371,\n 1.4945733923314797, -0.97451567077946588,\n 2.7458155629590104, -0.75534472129244146,\n -1.2323052282367883, -2.3362501021493713,\n 0.65728071718217163, 2.9330212548803298,\n -0.71201909017782516, 0.5229352837548622,\n 0.66665771035146226, 2.8443864806190855,\n 1.3277532013323454, -2.1473103113906209,\n -1.3938035493334802, -1.4922609138691223,\n 0.28314574426443873, -0.0078944414613061446,\n 0.73587500218353108, -1.3146858293944941,\n -0.75269334032210411, 1.4827201208501692,\n -0.19921351289694036, -1.3085797572141897,\n 1.942328748122542, 0.02699262313677293,\n -1.8330693340430899, 3.4193771225232736,\n -2.3362019798933114, -3.5620725203050472,\n -4.5062648971364343, 1.3022519390933986,\n -1.0656652512055347, -0.55111871394456635,\n 0.57925394525809126, -0.93296812358152814,\n -3.2161203498893749, -3.8995675920839759,\n -0.68139983646196156, 0.3494510546626397,\n -4.5555551215544581, 0.58104896189416777,\n 0.84461271494722856, 2.5894747264134295,\n 0.32942803714790875, 0.40990768160047042,\n 1.2092072534516118, -0.038281538337862839,\n -0.15942869475761728, 0.2319689288056091,\n 1.4883467280967757, -0.86258120705538532,\n -0.99905790382246884, -1.7303241535114227,\n -1.915514566543747, 0.65359994267727539,\n 3.0947444513703499, -1.9377191377288612,\n -0.37688085251892567, -2.0600023229118372,\n 1.8161728837209921, -0.63476372941625703,\n 0.35800793308084394, 0.6960563754583543,\n -2.1085580425488804, -0.20948857622140291,\n -0.45668523418905876, 1.3507111248550778,\n -2.4664893870126186, -2.3999230888455583,\n 1.5317331532322156, -1.1761957225659538,\n -1.3205916599511518, 0.22602704439468746,\n -0.64079759937207748, 3.7327629938775755,\n 0.51906291153559747, 0.32312014183613985,\n 1.8621498186707084, -0.11989349851924225,\n 0.097479400154900323, -2.1457508014679263,\n -4.5859428609613451, -2.4144137001286841,\n 0.22821885959438232, -2.0665941582107541,\n ],\n};\n",
|
|
23
|
+
"/**\n * The `pca_degenerate` dataset of the R package.\n *\n * A small two-column dataset that shows a degenerate PCA case: the two\n * components carry almost the same variance (sdev 17.86 and 17.41), so the\n * principal axes are barely distinguishable and the arrows of `plot_pca()`\n * come out nearly the same length in nearly perpendicular directions.\n *\n * The 16 rows below are the exact doubles of the R data file\n * (`../compstatslib/data/pca_degenerate.rda`), printed at 17 significant\n * digits, which round-trips an IEEE-754 double. **Exported from R, never\n * regenerated** — a JavaScript regeneration would silently change the demo.\n * Source of the printed values: `.claude/plans/pca-fixtures.md`, section F1.\n */\n\nimport type { Point } from \"../core/regression\";\n\n/** The 16 rows of the R `pca_degenerate` data frame, in file order. */\nexport const pcaDegenerate: readonly Point[] = [\n { x: 0.054881767057370599, y: 34.740158547326999 },\n { x: 0.054881767057370599, y: 24.678501253472401 },\n { x: 0.054881767057370599, y: 10.0433633715021 },\n { x: -0.49393590351651601, y: -0.56711159292636404 },\n { x: -0.128057456467252, y: -10.0799512162071 },\n { x: 1.8842740023036699, y: -16.665763263093702 },\n { x: -40.7405650789349, y: -17.763398604241502 },\n { x: -22.812521173521201, y: -17.214580933667602 },\n { x: -7.4456263974523997, y: -16.848702486618301 },\n { x: 7.7383291550917903, y: -16.482824039569099 },\n { x: 22.373467037062099, y: -15.934006368995201 },\n { x: 38.472118707229498, y: -15.7510671454706 },\n { x: 14.872958872552299, y: -16.2998848160445 },\n { x: 0.237820990582013, y: 18.641506877159699 },\n { x: -1.0427535740903999, y: -18.312216274815398 },\n { x: -12.019106985568101, y: -16.848702486618301 },\n];\n",
|
|
24
|
+
"/**\n * The drawing surface that every 2D plot writes to.\n *\n * R's base graphics draw to a current device. Nothing here does: a plot\n * function receives its target and draws only to that target.\n *\n * `Context2D` lists the exact members of `CanvasRenderingContext2D` that this\n * library uses. A real context satisfies the type, and a test can pass any\n * object that implements those members. This matters because happy-dom has no\n * 2D canvas context, so a DOM alone cannot exercise a plot function. See\n * `test/recording-context.ts` for the stub the plot tests use.\n */\n\n/** The part of a canvas 2D context that the plot functions draw through. */\nexport type Context2D = Pick<\n CanvasRenderingContext2D,\n | \"fillStyle\"\n | \"strokeStyle\"\n | \"lineWidth\"\n | \"font\"\n | \"textAlign\"\n | \"textBaseline\"\n | \"save\"\n | \"restore\"\n | \"beginPath\"\n | \"moveTo\"\n | \"lineTo\"\n | \"arc\"\n | \"rect\"\n | \"fillRect\"\n | \"clip\"\n | \"fill\"\n | \"stroke\"\n | \"fillText\"\n | \"setLineDash\"\n | \"translate\"\n | \"rotate\"\n>;\n\n/** A context together with the pixel size of its surface. */\nexport interface RenderTarget {\n readonly ctx: Context2D;\n readonly width: number;\n readonly height: number;\n}\n\n/**\n * What a plot function accepts.\n *\n * Pass a canvas in a browser. Pass a `RenderTarget` to draw through your own\n * context, which is how the tests run without a DOM.\n */\nexport type PlotTarget = HTMLCanvasElement | RenderTarget;\n\n/**\n * Reduce a plot target to a context and a size.\n *\n * A canvas reports the size of its pixel store, which is the size the plot\n * draws in. On a dense screen those two part company: the store has to hold\n * more pixels than the layout does, or the browser stretches the image and\n * softens every edge.\n *\n * This function never touches a canvas the caller passed in — the caller owns\n * it, and resizing it under them would throw away whatever else they drew. So\n * a caller who wants a crisp picture on such a screen does three things:\n *\n * 1. size the store at the layout size times `devicePixelRatio`, and set the\n * CSS width and height to the layout size;\n * 2. call `ctx.scale(ratio, ratio)` once, so drawing carries on in layout\n * pixels;\n * 3. pass `{ ctx, width, height }` with the **layout** size, rather than the\n * canvas.\n *\n * The third step matters. Handing over the canvas would report the store size\n * on top of a context that already scales, and the picture would come out at\n * the square of the ratio. Reporting layout pixels also keeps `eventPixel`\n * right, since it divides the surface size by the size of the client\n * rectangle. `resolveControlTarget` in `../interactive/target.ts` does all\n * three for the canvas it builds itself.\n */\nexport function resolveTarget(target: PlotTarget): RenderTarget {\n if (\"ctx\" in target) {\n return target;\n }\n const ctx = target.getContext(\"2d\");\n if (ctx === null) {\n throw new Error(\n \"plot target: the canvas gave no 2D context. In tests, pass \" +\n \"{ ctx, width, height } instead of a canvas element.\",\n );\n }\n return { ctx, width: target.width, height: target.height };\n}\n",
|
|
25
|
+
"/**\n * Shared 2D scales, ticks, and axis drawing.\n *\n * Every 2D plot in this library maps a world window onto a pixel rectangle and\n * draws the same style of axis box. That work lives here so no plot repeats\n * it. R gets the equivalent from `plot()` and `pretty()`.\n *\n * The scale is pure geometry: it holds numbers and returns numbers. Only\n * `drawAxes` touches a context, and it receives that context as an argument.\n */\n\nimport type { Context2D } from \"./target\";\n\n/** A closed range of world values. */\nexport interface Extent {\n readonly min: number;\n readonly max: number;\n}\n\n/** Pixels reserved outside the plot area for ticks, labels, and titles. */\nexport interface Margins {\n readonly top: number;\n readonly right: number;\n readonly bottom: number;\n readonly left: number;\n}\n\n/** The pixel rectangle that holds the data. */\nexport interface PlotArea {\n readonly left: number;\n readonly right: number;\n readonly top: number;\n readonly bottom: number;\n readonly width: number;\n readonly height: number;\n}\n\n/** What `createScale` needs: a surface size and a world window. */\nexport interface ScaleOptions {\n readonly width: number;\n readonly height: number;\n readonly x: Extent;\n readonly y: Extent;\n readonly margins?: Margins;\n}\n\n/** A two-way map between world values and pixels. */\nexport interface Scale {\n readonly area: PlotArea;\n readonly world: { readonly x: Extent; readonly y: Extent };\n /** Return the pixel column of a world x value. */\n toPixelX(x: number): number;\n /** Return the pixel row of a world y value. Pixel rows grow downward. */\n toPixelY(y: number): number;\n /** Return the world x value of a pixel column. */\n toWorldX(px: number): number;\n /** Return the world y value of a pixel row. */\n toWorldY(py: number): number;\n}\n\n/** What to draw around the plot area. */\nexport interface AxesOptions {\n /** Title under the x axis. Omit for no title. */\n readonly xLabel?: string;\n /** Title beside the y axis, rotated. Omit for no title. */\n readonly yLabel?: string;\n /** Ticks to aim for on each axis. The tick rule may give a few more or less. */\n readonly tickCount?: number;\n /**\n * Draw the box around the plot area. True by default.\n *\n * R's `plot()` draws the box under `frame.plot = TRUE` and drops it under\n * `frame.plot = FALSE`, keeping the two axis lines either way. The t-test\n * plot passes `frame = FALSE`, as its R original does.\n */\n readonly frame?: boolean;\n /**\n * Draw the y axis, its ticks and its labels. True by default.\n *\n * R's `yaxt = \"n\"` drops the vertical axis and leaves the horizontal one.\n * The sampling plot asks for that on each of its three panels, where the\n * height is a density or a count that no reader needs to measure.\n */\n readonly yAxis?: boolean;\n}\n\n/** Margins wide enough for two-digit tick labels and an axis title. */\nexport const DEFAULT_MARGINS: Margins = {\n top: 20,\n right: 20,\n bottom: 44,\n left: 52,\n};\n\nconst DEFAULT_TICK_COUNT = 5;\nconst TICK_LENGTH = 5;\nconst LABEL_GAP = 4;\n/** Pixels between a tick label and the axis title beyond it. */\nconst TITLE_GAP = 26;\nconst AXIS_COLOR = \"#000000\";\nconst AXIS_FONT = \"12px sans-serif\";\n\n/** Read a pair of limits as a range, whichever way round it was written. */\nexport function extentOf(limits: readonly [number, number]): Extent {\n return { min: Math.min(...limits), max: Math.max(...limits) };\n}\n\n/**\n * Build a scale for one surface and one world window.\n *\n * A world range of zero width maps to the middle of the plot area, which keeps\n * a degenerate data set visible instead of dividing by zero.\n */\nexport function createScale(options: ScaleOptions): Scale {\n const margins = options.margins ?? DEFAULT_MARGINS;\n const left = margins.left;\n const top = margins.top;\n const right = options.width - margins.right;\n const bottom = options.height - margins.bottom;\n const area: PlotArea = {\n left,\n right,\n top,\n bottom,\n width: right - left,\n height: bottom - top,\n };\n\n const world = { x: options.x, y: options.y };\n const spanX = options.x.max - options.x.min;\n const spanY = options.y.max - options.y.min;\n\n return {\n area,\n world,\n toPixelX(x) {\n if (spanX === 0) {\n return left + area.width / 2;\n }\n return left + ((x - options.x.min) / spanX) * area.width;\n },\n toPixelY(y) {\n if (spanY === 0) {\n return top + area.height / 2;\n }\n return bottom - ((y - options.y.min) / spanY) * area.height;\n },\n toWorldX(px) {\n if (area.width === 0) {\n return options.x.min;\n }\n return options.x.min + ((px - left) / area.width) * spanX;\n },\n toWorldY(py) {\n if (area.height === 0) {\n return options.y.min;\n }\n return options.y.min + ((bottom - py) / area.height) * spanY;\n },\n };\n}\n\n/**\n * Report whether a pixel lies in the plot area. An edge counts as inside.\n *\n * The interactive components ask this of every click, to keep a click on the\n * margin — the tick labels and the axis titles — from becoming a data point.\n *\n * @param area The plot area, from a scale.\n * @param pixel A position on the surface, in layout pixels.\n * @returns Whether the position is in the area, edges included.\n */\nexport function pixelInArea(\n area: PlotArea,\n pixel: { readonly x: number; readonly y: number },\n): boolean {\n return (\n pixel.x >= area.left &&\n pixel.x <= area.right &&\n pixel.y >= area.top &&\n pixel.y <= area.bottom\n );\n}\n\n/**\n * Choose readable tick values inside an extent.\n *\n * The step is 1, 2, or 5 times a power of ten, as in R's `pretty()`. Unlike\n * `pretty()`, this keeps every tick inside the extent instead of widening the\n * range to round numbers, because the plot window is fixed before the ticks\n * are chosen.\n *\n * @param extent The range to cover. A reversed range gives the same ticks.\n * @param count The number of intervals to aim for.\n * @returns The tick values, ascending. A range of zero width gives one tick.\n */\nexport function prettyTicks(\n extent: Extent,\n count: number = DEFAULT_TICK_COUNT,\n): number[] {\n const min = Math.min(extent.min, extent.max);\n const max = Math.max(extent.min, extent.max);\n if (min === max) {\n return [min];\n }\n\n const step = niceStep((max - min) / Math.max(1, count));\n const decimals = decimalsFor(step);\n // Nudge the bounds by a fraction of a step, so a tick that lands on the\n // bound survives the rounding error of the division.\n const guard = step * 1e-9;\n const first = Math.ceil((min - guard) / step);\n const last = Math.floor((max + guard) / step);\n\n return Array.from({ length: last - first + 1 }, (_unused, index) =>\n roundTo((first + index) * step, decimals),\n );\n}\n\n/**\n * Draw the axis box, the ticks, the tick labels, and the axis titles.\n *\n * @param ctx The context to draw to.\n * @param scale The scale that fixes the plot area and the world window.\n * @param options Titles and tick count. All are optional.\n */\nexport function drawAxes(\n ctx: Context2D,\n scale: Scale,\n options: AxesOptions = {},\n): void {\n const { area } = scale;\n const showYAxis = options.yAxis ?? true;\n const xTicks = prettyTicks(scale.world.x, options.tickCount);\n const yTicks = showYAxis ? prettyTicks(scale.world.y, options.tickCount) : [];\n\n ctx.save();\n ctx.setLineDash([]);\n ctx.strokeStyle = AXIS_COLOR;\n ctx.fillStyle = AXIS_COLOR;\n ctx.lineWidth = 1;\n ctx.font = AXIS_FONT;\n\n ctx.beginPath();\n if (options.frame ?? true) {\n ctx.rect(area.left, area.top, area.width, area.height);\n } else if (showYAxis) {\n // Without the box, the two axis lines still carry the ticks.\n ctx.moveTo(area.left, area.top);\n ctx.lineTo(area.left, area.bottom);\n ctx.lineTo(area.right, area.bottom);\n } else {\n ctx.moveTo(area.left, area.bottom);\n ctx.lineTo(area.right, area.bottom);\n }\n for (const tick of xTicks) {\n const px = scale.toPixelX(tick);\n ctx.moveTo(px, area.bottom);\n ctx.lineTo(px, area.bottom + TICK_LENGTH);\n }\n for (const tick of yTicks) {\n const py = scale.toPixelY(tick);\n ctx.moveTo(area.left, py);\n ctx.lineTo(area.left - TICK_LENGTH, py);\n }\n ctx.stroke();\n\n ctx.textAlign = \"center\";\n ctx.textBaseline = \"top\";\n for (const tick of xTicks) {\n ctx.fillText(\n String(tick),\n scale.toPixelX(tick),\n area.bottom + TICK_LENGTH + LABEL_GAP,\n );\n }\n\n ctx.textAlign = \"right\";\n ctx.textBaseline = \"middle\";\n for (const tick of yTicks) {\n ctx.fillText(\n String(tick),\n area.left - TICK_LENGTH - LABEL_GAP,\n scale.toPixelY(tick),\n );\n }\n\n if (options.xLabel !== undefined) {\n ctx.textAlign = \"center\";\n ctx.textBaseline = \"bottom\";\n ctx.fillText(\n options.xLabel,\n (area.left + area.right) / 2,\n area.bottom + TICK_LENGTH + LABEL_GAP + TITLE_GAP,\n );\n }\n\n if (options.yLabel !== undefined) {\n ctx.save();\n ctx.translate(\n area.left - TICK_LENGTH - LABEL_GAP - TITLE_GAP,\n (area.top + area.bottom) / 2,\n );\n ctx.rotate(-Math.PI / 2);\n ctx.textAlign = \"center\";\n ctx.textBaseline = \"middle\";\n ctx.fillText(options.yLabel, 0, 0);\n ctx.restore();\n }\n\n ctx.restore();\n}\n\n/** Round a step up or down to 1, 2, or 5 times a power of ten. */\nfunction niceStep(rawStep: number): number {\n const magnitude = 10 ** Math.floor(Math.log10(rawStep));\n const normalized = rawStep / magnitude;\n if (normalized < 1.5) {\n return magnitude;\n }\n if (normalized < 3) {\n return 2 * magnitude;\n }\n if (normalized < 7) {\n return 5 * magnitude;\n }\n return 10 * magnitude;\n}\n\n/** Return the decimal places a step of this size needs. */\nfunction decimalsFor(step: number): number {\n return Math.min(20, Math.max(0, -Math.floor(Math.log10(step))));\n}\n\n/** Round away the error of repeated addition, so 0.6 prints as \"0.6\". */\nfunction roundTo(value: number, decimals: number): number {\n return Number(value.toFixed(decimals));\n}\n",
|
|
26
|
+
"/**\n * Shared drawing primitives for the 2D plots.\n *\n * The post-port reuse audit found each of these written out in most of the\n * `plot/` modules, byte for byte. They live here so a change to any of them\n * reaches every plot at once. `axes.ts` keeps what belongs to an axis: the\n * world-to-pixel scale, the tick rule, and the frame.\n *\n * Nothing here reads a scale's world window. Each function takes pixels, so a\n * caller keeps its own world geometry and this file stays about the canvas.\n */\n\nimport type { PlotArea, Scale } from \"./axes\";\nimport type { Context2D } from \"./target\";\nimport type { Point } from \"../core/regression\";\n\n/** R: `lty = \"dotted\"`. */\nexport const DOTTED = [1, 3];\n\n/** R's `col = \"gray\"` is #BEBEBE. The CSS color of that name is darker. */\nconst POINT_COLOR = \"#bebebe\";\n/** R: `pch = 19, cex = 2`. */\nconst POINT_RADIUS = 6;\n\n/** R's `arrows()` draws in `par(\"fg\")`, which is black. */\nconst ARROW_COLOR = \"#000000\";\nconst ARROW_WIDTH = 1;\n/** R: `angle = 30`, the default, measured from the shaft. */\nconst HEAD_ANGLE = Math.PI / 6;\n/**\n * The shortest arrow that still gets drawn, in pixels.\n *\n * `?arrows`: \"The direction of a zero-length arrow is indeterminate, and\n * hence so is the direction of the arrowheads. To allow for rounding error,\n * arrowheads are omitted (with a warning) on any arrow of length less than\n * 1/1000 inch.\" That is this many pixels at the 96 pixels per inch a browser\n * calls an inch. R still draws the shaft, which covers no distance and so\n * paints nothing; this skips the whole arrow, which leaves the same picture\n * and keeps an indeterminate direction from reaching the arrowhead as a NaN.\n *\n * Both callers reach it in ordinary use: a principal component with no spread\n * — identical points, or points on a line — and a matrix with one column near\n * zero, which still has an inverse.\n */\nconst MIN_ARROW_PIXELS = 96 / 1000;\n\n/** Paint the whole surface white, the color of a fresh R device. */\nexport function clearSurface(\n ctx: Context2D,\n width: number,\n height: number,\n): void {\n ctx.setLineDash([]);\n ctx.fillStyle = \"#ffffff\";\n ctx.fillRect(0, 0, width, height);\n}\n\n/**\n * Keep drawing inside the plot area, as base graphics does by default.\n *\n * R's device clips the plot region and canvas clips nothing, so every plot\n * that can put ink outside its window needs this. The caller saves and\n * restores around it: the clip lifts with the state it was set in.\n */\nexport function clipToArea(ctx: Context2D, area: PlotArea): void {\n ctx.beginPath();\n ctx.rect(area.left, area.top, area.width, area.height);\n ctx.clip();\n}\n\n/** Draw each point as a filled dot. R: `pch = 19, cex = 2, col = \"gray\"`. */\nexport function drawDots(\n ctx: Context2D,\n scale: Scale,\n points: readonly Point[],\n): void {\n ctx.save();\n ctx.setLineDash([]);\n ctx.fillStyle = POINT_COLOR;\n for (const point of points) {\n ctx.beginPath();\n ctx.arc(\n scale.toPixelX(point.x),\n scale.toPixelY(point.y),\n POINT_RADIUS,\n 0,\n 2 * Math.PI,\n );\n ctx.fill();\n }\n ctx.restore();\n}\n\n/** How an arrow is drawn. The defaults are R's `arrows()` defaults. */\nexport interface ArrowOptions {\n /** The length of an arrowhead edge, in pixels. R measures it in inches. */\n readonly headLength: number;\n /** The dash pattern. R's `lty` covers the head as well as the shaft. */\n readonly dash?: readonly number[];\n readonly color?: string;\n readonly lineWidth?: number;\n}\n\n/**\n * Draw one arrow between two pixel positions.\n *\n * The head goes at the tip alone, which is what `arrows()` does with its\n * default `code = 2`. An arrow too short to have a direction is skipped\n * whole: see `MIN_ARROW_PIXELS`.\n */\nexport function drawArrow(\n ctx: Context2D,\n tail: readonly [number, number],\n tip: readonly [number, number],\n options: ArrowOptions,\n): void {\n const {\n headLength,\n dash = [],\n color = ARROW_COLOR,\n lineWidth = ARROW_WIDTH,\n } = options;\n\n const length = Math.hypot(tip[0] - tail[0], tip[1] - tail[1]);\n if (!Number.isFinite(length) || length < MIN_ARROW_PIXELS) {\n return;\n }\n\n // Back along the shaft from the tip, then a turn each way for the head.\n const back = Math.atan2(tail[1] - tip[1], tail[0] - tip[0]);\n const edge = (turn: number): readonly [number, number] => [\n tip[0] + headLength * Math.cos(back + turn),\n tip[1] + headLength * Math.sin(back + turn),\n ];\n\n ctx.save();\n ctx.strokeStyle = color;\n ctx.lineWidth = lineWidth;\n ctx.setLineDash([...dash]);\n ctx.beginPath();\n ctx.moveTo(tail[0], tail[1]);\n ctx.lineTo(tip[0], tip[1]);\n const [firstX, firstY] = edge(HEAD_ANGLE);\n const [secondX, secondY] = edge(-HEAD_ANGLE);\n ctx.moveTo(firstX, firstY);\n ctx.lineTo(tip[0], tip[1]);\n ctx.lineTo(secondX, secondY);\n ctx.stroke();\n ctx.restore();\n}\n",
|
|
27
|
+
"/**\n * The matrix inverse plot: a matrix and its inverse, each as a parallelogram\n * with an arrow along each column.\n *\n * This is the drawing half of `plot_matrix_inverse()` in\n * `../compstatslib/R/matrix_inverse_plot.R`. Every number it draws comes from\n * `invertMatrix` in `src/core/matrix.ts`; this module computes no arithmetic\n * of its own. The picture teaches one thing: the area of each parallelogram is\n * the determinant of the matrix that spans it, so a matrix that stretches\n * space has an inverse that squeezes it, and the two areas are reciprocal.\n *\n * Three things here differ from the other plots in this port.\n *\n * **A matrix with no inverse leaves the canvas untouched.** `solve(A)` is the\n * second line of the R function and `plot(NA, ...)` is the fourth, so R stops\n * with an error before it opens a window. Traced on a null device, the\n * singular case makes no graphics call at all. This function does the same and\n * returns the report, which names the reason. A caller that redraws while a\n * control moves has to clear its own surface, because this function will not.\n *\n * **The window does not hold its aspect.** The R call passes no `asp`, unlike\n * `plot_pca()`, so a square in the data is a square on screen only on a square\n * plot area. Measured in R, `par(\"usr\")` reads -3.24 3.24 -3.24 3.24 on a 10\n * by 5 inch device and the same on a 5 by 5 inch one. The parallelograms shear\n * with the surface, and that is R's picture.\n *\n * **The arrowhead is a physical size.** R asks for `length = 0.25`, measured\n * in inches, so the head does not shrink with a short vector. A vector short\n * enough loses its arrow altogether: see `MIN_ARROW_PIXELS` in `draw.ts`.\n */\n\nimport { invertMatrix } from \"../core/matrix\";\nimport type { Matrix2, MatrixInversion } from \"../core/matrix\";\nimport { createScale, drawAxes } from \"./axes\";\nimport type { Extent, Scale } from \"./axes\";\nimport { clearSurface, clipToArea, drawArrow } from \"./draw\";\nimport { resolveTarget } from \"./target\";\nimport type { Context2D, PlotTarget } from \"./target\";\n\n/** R: `xlim = c(-3, 3)` and `ylim = c(-3, 3)`, fixed in the function body. */\nconst WORLD: Extent = { min: -3, max: 3 };\n\n/** R's `rgb(1, 0, 0, 0.1)`, the fill of the matrix. */\nconst MATRIX_FILL = \"#FF00001A\";\n/** R's `rgb(0, 0, 1, 0.1)`, the fill of the inverse. */\nconst INVERSE_FILL = \"#0000FF1A\";\n/** R's `rgb(0, 0, 0, 0.3)`, the border of both parallelograms. */\nconst BORDER_COLOR = \"#0000004D\";\n/** R's `arrows()` draws in `par(\"fg\")`, which is black, at `lwd = 1`. */\nconst ARROW_COLOR = \"#000000\";\nconst LINE_WIDTH = 1;\n/**\n * The length of an arrowhead edge, in pixels.\n *\n * R asks for `length = 0.25`, measured in inches, which is 24 at the 96 pixels\n * an inch a browser assumes. The value does not follow the length of the arrow\n * or the size of the window, so a short vector carries a large head — which is\n * what R draws.\n */\nconst HEAD_LENGTH = 24;\n\n/**\n * Build the scale that `plotMatrixInverse` draws through.\n *\n * The window is R's fixed -3 to 3 on both axes. The two axes are scaled\n * separately, as R scales them: a wide surface makes a wide picture. Unlike R,\n * the limits are the literal ones — R pads a range by 4% first, and this port\n * drops that padding everywhere.\n *\n * @param width The pixel width of the surface.\n * @param height The pixel height of the surface.\n * @returns The map between world values and pixels.\n */\nexport function matrixInverseScale(width: number, height: number): Scale {\n return createScale({ width, height, x: WORLD, y: WORLD });\n}\n\n/**\n * Draw the matrix and its inverse.\n *\n * A matrix with no inverse draws nothing — not the axes, not the background.\n * See the note on this module: R stops before it draws, and the report this\n * function returns says why.\n *\n * @param target A canvas, or a context and a size. See `./target.ts`.\n * @param matrix The matrix, as two columns. See `Matrix2`.\n * @returns The determinant, the inverse, and the singularity, exactly as\n * `invertMatrix` reports them. R returns nothing, but a browser caller needs\n * the numbers it has just drawn — and needs the reason when nothing was\n * drawn.\n */\nexport function plotMatrixInverse(\n target: PlotTarget,\n matrix: Matrix2,\n): MatrixInversion {\n const inversion = invertMatrix(matrix);\n // The target is resolved first, so a target that cannot be drawn to reports\n // that whether or not the matrix has an inverse. Resolving draws nothing.\n const { ctx, width, height } = resolveTarget(target);\n\n if (inversion.inverse === null) {\n return inversion;\n }\n\n const scale = matrixInverseScale(width, height);\n clearSurface(ctx, width, height);\n // R: `frame.plot = FALSE` drops the box and keeps both axes. R names the two\n // titles explicitly, because `plot(NA, ...)` otherwise deparses the missing\n // value it was passed into them and the axes read \"Index\" and \"NA\".\n drawAxes(ctx, scale, { frame: false, xLabel: \"x\", yLabel: \"y\" });\n\n // R's device clips the plot region; canvas clips nothing. This is not a\n // detail here: the inverse of a nearly singular matrix has entries in the\n // hundreds, and the window is three units wide.\n const { area } = scale;\n ctx.save();\n clipToArea(ctx, area);\n\n drawMatrix(ctx, scale, matrix, MATRIX_FILL);\n drawMatrix(ctx, scale, inversion.inverse, INVERSE_FILL);\n\n ctx.restore();\n return inversion;\n}\n\n/**\n * Draw one matrix: the parallelogram its columns span, then an arrow along\n * each column. R's `plot_matrix_det()`.\n */\nfunction drawMatrix(\n ctx: Context2D,\n scale: Scale,\n matrix: Matrix2,\n fill: string,\n): void {\n drawParallelogram(ctx, scale, matrix, fill);\n drawColumnArrow(ctx, scale, matrix.x1, matrix.y1);\n drawColumnArrow(ctx, scale, matrix.x2, matrix.y2);\n}\n\n/**\n * Draw the parallelogram the two columns span.\n *\n * R's corners are the origin, the first column, the sum of the columns, and\n * the second column. `polygon()` closes the outline itself; this returns to\n * the origin to close it, which is the same shape.\n */\nfunction drawParallelogram(\n ctx: Context2D,\n scale: Scale,\n matrix: Matrix2,\n fill: string,\n): void {\n const corners = [\n [0, 0],\n [matrix.x1, matrix.y1],\n [matrix.x1 + matrix.x2, matrix.y1 + matrix.y2],\n [matrix.x2, matrix.y2],\n [0, 0],\n ] as const;\n\n ctx.save();\n ctx.setLineDash([]);\n ctx.fillStyle = fill;\n ctx.strokeStyle = BORDER_COLOR;\n ctx.lineWidth = LINE_WIDTH;\n ctx.beginPath();\n corners.forEach(([x, y], index) => {\n const px = scale.toPixelX(x);\n const py = scale.toPixelY(y);\n if (index === 0) {\n ctx.moveTo(px, py);\n } else {\n ctx.lineTo(px, py);\n }\n });\n ctx.fill();\n ctx.stroke();\n ctx.restore();\n}\n\n/**\n * Draw one arrow from the origin to a column vector.\n *\n * The head goes at the far end alone, which is what `arrows()` does with its\n * default `code = 2`.\n */\nfunction drawColumnArrow(\n ctx: Context2D,\n scale: Scale,\n x: number,\n y: number,\n): void {\n const tail = [scale.toPixelX(0), scale.toPixelY(0)] as const;\n const tip = [scale.toPixelX(x), scale.toPixelY(y)] as const;\n\n drawArrow(ctx, tail, tip, {\n headLength: HEAD_LENGTH,\n color: ARROW_COLOR,\n lineWidth: LINE_WIDTH,\n });\n}\n",
|
|
28
|
+
"/**\n * Number formatting shared by the plots.\n *\n * R writes numbers into a plot through `round(x, 2)` and its own coercion to\n * text. Both the regression stats block and the t-test error matrix need that\n * same treatment, so it lives here rather than in either of them.\n */\n\n/**\n * Round to two decimals for display, and report a missing value as R does.\n *\n * R prints `round(x, 2)`, which drops a trailing zero, so 2.50 reads as \"2.5\".\n * R rounds half to even and this rounds half away from zero; the two differ\n * only on an exact half at the third decimal.\n *\n * @param value The number to show. Null and non-finite read as \"NA\".\n * @returns The text to draw.\n */\nexport function formatStat(value: number | null): string {\n if (value === null || !Number.isFinite(value)) {\n return \"NA\";\n }\n return String(Math.round(value * 100) / 100);\n}\n\n/** How many significant digits R keeps when it turns a double into text. */\nconst R_SIGNIFICANT_DIGITS = 15;\n\n/**\n * Write a number the way R's `as.character()` does.\n *\n * R keeps 15 significant digits and drops what follows, so `1 - 0.07` prints\n * as \"0.93\". JavaScript's `String()` instead keeps every digit needed to\n * identify the double, and prints the same subtraction as\n * \"0.9299999999999999\". The t-test error matrix draws `1 - alpha` untouched,\n * and alpha steps in hundredths, so without this the panel shows that run of\n * nines at one of its ten settings.\n *\n * @param value The number to show.\n * @returns The text to draw. Non-finite reads as \"NA\", as elsewhere.\n */\nexport function formatNumber(value: number): string {\n if (!Number.isFinite(value)) {\n return \"NA\";\n }\n return String(Number(value.toPrecision(R_SIGNIFICANT_DIGITS)));\n}\n",
|
|
29
|
+
"/**\n * The logistic-regression plot: the points, the fitted probability curve, and\n * a small block of statistics.\n *\n * This is the drawing half of `plot_logit()` and `plot_points_logit()` in\n * `../compstatslib/R/logit_plot.R`. Every number it draws comes from\n * `logisticRegression` in `src/core/logit.ts`; this module computes no\n * statistics of its own.\n */\n\nimport { logisticRegression, predictLogit } from \"../core/logit\";\nimport type { LogitFit } from \"../core/logit\";\nimport type { Point } from \"../core/regression\";\nimport { createScale, drawAxes } from \"./axes\";\nimport type { Extent, Scale } from \"./axes\";\nimport { DOTTED, clearSurface, clipToArea, drawDots } from \"./draw\";\nimport { formatStat } from \"./format\";\nimport { resolveTarget } from \"./target\";\nimport type { Context2D, PlotTarget } from \"./target\";\n\n/**\n * Which corner of the plot area the statistics block sits in.\n *\n * R's `legend()` takes nine positions. These four are the ones a block of\n * text reads well in, and one of them is R's own default here.\n */\nexport type LegendLocation =\n | \"topleft\"\n | \"topright\"\n | \"bottomleft\"\n | \"bottomright\";\n\n/** What to draw. R's defaults throughout. */\nexport interface PlotLogitOptions {\n /** Fit the model and draw its curve. True by default. */\n readonly regression?: boolean;\n /** List the statistics in a corner. Needs `regression`. True by default. */\n readonly stats?: boolean;\n /** Left edge of the window, before the data widen it. R's `min_x`, 0. */\n readonly minX?: number;\n /** Right edge of the window, before the data widen it. R's `max_x`, 1. */\n readonly maxX?: number;\n /** Corner for the statistics. R's `legend_loc`, \"topleft\". */\n readonly legendLoc?: LegendLocation;\n}\n\n/** R: `ylim = c(0, 1)`, the range of a probability. */\nconst WORLD_Y: Extent = { min: 0, max: 1 };\n/** R: `min_x = 0, max_x = 1`, before the data widen them. */\nconst DEFAULT_MIN_X = 0;\nconst DEFAULT_MAX_X = 1;\n\nconst HALF_LINE_COLOR = \"lightgray\";\nconst CURVE_COLOR = \"cornflowerblue\";\nconst TEXT_COLOR = \"#000000\";\n/** R: `lwd = 2` on the curve. */\nconst CURVE_WIDTH = 2;\n/** R: `seq(min_x, max_x, len = 500)`. */\nconst CURVE_SAMPLES = 500;\n/** R: `abline(h = 0.5)`, the probability that divides the two outcomes. */\nconst HALF = 0.5;\n\nconst STATS_FONT = \"12px monospace\";\nconst STATS_LINE_HEIGHT = 15;\nconst STATS_PADDING = 8;\n/** The width R pads its labels to, from `\"Coefficient: \"`. */\nconst STATS_LABEL_WIDTH = 11;\n\n/**\n * Build the scale that `plotLogit` draws through.\n *\n * Unlike the regression plot, whose window is fixed, this one follows the\n * data: R starts at `min_x` and `max_x` and then widens them to cover every\n * point (`max_x <- max(max_x, points[[x_name]])`). The vertical window is\n * always a probability, 0 to 1.\n *\n * The interactive layer reads the scale from here, so the window stays\n * defined in one place. Nothing outside this module may restate it.\n *\n * @param width The pixel width of the surface.\n * @param height The pixel height of the surface.\n * @param points The observations the window has to cover.\n * @param options The same options `plotLogit` takes.\n * @returns The map between world values and pixels.\n */\nexport function logitScale(\n width: number,\n height: number,\n points: readonly Point[],\n options: PlotLogitOptions = {},\n): Scale {\n // A non-finite x is a missing value the core drops (na.omit); it must not\n // reach Math.min, where one NaN would poison the whole window.\n const finite = points.filter((point) => Number.isFinite(point.x));\n const min = finite.reduce(\n (low, point) => Math.min(low, point.x),\n options.minX ?? DEFAULT_MIN_X,\n );\n const max = finite.reduce(\n (high, point) => Math.max(high, point.x),\n options.maxX ?? DEFAULT_MAX_X,\n );\n return createScale({ width, height, x: { min, max }, y: WORLD_Y });\n}\n\n/**\n * Draw the points and their fitted probability curve.\n *\n * With fewer than two points the function draws what it has and fits nothing.\n * R stops at the same two places: it returns before `glm()` on an empty frame,\n * and again on a single point. The core would fit that single point — R's\n * `glm()` does, reporting a saturated intercept and no slope — but the plot\n * never asks it to, so neither does this.\n *\n * @param target A canvas, or a context and a size. See `./target.ts`.\n * @param points The observations. Each y must be 0 or 1.\n * @param options What to show and how wide to open the window.\n * @returns The fit the plot drew, or null if it drew none. R returns the\n * points instead, which the caller already holds.\n */\nexport function plotLogit(\n target: PlotTarget,\n points: readonly Point[],\n options: PlotLogitOptions = {},\n): LogitFit | null {\n const { ctx, width, height } = resolveTarget(target);\n const showRegression = options.regression ?? true;\n const showStats = options.stats ?? true;\n const legendLocation = options.legendLoc ?? \"topleft\";\n const scale = logitScale(width, height, points, options);\n\n clearSurface(ctx, width, height);\n drawAxes(ctx, scale, { xLabel: \"x\", yLabel: \"y\" });\n\n drawDots(ctx, scale, points);\n // R draws this after the points, and draws it on an empty frame too:\n // plot_points_logit() ends with abline(h = 0.5) whether or not it had data.\n drawHalfLine(ctx, scale);\n\n if (points.length < 2 || !showRegression) {\n return null;\n }\n\n const fit = logisticRegression(points);\n if (fit === null) {\n return null;\n }\n\n drawCurve(ctx, scale, fit);\n if (showStats) {\n drawStats(ctx, scale, fit, legendLocation);\n }\n return fit;\n}\n\n/** Draw the dotted line at one half. R: `abline(h = 0.5)`. */\nfunction drawHalfLine(ctx: Context2D, scale: Scale): void {\n const { area } = scale;\n ctx.save();\n ctx.strokeStyle = HALF_LINE_COLOR;\n ctx.lineWidth = 1;\n ctx.setLineDash(DOTTED);\n ctx.beginPath();\n ctx.moveTo(area.left, scale.toPixelY(HALF));\n ctx.lineTo(area.right, scale.toPixelY(HALF));\n ctx.stroke();\n ctx.restore();\n}\n\n/**\n * Draw the fitted probability across the window.\n *\n * R builds the curve from 500 predictions spread over the same window it\n * plotted, so this samples the same number at the same places. The curve stays\n * inside the window by construction, since a probability cannot leave 0 to 1,\n * but it is clipped to the plot area all the same: R's base graphics clip\n * every line to the plot region and canvas clips nothing.\n */\nfunction drawCurve(ctx: Context2D, scale: Scale, fit: LogitFit): void {\n const { area, world } = scale;\n const span = world.x.max - world.x.min;\n const samples = Array.from({ length: CURVE_SAMPLES }, (_unused, index) => {\n const x = world.x.min + (span * index) / (CURVE_SAMPLES - 1);\n return {\n px: scale.toPixelX(x),\n py: scale.toPixelY(predictLogit(fit, x)),\n };\n });\n\n ctx.save();\n clipToArea(ctx, area);\n ctx.strokeStyle = CURVE_COLOR;\n ctx.lineWidth = CURVE_WIDTH;\n ctx.setLineDash([]);\n ctx.beginPath();\n for (const [index, { px, py }] of samples.entries()) {\n if (index === 0) {\n ctx.moveTo(px, py);\n } else {\n ctx.lineTo(px, py);\n }\n }\n ctx.stroke();\n ctx.restore();\n}\n\n/**\n * List the statistics in one corner, in a monospace block.\n *\n * R draws this with `legend(legend_loc, ...)` under `par(family = \"mono\")`,\n * pasting three padded labels against `round(value, 2)`. An aliased slope\n * reads \"NA\", because that is what R's `round(NA, 2)` pastes.\n *\n * A block anchored to the bottom is held inside the plot area rather than\n * allowed to ride up past the top of it. R relies on base graphics clipping\n * text at the plot region, which canvas does not do.\n */\nfunction drawStats(\n ctx: Context2D,\n scale: Scale,\n fit: LogitFit,\n location: LegendLocation,\n): void {\n const rows: readonly (readonly [string, number | null])[] = [\n [\"Intercept\", fit.intercept],\n [\"Coefficient\", fit.slope],\n [\"AIC\", fit.aic],\n ];\n const { area } = scale;\n const onRight = location === \"topright\" || location === \"bottomright\";\n const atBottom = location === \"bottomleft\" || location === \"bottomright\";\n const x = onRight ? area.right - STATS_PADDING : area.left + STATS_PADDING;\n const top = atBottom\n ? Math.max(\n area.top + STATS_PADDING,\n area.bottom - STATS_PADDING - rows.length * STATS_LINE_HEIGHT,\n )\n : area.top + STATS_PADDING;\n\n ctx.save();\n ctx.setLineDash([]);\n ctx.fillStyle = TEXT_COLOR;\n ctx.font = STATS_FONT;\n ctx.textAlign = onRight ? \"right\" : \"left\";\n ctx.textBaseline = \"top\";\n for (const [index, [label, value]] of rows.entries()) {\n ctx.fillText(\n `${label.padEnd(STATS_LABEL_WIDTH)}: ${formatStat(value)}`,\n x,\n top + index * STATS_LINE_HEIGHT,\n );\n }\n ctx.restore();\n}\n",
|
|
30
|
+
"/**\n * One row per sample, each showing what that sample says about the mean it\n * came from — the drawing half of `plot_sample_ci()` in\n * `../compstatslib/R/sample_ci_plot.R`.\n *\n * The population is simulated, so its mean is known and drawn as a vertical\n * line. Each sample gets a row: a wide 99% interval, a 95% interval over it,\n * and a diamond at the sample mean. Rows whose intervals miss the true mean\n * are drawn again in coral over the blue, which is the whole lesson of the\n * picture — at 95% confidence, about one row in twenty should miss.\n *\n * Drawing order is faithful to R and is not incidental. R draws every row in\n * the good colors, then draws the missing ones again on top, then the mean\n * line last:\n *\n * ```r\n * segments_ci(..., good = TRUE) # every row, skyblue\n * segments_ci(...[bad], bad, good = FALSE) # the misses again, coral\n * abline(v = mean(population_data))\n * ```\n *\n * Within one of those calls R works in three vectorised passes — every 99%\n * span, then every 95% span, then every point — rather than finishing a row\n * at a time. This does the same, so overlapping neighbours stack as they do\n * in R.\n *\n * The statistics all come from `simulateSampleCi` in `src/core/sampling.ts`;\n * this module computes none of its own.\n */\n\nimport type { Rng } from \"../core/rng\";\nimport { seededRng } from \"../core/rng\";\nimport { simulateSampleCi } from \"../core/sampling\";\nimport type {\n SampleCiOptions,\n SampleCiSimulation,\n SampleInterval,\n} from \"../core/sampling\";\nimport { createScale, drawAxes } from \"./axes\";\nimport type { Scale } from \"./axes\";\nimport { clearSurface, clipToArea } from \"./draw\";\nimport { resolveTarget } from \"./target\";\nimport type { Context2D, PlotTarget } from \"./target\";\n\n/** What to draw. R's `plot_sample_ci()` arguments, plus the generator. */\nexport interface PlotSampleCiOptions extends SampleCiOptions {\n /**\n * Where the randomness comes from.\n *\n * One call draws one picture, so unlike the sampling plot there is nothing\n * to carry between calls and the default is harmless. It is still built the\n * same way — a stream seeded once from `Math.random()` — so a page that\n * says nothing about seeds gets a fresh picture each time, and\n * `seededRng(42)` repeats one exactly.\n */\n readonly rng?: Rng;\n}\n\n/**\n * R's palette names, resolved with `col2rgb()`.\n *\n * Only `lightskyblue` and `lightcoral` are also CSS color names. The other\n * four are R's own palette and mean nothing to a browser, so all six are\n * written as literals — the same choice slice 1 made when R's `gray` turned\n * out to be `#bebebe` rather than the darker CSS `gray`.\n */\nconst GOOD_COLORS = {\n span99: \"#87cefa\", // lightskyblue\n span95: \"#6ca6cd\", // skyblue3\n point: \"#4a708b\", // skyblue4\n} as const;\n\nconst BAD_COLORS = {\n span99: \"#f08080\", // lightcoral\n span95: \"#cd5b45\", // coral3\n point: \"#8b3e2f\", // coral4\n} as const;\n\nconst MEAN_LINE_COLOR = \"#000000\";\n/** R: `lwd = 3` on both spans. */\nconst SPAN_WIDTH = 3;\n/**\n * Half the height of a diamond, in pixels.\n *\n * R draws `pch = 18, cex = 0.6`. At the size this port gives `cex = 1`\n * elsewhere that would be under two pixels, which disappears on a dense\n * screen. Two and a half keeps the diamond visible and still fits the row\n * pitch at R's default of a hundred rows.\n */\nconst POINT_RADIUS = 2.5;\n\n/**\n * Build the scale the plot draws through.\n *\n * The window is R's: half a standard deviation of the population each side of\n * its mean, which is deliberately too narrow to hold every interval — the\n * ones that run off the edge are the interesting ones. Rows count upward from\n * one, so the first sample sits at the foot of the panel.\n *\n * A population with no spread leaves the width undefined. R stops there, on\n * a non-finite `xlim`. This opens a unit window around the mean instead, so\n * the frame and its axes still draw.\n *\n * @param width The pixel width of the surface.\n * @param height The pixel height of the surface.\n * @param simulation What `simulateSampleCi` returned.\n * @returns The map between world values and pixels.\n */\nexport function sampleCiScale(\n width: number,\n height: number,\n simulation: SampleCiSimulation,\n): Scale {\n const center = Number.isFinite(simulation.populationMean)\n ? simulation.populationMean\n : 0;\n const half = Number.isFinite(simulation.populationSd)\n ? simulation.populationSd / 2\n : 0.5;\n\n return createScale({\n width,\n height,\n x: { min: center - half, max: center + half },\n y: { min: 1, max: Math.max(1, simulation.intervals.length) },\n });\n}\n\n/**\n * Simulate a population, sample it, and draw what each sample says.\n *\n * A sample too small to have a spread gives an interval of NaN, which R would\n * report as NA. Such a row is left undrawn rather than turned into a shape at\n * a meaningless coordinate; the row still appears in the returned simulation,\n * where the caller can see the NaN for what it is.\n *\n * @param target A canvas, or a context and a size. See `./target.ts`.\n * @param options The sizes, the population's distribution, and the generator.\n * @returns The simulation drawn — the population's mean and spread, and every\n * sample's interval — so a caller need not run it again to read it.\n * @throws RangeError If the sample size is larger than the population, or a\n * count is negative or fractional. Both come from the core, which is where\n * R's own refusals live.\n */\nexport function plotSampleCi(\n target: PlotTarget,\n options: PlotSampleCiOptions = {},\n): SampleCiSimulation {\n const { ctx, width, height } = resolveTarget(target);\n const {\n rng = seededRng(Math.floor(Math.random() * 0x100000000)),\n ...simulationOptions\n } = options;\n\n const simulation = simulateSampleCi(rng, simulationOptions);\n const scale = sampleCiScale(width, height, simulation);\n\n clearSurface(ctx, width, height);\n drawAxes(ctx, scale, {\n xLabel: \"Confidence Intervals\",\n yLabel: \"Samples\",\n });\n\n // R's device clips the plot region; canvas clips nothing. The window is\n // narrower than the widest interval by design, so this is load-bearing.\n const { area } = scale;\n ctx.save();\n clipToArea(ctx, area);\n\n const rows = simulation.intervals\n .map((interval, index) => ({ interval, row: index + 1 }))\n .filter(({ interval }) => isDrawable(interval));\n\n drawRows(ctx, scale, rows, GOOD_COLORS);\n drawRows(\n ctx,\n scale,\n rows.filter(({ interval }) => interval.excludesPopulationMean),\n BAD_COLORS,\n );\n drawPopulationMean(ctx, scale, simulation.populationMean);\n\n ctx.restore();\n return simulation;\n}\n\n/** One row's worth of drawing, and which sample it belongs to. */\ninterface Row {\n readonly interval: SampleInterval;\n readonly row: number;\n}\n\n/** Whether a row has finite bounds to draw. */\nfunction isDrawable(interval: SampleInterval): boolean {\n return (\n Number.isFinite(interval.mean) &&\n Number.isFinite(interval.ci95.low) &&\n Number.isFinite(interval.ci95.high) &&\n Number.isFinite(interval.ci99.low) &&\n Number.isFinite(interval.ci99.high)\n );\n}\n\n/**\n * Draw a set of rows in one color scheme, in R's three passes.\n *\n * The wider interval goes down first so the narrower one reads on top of it,\n * and the sample mean goes over both.\n */\nfunction drawRows(\n ctx: Context2D,\n scale: Scale,\n rows: readonly Row[],\n colors: { span99: string; span95: string; point: string },\n): void {\n ctx.save();\n ctx.setLineDash([]);\n ctx.lineWidth = SPAN_WIDTH;\n\n ctx.strokeStyle = colors.span99;\n for (const { interval, row } of rows) {\n drawSpan(ctx, scale, interval.ci99.low, interval.ci99.high, row);\n }\n\n ctx.strokeStyle = colors.span95;\n for (const { interval, row } of rows) {\n drawSpan(ctx, scale, interval.ci95.low, interval.ci95.high, row);\n }\n\n ctx.fillStyle = colors.point;\n for (const { interval, row } of rows) {\n drawDiamond(ctx, scale, interval.mean, row);\n }\n\n ctx.restore();\n}\n\n/** Draw one horizontal span at a row. R: `segments(low, i, high, i)`. */\nfunction drawSpan(\n ctx: Context2D,\n scale: Scale,\n low: number,\n high: number,\n row: number,\n): void {\n const y = scale.toPixelY(row);\n ctx.beginPath();\n ctx.moveTo(scale.toPixelX(low), y);\n ctx.lineTo(scale.toPixelX(high), y);\n ctx.stroke();\n}\n\n/** Draw the sample mean. R: `points(mean, i, pch = 18)`, a filled diamond. */\nfunction drawDiamond(\n ctx: Context2D,\n scale: Scale,\n at: number,\n row: number,\n): void {\n const x = scale.toPixelX(at);\n const y = scale.toPixelY(row);\n ctx.beginPath();\n ctx.moveTo(x, y - POINT_RADIUS);\n ctx.lineTo(x + POINT_RADIUS, y);\n ctx.lineTo(x, y + POINT_RADIUS);\n ctx.lineTo(x - POINT_RADIUS, y);\n ctx.fill();\n}\n\n/** Draw the true mean down the panel. R: `abline(v = mean(population))`. */\nfunction drawPopulationMean(\n ctx: Context2D,\n scale: Scale,\n at: number,\n): void {\n if (!Number.isFinite(at)) {\n return;\n }\n const { area } = scale;\n const x = scale.toPixelX(at);\n\n ctx.save();\n ctx.setLineDash([]);\n ctx.strokeStyle = MEAN_LINE_COLOR;\n ctx.lineWidth = 1;\n ctx.beginPath();\n ctx.moveTo(x, area.top);\n ctx.lineTo(x, area.bottom);\n ctx.stroke();\n ctx.restore();\n}\n",
|
|
31
|
+
"/**\n * The three-panel sampling demonstration: a population, the samples drawn\n * from it, and how their statistic scatters.\n *\n * This is the drawing half of `plot_sampling()` in\n * `../compstatslib/R/sampling_plot.R`. The panels, top to bottom:\n *\n * 1. the population's density, dotted;\n * 2. this draw's samples — each sample's own density in translucent gray,\n * with the density of all of them pooled drawn over the top;\n * 3. a histogram of every statistic drawn so far, this call and all before it.\n *\n * All three share one horizontal window, frozen on the first call at the range\n * of the population. Later calls keep it however wide the population passed to\n * them happens to be, which is what lets the picture stay still while the\n * statistics pile up. A bar or a curve outside that window is clipped.\n *\n * **The state is the caller's.** R returns a `vars` list and its gadget hands\n * it back on the next call, which is how the window stays frozen and the\n * statistics accumulate. Nothing is kept here between calls: `plotSampling`\n * takes the state it was given, returns the state it made, and the caller —\n * `interactiveSampling`, or a demo page — holds it in between. That is the\n * port plan's rule against hidden accumulation.\n *\n * **`replot_population` is not ported.** R declares and documents it, and\n * `interactive_sampling()` passes `FALSE` for it, but `plot_sampling()`'s body\n * never reads it: the population panel is always redrawn. Porting a parameter\n * that does nothing would carry the defect forward as if it were a feature.\n */\n\nimport { extent, mean } from \"../core/arith\";\nimport { histogram } from \"../core/histogram\";\nimport type { Histogram } from \"../core/histogram\";\nimport { kernelDensity } from \"../core/kde\";\nimport type {\n KernelDensityEstimate,\n KernelDensityOptions,\n} from \"../core/kde\";\nimport { seededRng } from \"../core/rng\";\nimport type { Rng } from \"../core/rng\";\nimport { drawSamples } from \"../core/sampling\";\nimport { createScale, drawAxes } from \"./axes\";\nimport type { Extent, Scale } from \"./axes\";\nimport { DOTTED, clearSurface, clipToArea } from \"./draw\";\nimport { resolveTarget } from \"./target\";\nimport type { Context2D, PlotTarget } from \"./target\";\n\n/** Which of the three stacked panels a scale belongs to. */\nexport type SamplingPanel = \"population\" | \"samples\" | \"statistic\";\n\n/**\n * What one draw hands to the next.\n *\n * This is R's `vars` list, less the entries that are arguments anyway: R keeps\n * the population, the sample size and the statistic in there as well, and this\n * port takes all three afresh on every call, so holding them twice would let\n * the two copies disagree.\n */\nexport interface SamplingState {\n /** The left edge of the frozen window. R's `xmin`. */\n readonly xMin: number;\n /** The right edge of the frozen window. R's `xmax`. */\n readonly xMax: number;\n /** Every statistic drawn so far, oldest first. R's `sample_theta`. */\n readonly sampleTheta: readonly number[];\n}\n\n/** What to draw. The interactive layer forwards these untouched. */\nexport interface PlotSamplingOptions {\n /**\n * How many values in each sample. R's `sample_size`.\n *\n * R's `plot_sampling()` has no default here and fails without one; its\n * gadget defaults to 10. This port takes the gadget's default so that\n * `plotSampling(target, population)` draws something, which is the\n * no-argument-works property the port plan asks bundled demos to keep.\n */\n readonly sampleSize?: number;\n /** How many samples this draw takes. R's `reps`, 1 by default. */\n readonly reps?: number;\n /** The statistic to take from each sample. R's `theta`, the mean. */\n readonly theta?: (sample: readonly number[]) => number;\n /**\n * Where the randomness comes from.\n *\n * R reads one global stream, so its repeated draws carry on where the last\n * left off. A caller who draws more than once must pass one generator and\n * keep passing it — the default builds a new one per call, and two calls\n * that each build their own would each start from the beginning.\n *\n * The default is seeded from `Math.random()`, so a demo that says nothing\n * about seeds still gets a different draw each time, as an R session does.\n * Only the seed comes from there: pass `seededRng(42)` and every draw down\n * that stream repeats exactly.\n */\n readonly rng?: Rng;\n /**\n * What the last call returned, or nothing on the first call.\n *\n * R's `vars` argument, which its gadget caches. This is what freezes the\n * window and accumulates the statistics.\n */\n readonly state?: SamplingState | null;\n /**\n * Where every density curve puts its grid.\n *\n * `\"data\"`, the default, is R: each `density()` call spreads its 512 grid\n * points over the range of the values it was given. That is right while the\n * values and the window are of one size.\n *\n * `\"frozen\"` spreads them over the drawn window instead, through R's own\n * `from` and `to` arguments. A population that reaches far outside the\n * window needs this: at 512 points over a range a thousand times the window,\n * one grid step is wider than the whole panel, and the curve draws as a\n * straight line. The samples take the frozen grid as well, for the same\n * reason and so that every curve in the picture is comparable.\n */\n readonly densityWindow?: \"data\" | \"frozen\";\n}\n\n/** Everything this draw produced, for the caller to hold and to read. */\nexport interface PlotSamplingResult {\n /** The state to hand back on the next call. */\n readonly state: SamplingState;\n /** The samples this call drew, in draw order. */\n readonly samples: readonly (readonly number[])[];\n /** The statistic of each of those samples. */\n readonly thetas: readonly number[];\n /**\n * The histogram the third panel drew, or null when there was nothing to\n * count. Handed back so a caller need not bin the statistics again.\n *\n * It counts the statistics inside the window, not the whole pile. The panel\n * clips a bar outside the window anyway, and a statistic it cannot draw must\n * not set the width of the cells either — see `plotSampling`.\n */\n readonly histogram: Histogram | null;\n}\n\n/** R: `sample_size = 10` in `interactive_sampling()`. */\nconst DEFAULT_SAMPLE_SIZE = 10;\n/** R: `reps = 1`. */\nconst DEFAULT_REPS = 1;\n/** How many panels are stacked, and in which order. */\nconst PANELS: readonly SamplingPanel[] = [\"population\", \"samples\", \"statistic\"];\n\n/**\n * Margins inside each panel.\n *\n * R uses `mar = c(2, 2, 1, 1)` under `cex = 0.5`, which is about twelve pixels\n * below and beside each panel. The bottom here is wider because that is where\n * the axis, its ticks and their labels go, and the sides are wide enough to\n * hold the first and last tick label without running off the surface.\n */\nconst PANEL_MARGINS = { top: 8, right: 24, bottom: 28, left: 24 };\n\nconst CURVE_COLOR = \"#000000\";\nconst TEXT_COLOR = \"#000000\";\n/** R: `rgb(0.7, 0.7, 0.7, 0.5)`, one curve per sample. */\nconst SAMPLE_CURVE_COLOR = \"rgba(179, 179, 179, 0.5)\";\n/** R's `hist()` default `col`, with `border = FALSE`. */\nconst BAR_COLOR = \"lightgray\";\n/** R: `lwd = 2` on the population and pooled curves. */\nconst CURVE_WIDTH = 2;\n/** R: `lwd = 1` on each sample's own curve. */\nconst SAMPLE_CURVE_WIDTH = 1;\n\n/**\n * Panel labels.\n *\n * R writes the first two through `boldly()`, which wraps them in `bold()`, and\n * the third as a plain string. R's `cex = 0.5` would put these at about six\n * pixels: the t-test plot already learned that such text is unreadable on a\n * canvas, so these follow that panel's choice of legibility over fidelity.\n */\nconst LABEL_FONT = \"bold 11px sans-serif\";\nconst COUNT_FONT = \"11px sans-serif\";\nconst LABEL_LINE_HEIGHT = 13;\n\n/**\n * Build the scale for one panel.\n *\n * The three panels are stacked in equal bands and share the window, so the\n * same world x lands in the same pixel column in each. The interactive layer\n * and any demo read their geometry from here rather than restating it.\n *\n * @param width The pixel width of the surface.\n * @param height The pixel height of the surface.\n * @param panel Which band to build.\n * @param window The frozen horizontal window, shared by all three.\n * @param yMax The tallest value the panel has to show. Zero is always the\n * foot of the panel, as it is for a density and for a count.\n * @returns The map between world values and pixels for that panel.\n */\nexport function samplingScale(\n width: number,\n height: number,\n panel: SamplingPanel,\n window: Extent,\n yMax: number,\n): Scale {\n const band = height / PANELS.length;\n const index = PANELS.indexOf(panel);\n const top = index * band;\n\n return createScale({\n width,\n height,\n x: window,\n y: { min: 0, max: yMax },\n margins: {\n top: top + PANEL_MARGINS.top,\n bottom: height - (top + band) + PANEL_MARGINS.bottom,\n left: PANEL_MARGINS.left,\n right: PANEL_MARGINS.right,\n },\n });\n}\n\n/**\n * Draw one round of sampling, and hand back what the next round needs.\n *\n * A population of fewer than two values has no density, and R's `density()`\n * stops there. A library cannot stop: this draws the three panels and their\n * axes with nothing in them, takes no samples, and returns a state with the\n * window set and no statistics. The same holds for the samples themselves —\n * a sample of one value gets no curve of its own, though it still counts\n * toward the pooled one and toward the histogram.\n *\n * @param target A canvas, or a context and a size. See `./target.ts`.\n * @param population The values to sample from.\n * @param options The sample size, the repetitions, the statistic, the\n * generator, and the state of the last call.\n * @returns This draw's samples and statistics, the histogram drawn from the\n * accumulated statistics, and the state to pass back next time.\n * @throws RangeError If the sample size is larger than the population, or if\n * a count is negative or fractional — both from `drawSamples`, which is\n * where R's own refusals live.\n */\nexport function plotSampling(\n target: PlotTarget,\n population: readonly number[],\n options: PlotSamplingOptions = {},\n): PlotSamplingResult {\n const { ctx, width, height } = resolveTarget(target);\n const {\n sampleSize = DEFAULT_SAMPLE_SIZE,\n reps = DEFAULT_REPS,\n theta = mean,\n rng = seededRng(Math.floor(Math.random() * 0x100000000)),\n state = null,\n densityWindow = \"data\",\n } = options;\n\n const window = frozenWindow(population, state);\n const grid: KernelDensityOptions =\n densityWindow === \"frozen\" ? { from: window.min, to: window.max } : {};\n const drawable = population.length >= 2;\n const draw = drawable\n ? drawSamples(rng, population, { sampleSize, reps, theta })\n : { samples: [] as readonly (readonly number[])[], thetas: [] as readonly number[] };\n const sampleTheta = [...(state?.sampleTheta ?? []), ...draw.thetas];\n\n clearSurface(ctx, width, height);\n\n drawDensityPanel(\n ctx,\n width,\n height,\n \"population\",\n window,\n drawable ? kernelDensity(population, grid) : null,\n [],\n \"Population Distribution\",\n );\n\n const pooled = draw.samples.flat();\n drawDensityPanel(\n ctx,\n width,\n height,\n \"samples\",\n window,\n pooled.length >= 2 ? kernelDensity(pooled, grid) : null,\n draw.samples\n .filter((sample) => sample.length >= 2)\n .map((sample) => kernelDensity(sample, grid)),\n \"Sample Distribution\",\n );\n\n // Only the statistics inside the window are binned. R bins the whole pile,\n // and its cell edges therefore follow the widest statistic drawn so far,\n // even though its device clips the bar that holds it. That is unreadable\n // under a population whose statistic does not settle: one sample mean out at\n // 800 makes every cell 100 units wide, and a 124-wide panel then shows one\n // flat bar. The count in the label still reports the whole pile.\n const countable = sampleTheta.filter(\n (theta) => theta >= window.min && theta <= window.max,\n );\n const counted = countable.length > 0 ? histogram(countable) : null;\n drawStatisticPanel(ctx, width, height, window, counted, sampleTheta.length);\n\n return {\n state: { xMin: window.min, xMax: window.max, sampleTheta },\n samples: draw.samples,\n thetas: draw.thetas,\n histogram: counted,\n };\n}\n\n/**\n * Return the window to draw in.\n *\n * R reads `xmin`/`xmax` back out of `vars` whenever it has them and only falls\n * back to the range of the population on the first call, so a population that\n * arrives wider later does not move the picture.\n */\nfunction frozenWindow(\n population: readonly number[],\n state: SamplingState | null,\n): Extent {\n if (state !== null) {\n return { min: state.xMin, max: state.xMax };\n }\n if (population.length === 0) {\n return { min: 0, max: 1 };\n }\n const [min, max] = extent(population);\n return { min, max };\n}\n\n/**\n * Draw one of the two density panels.\n *\n * The panel's height comes from the curve R plots first — the population's in\n * the top panel, the pooled samples' in the middle one. Each sample's own\n * curve is drawn against that same height and clipped, exactly as R's device\n * clips it, so a narrow sample with a tall peak cannot escape its panel.\n */\nfunction drawDensityPanel(\n ctx: Context2D,\n width: number,\n height: number,\n panel: SamplingPanel,\n window: Extent,\n main: KernelDensityEstimate | null,\n perSample: readonly KernelDensityEstimate[],\n label: string,\n): void {\n const peak = main === null ? 1 : highestOf(main.y);\n const scale = samplingScale(width, height, panel, window, peak);\n drawAxes(ctx, scale, { frame: false, yAxis: false });\n // The label goes on even when there is no curve under it, so an empty\n // picture still says which panel is which.\n drawLabel(ctx, scale, [label], peak / 2, LABEL_FONT);\n\n if (main === null) {\n return;\n }\n\n // R plots the pooled curve, draws the per-sample curves over it, then\n // strokes the pooled curve again to bring it back to the front.\n drawCurve(ctx, scale, main, CURVE_COLOR, CURVE_WIDTH, panel === \"population\");\n for (const estimate of perSample) {\n drawCurve(\n ctx,\n scale,\n estimate,\n SAMPLE_CURVE_COLOR,\n SAMPLE_CURVE_WIDTH,\n false,\n );\n }\n if (panel === \"samples\") {\n // R's second `lines(samd, ...)`. It belongs to this panel alone: the\n // population panel is drawn once, and re-stroking it would put a solid\n // curve over the dotted one.\n drawCurve(ctx, scale, main, CURVE_COLOR, CURVE_WIDTH, false);\n }\n}\n\n/** Draw the histogram of every statistic drawn so far. */\nfunction drawStatisticPanel(\n ctx: Context2D,\n width: number,\n height: number,\n window: Extent,\n counted: Histogram | null,\n drawnSoFar: number,\n): void {\n const tallest = counted === null ? 1 : highestOf(counted.counts);\n const scale = samplingScale(width, height, \"statistic\", window, tallest);\n drawAxes(ctx, scale, { frame: false, yAxis: false });\n\n if (counted !== null) {\n drawBars(ctx, scale, counted);\n }\n\n // R: paste(\"Sampling Statistic\", \"\\n(\", length(sample_theta), \")\"), whose\n // single-space joins and inner line break are kept exactly.\n drawLabel(\n ctx,\n scale,\n [\"Sampling Statistic \", `( ${drawnSoFar} )`],\n tallest / 2,\n COUNT_FONT,\n );\n}\n\n/** Draw a density estimate as a line, clipped to its panel. */\nfunction drawCurve(\n ctx: Context2D,\n scale: Scale,\n estimate: KernelDensityEstimate,\n color: string,\n lineWidth: number,\n dotted: boolean,\n): void {\n const { area } = scale;\n ctx.save();\n clipToArea(ctx, area);\n ctx.strokeStyle = color;\n ctx.lineWidth = lineWidth;\n ctx.setLineDash(dotted ? DOTTED : []);\n ctx.beginPath();\n estimate.x.forEach((x, index) => {\n const px = scale.toPixelX(x);\n const py = scale.toPixelY(estimate.y[index] as number);\n if (index === 0) {\n ctx.moveTo(px, py);\n } else {\n ctx.lineTo(px, py);\n }\n });\n ctx.stroke();\n ctx.restore();\n}\n\n/**\n * Draw the histogram bars, clipped to the panel.\n *\n * R's call passes `border = FALSE`, so each bar is a filled rectangle with no\n * outline. The bars are clipped because the window belongs to the population:\n * a statistic can land outside it, and R's device clips the bar that holds it.\n */\nfunction drawBars(ctx: Context2D, scale: Scale, counted: Histogram): void {\n const { area } = scale;\n const foot = scale.toPixelY(0);\n\n ctx.save();\n clipToArea(ctx, area);\n ctx.setLineDash([]);\n ctx.fillStyle = BAR_COLOR;\n counted.counts.forEach((count, index) => {\n const left = scale.toPixelX(counted.breaks[index] as number);\n const right = scale.toPixelX(counted.breaks[index + 1] as number);\n const top = scale.toPixelY(count);\n ctx.fillRect(left, top, right - left, foot - top);\n });\n ctx.restore();\n}\n\n/**\n * Write a panel's label at the left of the window, halfway up its content.\n *\n * R: `text(xmin, max(y)/2, label, adj = 0)` — left-justified at the window's\n * left edge and centerd on that height.\n */\nfunction drawLabel(\n ctx: Context2D,\n scale: Scale,\n lines: readonly string[],\n at: number,\n font: string,\n): void {\n ctx.save();\n ctx.setLineDash([]);\n ctx.fillStyle = TEXT_COLOR;\n ctx.font = font;\n ctx.textAlign = \"left\";\n ctx.textBaseline = \"middle\";\n lines.forEach((line, index) => {\n ctx.fillText(\n line,\n scale.area.left,\n scale.toPixelY(at) + index * LABEL_LINE_HEIGHT,\n );\n });\n ctx.restore();\n}\n\n/** Return the largest value, or 1 for an empty or flat set. */\nfunction highestOf(values: readonly number[]): number {\n const highest = values.reduce((high, value) => (value > high ? value : high), 0);\n return highest > 0 ? highest : 1;\n}\n",
|
|
32
|
+
"/**\n * The PCA plot: the points, and an arrow along each principal component.\n *\n * This is the drawing half of `plot_pca()` in `../compstatslib/R/pca_plot.R`.\n * Every number it draws comes from `principalComponents` in\n * `src/core/pca.ts`; this module computes no statistics of its own.\n *\n * Two things here differ from every earlier plot in this port.\n *\n * **The window keeps equal units on both axes.** R's call passes `asp = 1`,\n * which makes a right angle on the data look like a right angle on screen —\n * the whole point of a picture about perpendicular components. R holds the\n * limits it was given on whichever axis constrains the fit and widens the\n * other about its middle; `pcaScale` does the same. Measured from R on a null\n * device with `xlim = ylim = c(-50, 50)`: a 10 by 5 inch device reports\n * `par(\"usr\")` of `-149.696 149.696 -54 54`, and a 5 by 10 inch device\n * reports `-54 54 -117.191 117.191`.\n *\n * **Mean-centering is not a statistical option.** It moves the arrows'\n * anchor and nothing else. The components are the same either way, because\n * R's `prcomp` centers internally whatever it is handed (fixture F7 shows the\n * two runs agreeing bit for bit), and the points on screen are the raw ones —\n * R plots `points`, not `mc_points`.\n */\n\nimport { principalComponents } from \"../core/pca\";\nimport type { PcaResult } from \"../core/pca\";\nimport type { Point } from \"../core/regression\";\nimport { DEFAULT_MARGINS, createScale, drawAxes, extentOf } from \"./axes\";\nimport type { Extent, Scale } from \"./axes\";\nimport { DOTTED, clearSurface, clipToArea, drawArrow, drawDots } from \"./draw\";\nimport { resolveTarget } from \"./target\";\nimport type { Context2D, PlotTarget } from \"./target\";\n\n/** R's `plot_pca()` arguments, less the points. */\nexport interface PlotPcaOptions {\n /**\n * Anchor the arrows on the middle of the data. True by default, as in R.\n *\n * False anchors them on the origin instead. Nothing else changes: not the\n * components, not the points, only where the arrows are drawn.\n */\n readonly meancenter?: boolean;\n /** The x limits, R's `xlim`. Both default to R's `c(-50, 50)`. */\n readonly xlim?: readonly [number, number];\n /** The y limits, R's `ylim`. */\n readonly ylim?: readonly [number, number];\n}\n\n/** R: `xlim = c(-50, 50)` and `ylim = c(-50, 50)`. */\nconst DEFAULT_LIMITS: readonly [number, number] = [-50, 50];\n\n/**\n * The length of an arrowhead edge, in pixels.\n *\n * R asks for `length = 0.1`, measured in inches. At the 96 pixels per inch a\n * browser calls an inch that is 9.6, rounded here to a round 10.\n */\nconst HEAD_LENGTH = 10;\n\n/**\n * Build the scale that `plotPca` draws through, with equal units per pixel.\n *\n * The interactive layer needs this to turn a click into a world coordinate.\n * It reads the scale from here so the window rule stays in one place.\n *\n * Both requested ranges are honoured or widened, never narrowed: the axis\n * that needs the most world per pixel keeps its limits, and the other grows\n * about its middle until the two agree. Unlike R, the constraining axis keeps\n * the literal limits it was given — R would pad them by 4% first, and this\n * port drops that padding everywhere.\n *\n * @param width The pixel width of the surface.\n * @param height The pixel height of the surface.\n * @param options The limits. A reversed pair reads as the range it spans, not\n * as a flipped axis.\n * @returns The map between world values and pixels.\n */\nexport function pcaScale(\n width: number,\n height: number,\n options: PlotPcaOptions = {},\n): Scale {\n const x = extentOf(options.xlim ?? DEFAULT_LIMITS);\n const y = extentOf(options.ylim ?? DEFAULT_LIMITS);\n const areaWidth = width - DEFAULT_MARGINS.left - DEFAULT_MARGINS.right;\n const areaHeight = height - DEFAULT_MARGINS.top - DEFAULT_MARGINS.bottom;\n\n const perPixel = Math.max(\n (x.max - x.min) / areaWidth,\n (y.max - y.min) / areaHeight,\n );\n // A surface too small to hold a plot area, or a pair of limits with no\n // width at all, leaves nothing to equalize. The window stays as asked and\n // `createScale` handles the degenerate span.\n if (!(areaWidth > 0 && areaHeight > 0 && perPixel > 0)) {\n return createScale({ width, height, x, y });\n }\n\n return createScale({\n width,\n height,\n x: widenedTo(x, perPixel * areaWidth),\n y: widenedTo(y, perPixel * areaHeight),\n });\n}\n\n/**\n * Draw the points and the arrows along their principal components.\n *\n * R guards this drawing at three sizes, and so does this. With no points it\n * draws an empty pair of axes. Below three points it draws the points alone:\n * two points always lie on a line, so the second component would be an arrow\n * of no length and the first would say only what the eye already sees. The\n * core will answer for one or two points; this asks it only from three, which\n * is R's own `nrow(points) >= 3`.\n *\n * @param target A canvas, or a context and a size. See `./target.ts`.\n * @param points The observations, in world coordinates.\n * @param options The anchor and the window.\n * @returns The components drawn, or null below three points. R returns the\n * same `prcomp` result, invisibly, and NULL at the same two guards.\n */\nexport function plotPca(\n target: PlotTarget,\n points: readonly Point[],\n options: PlotPcaOptions = {},\n): PcaResult | null {\n const { ctx, width, height } = resolveTarget(target);\n const scale = pcaScale(width, height, options);\n\n clearSurface(ctx, width, height);\n drawAxes(ctx, scale, { xLabel: \"x\", yLabel: \"y\" });\n\n if (points.length === 0) {\n return null;\n }\n\n // R's device clips the plot region; canvas clips nothing. A fixed window of\n // -50 to 50 says nothing about where the points are, so this matters.\n const { area } = scale;\n ctx.save();\n clipToArea(ctx, area);\n\n drawDots(ctx, scale, points);\n\n const result = points.length >= 3 ? principalComponents(points) : null;\n if (result !== null) {\n // R's anchor is `mc_diff`, which is the column means under `meancenter`\n // and the origin otherwise. R also drops to the origin at one point,\n // which no arrow can reach: it takes three points to get here.\n const anchor = (options.meancenter ?? true) ? result.center : ORIGIN;\n drawComponent(ctx, scale, result, anchor, 0, []);\n drawComponent(ctx, scale, result, anchor, 1, DOTTED);\n }\n\n ctx.restore();\n return result;\n}\n\nconst ORIGIN: Point = { x: 0, y: 0 };\n\n/** Grow an extent to a span, keeping its middle where it is. */\nfunction widenedTo(extent: Extent, span: number): Extent {\n const middle = (extent.min + extent.max) / 2;\n return { min: middle - span / 2, max: middle + span / 2 };\n}\n\n/**\n * Draw one component as an arrow through the anchor.\n *\n * R's `vec` matrix is the loading vector scaled by the component's standard\n * deviation, and the arrow runs from `-vec` to `+vec` about the anchor — so\n * its length shows how much spread the component carries. The head goes at\n * the `+vec` end alone, which is what `arrows()` does with its default\n * `code = 2`.\n */\nfunction drawComponent(\n ctx: Context2D,\n scale: Scale,\n result: PcaResult,\n anchor: Point,\n component: 0 | 1,\n dash: readonly number[],\n): void {\n const loadings = result.rotation[component];\n const sdev = result.sdev[component];\n const tail = [\n scale.toPixelX(anchor.x - loadings[0] * sdev),\n scale.toPixelY(anchor.y - loadings[1] * sdev),\n ] as const;\n const tip = [\n scale.toPixelX(anchor.x + loadings[0] * sdev),\n scale.toPixelY(anchor.y + loadings[1] * sdev),\n ] as const;\n\n drawArrow(ctx, tail, tip, { headLength: HEAD_LENGTH, dash });\n}\n",
|
|
33
|
+
"/**\n * The regression plot: points, the mean crosshair, the fitted line, and a\n * block of statistics.\n *\n * This is the drawing half of `plot_regression()` in\n * `../compstatslib/R/regression_plot.R`. Every number it draws comes from\n * `linearRegression` in `src/core/regression.ts`; this module computes no\n * statistics of its own.\n */\n\nimport { mean } from \"../core/arith\";\nimport { linearRegression } from \"../core/regression\";\nimport type { Point, RegressionFit } from \"../core/regression\";\nimport { createScale, drawAxes, extentOf } from \"./axes\";\nimport { formatStat } from \"./format\";\nimport type { Extent, Scale } from \"./axes\";\nimport { DOTTED, clearSurface, clipToArea, drawDots } from \"./draw\";\nimport { resolveTarget } from \"./target\";\nimport type { Context2D, PlotTarget } from \"./target\";\n\n/** What to show, and where. The toggles default to true, as in R. */\nexport interface PlotRegressionOptions {\n /** Draw the mean crosshair and the fitted line. */\n readonly regression?: boolean;\n /** List the statistics at the top left. Needs `regression`. */\n readonly stats?: boolean;\n /** The x limits, R's `xlim`. Both default to R's `c(-5, 50)`. */\n readonly xlim?: readonly [number, number];\n /** The y limits, R's `ylim`. */\n readonly ylim?: readonly [number, number];\n}\n\n/** R: `xlim = c(-5, max_x)` and `ylim = c(-5, max_x)`, with `max_x = 50`. */\nconst WORLD: Extent = { min: -5, max: 50 };\n\nconst CROSSHAIR_COLOR = \"lightgray\";\nconst LINE_COLOR = \"cornflowerblue\";\nconst TEXT_COLOR = \"#000000\";\nconst STATS_FONT = \"12px monospace\";\nconst STATS_LINE_HEIGHT = 15;\nconst STATS_PADDING = 8;\nconst STATS_LABEL_WIDTH = 13;\n\n/**\n * Build the scale that `plotRegression` draws through.\n *\n * The interactive layer needs this to turn a click into a world coordinate.\n * It reads the scale from here so that the world window stays defined in one\n * place. Nothing outside this module may restate it.\n *\n * @param width The pixel width of the surface.\n * @param height The pixel height of the surface.\n * @param options The limits. A reversed pair reads as the range it spans, not\n * as a flipped axis. Omitted limits keep the R teaching window.\n * @returns The map between world values and pixels.\n */\nexport function regressionScale(\n width: number,\n height: number,\n options: PlotRegressionOptions = {},\n): Scale {\n return createScale({\n width,\n height,\n x: options.xlim ? extentOf(options.xlim) : WORLD,\n y: options.ylim ? extentOf(options.ylim) : WORLD,\n });\n}\n\n/**\n * Draw the points and their regression.\n *\n * With no points, the function draws empty axes. With one point, it draws the\n * point alone, because a single point has no line to fit. R stops at the same\n * two places.\n *\n * @param target A canvas, or a context and a size. See `./target.ts`.\n * @param points The observations, in world coordinates.\n * @param options What to show.\n * @returns The fit that the plot drew, or null if there are no points. R\n * returns the points instead, which the caller already holds.\n */\nexport function plotRegression(\n target: PlotTarget,\n points: readonly Point[],\n options: PlotRegressionOptions = {},\n): RegressionFit | null {\n const { ctx, width, height } = resolveTarget(target);\n const showRegression = options.regression ?? true;\n const showStats = options.stats ?? true;\n const scale = regressionScale(width, height, options);\n\n clearSurface(ctx, width, height);\n drawAxes(ctx, scale, { xLabel: \"x\", yLabel: \"y\" });\n\n const fit = linearRegression(points);\n if (fit === null) {\n return null;\n }\n\n drawDots(ctx, scale, points);\n if (points.length < 2 || !showRegression) {\n return fit;\n }\n\n drawMeanCrosshair(ctx, scale, points);\n if (fit.slope !== null) {\n drawFittedLine(ctx, scale, fit.intercept, fit.slope);\n }\n if (showStats) {\n drawStats(ctx, scale, fit);\n }\n return fit;\n}\n\n/**\n * Draw the two dotted segments that mark the means.\n *\n * R: `segments(0, mean_y, max_x, mean_y)` and\n * `segments(mean_x, 0, mean_x, mean_y)`. The vertical segment stops at the\n * horizontal one. R's window always contains its 0 origin; a custom window\n * that does not clamps that origin to its nearest edge, so the segments span\n * the window instead of leaving it.\n */\nfunction drawMeanCrosshair(\n ctx: Context2D,\n scale: Scale,\n points: readonly Point[],\n): void {\n const meanX = mean(points.map((point) => point.x));\n const meanY = mean(points.map((point) => point.y));\n const { x, y } = scale.world;\n const originX = Math.min(Math.max(0, x.min), x.max);\n const originY = Math.min(Math.max(0, y.min), y.max);\n\n ctx.save();\n ctx.strokeStyle = CROSSHAIR_COLOR;\n ctx.lineWidth = 1;\n ctx.setLineDash(DOTTED);\n ctx.beginPath();\n ctx.moveTo(scale.toPixelX(originX), scale.toPixelY(meanY));\n ctx.lineTo(scale.toPixelX(x.max), scale.toPixelY(meanY));\n ctx.moveTo(scale.toPixelX(meanX), scale.toPixelY(originY));\n ctx.lineTo(scale.toPixelX(meanX), scale.toPixelY(meanY));\n ctx.stroke();\n ctx.restore();\n}\n\n/**\n * Draw the fitted line across the plot area.\n *\n * R's `abline()` draws across the whole plot region, so this draws the line at\n * both edges of the world window and clips it to the area. A steep line then\n * leaves through the top or the bottom, as it does in R.\n */\nfunction drawFittedLine(\n ctx: Context2D,\n scale: Scale,\n intercept: number,\n slope: number,\n): void {\n const { area, world } = scale;\n ctx.save();\n clipToArea(ctx, area);\n ctx.strokeStyle = LINE_COLOR;\n ctx.lineWidth = 2;\n ctx.setLineDash([]);\n ctx.beginPath();\n ctx.moveTo(\n scale.toPixelX(world.x.min),\n scale.toPixelY(intercept + slope * world.x.min),\n );\n ctx.lineTo(\n scale.toPixelX(world.x.max),\n scale.toPixelY(intercept + slope * world.x.max),\n );\n ctx.stroke();\n ctx.restore();\n}\n\n/**\n * List the statistics at the top left, in a monospace block.\n *\n * R draws this with `legend(\"topleft\", ...)` under `par(family = \"mono\")`.\n */\nfunction drawStats(ctx: Context2D, scale: Scale, fit: RegressionFit): void {\n const rows: readonly (readonly [string, number | null])[] = [\n [\"Raw intercept\", fit.intercept],\n [\"Raw slope\", fit.slope],\n [\"Correlation\", fit.correlation],\n [\"SSR\", fit.ssr],\n [\"SSE\", fit.sse],\n [\"SST\", fit.sst],\n [\"R-squared\", fit.rSquared],\n ];\n\n ctx.save();\n ctx.setLineDash([]);\n ctx.fillStyle = TEXT_COLOR;\n ctx.font = STATS_FONT;\n ctx.textAlign = \"left\";\n ctx.textBaseline = \"top\";\n for (const [index, [label, value]] of rows.entries()) {\n ctx.fillText(\n `${label.padEnd(STATS_LABEL_WIDTH)}: ${formatStat(value)}`,\n scale.area.left + STATS_PADDING,\n scale.area.top + STATS_PADDING + index * STATS_LINE_HEIGHT,\n );\n }\n ctx.restore();\n}\n",
|
|
34
|
+
"/**\n * The t-test plot: two distributions, the regions they decide between, and an\n * optional matrix of the four outcomes.\n *\n * This is the drawing half of `plot_t_test()` in\n * `../compstatslib/R/t_statistic_plot.R`, whose work is spread across\n * `t_null_plot()`, `t_alt_lines()`, `plott()`, `plotdist()`, and\n * `plot_error_matrix()`. Every number comes from `tTestStats` in\n * `src/core/ttest.ts` and every curve height from `dt` in\n * `src/core/tdist.ts`; this module computes no statistics of its own.\n */\n\nimport { dt } from \"../core/tdist\";\nimport { tTestStats } from \"../core/ttest\";\nimport type { TTestOptions, TTestStats } from \"../core/ttest\";\nimport { createScale, drawAxes } from \"./axes\";\nimport type { Extent, Scale } from \"./axes\";\nimport { clearSurface, clipToArea } from \"./draw\";\nimport { formatNumber, formatStat } from \"./format\";\nimport { resolveTarget } from \"./target\";\nimport type { Context2D, PlotTarget } from \"./target\";\n\n/**\n * What to draw. The four test parameters pass straight through to\n * `tTestStats`, which is the pass-through R does with its own arguments.\n */\nexport interface PlotTTestOptions extends TTestOptions {\n /** Show the matrix of the four outcomes. False by default, as in R. */\n readonly errorMatrix?: boolean;\n}\n\n/** R: `xlim = c(-6, 6)` in both `t_null_plot()` and `t_alt_lines()`. */\nconst WORLD_X: Extent = { min: -6, max: 6 };\n\n/** R: `rgb(0.75, 0.1, 0.1)`. */\nconst NULL_COLOR = \"#bf1a1a\";\n/** R: `rgb(1, 0.5, 0.5)`. */\nconst NULL_FILL = \"#ff8080\";\n/** R: `rgb(0.1, 0.1, 0.75)`. */\nconst ALT_COLOR = \"#1a1abf\";\n/** R: `rgb(0.4, 0.4, 1, 0.3)`, used for the fill and the median segment. */\nconst ALT_FILL = \"rgba(102, 102, 255, 0.3)\";\n/** R: `rgb(0.30, 0.50, 0.75, 0.5)`, the two \"Correct!\" cells. */\nconst CORRECT_FILL = \"rgba(77, 128, 191, 0.5)\";\nconst TEXT_COLOR = \"#000000\";\n\n/** R: `lwd = 2`, the default in `plott()`. */\nconst LINE_WIDTH = 2;\n/** R: `lty = \"dashed\"`, which is 4 on and 4 off. */\nconst DASHED = [4, 4];\n/** R: `lwd = 4` on the ring around the likely row. */\nconst HIGHLIGHT_WIDTH = 4;\n\n/** R: `xseq = seq(ncp - 6, ncp + 6, length = 1000)`. */\nconst CURVE_SAMPLES = 1000;\nconst CURVE_HALF_WIDTH = 6;\n/**\n * R samples a fill at `by = 0.001`, which over the widest region would be tens\n * of thousands of points for a shape a few hundred pixels wide. Sampling once\n * per pixel draws the same picture for a fraction of the work, which matters\n * because the interactive layer redraws on every slider tick.\n */\nconst MAX_FILL_SAMPLES = 2000;\n\n/** The error-matrix geometry, read off the `recttext()` calls in R. */\nconst MATRIX = {\n left: -5.5,\n middle: -4,\n right: -2.5,\n bottom: 0.125,\n center: 0.25,\n top: 0.375,\n} as const;\n\n/** Room above the matrix for the column captions R puts at `yt + 0.02`. */\nconst MATRIX_HEADROOM = 0.045;\n\n/**\n * Type sizes for the error matrix.\n *\n * R sets these with `cex` values of 0.45, 0.5, and 0.75, which land near 6, 7,\n * and 10 pixels on a canvas. That is small enough to be hard to read, and the\n * matrix is the part of the picture a reader spends the longest on. This is\n * the deliberate deviation from R's `cex` arithmetic: the panel is sized to be\n * read.\n */\nconst CAPTION_FONT_SIZE = 9;\nconst TITLE_FONT_SIZE = 10;\nconst VALUE_FONT_SIZE = 13;\n/** The room one caption line takes in the stack, when there is room for it. */\nconst CAPTION_LINE_HEIGHT = 12;\n/** However narrow the gap, a caption never shrinks past this. */\nconst MIN_CAPTION_FONT_SIZE = 6;\n\n/**\n * Build the scale that `plotTTest` draws through.\n *\n * The x window is R's fixed `c(-6, 6)`. The y window follows R's default,\n * which is the range of the plotted density widened by four percent at each\n * end, except that showing the error matrix raises the top enough to hold it.\n * R leaves the window alone there and lets the matrix fall outside the plot\n * region, where base graphics clip it away: at one degree of freedom the null\n * curve only reaches 0.32 and the top row of cells is cut off. Growing the\n * window instead keeps the panel whole.\n *\n * @param width The pixel width of the surface.\n * @param height The pixel height of the surface.\n * @param options The same options `plotTTest` takes.\n * @returns The map between world values and pixels.\n */\nexport function tTestScale(\n width: number,\n height: number,\n options: PlotTTestOptions = {},\n): Scale {\n const { errorMatrix = false, ...statOptions } = options;\n return buildScale(width, height, tTestStats(statOptions), errorMatrix);\n}\n\n/** The scale, once the statistics are already in hand. */\nfunction buildScale(\n width: number,\n height: number,\n stats: TTestStats,\n showErrorMatrix: boolean,\n): Scale {\n const peak = Math.max(dt(0, stats.df), stats.altMedianDensity);\n const tallest = Number.isFinite(peak) && peak > 0 ? peak : 1;\n const top = showErrorMatrix\n ? Math.max(1.04 * tallest, MATRIX.top + MATRIX_HEADROOM)\n : 1.04 * tallest;\n\n return createScale({\n width,\n height,\n x: WORLD_X,\n y: { min: -0.04 * tallest, max: top },\n });\n}\n\n/**\n * Draw the null and alternative distributions of a t test.\n *\n * @param target A canvas, or a context and a size. See `./target.ts`.\n * @param options The test parameters, and whether to show the error matrix.\n * @returns The statistics behind the picture. R returns these invisibly from\n * `t_alt_lines()`; a browser caller wants them, so they come back here.\n */\nexport function plotTTest(\n target: PlotTarget,\n options: PlotTTestOptions = {},\n): TTestStats {\n const { ctx, width, height } = resolveTarget(target);\n const { errorMatrix: showErrorMatrix = false, ...statOptions } = options;\n\n const stats = tTestStats(statOptions);\n const scale = buildScale(width, height, stats, showErrorMatrix);\n\n clearSurface(ctx, width, height);\n drawAxes(ctx, scale, { frame: false });\n\n // R draws the null first, then the alternative over it. Within each, the\n // shaded region goes down before the curve, so the outline stays visible.\n drawFill(ctx, scale, stats.nullFill, stats.df, 0, NULL_FILL);\n drawCurve(ctx, scale, stats.df, 0, NULL_COLOR, []);\n\n drawFill(ctx, scale, stats.altFill, stats.df, stats.t, ALT_FILL);\n drawMedianSegment(ctx, scale, stats);\n drawCurve(ctx, scale, stats.df, stats.t, ALT_COLOR, DASHED);\n\n if (showErrorMatrix) {\n drawErrorMatrix(ctx, scale, stats);\n }\n\n return stats;\n}\n\n/**\n * Draw one density curve.\n *\n * R samples over `ncp ± 6` whatever the window is, and lets the plot clip what\n * falls outside. This does the same, under a clip to the plot area.\n */\nfunction drawCurve(\n ctx: Context2D,\n scale: Scale,\n df: number,\n ncp: number,\n color: string,\n dash: readonly number[],\n): void {\n ctx.save();\n clipToArea(ctx, scale.area);\n ctx.strokeStyle = color;\n ctx.lineWidth = LINE_WIDTH;\n ctx.setLineDash([...dash]);\n ctx.beginPath();\n\n const from = ncp - CURVE_HALF_WIDTH;\n const step = (2 * CURVE_HALF_WIDTH) / (CURVE_SAMPLES - 1);\n for (let index = 0; index < CURVE_SAMPLES; index += 1) {\n const x = from + index * step;\n const px = scale.toPixelX(x);\n const py = scale.toPixelY(dt(x, df, ncp));\n if (index === 0) {\n ctx.moveTo(px, py);\n } else {\n ctx.lineTo(px, py);\n }\n }\n\n ctx.stroke();\n ctx.restore();\n}\n\n/** A span of the x axis to shade. */\ninterface Span {\n readonly from: number;\n readonly to: number;\n}\n\n/**\n * Shade the region under a curve between two quantiles.\n *\n * The range is clamped to the visible window first. `tTestStats` reports the\n * power region as starting at −Infinity once beta underflows, which R feeds\n * straight to `seq()` and dies on; clamping turns that into the honest\n * picture, a region running off the left edge.\n */\nfunction drawFill(\n ctx: Context2D,\n scale: Scale,\n range: Span,\n df: number,\n ncp: number,\n color: string,\n): void {\n const from = Math.max(range.from, WORLD_X.min);\n const to = Math.min(range.to, WORLD_X.max);\n if (!(to > from)) {\n return;\n }\n\n const fromPixel = scale.toPixelX(from);\n const toPixel = scale.toPixelX(to);\n const samples = Math.max(\n 2,\n Math.min(MAX_FILL_SAMPLES, Math.ceil(toPixel - fromPixel)),\n );\n const step = (to - from) / (samples - 1);\n const baseline = scale.toPixelY(0);\n\n ctx.save();\n clipToArea(ctx, scale.area);\n ctx.fillStyle = color;\n ctx.setLineDash([]);\n ctx.beginPath();\n ctx.moveTo(fromPixel, baseline);\n for (let index = 0; index < samples; index += 1) {\n const x = from + index * step;\n ctx.lineTo(scale.toPixelX(x), scale.toPixelY(dt(x, df, ncp)));\n }\n ctx.lineTo(toPixel, baseline);\n ctx.fill();\n ctx.restore();\n}\n\n/**\n * Mark the alternative's midpoint.\n *\n * R passes `quants = c(0.5)` to `plott()`, which draws a segment from the axis\n * up to the curve, in the same translucent color as the fill.\n */\nfunction drawMedianSegment(\n ctx: Context2D,\n scale: Scale,\n stats: TTestStats,\n): void {\n if (!Number.isFinite(stats.altMedian)) {\n return;\n }\n\n ctx.save();\n clipToArea(ctx, scale.area);\n ctx.strokeStyle = ALT_FILL;\n ctx.lineWidth = LINE_WIDTH;\n ctx.setLineDash([]);\n ctx.beginPath();\n ctx.moveTo(scale.toPixelX(stats.altMedian), scale.toPixelY(0));\n ctx.lineTo(\n scale.toPixelX(stats.altMedian),\n scale.toPixelY(stats.altMedianDensity),\n );\n ctx.stroke();\n ctx.restore();\n}\n\n/**\n * Draw the four outcomes of the test, and ring the likely row.\n *\n * R builds this from four `recttext()` calls plus a fifth for the ring. Two of\n * the four values are printed through `round(x, 2)` and two are not, which is\n * R's own inconsistency and is kept.\n */\nfunction drawErrorMatrix(\n ctx: Context2D,\n scale: Scale,\n stats: TTestStats,\n): void {\n const { errorMatrix: cells, alpha } = stats;\n\n ctx.save();\n ctx.setLineDash([]);\n\n drawCell(ctx, scale, {\n left: MATRIX.left,\n bottom: MATRIX.center,\n right: MATRIX.middle,\n top: MATRIX.top,\n fill: NULL_FILL,\n title: \"Type I error\",\n value: formatNumber(alpha),\n rowCaption: [\"If evidence says\", \"REJECT\", \"null hypothesis\"],\n columnCaption: [\"If null is\", \"really TRUE\"],\n });\n\n drawCell(ctx, scale, {\n left: MATRIX.middle,\n bottom: MATRIX.center,\n right: MATRIX.right,\n top: MATRIX.top,\n fill: CORRECT_FILL,\n title: \"Correct!\",\n value: formatStat(cells.correctReject),\n columnCaption: [\"If null is\", \"really FALSE\"],\n });\n\n drawCell(ctx, scale, {\n left: MATRIX.left,\n bottom: MATRIX.bottom,\n right: MATRIX.middle,\n top: MATRIX.center,\n fill: CORRECT_FILL,\n title: \"Correct!\",\n value: formatNumber(cells.correctFailToReject),\n rowCaption: [\"If evidence says\", \"CANNOT REJECT\", \"null hypothesis\"],\n });\n\n drawCell(ctx, scale, {\n left: MATRIX.middle,\n bottom: MATRIX.bottom,\n right: MATRIX.right,\n top: MATRIX.center,\n fill: NULL_FILL,\n title: \"Type II error\",\n value: formatStat(cells.typeTwo),\n });\n\n const ringBottom = cells.highlightTopRow ? MATRIX.center : MATRIX.bottom;\n const ringTop = cells.highlightTopRow ? MATRIX.top : MATRIX.center;\n ctx.strokeStyle = ALT_COLOR;\n ctx.lineWidth = HIGHLIGHT_WIDTH;\n ctx.beginPath();\n ctx.rect(...worldRect(scale, MATRIX.left, ringBottom, MATRIX.right, ringTop));\n ctx.stroke();\n\n ctx.restore();\n}\n\n/** Everything one cell of the matrix needs. */\ninterface Cell {\n readonly left: number;\n readonly bottom: number;\n readonly right: number;\n readonly top: number;\n readonly fill: string;\n readonly title: string;\n readonly value: string;\n /** Rotated text to the left of the row. */\n readonly rowCaption?: readonly string[];\n /** Text above the column. */\n readonly columnCaption?: readonly string[];\n}\n\n/** R's `recttext()`: a filled box, its captions, its title, and its value. */\nfunction drawCell(ctx: Context2D, scale: Scale, cell: Cell): void {\n const centerX = (cell.left + cell.right) / 2;\n const centerY = (cell.bottom + cell.top) / 2;\n\n ctx.fillStyle = cell.fill;\n ctx.beginPath();\n ctx.rect(...worldRect(scale, cell.left, cell.bottom, cell.right, cell.top));\n ctx.fill();\n\n ctx.fillStyle = TEXT_COLOR;\n ctx.textAlign = \"center\";\n\n ctx.font = `${TITLE_FONT_SIZE}px sans-serif`;\n ctx.textBaseline = \"top\";\n // R: the title sits just inside the top edge, at `yt - 0.015`.\n ctx.fillText(\n cell.title,\n scale.toPixelX(centerX),\n scale.toPixelY(cell.top - 0.015),\n );\n\n ctx.font = `${VALUE_FONT_SIZE}px sans-serif`;\n ctx.textBaseline = \"middle\";\n ctx.fillText(cell.value, scale.toPixelX(centerX), scale.toPixelY(centerY));\n\n ctx.font = `${CAPTION_FONT_SIZE}px sans-serif`;\n if (cell.columnCaption !== undefined) {\n // R: the column caption sits above the top edge, at `yt + 0.02`. The block\n // stacks upward, so its last line lands on that anchor. On a short surface\n // that anchor would carry the stack off the top of the plot, so it gives\n // way, for the same reason the row captions do: canvas clips no text.\n ctx.textBaseline = \"bottom\";\n const blockHeight =\n (cell.columnCaption.length - 1) * CAPTION_LINE_HEIGHT;\n const anchor = Math.max(\n scale.toPixelY(cell.top + 0.02),\n scale.area.top + blockHeight + CAPTION_FONT_SIZE,\n );\n drawLines(\n ctx,\n cell.columnCaption,\n scale.toPixelX(centerX),\n anchor - blockHeight,\n CAPTION_LINE_HEIGHT,\n );\n }\n\n if (cell.rowCaption !== undefined) {\n const gapLeft = scale.toPixelX(WORLD_X.min);\n const layout = fitCaption(\n scale.toPixelX(cell.left) - gapLeft,\n cell.rowCaption.length,\n );\n\n ctx.save();\n ctx.font = `${layout.fontSize}px sans-serif`;\n // Turned a quarter turn, the stack runs left to right across the gap, so\n // the offset between lines becomes a horizontal step on the surface.\n ctx.translate(gapLeft + layout.start, scale.toPixelY(centerY));\n ctx.rotate(-Math.PI / 2);\n ctx.textAlign = \"center\";\n ctx.textBaseline = \"middle\";\n drawLines(ctx, cell.rowCaption, 0, 0, layout.advance);\n ctx.restore();\n }\n}\n\n/** How a stack of caption lines is laid out across the gap it has to fit. */\ninterface CaptionLayout {\n /** The step from one line to the next. */\n readonly advance: number;\n /** The type size that step leaves room for. */\n readonly fontSize: number;\n /** Where the first line sits, measured from the start of the gap. */\n readonly start: number;\n}\n\n/**\n * Fit a stack of caption lines into the space beside the cells.\n *\n * R anchors the turned row captions at `xl - 0.45` and leaves the rest to base\n * graphics, which clips whatever crosses the edge of the plot region. Canvas\n * clips no text, so the same anchor puts two of each block's three lines out\n * over the axis numbers. This is the deliberate deviation: the block is\n * centerd in the gap between the edge of the plot and the edge of the cells,\n * which is where R's clipped version ends up looking like it sits anyway.\n *\n * The gap is a fixed share of the width, half a unit of the twelve the picture\n * spans, so a wider surface carries larger type. At 900 pixels and above the\n * captions reach their full size and the lines sit a clear step apart.\n *\n * A narrow surface leaves a narrow gap. The lines then close up and the type\n * shrinks to match, so the block still stays inside the picture rather than\n * spilling over the axis. Below about 580 pixels the type reaches its floor\n * while the lines keep closing, and the block starts to crowd itself; staying\n * inside the picture matters more than staying loose, and a panel this size is\n * past the point of reading comfortably in any case.\n */\nfunction fitCaption(gap: number, lineCount: number): CaptionLayout {\n const advance = Math.min(CAPTION_LINE_HEIGHT, gap / lineCount);\n const fontSize = Math.max(\n MIN_CAPTION_FONT_SIZE,\n Math.min(CAPTION_FONT_SIZE, advance - 1),\n );\n return { advance, fontSize, start: (gap - (lineCount - 1) * advance) / 2 };\n}\n\n/** Draw a caption that R writes with newlines in it. */\nfunction drawLines(\n ctx: Context2D,\n lines: readonly string[],\n x: number,\n y: number,\n advance: number,\n): void {\n for (const [index, line] of lines.entries()) {\n ctx.fillText(line, x, y + index * advance);\n }\n}\n\n/** Turn a world box into the arguments `rect` wants, top-left and size. */\nfunction worldRect(\n scale: Scale,\n left: number,\n bottom: number,\n right: number,\n top: number,\n): [number, number, number, number] {\n const x = scale.toPixelX(left);\n const y = scale.toPixelY(top);\n return [x, y, scale.toPixelX(right) - x, scale.toPixelY(bottom) - y];\n}\n",
|
|
35
|
+
"/**\n * The target that every interactive component attaches to.\n *\n * An interactive component needs two things: a surface to draw on and an\n * element that reports the clicks. A canvas is both, so a browser caller\n * passes one canvas.\n *\n * A test cannot pass a canvas. happy-dom gives a canvas no 2D context, so\n * nothing can draw to it. A test therefore passes the two things apart: a\n * recording surface to draw to, and a real element to dispatch events at. This\n * split is the pattern for every module in `src/interactive/`.\n */\n\nimport { resolveTarget } from \"../plot/target\";\nimport type { RenderTarget } from \"../plot/target\";\n\n/** The part of an element that an interactive component listens to. */\nexport type ClickSource = Pick<\n HTMLElement,\n \"addEventListener\" | \"removeEventListener\" | \"getBoundingClientRect\"\n>;\n\n/** A surface and an element, held apart. */\nexport interface SplitTarget {\n /** Where the component draws. */\n readonly surface: RenderTarget;\n /** Where the component listens. */\n readonly element: ClickSource;\n}\n\n/** What an interactive component accepts. */\nexport type InteractiveTarget = HTMLCanvasElement | SplitTarget;\n\n/**\n * Reduce an interactive target to a surface and an element.\n *\n * @param target A canvas, or a surface and an element.\n * @returns The two parts.\n * @throws Error If a canvas gives no 2D context.\n */\nexport function resolveInteractiveTarget(\n target: InteractiveTarget,\n): SplitTarget {\n if (\"surface\" in target) {\n return target;\n }\n return { surface: resolveTarget(target), element: target };\n}\n\n/**\n * Convert the position of a mouse event to a pixel of the surface.\n *\n * CSS may show a canvas at a size other than its pixel size. The function\n * therefore scales the position by the ratio of the two. A rectangle of zero\n * width or height carries no ratio, so the function reads the client position\n * as a surface pixel. Only a hidden element and a stub report such a\n * rectangle.\n *\n * @param element The element that received the event.\n * @param surface The drawing surface and its pixel size.\n * @param event The mouse event.\n * @returns The pixel column and row of the event.\n */\nexport function eventPixel(\n element: ClickSource,\n surface: RenderTarget,\n event: MouseEvent,\n): { readonly x: number; readonly y: number } {\n const rect = element.getBoundingClientRect();\n const scaleX = rect.width === 0 ? 1 : surface.width / rect.width;\n const scaleY = rect.height === 0 ? 1 : surface.height / rect.height;\n return {\n x: (event.clientX - rect.left) * scaleX,\n y: (event.clientY - rect.top) * scaleY,\n };\n}\n\n/**\n * A drawing surface and an element to build controls in, held apart.\n *\n * This is the `SplitTarget` idea again, for a component driven by a panel of\n * controls rather than by clicks on the picture. A test passes the two parts,\n * so it can drive the inputs without a real layout and without a canvas that\n * happy-dom cannot give a context to.\n */\nexport interface PanelTarget {\n /** Where the component draws. */\n readonly surface: RenderTarget;\n /** Where the component builds its inputs. The caller keeps this element. */\n readonly controls: HTMLElement;\n}\n\n/**\n * What a control-driven component accepts.\n *\n * Pass a container element in a browser and the component fills it. Pass a\n * `PanelTarget` to hold the two parts apart, which is how the tests run.\n */\nexport type ControlTarget = HTMLElement | PanelTarget;\n\n/** A resolved panel, and a way to take back anything that was built for it. */\nexport interface ResolvedPanel {\n readonly surface: RenderTarget;\n readonly controls: HTMLElement;\n /** Remove what this resolver created. A no-op when the caller supplied it. */\n release(): void;\n}\n\n/** R lays the gadget out as a 140px column of controls beside the plot. */\nconst CONTROL_COLUMN_WIDTH = 140;\nconst FALLBACK_WIDTH = 640;\nconst FALLBACK_HEIGHT = 400;\n\n/**\n * Reduce a control target to a surface and a place to build controls.\n *\n * Given a container, this builds the canvas and the controls column inside it\n * in R's arrangement, and `release()` takes them out again. Given the parts,\n * it passes them through and `release()` does nothing, because the caller owns\n * what it supplied.\n *\n * @param target A container element, or a surface and a controls host.\n * @returns The two parts and a way to undo what was built.\n * @throws Error If a container is a canvas, or if a created canvas gives no 2D\n * context.\n */\nexport function resolveControlTarget(target: ControlTarget): ResolvedPanel {\n if (\"surface\" in target) {\n return {\n surface: target.surface,\n controls: target.controls,\n release: () => undefined,\n };\n }\n\n if (target.tagName === \"CANVAS\") {\n throw new Error(\n \"interactive target: pass a container element to build the panel in, \" +\n \"not a canvas. A canvas has no room for the controls.\",\n );\n }\n\n const owner = target.ownerDocument;\n const controls = owner.createElement(\"div\");\n controls.style.width = `${CONTROL_COLUMN_WIDTH}px`;\n controls.style.flexShrink = \"0\";\n controls.style.padding = \"4px 6px\";\n controls.style.overflowY = \"auto\";\n\n const width =\n target.clientWidth > CONTROL_COLUMN_WIDTH\n ? target.clientWidth - CONTROL_COLUMN_WIDTH\n : FALLBACK_WIDTH;\n const height =\n target.clientHeight > 0 ? target.clientHeight : FALLBACK_HEIGHT;\n const ratio = pixelRatio();\n\n // The store holds one pixel per device pixel; the style keeps the element\n // the size the layout asked for. Without this the browser stretches a\n // smaller image over a denser screen and every edge, letters worst of all,\n // comes out soft.\n const canvas = owner.createElement(\"canvas\");\n canvas.width = Math.round(width * ratio);\n canvas.height = Math.round(height * ratio);\n canvas.style.width = `${width}px`;\n canvas.style.height = `${height}px`;\n canvas.style.minWidth = \"0\";\n\n target.style.display = \"flex\";\n target.appendChild(controls);\n target.appendChild(canvas);\n\n const remove = (): void => {\n controls.remove();\n canvas.remove();\n };\n\n const context = canvas.getContext(\"2d\");\n if (context === null) {\n // Leave the container as it was found rather than half filled.\n remove();\n throw new Error(\n \"interactive target: the canvas gave no 2D context. In tests, pass \" +\n \"{ surface: { ctx, width, height }, controls } instead of a container.\",\n );\n }\n\n // Scale once, here, so that everything drawn afterwards keeps working in\n // layout pixels and knows nothing about the density of the screen.\n if (ratio !== 1) {\n context.scale(ratio, ratio);\n }\n\n return { surface: { ctx: context, width, height }, controls, release: remove };\n}\n\n/**\n * An element for Plotly to fill, and an element to build controls in.\n *\n * The third target shape of the port, after `SplitTarget` and `PanelTarget`.\n * The 3D components need it because Plotly draws into an element of its own\n * rather than onto a canvas: there is no context to hand around, and the\n * element the plot lives in is the element the engine later purges.\n */\nexport interface Plot3dPanel {\n /** Where Plotly draws. The component gives this element to the plot. */\n readonly plot: HTMLElement;\n /** Where the component builds its inputs. */\n readonly controls: HTMLElement;\n}\n\n/**\n * What a 3D component accepts.\n *\n * Pass a container element in a browser and the component fills it. Pass the\n * two parts to hold them apart, which is how the tests run.\n */\nexport type Plot3dTarget = HTMLElement | Plot3dPanel;\n\n/** A resolved 3D panel, and a way to take back anything built for it. */\nexport interface ResolvedPlot3dPanel extends Plot3dPanel {\n /** Remove what this resolver created. A no-op when the caller supplied it. */\n release(): void;\n}\n\n/** The height a plot element falls back to when the container declares none. */\nconst FALLBACK_PLOT_HEIGHT = 480;\n\n/**\n * Reduce a 3D target to a plot element and a place to build controls.\n *\n * Given a container, this builds the two elements in R's own arrangement: a\n * strip of controls across the top, and the plot filling what is left. R's\n * gadget lays both 3D families out that way (`flex-direction: column`, the\n * control rows `flex-shrink: 0`, the plot `flex: 1`).\n *\n * @param target A container element, or the two parts.\n * @returns The two parts and a way to undo what was built.\n * @throws Error If a container is a canvas, which Plotly cannot draw into.\n */\nexport function resolvePlot3dTarget(target: Plot3dTarget): ResolvedPlot3dPanel {\n if (\"plot\" in target) {\n return {\n plot: target.plot,\n controls: target.controls,\n release: () => undefined,\n };\n }\n\n if (target.tagName === \"CANVAS\") {\n throw new Error(\n \"interactive target: pass a container element to build the panel in, \" +\n \"not a canvas. Plotly draws into an element of its own.\",\n );\n }\n\n const owner = target.ownerDocument;\n const controls = owner.createElement(\"div\");\n controls.style.display = \"flex\";\n controls.style.flexWrap = \"wrap\";\n controls.style.gap = \"12px\";\n controls.style.padding = \"8px\";\n controls.style.flexShrink = \"0\";\n\n const plot = owner.createElement(\"div\");\n plot.style.flex = \"1 1 auto\";\n plot.style.minHeight =\n target.clientHeight > 0 ? \"0\" : `${FALLBACK_PLOT_HEIGHT}px`;\n\n target.style.display = \"flex\";\n target.style.flexDirection = \"column\";\n target.appendChild(controls);\n target.appendChild(plot);\n\n return {\n plot,\n controls,\n release: () => {\n controls.remove();\n plot.remove();\n },\n };\n}\n\n/**\n * How many device pixels the screen puts in a layout pixel.\n *\n * Anything other than a positive number, which is what a test environment\n * without a screen reports, counts as one.\n */\nfunction pixelRatio(): number {\n const ratio = globalThis.devicePixelRatio;\n return typeof ratio === \"number\" && ratio > 0 ? ratio : 1;\n}\n",
|
|
36
|
+
"/**\n * The controls the 3D components build, and the rule for where a slider may\n * stand.\n *\n * Both 3D gadgets of the R package are panels of shiny inputs above a plot,\n * and both need the same three things: a labeled slider that shows its own\n * value, a labeled picker, and a line of text for what R would have written\n * to the console. They are here so that the two components differ in what\n * they control, not in how a control is made.\n *\n * The 2D components came first and built their controls inline. The reuse\n * audit at the end of the port found the same builders written out again in\n * each of them, so they share this module too: `interactiveTTest` and\n * `interactiveMatrixInverse` for the slider and the standing rule,\n * `interactiveSampling` for the picker.\n */\n\n/** Where a slider may stand: R's `min`, `max` and `step`. */\nexport interface SliderRange {\n readonly min: number;\n readonly max: number;\n readonly step: number;\n}\n\n/** A control, its label, and the element the component listens to. */\nexport interface BuiltSlider {\n /** The label element that holds the caption, the value, and the input. */\n readonly wrapper: HTMLElement;\n readonly input: HTMLInputElement;\n /** The element that shows the current value beside the caption. */\n readonly readout: HTMLElement;\n}\n\n/** A picker, its label, and the element the component listens to. */\nexport interface BuiltSelect {\n readonly wrapper: HTMLElement;\n readonly input: HTMLSelectElement;\n}\n\n/** A line of text for what R writes to the console. */\nexport interface BuiltNote {\n readonly element: HTMLElement;\n /** Show a note, or take the line away when there is none. */\n show(text: string | null): void;\n}\n\n/**\n * Correct a starting value to something the slider can stand at.\n *\n * Three rules, in order, taken from the `validate()` of the\n * `ion.rangeSlider.js` that ships inside shiny. A value that is not a number\n * becomes the minimum; a value outside the range moves to the nearer bound; a\n * value that misses the step moves to the nearest step, counted from the\n * minimum.\n *\n * The third rule is not R's — R leaves the handle wherever it was asked to\n * stand — but an HTML range input rounds a step-mismatched value by itself,\n * so a component that kept the asked-for number would draw one picture and\n * show a slider standing at another.\n *\n * @param given The value the caller asked for, or nothing.\n * @param fallback The value R's own argument defaults to.\n * @param range Where the slider may stand.\n * @returns A value on the slider's own grid.\n */\nexport function startingSliderValue(\n given: number | undefined,\n fallback: number,\n range: SliderRange,\n): number {\n if (given === undefined) {\n return fallback;\n }\n if (!Number.isFinite(given)) {\n return range.min;\n }\n\n const clamped = Math.min(range.max, Math.max(range.min, given));\n // Scaling both sides by the step's own precision keeps a value that sits\n // exactly halfway from rounding the wrong way: 1.25 is 32.5 steps above a\n // minimum of −2, and 3.25 / 0.1 alone lands a hair below that.\n const decimals = decimalsOf(range.step);\n const factor = 10 ** decimals;\n const steps = Math.round(\n ((clamped - range.min) * factor) / (range.step * factor),\n );\n const snapped = Number((range.min + steps * range.step).toFixed(decimals));\n // Every range in this port has its maximum on the step grid, so this only\n // guards a range that does not.\n return Math.min(range.max, snapped);\n}\n\n/** Return how many decimals a step carries. */\nfunction decimalsOf(step: number): number {\n return String(step).split(\".\")[1]?.length ?? 0;\n}\n\n/**\n * Build one labeled slider that shows its own value.\n *\n * @param owner The document to build in.\n * @param name The name the component reads the event by.\n * @param label R's own caption.\n * @param range Where the slider may stand.\n * @param value Where it starts. Pass a value `startingSliderValue` returned.\n * @returns The wrapper to append, the input to listen to, and the readout.\n */\nexport function buildSlider(\n owner: Document,\n name: string,\n label: string,\n range: SliderRange,\n value: number,\n): BuiltSlider {\n const wrapper = owner.createElement(\"label\");\n wrapper.style.display = \"block\";\n\n const caption = owner.createElement(\"span\");\n caption.textContent = `${label} `;\n\n const readout = owner.createElement(\"output\");\n readout.textContent = String(value);\n\n const input = owner.createElement(\"input\");\n input.type = \"range\";\n input.name = name;\n input.min = String(range.min);\n input.max = String(range.max);\n input.step = String(range.step);\n input.value = String(value);\n input.style.width = \"100%\";\n\n wrapper.appendChild(caption);\n wrapper.appendChild(readout);\n wrapper.appendChild(input);\n return { wrapper, input, readout };\n}\n\n/**\n * Build one labeled picker.\n *\n * @param owner The document to build in.\n * @param name The name the component reads the event by.\n * @param label R's own caption.\n * @param choices The options, in the order R lists them. A numeric choice\n * reaches the DOM as its own text, which is how R's own numeric choices\n * arrive there.\n * @param selected The option to start on.\n * @returns The wrapper to append, and the input to listen to.\n */\nexport function buildSelect(\n owner: Document,\n name: string,\n label: string,\n choices: readonly (string | number)[],\n selected: string | number,\n): BuiltSelect {\n const wrapper = owner.createElement(\"label\");\n wrapper.style.display = \"block\";\n\n const caption = owner.createElement(\"span\");\n caption.textContent = `${label} `;\n\n const input = owner.createElement(\"select\");\n input.name = name;\n input.style.width = \"100%\";\n for (const choice of choices) {\n const option = owner.createElement(\"option\");\n option.value = String(choice);\n option.textContent = String(choice);\n input.appendChild(option);\n }\n input.value = String(selected);\n\n wrapper.appendChild(caption);\n wrapper.appendChild(input);\n return { wrapper, input };\n}\n\n/**\n * Build the line that carries what R writes to the console.\n *\n * A library cannot write to a console, and the plot layer therefore returns\n * its message instead of printing it. One element, whose text is replaced,\n * so that a hundred redraws leave one sentence rather than a hundred.\n *\n * @param owner The document to build in.\n * @returns The element to append, and the way to set its text.\n */\nexport function buildNote(owner: Document): BuiltNote {\n const element = owner.createElement(\"p\");\n element.style.margin = \"4px 0 0\";\n element.style.fontSize = \"12px\";\n element.textContent = \"\";\n element.hidden = true;\n\n return {\n element,\n show(text: string | null): void {\n element.textContent = text ?? \"\";\n element.hidden = text === null;\n },\n };\n}\n",
|
|
37
|
+
"/**\n * The interactive matrix inverse: move the four sliders and watch the inverse\n * answer.\n *\n * This is the port of `interactive_matrix_inverse()` in\n * `../compstatslib/R/matrix_inverse_interactive.R`. R builds a miniUI gadget\n * with four sliders, one for each entry of the matrix, and re-runs\n * `plot_matrix_inverse()` whenever one moves. This module does the same: it\n * owns the controls and the matrix in them, and hands every draw to\n * `plotMatrixInverse`. It contains no drawing of the picture and no\n * arithmetic.\n *\n * `plotMatrixInverse` takes no options, so there is nothing for this module to\n * forward and the forwarding rule of CLAUDE.md is met by having nothing to\n * pass. **A plot option added later must be threaded through here**, in the\n * way `interactiveTTest` does it: pull the component's own fields out of the\n * options object and pass the rest to the plot untouched.\n *\n * R's `runGadget()` blocks until \"Done\" and then returns nothing at all —\n * `stopApp(NULL)`, from both Done and Cancel, and there is no Reset. Nothing\n * blocks in a browser, so this returns a handle at once. `getValues()` reads\n * the sliders and `getResult()` reads what was last drawn. `done()` reports\n * the current matrix to the `onDone` callback: R hands back nothing, but a\n * callback with no argument would only send the caller back to the handle for\n * the values it just asked about.\n *\n * **The component draws one thing itself: the notice.** When the matrix has no\n * inverse, `plotMatrixInverse` makes no drawing call at all, because R's\n * `solve()` stops the R function before it draws. In R the user is not left\n * looking at the last picture: shiny catches the error and prints it in the\n * plot panel, which is not part of `plot_matrix_inverse`. So this module\n * clears the surface and writes R's own message on it. That is the state of\n * the gadget, reported to the user, and not a second way to draw a matrix.\n */\n\nimport type { Matrix2, MatrixInversion } from \"../core/matrix\";\nimport { plotMatrixInverse } from \"../plot/matrixInverse\";\nimport type { RenderTarget } from \"../plot/target\";\nimport { buildSlider, startingSliderValue } from \"./controls\";\nimport type { SliderRange } from \"./controls\";\nimport { resolveControlTarget } from \"./target\";\nimport type { ControlTarget } from \"./target\";\n\n/**\n * Where the sliders start: R's `x1_init = 1, y1_init = 2, x2_init = 2,\n * y2_init = 1`.\n *\n * The matrix inverts, and both of its columns and both columns of its inverse\n * are inside the fixed window of the plot.\n */\nexport const DEFAULT_MATRIX_INVERSE_VALUES: Matrix2 = {\n x1: 1,\n y1: 2,\n x2: 2,\n y2: 1,\n};\n\n/**\n * What the component accepts.\n *\n * The four entries are the starting positions of the sliders, named as R names\n * the matrix. R calls its own arguments `x1_init` and so on, to keep them\n * apart from the reactive inputs of the same name; an options object is\n * already the starting state, so the suffix would say nothing here.\n *\n * A value outside the slider range, or one that misses its step, is corrected\n * at construction. See `startingValue`.\n */\nexport interface InteractiveMatrixInverseOptions extends Partial<Matrix2> {\n /** What to run on `done()`. */\n readonly onDone?: (values: Matrix2) => void;\n}\n\n/** What the caller holds after the component starts. */\nexport interface InteractiveMatrixInverseHandle {\n /** Return the matrix the sliders stand at now. */\n getValues(): Matrix2;\n /**\n * Return what the last draw reported: the determinant, the inverse, and the\n * singularity. The inverse is null exactly when the picture is a notice.\n */\n getResult(): MatrixInversion;\n /** Hand the current matrix to the `onDone` callback. */\n done(): void;\n /** Stop listening and take back the controls that were built. */\n destroy(): void;\n}\n\n/**\n * R's four sliders, in R's order.\n *\n * Every one of them is `sliderInput(\"x1\", \"x1\", min = -2, max = 2, value =\n * x1_init, step = 0.1)`, so the name is also the caption R shows, and the\n * range is the same for all four. The starting positions are not here: they\n * come from `DEFAULT_MATRIX_INVERSE_VALUES`.\n */\nconst ENTRIES = [\"x1\", \"y1\", \"x2\", \"y2\"] as const;\nconst SLIDER_MIN = -2;\nconst SLIDER_MAX = 2;\nconst SLIDER_STEP = 0.1;\n/** R: all four sliders run -2 to 2 by 0.1. */\nconst SLIDER_RANGE: SliderRange = {\n min: SLIDER_MIN,\n max: SLIDER_MAX,\n step: SLIDER_STEP,\n};\n\nconst NOTICE_BACKGROUND = \"#ffffff\";\n/** A color of this port's own choosing, so a message does not read as a\n * picture. R leaves the styling of its error text to shiny. */\nconst NOTICE_COLOR = \"#b22222\";\nconst NOTICE_FONT = \"12px monospace\";\nconst NOTICE_MARGIN = 12;\n/** C's `%g` carries six significant digits, which is what R's error shows. */\nconst SIGNIFICANT_DIGITS = 6;\n\n/**\n * Start an interactive matrix inverse on a target.\n *\n * The component builds its controls, draws once, and redraws on every change.\n *\n * @param target A container element, or a surface and a controls host. See\n * `./target.ts`.\n * @param options Starting entries and a done callback.\n * @returns The handle to the running component.\n * @throws Error If the target is a canvas, or if a canvas gives no 2D context.\n */\nexport function interactiveMatrixInverse(\n target: ControlTarget,\n options: InteractiveMatrixInverseOptions = {},\n): InteractiveMatrixInverseHandle {\n const { onDone } = options;\n const panel = resolveControlTarget(target);\n\n const startingValue = (name: keyof Matrix2): number =>\n startingSliderValue(\n options[name],\n DEFAULT_MATRIX_INVERSE_VALUES[name],\n SLIDER_RANGE,\n );\n\n let values: Matrix2 = {\n x1: startingValue(\"x1\"),\n y1: startingValue(\"y1\"),\n x2: startingValue(\"x2\"),\n y2: startingValue(\"y2\"),\n };\n\n const owner = panel.controls.ownerDocument;\n const built: HTMLElement[] = [];\n const inputs: HTMLInputElement[] = [];\n const readouts = new Map<string, HTMLElement>();\n let destroyed = false;\n\n function draw(): MatrixInversion {\n const drawn = plotMatrixInverse(panel.surface, values);\n if (drawn.singularity !== null) {\n showNotice(panel.surface, drawn);\n }\n return drawn;\n }\n\n function handleInput(event: Event): void {\n const input = event.target as HTMLInputElement;\n const name = input.name as keyof Matrix2;\n if (!(name in values)) {\n return;\n }\n\n // A slider keeps its own value inside the range and on the step, so what\n // it reports needs no correcting.\n values = { ...values, [name]: Number(input.value) };\n const readout = readouts.get(name);\n if (readout !== undefined) {\n readout.textContent = input.value;\n }\n result = draw();\n }\n\n for (const name of ENTRIES) {\n // R captions each slider with the argument's own name.\n const { wrapper, input, readout } = buildSlider(\n owner,\n name,\n name,\n SLIDER_RANGE,\n values[name],\n );\n readouts.set(name, readout);\n\n panel.controls.appendChild(wrapper);\n built.push(wrapper);\n inputs.push(input);\n input.addEventListener(\"input\", handleInput);\n }\n\n let result = draw();\n\n return {\n getValues: () => ({ ...values }),\n getResult: () => result,\n done() {\n onDone?.({ ...values });\n },\n destroy() {\n if (destroyed) {\n return;\n }\n destroyed = true;\n for (const input of inputs) {\n input.removeEventListener(\"input\", handleInput);\n }\n for (const node of built) {\n node.remove();\n }\n panel.release();\n },\n };\n}\n\n/**\n * Clear the surface and write why there is no picture.\n *\n * The text is R's own, from the two errors `solve()` raises. A surface too\n * narrow for the line cuts it off: the drawing surface of this library has no\n * way to measure text, and R's message is one line.\n */\nfunction showNotice(surface: RenderTarget, report: MatrixInversion): void {\n const { ctx, width, height } = surface;\n\n ctx.save();\n ctx.setLineDash([]);\n ctx.fillStyle = NOTICE_BACKGROUND;\n ctx.fillRect(0, 0, width, height);\n ctx.fillStyle = NOTICE_COLOR;\n ctx.font = NOTICE_FONT;\n ctx.textAlign = \"left\";\n ctx.textBaseline = \"top\";\n ctx.fillText(messageFor(report), NOTICE_MARGIN, NOTICE_MARGIN);\n ctx.restore();\n}\n\n/** Build R's own error message for a matrix that has no inverse. */\nfunction messageFor(report: MatrixInversion): string {\n if (report.singularity === \"exact\") {\n const pivot = report.zeroPivot ?? 1;\n return `Lapack routine dgesv: system is exactly singular: U[${pivot},${pivot}] = 0`;\n }\n return (\n \"system is computationally singular: reciprocal condition number = \" +\n formatConditionNumber(report.rcond)\n );\n}\n\n/**\n * Format the condition number as R's error prints it.\n *\n * R writes it with C's `%g`: six significant digits and no trailing zeros. R's\n * own message reads `reciprocal condition number = 5.55112e-17`, which is six\n * digits; the fixture document calls it five.\n *\n * Only one branch of `%g` can happen here, so only that branch is written.\n * This message is built for a matrix R calls computationally singular, and\n * that name is given only below one machine epsilon, where `%g` always takes\n * the exponent form. Do not reuse this for a number of another size: `%g`\n * writes anything from 1e-4 up in the plain form instead.\n */\nfunction formatConditionNumber(value: number): string {\n const [mantissa, exponentText] = value\n .toExponential(SIGNIFICANT_DIGITS - 1)\n .split(\"e\");\n const exponent = Number(exponentText);\n // C writes the exponent with a sign and at least two digits.\n const sign = exponent < 0 ? \"-\" : \"+\";\n const digits = String(Math.abs(exponent)).padStart(2, \"0\");\n\n return `${withoutTrailingZeros(String(mantissa))}e${sign}${digits}`;\n}\n\n/** Drop the trailing zeros of a decimal fraction, and a bare decimal point. */\nfunction withoutTrailingZeros(text: string): string {\n if (!text.includes(\".\")) {\n return text;\n }\n return text.replace(/0+$/, \"\").replace(/\\.$/, \"\");\n}\n",
|
|
38
|
+
"/**\n * The interactive sampling demonstration: press Sample and watch the\n * distribution of the statistic fill in.\n *\n * This is the port of `interactive_sampling()` in\n * `../compstatslib/R/sampling_interactive.R`. R builds a miniUI gadget with\n * two selects and a button, and re-runs `plot_sampling()` when the button is\n * pressed. This module does the same: it owns the controls, one generator, and\n * the state that piles up between draws, and hands every draw to\n * `plotSampling`. It contains no drawing code and no statistics.\n *\n * Three things set this gadget apart from the other three in the port:\n *\n * - **The button is the only thing that draws.** R wires the button to a\n * trigger and isolates the selects (`shiny::isolate`), so moving a select\n * changes what the *next* press will do and nothing more. A gadget that\n * redrew on every change would resample ten thousand values each time a\n * reader browsed the menu.\n * - **It keeps drawing down one stream.** R reads one global generator, so\n * each press carries on where the last stopped. One generator is built here\n * when the component starts and used for every draw after.\n * - **Done hands back the cache.** R's `stopApp(cache())` returns everything\n * drawn so far; the t-test and logit gadgets return nothing at all.\n *\n * `reset()` has no counterpart in R, as in the other click-collectors. It\n * empties the pile and redraws with no new samples, so the panels come back\n * blank and the window is set again from the population.\n */\n\nimport { seededRng } from \"../core/rng\";\nimport { plotSampling } from \"../plot/sampling\";\nimport type {\n PlotSamplingOptions,\n PlotSamplingResult,\n SamplingState,\n} from \"../plot/sampling\";\nimport { buildSelect } from \"./controls\";\nimport { resolveControlTarget } from \"./target\";\nimport type { ControlTarget } from \"./target\";\n\n/** Everything the panel of controls holds. */\nexport interface SamplingValues {\n /** How many values each sample takes. R's `sample_size` select. */\n readonly sampleSize: number;\n /** How many samples the next press draws. R's `reps` select. */\n readonly reps: number;\n}\n\n/**\n * What the component accepts.\n *\n * The options of `plotSampling` pass through to it, and the sample size and\n * repetition count double as the starting positions of the two selects. This\n * is the equivalent of R's `...` forwarding.\n */\nexport interface InteractiveSamplingOptions extends PlotSamplingOptions {\n /** What to run on `done()`. */\n readonly onDone?: (state: SamplingState) => void;\n}\n\n/** What the caller holds after the component starts. */\nexport interface InteractiveSamplingHandle {\n /** Return where the two selects stand now. */\n getValues(): SamplingValues;\n /** Return everything drawn so far — R's gadget cache. */\n getState(): SamplingState;\n /** Empty the pile and redraw with no samples. */\n reset(): void;\n /** Hand the accumulated state to the `onDone` callback. */\n done(): void;\n /** Stop listening and take back the controls that were built. */\n destroy(): void;\n}\n\n/** R: `choices = c(10, 100, 500, 1000, 5000, 10000)`. */\nconst SAMPLE_SIZES: readonly number[] = [10, 100, 500, 1000, 5000, 10000];\n/** R: `choices = c(1, 5, 10, 50, 100), selected = 1`. */\nconst REPETITIONS: readonly number[] = [1, 5, 10, 50, 100];\n\nconst SAMPLE_SIZE_NAME = \"sampleSize\";\nconst SAMPLE_SIZE_LABEL = \"Sample size\";\nconst REPS_NAME = \"reps\";\nconst REPS_LABEL = \"Repetitions\";\nconst BUTTON_LABEL = \"Sample\";\n\n/**\n * Start an interactive sampling demonstration on a target.\n *\n * The component builds its controls, draws once, and redraws on every press\n * of its button.\n *\n * @param target A container element, or a surface and a controls host. See\n * `./target.ts`.\n * @param population The values to sample from.\n * @param options Starting values, a done callback, and the options of\n * `plotSampling`.\n * @returns The handle to the running component.\n * @throws Error If a canvas is passed as the container, or gives no 2D\n * context.\n */\nexport function interactiveSampling(\n target: ControlTarget,\n population: readonly number[],\n options: InteractiveSamplingOptions = {},\n): InteractiveSamplingHandle {\n // Everything `plotSampling` may grow later stays in `forwarded` and reaches\n // it untouched, so a new plot option needs no edit here.\n const {\n onDone,\n sampleSize,\n reps,\n rng: given,\n state: initial,\n ...forwarded\n } = options;\n const panel = resolveControlTarget(target);\n\n let values: SamplingValues = {\n // R's selectInput ignores a `selected` that is not one of its choices,\n // and the browser then shows the first option. Snapping to the first\n // choice keeps what is drawn and what the select shows in step.\n sampleSize: chooseFrom(sampleSize, SAMPLE_SIZES),\n reps: chooseFrom(reps, REPETITIONS),\n };\n\n // One generator for the whole life of the component, so each press carries\n // on where the last stopped. This is why the default is built here and not\n // left to `plotSampling`, which builds a fresh one per call: two presses\n // would then draw down two separate streams.\n const rng = given ?? seededRng(Math.floor(Math.random() * 0x100000000));\n let state: SamplingState | null = initial ?? null;\n let last: PlotSamplingResult;\n\n const owner = panel.controls.ownerDocument;\n const built: HTMLElement[] = [];\n let destroyed = false;\n\n function draw(repetitions: number): void {\n last = plotSampling(panel.surface, population, {\n ...forwarded,\n sampleSize: values.sampleSize,\n reps: repetitions,\n rng,\n state,\n });\n state = last.state;\n }\n\n function handleChange(event: Event): void {\n const input = event.target as HTMLSelectElement;\n const numeric = Number(input.value);\n values =\n input.name === SAMPLE_SIZE_NAME\n ? { ...values, sampleSize: numeric }\n : { ...values, reps: numeric };\n }\n\n function handlePress(): void {\n draw(values.reps);\n }\n\n const sizeSelect = buildSelect(\n owner,\n SAMPLE_SIZE_NAME,\n SAMPLE_SIZE_LABEL,\n SAMPLE_SIZES,\n values.sampleSize,\n );\n const repsSelect = buildSelect(\n owner,\n REPS_NAME,\n REPS_LABEL,\n REPETITIONS,\n values.reps,\n );\n const button = owner.createElement(\"button\");\n button.type = \"button\";\n button.textContent = BUTTON_LABEL;\n\n for (const wrapper of [sizeSelect.wrapper, repsSelect.wrapper]) {\n panel.controls.appendChild(wrapper);\n built.push(wrapper);\n }\n panel.controls.appendChild(button);\n built.push(button);\n\n sizeSelect.input.addEventListener(\"change\", handleChange);\n repsSelect.input.addEventListener(\"change\", handleChange);\n button.addEventListener(\"click\", handlePress);\n\n // R's renderPlot runs as the gadget opens, at the initial settings.\n draw(values.reps);\n\n return {\n getValues: () => ({ ...values }),\n getState: () => last.state,\n reset() {\n // Drop the pile, then redraw taking no samples: the panels come back\n // empty and the window is set again from the population.\n state = null;\n draw(0);\n },\n done() {\n onDone?.(last.state);\n },\n destroy() {\n if (destroyed) {\n return;\n }\n destroyed = true;\n sizeSelect.input.removeEventListener(\"change\", handleChange);\n repsSelect.input.removeEventListener(\"change\", handleChange);\n button.removeEventListener(\"click\", handlePress);\n for (const node of built) {\n node.remove();\n }\n panel.release();\n },\n };\n}\n\n/** Take the asked-for value if the select offers it, else its first choice. */\nfunction chooseFrom(asked: number | undefined, choices: readonly number[]): number {\n const first = choices[0] as number;\n return asked !== undefined && choices.includes(asked) ? asked : first;\n}\n",
|
|
39
|
+
"/**\n * The interactive t test: move the sliders and watch the power change.\n *\n * This is the port of `interactive_t_test()` in\n * `../compstatslib/R/t_statistic_interactive.R`. R builds a miniUI gadget with\n * four sliders and a checkbox, and re-runs `plot_t_test()` whenever one moves.\n * This module does the same: it owns the controls and the values in them, and\n * hands every draw to `plotTTest`. It contains no drawing code and no\n * statistics.\n *\n * R's `runGadget()` blocks until \"Done\" and then returns nothing at all —\n * `stopApp(NULL)`. Nothing blocks in a browser, so this returns a handle at\n * once. `getValues()` reads the controls and `getStats()` reads what was last\n * drawn. `done()` reports the current values to the `onDone` callback: R hands\n * back nothing, but a callback with no argument would only send the caller\n * back to the handle for the values it just asked about.\n */\n\nimport { DEFAULT_T_TEST_OPTIONS } from \"../core/ttest\";\nimport type { TTestStats } from \"../core/ttest\";\nimport { plotTTest } from \"../plot/tTest\";\nimport type { PlotTTestOptions } from \"../plot/tTest\";\nimport { buildSlider } from \"./controls\";\nimport { resolveControlTarget } from \"./target\";\nimport type { ControlTarget } from \"./target\";\n\n/** Everything the panel of controls holds. */\nexport interface TTestValues {\n /** The difference the alternative hypothesis claims. */\n readonly diff: number;\n /** The population standard deviation. */\n readonly sd: number;\n /** The sample size. */\n readonly n: number;\n /** The significance level. */\n readonly alpha: number;\n /** Whether the matrix of the four outcomes is showing. */\n readonly errorMatrix: boolean;\n}\n\n/**\n * What the component accepts.\n *\n * The options of `plotTTest` pass through to it, and the four test parameters\n * double as the starting positions of the sliders. This is the equivalent of\n * R's `...` forwarding.\n */\nexport interface InteractiveTTestOptions extends PlotTTestOptions {\n /** What to run on `done()`. */\n readonly onDone?: (values: TTestValues) => void;\n}\n\n/** What the caller holds after the component starts. */\nexport interface InteractiveTTestHandle {\n /** Return where the controls stand now. */\n getValues(): TTestValues;\n /** Return the statistics behind the last picture drawn. */\n getStats(): TTestStats;\n /** Hand the current values to the `onDone` callback. */\n done(): void;\n /** Stop listening and take back the controls that were built. */\n destroy(): void;\n}\n\n/** One slider, exactly as R's `sliderInput()` declares it. */\ninterface SliderSpec {\n readonly name: \"diff\" | \"sd\" | \"n\" | \"alpha\";\n readonly label: string;\n readonly min: number;\n readonly max: number;\n readonly step: number;\n}\n\n/**\n * The four sliders of R's gadget.\n *\n * The starting positions are not here: they come from\n * `DEFAULT_T_TEST_OPTIONS`, which already holds R's own defaults, so the panel\n * and `plot_t_test()` cannot drift apart.\n */\nconst SLIDERS: readonly SliderSpec[] = [\n { name: \"diff\", label: \"Difference\", min: 0, max: 4, step: 0.1 },\n { name: \"sd\", label: \"Std Dev\", min: 1, max: 5, step: 0.1 },\n { name: \"n\", label: \"Sample Size\", min: 2, max: 500, step: 1 },\n { name: \"alpha\", label: \"Alpha\", min: 0.01, max: 0.1, step: 0.01 },\n];\n\n/** R: `checkboxInput(\"error_matrix\", \"Error Matrix\", value = FALSE)`. */\nconst ERROR_MATRIX_NAME = \"errorMatrix\";\nconst ERROR_MATRIX_LABEL = \"Error Matrix\";\n\n/**\n * Start an interactive t test on a target.\n *\n * The component builds its controls, draws once, and redraws on every change.\n *\n * @param target A container element, or a surface and a controls host. See\n * `./target.ts`.\n * @param options Starting values, a done callback, and the options of\n * `plotTTest`.\n * @returns The handle to the running component.\n * @throws Error If a canvas gives no 2D context.\n */\nexport function interactiveTTest(\n target: ControlTarget,\n options: InteractiveTTestOptions = {},\n): InteractiveTTestHandle {\n // Everything `plotTTest` may grow later stays in `forwarded` and reaches it\n // untouched, so a new plot option needs no edit here.\n const { onDone, diff, sd, n, alpha, errorMatrix, ...forwarded } = options;\n const panel = resolveControlTarget(target);\n\n let values: TTestValues = {\n diff: diff ?? DEFAULT_T_TEST_OPTIONS.diff,\n sd: sd ?? DEFAULT_T_TEST_OPTIONS.sd,\n n: n ?? DEFAULT_T_TEST_OPTIONS.n,\n alpha: alpha ?? DEFAULT_T_TEST_OPTIONS.alpha,\n errorMatrix: errorMatrix ?? false,\n };\n\n const owner = panel.controls.ownerDocument;\n const built: HTMLElement[] = [];\n const inputs: HTMLInputElement[] = [];\n const readouts = new Map<string, HTMLElement>();\n let destroyed = false;\n\n function draw(): TTestStats {\n return plotTTest(panel.surface, { ...forwarded, ...values });\n }\n\n /** Read one control into a fresh set of values. */\n function withControl(input: HTMLInputElement): TTestValues {\n const numeric = Number(input.value);\n switch (input.name) {\n case \"diff\":\n return { ...values, diff: numeric };\n case \"sd\":\n return { ...values, sd: numeric };\n case \"n\":\n return { ...values, n: numeric };\n case \"alpha\":\n return { ...values, alpha: numeric };\n case ERROR_MATRIX_NAME:\n return { ...values, errorMatrix: input.checked };\n default:\n return values;\n }\n }\n\n function handleInput(event: Event): void {\n const input = event.target as HTMLInputElement;\n values = withControl(input);\n const readout = readouts.get(input.name);\n if (readout !== undefined) {\n readout.textContent = input.value;\n }\n stats = draw();\n }\n\n /** Add one control to the panel and start listening to it. */\n function mount(wrapper: HTMLElement, input: HTMLInputElement): void {\n panel.controls.appendChild(wrapper);\n built.push(wrapper);\n inputs.push(input);\n input.addEventListener(\"input\", handleInput);\n }\n\n for (const spec of SLIDERS) {\n const { wrapper, input, readout } = buildSlider(\n owner,\n spec.name,\n spec.label,\n spec,\n values[spec.name],\n );\n readouts.set(spec.name, readout);\n mount(wrapper, input);\n }\n\n const checkboxWrapper = owner.createElement(\"label\");\n checkboxWrapper.style.display = \"block\";\n const checkbox = owner.createElement(\"input\");\n checkbox.type = \"checkbox\";\n checkbox.name = ERROR_MATRIX_NAME;\n checkbox.checked = values.errorMatrix;\n checkboxWrapper.appendChild(checkbox);\n checkboxWrapper.appendChild(owner.createTextNode(` ${ERROR_MATRIX_LABEL}`));\n mount(checkboxWrapper, checkbox);\n\n let stats = draw();\n\n return {\n getValues: () => ({ ...values }),\n getStats: () => stats,\n done() {\n onDone?.({ ...values });\n },\n destroy() {\n if (destroyed) {\n return;\n }\n destroyed = true;\n for (const input of inputs) {\n input.removeEventListener(\"input\", handleInput);\n }\n for (const node of built) {\n node.remove();\n }\n panel.release();\n },\n };\n}\n",
|
|
40
|
+
"/**\n * The interactive logit: click to add an outcome, and watch the curve move.\n *\n * This is the port of `interactive_logit()` in\n * `../compstatslib/R/logit_interactive.R`. R holds the points in a reactive\n * value and calls `plot_logit()` on every change, forwarding its `...`\n * arguments. This module does the same: it owns the points and the clicks, and\n * it hands every draw to `plotLogit`. It contains no drawing code and no\n * statistics.\n *\n * R's `runGadget()` blocks and returns the points when the user clicks \"Done\".\n * A browser blocks at nothing, so this returns a handle at once. Read the\n * points from `getPoints()`, or call `done()` to hand them to the `onDone`\n * callback.\n */\n\nimport type { Point } from \"../core/regression\";\nimport { logitScale, plotLogit } from \"../plot/logit\";\nimport type { PlotLogitOptions } from \"../plot/logit\";\nimport { pixelInArea } from \"../plot/axes\";\nimport { eventPixel, resolveInteractiveTarget } from \"./target\";\nimport type { InteractiveTarget } from \"./target\";\n\n/**\n * What the component accepts.\n *\n * The options of `plotLogit` pass through to it unchanged. This is the\n * equivalent of R's `...` forwarding.\n */\nexport interface InteractiveLogitOptions extends PlotLogitOptions {\n /** Points to start from. R takes these as its `points` argument. */\n readonly initialPoints?: readonly Point[];\n /** What to run on `done()`. */\n readonly onDone?: (points: readonly Point[]) => void;\n}\n\n/** What the caller holds after the component starts. */\nexport interface InteractiveLogitHandle {\n /** Return the points collected so far, in click order. */\n getPoints(): readonly Point[];\n /** Drop every point and redraw. This drops the initial points too. */\n reset(): void;\n /** Hand the points to the `onDone` callback. */\n done(): void;\n /** Stop listening. The points stay readable. */\n destroy(): void;\n}\n\n/**\n * R: `interactive_logit(points, formula, min_x = 0, max_x = 50, ...)`.\n *\n * The right edge is 50 here and 1 in `plot_logit()`. The two genuinely differ\n * in R: the gadget opens on a wide window to click into, and the plotting\n * function opens on the narrow one its own default implies.\n */\nconst DEFAULT_MIN_X = 0;\nconst DEFAULT_MAX_X = 50;\n\n/**\n * Start an interactive logit on a target.\n *\n * The component draws at once, so an empty start shows empty axes. Each click\n * inside the plot area adds one point and redraws. A click outside that area\n * does nothing, because the world window ends at the edge of the area and a\n * point beyond it would not appear.\n *\n * @param target A canvas, or a surface and an element. See `./target.ts`.\n * @param options Points to start from, a done callback, and the options of\n * `plotLogit`.\n * @returns The handle to the running component.\n * @throws Error If a canvas gives no 2D context.\n */\nexport function interactiveLogit(\n target: InteractiveTarget,\n options: InteractiveLogitOptions = {},\n): InteractiveLogitHandle {\n const {\n initialPoints,\n onDone,\n minX = DEFAULT_MIN_X,\n maxX = DEFAULT_MAX_X,\n ...plotOptions\n } = options;\n const { surface, element } = resolveInteractiveTarget(target);\n\n let points: readonly Point[] = initialPoints ?? [];\n\n // R widens the right edge once, from the points it was handed\n // (`if (nrow(points) > 0) max_x <- max(max_x, points[[x_name]])`), and holds\n // that value for the rest of the session. Nothing collected later can push\n // past it, because every click is clamped to it.\n const rightEdge = points.reduce(\n (edge, point) => Math.max(edge, point.x),\n maxX,\n );\n\n function draw(): void {\n plotLogit(surface, points, { ...plotOptions, minX, maxX: rightEdge });\n }\n\n function handleClick(event: MouseEvent): void {\n // The window follows the points, so the scale is read again on each click\n // rather than held from the start: a point outside the default window\n // moves every pixel of the picture. `logitScale` is the one place that\n // window is defined, and `plotLogit` draws through the same call.\n const scale = logitScale(surface.width, surface.height, points, {\n minX,\n maxX: rightEdge,\n });\n const pixel = eventPixel(element, surface, event);\n if (!pixelInArea(scale.area, pixel)) {\n return;\n }\n\n points = [\n ...points,\n {\n // R: `click_x <- max(0, min(max_x, click$x))`. The lower bound is zero\n // rather than `min_x`, so a click left of the origin lands on it even\n // when the window opens further left. That is R's rule as written.\n x: clamp(scale.toWorldX(pixel.x), 0, rightEdge),\n // R: `click_y <- round(click$y)`. This is what makes every outcome a 0\n // or a 1, which `logisticRegression` requires. JavaScript rounds an\n // exact half up and R rounds it to even, so a click on the half line\n // itself becomes a 1 here and a 0 in R. The same deviation is already\n // recorded for `formatStat`.\n y: Math.round(clamp(scale.toWorldY(pixel.y), 0, 1)),\n },\n ];\n draw();\n }\n\n draw();\n element.addEventListener(\"click\", handleClick);\n\n return {\n getPoints: () => [...points],\n reset() {\n points = [];\n draw();\n },\n done() {\n onDone?.([...points]);\n },\n destroy() {\n element.removeEventListener(\"click\", handleClick);\n },\n };\n}\n\n/** Hold a value between two bounds. */\nfunction clamp(value: number, low: number, high: number): number {\n return Math.min(high, Math.max(low, value));\n}\n",
|
|
41
|
+
"/**\n * The interactive PCA: click to add a point, and watch the components turn.\n *\n * This is the port of `interactive_pca()` in\n * `../compstatslib/R/pca_interactive.R`. R holds the points in a reactive\n * value and calls `plot_pca()` on every change. This module does the same: it\n * owns the points and the clicks, and it hands every draw to `plotPca`. It\n * contains no drawing code and no statistics.\n *\n * R's `runGadget()` blocks and returns `list(points, pca)` when the user\n * clicks \"Done\". A browser blocks at nothing, so this returns a handle at\n * once. Read the two through `getPoints()` and `getFit()`, or call `done()` to\n * hand both to the `onDone` callback.\n *\n * **A click is taken as it comes.** R appends `data.frame(x = click$x,\n * y = click$y)` with no clamp, no rounding, and no test that the click landed\n * inside the plot area — and shiny reports a coordinate for a click anywhere\n * on the plot image, margins included. So this accepts a click anywhere on the\n * surface and keeps the world coordinate whole. That is a deliberate\n * difference from `interactiveRegression`, which ignores a click outside the\n * plot area because its window ends there, and from `interactiveLogit`, which\n * must clamp and round because its outcome is 0 or 1. Here a point outside the\n * window is a real state, and the plot clips it out of sight while it goes on\n * counting towards the components.\n */\n\nimport type { PcaResult } from \"../core/pca\";\nimport type { Point } from \"../core/regression\";\nimport { pcaScale, plotPca } from \"../plot/pca\";\nimport type { PlotPcaOptions } from \"../plot/pca\";\nimport { eventPixel, resolveInteractiveTarget } from \"./target\";\nimport type { InteractiveTarget } from \"./target\";\n\n/**\n * What `done()` hands over: R's `list(points = ..., pca = ...)`.\n *\n * R's `pca` field is `fit` here. Inside a component that is already about\n * PCA, a field called `pca` says nothing, and \"the fit\" is what this port\n * calls the thing a plot computed from a set of points everywhere else —\n * `plotRegression` and `plotLogit` both return one.\n */\nexport interface InteractivePcaResult {\n /** The points collected, in click order. */\n readonly points: readonly Point[];\n /** What the last draw computed, or null below three points. */\n readonly fit: PcaResult | null;\n}\n\n/**\n * What the component accepts.\n *\n * The options of `plotPca` pass through to it unchanged. This is the\n * equivalent of R's `...` forwarding, though R's own `interactive_pca()`\n * exposes only `meancenter` and leaves the window at the `plot_pca` defaults.\n */\nexport interface InteractivePcaOptions extends PlotPcaOptions {\n /** Points to start from. R starts from an empty data frame. */\n readonly initialPoints?: readonly Point[];\n /** What to run on `done()`. */\n readonly onDone?: (result: InteractivePcaResult) => void;\n}\n\n/** What the caller holds after the component starts. */\nexport interface InteractivePcaHandle {\n /** Return the points collected so far, in click order. */\n getPoints(): readonly Point[];\n /**\n * Return what the last draw computed.\n *\n * Null below three points, which is `plot_pca`'s own guard: R's\n * `pca_result` holds whatever the last `plot_pca()` returned, and that is\n * NULL there too.\n */\n getFit(): PcaResult | null;\n /** Drop every point and redraw. This drops the initial points too. */\n reset(): void;\n /** Hand the points and the fit to the `onDone` callback. */\n done(): void;\n /** Stop listening. The points and the fit stay readable. */\n destroy(): void;\n}\n\n/**\n * Start an interactive PCA on a target.\n *\n * The component draws at once, so an empty start shows empty axes. Each click\n * adds one point and redraws. From the third point the two component arrows\n * appear and turn with every point after it.\n *\n * @param target A canvas, or a surface and an element. See `./target.ts`.\n * @param options Points to start from, a done callback, and the options of\n * `plotPca`.\n * @returns The handle to the running component.\n * @throws Error If a canvas gives no 2D context.\n */\nexport function interactivePca(\n target: InteractiveTarget,\n options: InteractivePcaOptions = {},\n): InteractivePcaHandle {\n const { initialPoints, onDone, ...plotOptions } = options;\n const { surface, element } = resolveInteractiveTarget(target);\n\n let points: readonly Point[] = initialPoints ?? [];\n let fit: PcaResult | null = null;\n\n function draw(): void {\n fit = plotPca(surface, points, plotOptions);\n }\n\n function handleClick(event: MouseEvent): void {\n // Built fresh for every click. The window is fixed, but `asp = 1` ties it\n // to the size of the surface, so a scale kept from an earlier size would\n // put the point somewhere plausible and wrong.\n const scale = pcaScale(surface.width, surface.height, plotOptions);\n const pixel = eventPixel(element, surface, event);\n points = [\n ...points,\n { x: scale.toWorldX(pixel.x), y: scale.toWorldY(pixel.y) },\n ];\n draw();\n }\n\n draw();\n element.addEventListener(\"click\", handleClick);\n\n return {\n getPoints: () => [...points],\n getFit: () => fit,\n reset() {\n points = [];\n draw();\n },\n done() {\n onDone?.({ points: [...points], fit });\n },\n destroy() {\n element.removeEventListener(\"click\", handleClick);\n },\n };\n}\n",
|
|
42
|
+
"/**\n * The interactive regression: click to add a point, and watch the line move.\n *\n * This is the port of `interactive_regression()` in\n * `../compstatslib/R/regression_interactive.R`. R holds the points in a\n * reactive value and calls `plot_regression()` on every change, forwarding its `...`\n * arguments. This module does the same: it owns the points and the clicks, and\n * it hands every draw to `plotRegression`. It contains no drawing code and no\n * statistics.\n *\n * R's `runGadget()` blocks and returns the points when the user clicks \"Done\".\n * A browser blocks at nothing, so this returns a handle at once. Read the\n * points from `getPoints()`, or call `done()` to hand them to the `onDone`\n * callback.\n */\n\nimport type { Point } from \"../core/regression\";\nimport { pixelInArea } from \"../plot/axes\";\nimport { plotRegression, regressionScale } from \"../plot/regression\";\nimport type { PlotRegressionOptions } from \"../plot/regression\";\nimport { eventPixel, resolveInteractiveTarget } from \"./target\";\nimport type { InteractiveTarget } from \"./target\";\n\n/**\n * What the component accepts.\n *\n * The options of `plotRegression` pass through to it unchanged. This is the\n * equivalent of R's `...` forwarding.\n */\nexport interface InteractiveRegressionOptions extends PlotRegressionOptions {\n /** Points to start from. R takes these as its `points` argument. */\n readonly initialPoints?: readonly Point[];\n /** What to run on `done()`. */\n readonly onDone?: (points: readonly Point[]) => void;\n}\n\n/** What the caller holds after the component starts. */\nexport interface InteractiveRegressionHandle {\n /** Return the points collected so far, in click order. */\n getPoints(): readonly Point[];\n /** Drop every point and redraw. This drops the initial points too. */\n reset(): void;\n /** Hand the points to the `onDone` callback. */\n done(): void;\n /** Stop listening. The points stay readable. */\n destroy(): void;\n}\n\n/**\n * Start an interactive regression on a target.\n *\n * The component draws at once, so an empty start shows empty axes. Each click\n * inside the plot area adds one point and redraws. A click outside the plot\n * area does nothing, because the world window of the plot ends at the edge of\n * that area and a point beyond it would not appear.\n *\n * @param target A canvas, or a surface and an element. See `./target.ts`.\n * @param options Points to start from, a done callback, and the options of\n * `plotRegression`.\n * @returns The handle to the running component.\n * @throws Error If a canvas gives no 2D context.\n */\nexport function interactiveRegression(\n target: InteractiveTarget,\n options: InteractiveRegressionOptions = {},\n): InteractiveRegressionHandle {\n const { initialPoints, onDone, ...plotOptions } = options;\n const { surface, element } = resolveInteractiveTarget(target);\n const scale = regressionScale(surface.width, surface.height);\n\n let points: readonly Point[] = initialPoints ?? [];\n\n function draw(): void {\n plotRegression(surface, points, plotOptions);\n }\n\n function handleClick(event: MouseEvent): void {\n const pixel = eventPixel(element, surface, event);\n if (!pixelInArea(scale.area, pixel)) {\n return;\n }\n points = [\n ...points,\n { x: scale.toWorldX(pixel.x), y: scale.toWorldY(pixel.y) },\n ];\n draw();\n }\n\n draw();\n element.addEventListener(\"click\", handleClick);\n\n return {\n getPoints: () => [...points],\n reset() {\n points = [];\n draw();\n },\n done() {\n onDone?.([...points]);\n },\n destroy() {\n element.removeEventListener(\"click\", handleClick);\n },\n };\n}\n"
|
|
43
|
+
],
|
|
44
|
+
"mappings": "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IACrB;AAAA,IAAoB;AAAA,IACpB;AAAA,IAAqB;AAAA,IACrB;AAAA,IAAoB;AAAA,IACpB;AAAA,IAAqB;AAAA,IACrB;AAAA,IAAqB;AAAA,IACrB;AAAA,IAAoB;AAAA,IACpB;AAAA,IAAoB;AAAA,IACpB;AAAA,IAAqB;AAAA,IACrB;AAAA,IAAoB;AAAA,IACpB;AAAA,IAAsB;AAAA,IACtB;AAAA,IAAmB;AAAA,IACnB;AAAA,IAAqB;AAAA,IACrB;AAAA,IAAoB;AAAA,IACpB;AAAA,IAAsB;AAAA,IACtB;AAAA,IAAoB;AAAA,IACpB;AAAA,IAAoB;AAAA,IACpB;AAAA,IAAqB;AAAA,IACrB;AAAA,IAAsB;AAAA,IACtB;AAAA,IAAqB;AAAA,IACrB;AAAA,IAAoB;AAAA,IACpB;AAAA,IAAqB;AAAA,IACrB;AAAA,IAAqB;AAAA,IACrB;AAAA,IAAsB;AAAA,IACtB;AAAA,IAAoB;AAAA,IACpB;AAAA,IAAqB;AAAA,IACrB;AAAA,IAAqB;AAAA,IACrB;AAAA,IAAqB;AAAA,IACrB;AAAA,IAAoB;AAAA,IACpB;AAAA,IAAqB;AAAA,IACrB;AAAA,IAAsB;AAAA,IACtB;AAAA,IAAoB;AAAA,IACpB;AAAA,IAAoB;AAAA,IACpB;AAAA,IAAqB;AAAA,IACrB;AAAA,IAAqB;AAAA,IACrB;AAAA,IAAsB;AAAA,IACtB;AAAA,IAAqB;AAAA,IACrB;AAAA,IAAoB;AAAA,IACpB;AAAA,IAAqB;AAAA,IACrB;AAAA,IAAqB;AAAA,IACrB;AAAA,IAAqB;AAAA,IACrB;AAAA,IAAsB;AAAA,IACtB;AAAA,IAAsB;AAAA,IACtB;AAAA,IAAmB;AAAA,IACnB;AAAA,IAAqB;AAAA,IACrB;AAAA,IAAqB;AAAA,IACrB;AAAA,IAAqB;AAAA,IACrB;AAAA,IAAqB;AAAA,IACrB;AAAA,IAAqB;AAAA,IACrB;AAAA,IAAqB;AAAA,IACrB;AAAA,IAAsB;AAAA,IACtB;AAAA,IAAqB;AAAA,IACrB;AAAA,IAAqB;AAAA,IACrB;AAAA,IAAqB;AAAA,IACrB;AAAA,IAAoB;AAAA,IACpB;AAAA,I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",
|
|
45
|
+
"debugId": "2FC10F4594685E1B64756E2164756E21",
|
|
46
|
+
"names": []
|
|
47
|
+
}
|