Packages

Purely functional, purely Gleam implementation of statistical methods.

Current section

Files

Jump to
gelman src gelman@summarize.erl
Raw

src/gelman@summarize.erl

-module(gelman@summarize).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/gelman/summarize.gleam").
-export([mean/1, variance/1, standard_deviation/1, skewness/1, kurtosis/1, geometric_mean/1, harmonic_mean/1, generalized_mean/2, moment/2, median/1, interquartile_range/1, median_absolute_deviation/1]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" This module defines functions that compute the\n"
" summary statistics for a given dataset.\n"
"\n"
" A summary statistic is a function that takes many\n"
" data points and returns one single value that describes a\n"
" property of the dataset.\n"
"\n"
" Each function within this module takes as its first\n"
" argument a dataset, which is a list of floats.\n"
"\n"
" Every function within this module could potentially\n"
" produce StatsError. For more information about the\n"
" what each error represents, please refer to gelman/error\n"
"\n"
).
-file("src/gelman/summarize.gleam", 26).
?DOC(" Computes the mean of a dataset in one pass.\n").
-spec mean(list(float())) -> {ok, float()} | {error, gelman@error:stats_error()}.
mean(Dataset) ->
case Dataset of
[] ->
{error, empty_dataset};
_ ->
{ok, gelman@internal@nonempty@summarize:mean(Dataset)}
end.
-file("src/gelman/summarize.gleam", 35).
?DOC(
" Computes the variance of a dataset in one pass, using\n"
" sum and the sum of squares.\n"
).
-spec variance(list(float())) -> {ok, float()} |
{error, gelman@error:stats_error()}.
variance(Dataset) ->
case Dataset of
[] ->
{error, empty_dataset};
_ ->
{ok, gelman@internal@nonempty@summarize:variance(Dataset)}
end.
-file("src/gelman/summarize.gleam", 50).
?DOC(
" Computes the standard deviation of a dataset in one pass.\n"
" This is simply equal to the square root of the variance.\n"
" That is the standard deviation _without_ correction for degrees\n"
" of freedom.\n"
"\n"
" TODO:\n"
" Implement standard deviation with correction for\n"
" degrees of freedom\n"
).
-spec standard_deviation(list(float())) -> {ok, float()} |
{error, gelman@error:stats_error()}.
standard_deviation(Dataset) ->
_pipe = variance(Dataset),
gleam@result:map(_pipe, fun gelman@internal@helpers:sqrt/1).
-file("src/gelman/summarize.gleam", 57).
?DOC(
" Computes the variance of a dataset in one pass, using\n"
" sum, sum of squares and sum of cubes.\n"
).
-spec skewness(list(float())) -> {ok, float()} |
{error, gelman@error:stats_error()}.
skewness(Dataset) ->
case Dataset of
[] ->
{error, empty_dataset};
_ ->
{ok, gelman@internal@nonempty@summarize:skewness(Dataset)}
end.
-file("src/gelman/summarize.gleam", 70).
?DOC(
" Computes the kurtosis of a dataset in one pass, using\n"
" sum, sum of squares, sum of cubes and sum of fourth powers.\n"
"\n"
" Note: this is standard kurtosis, not Fisher kurtosis (also known\n"
" as excess kurtosis). To compute excess kurtosis, subtract 3 from\n"
" this result.\n"
).
-spec kurtosis(list(float())) -> {ok, float()} |
{error, gelman@error:stats_error()}.
kurtosis(Dataset) ->
case Dataset of
[] ->
{error, empty_dataset};
_ ->
{ok, gelman@internal@nonempty@summarize:kurtosis(Dataset)}
end.
-file("src/gelman/summarize.gleam", 81).
?DOC(
" Computes the geometric mean in one pass. Since there is\n"
" no list fusion in Gleam, this means that the geometric transformation\n"
" is applied in a single fold, as opposed to |> map(transform) |> mean(),\n"
" to avoid memory overhead of materializing the intermediate list.\n"
).
-spec geometric_mean(list(float())) -> {ok, float()} |
{error, gelman@error:stats_error()}.
geometric_mean(Dataset) ->
case Dataset of
[] ->
{error, empty_dataset};
_ ->
gleam@result:'try'(
gelman@internal@nonempty@summarize:geometric_mean(Dataset),
fun(Gm) -> {ok, Gm} end
)
end.
-file("src/gelman/summarize.gleam", 93).
?DOC(
" Computes the geometric mean in one pass, avoiding the memory\n"
" overhead of materializing the intermediate list.\n"
).
-spec harmonic_mean(list(float())) -> {ok, float()} |
{error, gelman@error:stats_error()}.
harmonic_mean(Dataset) ->
case Dataset of
[] ->
{error, empty_dataset};
_ ->
gleam@result:'try'(
gelman@internal@nonempty@summarize:harmonic_mean(Dataset),
fun(Hm) -> {ok, Hm} end
)
end.
-file("src/gelman/summarize.gleam", 108).
?DOC(
" Computes the geometric mean in one pass, avoiding the memory\n"
" overhead of materializing the intermediate list.\n"
" This is also known as the Hoelder mean.\n"
" When the power p is zero, we send the geometric mean,\n"
" which is the limit of the generalized mean as it tends to zero.\n"
).
-spec generalized_mean(list(float()), float()) -> {ok, float()} |
{error, gelman@error:stats_error()}.
generalized_mean(Dataset, P) ->
case {P, Dataset} of
{+0.0, _} ->
geometric_mean(Dataset);
{_, []} ->
{error, empty_dataset};
{_, _} ->
gleam@result:'try'(
gelman@internal@nonempty@summarize:generalized_mean(Dataset, P),
fun(Gm) -> {ok, Gm} end
)
end.
-file("src/gelman/summarize.gleam", 126).
-spec moment(list(float()), integer()) -> {ok, float()} |
{error, gelman@error:stats_error()}.
moment(Dataset, N) ->
case {Dataset, N} of
{_, N@1} when N@1 < 1 ->
{error,
{invalid_parameter,
<<"Nth moment around the mean: N must be positive integer."/utf8>>}};
{[], _} ->
{error, empty_dataset};
{_, _} ->
{ok, gelman@internal@nonempty@summarize:moment(Dataset, N)}
end.
-file("src/gelman/summarize.gleam", 155).
-spec extract_middle_element(list(float()), integer()) -> float().
extract_middle_element(Dataset, N) ->
Y@1 = case begin
_pipe = Dataset,
_pipe@1 = gleam@list:drop(_pipe, (N - 1) div 2),
gleam@list:take(_pipe@1, 1)
end of
[Y] -> Y;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"you must call this function with the right length"/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"gelman/summarize"/utf8>>,
function => <<"extract_middle_element"/utf8>>,
line => 156,
value => _assert_fail,
start => 5069,
'end' => 5149,
pattern_start => 5080,
pattern_end => 5083})
end,
Y@1.
-file("src/gelman/summarize.gleam", 164).
-spec average_middle_two_elements(list(float()), integer()) -> float().
average_middle_two_elements(Dataset, N) ->
{Y1@1, Y2@1} = case begin
_pipe = Dataset,
_pipe@1 = gleam@list:drop(_pipe, (N - 2) div 2),
gleam@list:take(_pipe@1, 2)
end of
[Y1, Y2] -> {Y1, Y2};
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"you must call this function with the right length"/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"gelman/summarize"/utf8>>,
function => <<"average_middle_two_elements"/utf8>>,
line => 165,
value => _assert_fail,
start => 5290,
'end' => 5375,
pattern_start => 5301,
pattern_end => 5309})
end,
(Y1@1 + Y2@1) / 2.0.
-file("src/gelman/summarize.gleam", 140).
-spec median(list(float())) -> {ok, float()} |
{error, gelman@error:stats_error()}.
median(Dataset) ->
Sorted = gleam@list:sort(Dataset, fun gleam@float:compare/2),
N = erlang:length(Dataset),
case Sorted of
[] ->
{error, empty_dataset};
[X0] ->
{ok, X0};
[X0@1, X1] ->
{ok, (X0@1 + X1) / 2.0};
[_, X1@1, _] ->
{ok, X1@1};
[_, X1@2, X2, _] ->
{ok, (X1@2 + X2) / 2.0};
[_, _, X2@1, _, _] ->
{ok, X2@1};
_ when (N rem 2) =:= 0 ->
{ok, average_middle_two_elements(Sorted, N)};
_ ->
{ok, extract_middle_element(Sorted, N)}
end.
-file("src/gelman/summarize.gleam", 176).
-spec interquartile_range(list(float())) -> {ok, float()} |
{error, gelman@error:stats_error()}.
interquartile_range(Dataset) ->
_pipe = Dataset,
_pipe@1 = gelman@discretize:quantiles(_pipe, [0.25, 0.75]),
gleam@result:map(_pipe@1, fun(X) -> case X of
[L, R] ->
R - L;
_ ->
erlang:error(#{gleam_error => panic,
message => <<"this shouldn't happen as you asked for two values."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"gelman/summarize"/utf8>>,
function => <<"interquartile_range"/utf8>>,
line => 182})
end end).
-file("src/gelman/summarize.gleam", 192).
-spec median_absolute_deviation(list(float())) -> {ok, float()} |
{error, gelman@error:stats_error()}.
median_absolute_deviation(Dataset) ->
gleam@result:'try'(
median(Dataset),
fun(M) ->
Deviations = begin
_pipe = Dataset,
gleam@list:map(
_pipe,
fun(X) -> gleam@float:absolute_value(X - M) end
)
end,
median(Deviations)
end
).