March Docs

Stats

Stats module: descriptive statistics on List(Float).

All functions are pure — no side effects. Primary input type: List(Float).

Functions that require non-empty input declare it in their signature as {List(Float) | len(_) > 0} (a refinement the compiler checks at every call site) and still panic on an empty list as the runtime backstop. sum, count, variance_pop, std_dev_pop and the *_safe variants accept any list. To migrate a caller the checker flags, run march --check --refine-suggest <caller> file.march: it proposes the parameter refinement to restate upward, or guard the call with match xs do Nil -> ... _ -> ... end.

Design: all sorting uses List.sort_by with Float comparison. Variance uses Welford's online algorithm for numerical stability.

Types

typeQuantileMethodQuantileMethod =#

QuantileMethod selects one of the nine standard interpolation rules: InvertedCdf — Type 1: discontinuous inverse empirical CDF AveragedInvertedCdf — Type 2: Type 1 with averaging at jumps ClosestObservation — Type 3: nearest observation (half-to-even) InterpolatedInvertedCdf — Type 4: linear, nodes at k/n Hazen — Type 5: linear, nodes at (k − 1/2)/n Weibull — Type 6: linear, nodes at k/(n+1) Linear — Type 7: linear, nodes at (k − 1)/(n − 1) — R/numpy default MedianUnbiased — Type 8: linear, distribution-free NormalUnbiased — Type 9: linear, normal-theory unbiased

Functions

fncorrelationcorrelation(xs : {List(Float) | len(_) >= 2},#

Pearson correlation coefficient. Returns a value in [-1, 1]. Panics if lists have different lengths, fewer than 2 elements, or if either list has zero standard deviation.

The two structural preconditions are declared in the signature (xs has at least 2 elements; ys has exactly as many as xs), so a provable violation of either is a compile error. The zero-standard-deviation panic is data-dependent (it turns on the Float values in the lists, not on their shape), so no measure can express it and it stays a runtime check.

Examples

march> (Stats.correlation([1.0, 2.0, 3.0], [6.0, 4.0, 2.0])) == -1.0
true
fncountcount(xs : List(Float)) : Int#

Number of elements.

Examples

march> Stats.count([1.0, 2.0, 2.0])
3
fncovariancecovariance(xs : {List(Float) | len(_) >= 2},#

Sample covariance (denominator n-1) of two equal-length lists. Panics if lengths differ or if either list has fewer than 2 elements.

Both structural preconditions are declared in the signature: xs must have at least 2 elements and ys must have exactly as many as xs, so a call the compiler can prove violates either is a compile error rather than a runtime panic. Lengths it cannot relate are accepted in silence (March reports only definite failures) and the panics below remain the runtime backstop.

Examples

march> (Stats.covariance([1.0, 2.0, 3.0], [2.0, 4.0, 6.0])) == 2.0
true
fnfive_number_summaryfive_number_summary(xs : {List(Float) | len(_) > 0}, method : QuantileMethod) : (Float, Float, Float, Float, Float)#

Five-number summary with selectable method: (min, Q1, median, Q3, max). Sorts once. Panics on an empty list.

Examples

march> (Stats.five_number_summary([5.0, 1.0, 4.0, 2.0, 3.0], Stats.Linear)) == (1.0, 2.0, 3.0, 4.0, 5.0)
true
fniqriqr(xs : {List(Float) | len(_) > 0}, method : QuantileMethod) : Float#

Interquartile range: Q3 - Q1 using the given method.

Uses the same sorted list for both quartiles (one sort, not two). Precondition: xs is non-empty ({List(Float) | len(_) > 0}); panics on empty input.

Examples

march> (Stats.iqr([1.0, 2.0, 3.0, 4.0, 5.0], Stats.Linear)) == 2.0
true
march> (Stats.iqr([7.0], Stats.Linear)) == 0.0
true
fniqr_defaultiqr_default(xs : {List(Float) | len(_) > 0}) : Float#

Interquartile range using Linear (Type 7) interpolation. Precondition: xs is non-empty ({List(Float) | len(_) > 0}).

Examples

march> (Stats.iqr_default([1.0, 2.0, 3.0, 4.0, 5.0])) == 2.0
true
fnlinear_regressionlinear_regression(xs : {List(Float) | len(_) >= 2},#

Simple least-squares linear regression: y = slope * x + intercept. Returns (slope, intercept). Panics if lists have different lengths or fewer than 2 elements, or if xs has zero variance (vertical line).

The two structural preconditions are declared in the signature (xs has at least 2 points; ys has exactly as many as xs), so a provable violation of either is a compile error. The zero-variance panic is data-dependent (it turns on the Float values in xs, not on their shape), so no measure can express it and it stays a runtime check.

Examples

march> (Stats.linear_regression([0.0, 1.0, 2.0], [1.0, 3.0, 5.0])) == (2.0, 1.0)
true
fnmax_safemax_safe(xs : List(Float)) : Result(Float, String)#

Maximum value, returning Err on an empty list.

Examples

march> (Stats.max_safe([2.0, -1.0])) == Ok(2.0)
true
march> Stats.max_safe([])
Err("Stats.max_safe: empty list")
fnmax_valmax_val(xs : {List(Float) | len(_) > 0}) : Float#

Maximum value. Panics on an empty list.

Examples

march> (Stats.max_val([3.0, -1.5, 2.0])) == 3.0
true
fnmeanmean(xs : {List(Float) | len(_) > 0}) : Float#

Arithmetic mean. Panics on an empty list.

Examples

march> Stats.mean([1.0, 2.0, 4.5])
2.5
fnmean_safemean_safe(xs : List(Float)) : Result(Float, String)#

Mean, returning Err on an empty list instead of panicking.

Examples

march> Stats.mean_safe([1.0, 2.0])
Ok(1.5)
march> Stats.mean_safe([])
Err("Stats.mean_safe: empty list")
fnmedianmedian(xs : {List(Float) | len(_) > 0}) : Float#

Median (50th percentile): the middle value, or the mean of the two middle values for an even-length list. Precondition: xs is non-empty ({List(Float) | len(_) > 0}, checked at compile time); panics on an empty list if the checker could not decide it.

Examples

march> (Stats.median([3.0, 1.0, 2.0])) == 2.0
true
march> Stats.median([4.0, 1.0, 3.0, 2.0])
2.5
fnmin_safemin_safe(xs : List(Float)) : Result(Float, String)#

Minimum value, returning Err on an empty list.

Examples

march> (Stats.min_safe([2.0, -1.0])) == Ok(-1.0)
true
march> Stats.min_safe([])
Err("Stats.min_safe: empty list")
fnmin_valmin_val(xs : {List(Float) | len(_) > 0}) : Float#

Minimum value. Panics on an empty list.

Examples

march> Stats.min_val([3.0, -1.5, 2.0])
-1.5
fnmodemode(xs : {List(Float) | len(_) > 0}) : Float#

Mode: the most frequently occurring value (compared with ==). When there are ties, returns the smallest of the tied values (the first encountered in sorted order). Panics on an empty list.

Examples

march> (Stats.mode([3.0, 1.0, 3.0, 2.0])) == 3.0
true
march> (Stats.mode([2.0, 1.0, 2.0, 1.0])) == 1.0
true
fnpercentilepercentile(xs : {List(Float) | len(_) > 0}, p : {Float | _ >= 0.0 && _ <= 100.0}) : Float#

Percentile using linear interpolation between the two nearest ranks of the sorted data (the same rule as quantile(xs, p / 100.0, Linear)). p must be in [0.0, 100.0]. Panics on an empty list or out-of-range p.

Examples

march> (Stats.percentile([1.0, 2.0, 3.0, 4.0, 5.0], 25.0)) == 2.0
true
march> Stats.percentile([10.0, 20.0], 25.0)
12.5
march> (Stats.percentile([5.0, 1.0, 3.0], 100.0)) == 5.0
true
fnquantilequantile(xs : {List(Float) | len(_) > 0}, q : {Float | _ >= 0.0 && _ <= 1.0}, method : QuantileMethod) : Float#

Quantile at probability q in [0.0, 1.0] using the given interpolation method (QuantileMethod, Hyndman & Fan Types 1-9). Panics on an empty list or out-of-range q. Sorts the input: O(n log n).

For a percentile-style API (q in 0..100), see percentile. The existing percentile(xs, p) is equivalent to quantile(xs, p / 100.0, Linear).

Examples

march> (Stats.quantile([1.0, 2.0, 3.0, 4.0], 0.5, Stats.InvertedCdf)) == 2.0
true
march> Stats.quantile([1.0, 2.0, 3.0, 4.0], 0.5, Stats.AveragedInvertedCdf)
2.5
march> Stats.quantile([1.0, 2.0, 3.0, 4.0], 0.25, Stats.Linear)
1.75
march> Stats.quantile([1.0, 2.0, 3.0, 4.0], 0.25, Stats.Weibull)
1.25
fnquantile_defaultquantile_default(xs : {List(Float) | len(_) > 0}, q : {Float | _ >= 0.0 && _ <= 1.0}) : Float#

Quantile with Linear interpolation (Type 7, R/numpy default). Convenience alias for quantile(xs, q, Linear), and carries the same preconditions: xs is non-empty and q is in [0.0, 1.0].

Examples

march> (Stats.quantile_default([1.0, 2.0, 3.0, 4.0, 5.0], 0.75)) == 4.0
true
fnquantilesquantiles(xs : {List(Float) | len(_) > 0}, qs : List(Float), method : QuantileMethod) : List(Float)#

Batched quantiles: returns [quantile(xs, q, method) for q in qs], in the order of qs. Sorts the input once and reuses it for each q. Panics on an empty xs or if any q is outside [0.0, 1.0].

Examples

march> Stats.quantiles([4.0, 1.0, 3.0, 2.0], [0.25, 0.5, 0.75], Stats.Linear)
[1.75, 2.5, 3.25]
march> Stats.quantiles([1.0, 2.0], [], Stats.Linear)
[]
fnstd_devstd_dev(xs : {List(Float) | len(_) > 0}) : Float#

Sample standard deviation (square root of variance). Returns 0.0 for a single element. Precondition: xs is non-empty ({List(Float) | len(_) > 0}); panics on an empty list.

Examples

march> (Stats.std_dev([1.0, 3.0, 5.0])) == 2.0
true
fnstd_dev_popstd_dev_pop(xs : List(Float)) : Float#

Population standard deviation (square root of variance_pop). Returns 0.0 for a single element or an empty list.

Examples

march> (Stats.std_dev_pop([2.0, 4.0, 4.0, 4.0, 5.0, 5.0, 7.0, 9.0])) == 2.0
true
fnstd_dev_safestd_dev_safe(xs : List(Float)) : Result(Float, String)#

Sample standard deviation, returning Err on an empty or single-element list.

Examples

march> (Stats.std_dev_safe([1.0, 3.0, 5.0])) == Ok(2.0)
true
march> Stats.std_dev_safe([5.0])
Err("Stats.std_dev_safe: need at least 2 elements")
fnsumsum(xs : List(Float)) : Float#

Sum of all elements. Returns 0.0 for an empty list.

Examples

march> (Stats.sum([1.5, 2.5, 4.0])) == 8.0
true
march> (Stats.sum([])) == 0.0
true
fnvariancevariance(xs : {List(Float) | len(_) > 0}) : Float#

Sample variance (denominator n-1). Returns 0.0 for a single element. Precondition: xs is non-empty ({List(Float) | len(_) > 0}); panics on an empty list.

Uses Welford's online algorithm for numerical stability on inputs where the naive two-pass sum-of-squares would suffer catastrophic cancellation (near-constant data, shifted distributions).

Examples

march> (Stats.variance([1.0, 3.0, 5.0])) == 4.0
true
march> (Stats.variance([3.0])) == 0.0
true
fnvariance_popvariance_pop(xs : List(Float)) : Float#

Population variance (denominator n). Returns 0.0 for a single element or an empty list.

Uses Welford's online algorithm (same stability benefit as variance).

Examples

march> (Stats.variance_pop([2.0, 4.0, 4.0, 4.0, 5.0, 5.0, 7.0, 9.0])) == 4.0
true
march> (Stats.variance_pop([])) == 0.0
true
fnvariance_safevariance_safe(xs : List(Float)) : Result(Float, String)#

Sample variance, returning Err on an empty list. A single element gives Ok(0.0) (unlike std_dev_safe).

Examples

march> (Stats.variance_safe([1.0, 3.0, 5.0])) == Ok(4.0)
true
march> (Stats.variance_safe([5.0])) == Ok(0.0)
true