# core/analyzers/baseline.analyzer.ts

> 98 lines of code and 43 definitions.

Tree: GovLab Patterns
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/patterns#file-patterns-core-analyzers-baseline-analyzer-ts
Source text: https://banes-lab.com/source/patterns/core/analyzers/baseline.analyzer.ts.txt

Listed in [core/analyzers](https://banes-lab.com/api/source/patterns/core/analyzers.md), before [core/analyzers/code.analyzer.ts](https://banes-lab.com/source/patterns/core/analyzers/code.analyzer.ts.md).

## Definitions

- `uniformity` (lexical_declaration, line 98, exported)
- `erfc` (lexical_declaration, line 30, exported)
- `erf` (lexical_declaration, line 22, exported)
- `normalSignificance` (lexical_declaration, line 34, exported)
- `transitionIndependence` (lexical_declaration, line 81, exported)
- `marginalsOf` (lexical_declaration, line 56)
- `chiSquareSf` (lexical_declaration, line 39, exported)
- `autocorrelationSignificance` (lexical_declaration, line 49, exported)
- `A1` (lexical_declaration, line 4)
- `A2` (lexical_declaration, line 5)
- `A3` (lexical_declaration, line 6)
- `A4` (lexical_declaration, line 7)
- `A5` (lexical_declaration, line 8)
- `P` (lexical_declaration, line 9)
- `ERFC_MAX` (lexical_declaration, line 10)
- `ALPHA` (lexical_declaration, line 11)
- `NINE` (lexical_declaration, line 12)
- `THREE` (lexical_declaration, line 13)
- `THIRD` (lexical_declaration, line 14)
- `MIN_SERIES` (lexical_declaration, line 15)
- `HALF` (lexical_declaration, line 16)
- `TWO` (lexical_declaration, line 17)
- `SQUARE` (lexical_declaration, line 18)
- `INSIGNIFICANT` (lexical_declaration, line 20)
- `sign` (lexical_declaration, line 23, exported)
- `ax` (lexical_declaration, line 24, exported)
- `t` (lexical_declaration, line 25, exported)
- `y` (lexical_declaration, line 26, exported)
- `cubeRoot` (lexical_declaration, line 43, exported)
- `mean` (lexical_declaration, line 44, exported)
- `stddev` (lexical_declaration, line 45, exported)
- `row` (lexical_declaration, line 57)
- `col` (lexical_declaration, line 58)
- `chiFromTransitions` (lexical_declaration, line 66)
- `sum` (lexical_declaration, line 71)
- `marginals` (lexical_declaration, line 88, exported)
- `chi` (lexical_declaration, line 93, exported)
- `categories` (lexical_declaration, line 99, exported)
- `total` (lexical_declaration, line 100, exported)
- `expected` (lexical_declaration, line 104, exported)
- `statistic` (lexical_declaration, line 105, exported)
- `dof` (lexical_declaration, line 106, exported)
- `pValue` (lexical_declaration, line 107, exported)

## Contained in

- [core/analyzers](https://banes-lab.com/anatomy/patterns/folder-patterns-core-analyzers.md)

## Uses

- [core/counters/base.counter.ts](https://banes-lab.com/source/patterns/core/counters/base.counter.ts.md)

## Used by

- [core/aggregators/representation.distribution.aggregator.ts](https://banes-lab.com/source/patterns/core/aggregators/representation.distribution.aggregator.ts.md)
- [core/aggregators/representation.graph.aggregator.ts](https://banes-lab.com/source/patterns/core/aggregators/representation.graph.aggregator.ts.md)
- [core/aggregators/representation.sequence.aggregator.ts](https://banes-lab.com/source/patterns/core/aggregators/representation.sequence.aggregator.ts.md)

## Linked from

- [core/aggregators](https://banes-lab.com/anatomy/patterns/folder-patterns-core-aggregators.md)
- [core/analyzers](https://banes-lab.com/anatomy/patterns/folder-patterns-core-analyzers.md)
- [core/counters](https://banes-lab.com/anatomy/patterns/folder-patterns-core-counters.md)

## Source

```typescript
import type { Marginals, Significance, Transition, Uniformity } from "#types/baseline.types";
import { increment, sumOf } from "#core/counters/base.counter";

const A1 = 0.254829592;
const A2 = -0.284496736;
const A3 = 1.421413741;
const A4 = -1.453152027;
const A5 = 1.061405429;
const P = 0.3275911;
const ERFC_MAX = 2;
const ALPHA = 0.05;
const NINE = 9;
const THREE = 3;
const THIRD = 1 / THREE;
const MIN_SERIES = 3;
const HALF = 0.5;
const TWO = 2;
const SQUARE = 2;

const INSIGNIFICANT: Significance = { pValue: 1, significant: false, statistic: 0 };

export const erf = function erf(x: number): number {
    const sign = x < 0 ? -1 : 1;
    const ax = Math.abs(x);
    const t = 1 / (1 + P * ax);
    const y = 1 - ((((A5 * t + A4) * t + A3) * t + A2) * t + A1) * t * Math.exp(-ax * ax);
    return sign * y;
};

export const erfc = function erfc(x: number): number {
    return Math.min(ERFC_MAX, Math.max(0, 1 - erf(x)));
};

export const normalSignificance = function normalSignificance(z: number): Significance {
    const pValue = erfc(Math.abs(z) / Math.SQRT2);
    return { pValue, significant: pValue < ALPHA, statistic: z };
};

export const chiSquareSf = function chiSquareSf(statistic: number, dof: number): number {
    if (dof <= 0 || statistic <= 0) {
        return 1;
    }
    const cubeRoot = (statistic / dof) ** THIRD;
    const mean = 1 - TWO / (NINE * dof);
    const stddev = Math.sqrt(TWO / (NINE * dof));
    return HALF * erfc((cubeRoot - mean) / stddev / Math.SQRT2);
};

export const autocorrelationSignificance = function autocorrelationSignificance(
    correlation: number,
    count: number,
): Significance {
    return count < MIN_SERIES ? INSIGNIFICANT : normalSignificance(correlation * Math.sqrt(count));
};

const marginalsOf = function marginalsOf(transitions: readonly Transition[]): Marginals {
    const row = new Map<string, number>();
    const col = new Map<string, number>();
    for (const { source, target, count } of transitions) {
        increment(row, source, count);
        increment(col, target, count);
    }
    return { col, row };
};

const chiFromTransitions = function chiFromTransitions(
    transitions: readonly Transition[],
    marginals: Marginals,
    total: number,
): number {
    let sum = 0;
    for (const { source, target, count } of transitions) {
        const expected = (marginals.row.get(source) ?? 0) * (marginals.col.get(target) ?? 0);
        if (expected > 0) {
            sum += (count * count * total) / expected;
        }
    }
    return sum - total;
};

export const transitionIndependence = function transitionIndependence(
    transitions: readonly Transition[],
): Significance {
    const total = sumOf(transitions.map((transition) => transition.count));
    if (total === 0) {
        return INSIGNIFICANT;
    }
    const marginals = marginalsOf(transitions);
    const dof = (marginals.row.size - 1) * (marginals.col.size - 1);
    if (dof <= 0) {
        return INSIGNIFICANT;
    }
    const chi = chiFromTransitions(transitions, marginals, total);
    const pValue = chiSquareSf(chi, dof);
    return { pValue, significant: pValue < ALPHA, statistic: chi };
};

export const uniformity = function uniformity(counts: ReadonlyMap<string, number>): Uniformity {
    const categories = counts.size;
    const total = sumOf(counts.values());
    if (categories <= 1 || total === 0) {
        return { chiSquare: 0, dof: 0, pValue: 1, uniform: true };
    }
    const expected = total / categories;
    const statistic = sumOf([...counts.values()].map((count) => (count - expected) ** SQUARE / expected));
    const dof = categories - 1;
    const pValue = chiSquareSf(statistic, dof);
    return { chiSquare: statistic, dof, pValue, uniform: pValue >= ALPHA };
};
```
