Bland.Stats (Elixir Technical Drawing v0.6.0)

Copy Markdown View Source

Small stats helpers used by the box-plot, Q-Q, and histogram routines. Intentionally narrow — this is not a general stats library.

Summary

Functions

Computes a box-plot summary — quartiles plus Tukey fences for whisker endpoints. Outliers are observations beyond 1.5·IQR (default).

Arithmetic mean of the finite values. Empty input returns 0.0.

Inverse standard-normal CDF (probit). Uses the Beasley-Springer / Moro approximation — accurate to about 1e-9 across the usual range.

Linearly-interpolated quantile (type-7, R's default). p is in [0, 1].

quantile/2 for input that is already sorted ascending.

Sample standard deviation, Bessel-corrected (n-1). Fewer than two observations returns 0.0.

Standard error of the mean: sample_std / sqrt(n).

Inverse Student-t CDF for df degrees of freedom.

Functions

boxplot_stats(values, whisker_iqr \\ 1.5)

@spec boxplot_stats([number()], number()) :: map()

Computes a box-plot summary — quartiles plus Tukey fences for whisker endpoints. Outliers are observations beyond 1.5·IQR (default).

Returns %{min, q1, median, q3, max, outliers} where min/max are the whisker endpoints (not the raw extrema — those are captured in :outliers).

iex> s = Bland.Stats.boxplot_stats([1, 2, 3, 4, 5, 6, 7, 100])
iex> {s.median, length(s.outliers)}
{4.5, 1}

mean(values)

@spec mean([number()]) :: float()

Arithmetic mean of the finite values. Empty input returns 0.0.

iex> Bland.Stats.mean([1, 2, 3, 4])
2.5

normal_quantile(p)

@spec normal_quantile(number()) :: float() | nil

Inverse standard-normal CDF (probit). Uses the Beasley-Springer / Moro approximation — accurate to about 1e-9 across the usual range.

iex> abs(Bland.Stats.normal_quantile(0.5)) < 1.0e-9
true

iex> Float.round(Bland.Stats.normal_quantile(0.975), 4)
1.96

quantile(values, p)

@spec quantile([number()], number()) :: float()

Linearly-interpolated quantile (type-7, R's default). p is in [0, 1].

iex> Bland.Stats.quantile([1, 2, 3, 4, 5], 0.5)
3.0

quantile_sorted(sorted, p)

@spec quantile_sorted([number()], number()) :: float()

quantile/2 for input that is already sorted ascending.

Callers computing several quantiles of the same sample — boxplot_stats/2 wants three — should sort once and come through here rather than paying for a sort per quantile.

iex> Bland.Stats.quantile_sorted([1, 2, 3, 4, 5], 0.25)
2.0

sample_std(values)

@spec sample_std([number()]) :: float()

Sample standard deviation, Bessel-corrected (n-1). Fewer than two observations returns 0.0.

iex> Bland.Stats.sample_std([2, 4, 4, 4, 5, 5, 7, 9])
2.138089935299395

sem(values)

@spec sem([number()]) :: float()

Standard error of the mean: sample_std / sqrt(n).

iex> Float.round(Bland.Stats.sem([1, 2, 3, 4, 5]), 6)
0.707107

student_t_quantile(p, df)

@spec student_t_quantile(number(), non_neg_integer()) :: float() | nil

Inverse Student-t CDF for df degrees of freedom.

Exact for df <= 2; a Cornish-Fisher expansion above that, which sits within about 5e-3 of the true quantile at df = 3 and tightens rapidly — ample for sizing an error bar. df of 0 falls back to the normal quantile. Returns nil outside (0, 1), matching normal_quantile/1.

iex> Float.round(Bland.Stats.student_t_quantile(0.975, 10), 3)
2.228