# `Sidereon.Statistics`
[🔗](https://github.com/neilberkman/sidereon-ex/blob/main/lib/sidereon/statistics.ex#L1)

Residual-distribution diagnostics: sample moments and normality tests.

Post-fit residuals from a converged least-squares solve should look like
zero-mean Gaussian noise. These primitives quantify departures from that ideal
on an arbitrary residual set: sample skewness and kurtosis, the combined
moments, the Jarque-Bera moment test, and the Shapiro-Wilk W test.

The moment definitions match `scipy.stats` (the central moments are the
population/biased moments), so a caller can cross-check against the reference
implementation. The numerical modeling lives in the `sidereon-core` Rust core;
this module marshals the residual list and convention flags and decodes the
results.

Each function returns `{:ok, value}` or `{:error, reason}`, where `reason` is a
typed atom: `:non_finite`, `:insufficient_data`, `:zero_variance`, or
`:zero_range`.

# `moments`

```elixir
@type moments() :: %{
  mean: float(),
  variance: float(),
  skewness: float(),
  kurtosis: float()
}
```

Sample moments: `mean`, the biased `variance`, `skewness`, and the excess
`kurtosis` (Gaussian -> 0 when `fisher: true`).

# `jarque_bera`

```elixir
@spec jarque_bera([number()]) ::
  {:ok, %{statistic: float(), p_value: float()}} | {:error, atom()}
```

Jarque-Bera normality test (`scipy.stats.jarque_bera`).

Returns `{:ok, %{statistic: jb, p_value: p}}` with the chi-square(2) upper-tail
p-value `exp(-jb/2)`. Needs at least two residuals.

# `kurtosis`

```elixir
@spec kurtosis(
  [number()],
  keyword()
) :: {:ok, float()} | {:error, atom()}
```

Sample kurtosis.

`fisher: true` (default) returns the excess kurtosis `m4 / m2^2 - 3`
(Gaussian -> 0); `fisher: false` returns the Pearson kurtosis (Gaussian -> 3).
`bias: false` applies the sample correction, which needs at least four
residuals.

# `moments`

```elixir
@spec moments(
  [number()],
  keyword()
) :: {:ok, moments()} | {:error, atom()}
```

Mean, biased variance, skewness, and excess kurtosis in one pass.

`:fisher` and `:bias` select the kurtosis convention and the bias correction,
exactly as in `skewness/2` and `kurtosis/2`.

# `shapiro_wilk`

```elixir
@spec shapiro_wilk([number()]) ::
  {:ok, %{w: float(), p_value: float()}} | {:error, atom()}
```

Shapiro-Wilk W test for normality (Royston AS R94, the `scipy.stats.shapiro`
algorithm).

Returns `{:ok, %{w: w, p_value: p}}` with `w` in `(0, 1]` (closer to one is
more Gaussian). Needs at least three residuals; returns `{:error, :zero_range}`
when every residual is equal.

# `skewness`

```elixir
@spec skewness(
  [number()],
  keyword()
) :: {:ok, float()} | {:error, atom()}
```

Sample skewness.

`bias: true` (default) is the Fisher-Pearson coefficient `g1 = m3 / m2^(3/2)`
(`scipy.stats.skew`); `bias: false` applies the sample correction
(`scipy.stats.skew(bias=False)`), which needs at least three residuals.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
