Current section
Files
Jump to
Current section
Files
src/gleam_stats@stats.erl
-module(gleam_stats@stats).
-compile(no_auto_import).
-export([sum/1, mean/1, median/1, hmean/1, gmean/1, var/2, std/2, moment/2, skewness/1, kurtosis/1, zscore/2, percentile/2, iqr/1, freedman_diaconis_rule/1, histogram/2, correlation/2, trim/3, isclose/4, allclose/4, amax/1, amin/1, argmax/1, argmin/1]).
-export_type([range/0]).
-type range() :: {range, float(), float()}.
-spec sum(list(float())) -> float().
sum(Arr) ->
case Arr of
[] ->
0.0;
_@1 ->
_pipe = Arr,
gleam@list:fold(_pipe, 0.0, fun(Acc, A) -> A + Acc end)
end.
-spec mean(list(float())) -> {ok, float()} | {error, nil}.
mean(Arr) ->
case Arr of
[] ->
{error, nil};
_@1 ->
_pipe = Arr,
_pipe@1 = sum(_pipe),
_pipe@2 = (fun(A) ->
case gleam@int:to_float(gleam@list:length(Arr)) of
0.0 -> 0.0;
Gleam@denominator -> A
/ Gleam@denominator
end
end)(_pipe@1),
{ok, _pipe@2}
end.
-spec median(list(float())) -> {ok, float()} | {error, nil}.
median(Arr) ->
case Arr of
[] ->
{error, nil};
_@1 ->
Count = gleam@list:length(Arr),
Mid = gleam@list:length(Arr) div 2,
Sorted = gleam@list:sort(Arr, fun gleam@float:compare/2),
case gleam@int:is_odd(Count) of
true ->
case gleam@list:at(Sorted, Mid) of
{ok, Val0} ->
_pipe = Val0,
{ok, _pipe};
_@2 ->
{error, nil}
end;
false ->
case {gleam@list:at(Sorted, Mid - 1),
gleam@list:at(Sorted, Mid)} of
{{ok, Val0@1}, {ok, Val1}} ->
_pipe@1 = [Val0@1, Val1],
mean(_pipe@1)
end
end
end.
-spec hmean(list(float())) -> {ok, float()} | {error, nil}.
hmean(Arr) ->
case Arr of
[] ->
{error, nil};
_@1 ->
Xarr = begin
_pipe = Arr,
gleam@list:try_map(_pipe, fun(A) -> case A >= 0.0 of
true ->
{ok, case A of
0.0 -> 0.0;
Gleam@denominator -> 1.0 / Gleam@denominator
end};
false ->
{error, nil}
end end)
end,
case Xarr of
{error, nil} ->
{error, nil};
{ok, Xarr@1} ->
_pipe@1 = Xarr@1,
_pipe@2 = sum(_pipe@1),
_pipe@3 = (fun(X) -> case X of
0.0 -> 0.0;
Gleam@denominator@1 -> gleam@int:to_float(
gleam@list:length(Xarr@1)
) / Gleam@denominator@1
end end)(_pipe@2),
{ok, _pipe@3}
end
end.
-spec gmean(list(float())) -> {ok, float()} | {error, nil}.
gmean(Arr) ->
case Arr of
[] ->
{error, nil};
_@1 ->
Xval = begin
_pipe = Arr,
gleam@list:try_fold(_pipe, 1.0, fun(Acc, A) -> case A >= 0.0 of
true ->
{ok, Acc * A};
false ->
{error, nil}
end end)
end,
case Xval of
{error, nil} ->
{error, nil};
{ok, Xval@1} ->
_pipe@1 = Xval@1,
_pipe@2 = (fun(X) ->
gleam@float:power(
X,
case gleam@int:to_float(gleam@list:length(Arr)) of
0.0 -> 0.0;
Gleam@denominator -> 1.0
/ Gleam@denominator
end
)
end)(_pipe@1),
{ok, _pipe@2}
end
end.
-spec var(list(float()), integer()) -> {ok, float()} | {error, nil}.
var(Arr, Ddof) ->
case Arr of
[] ->
{error, nil};
_@1 ->
Mean = mean(Arr),
case Mean of
{ok, Mean@1} ->
_pipe = Arr,
_pipe@1 = gleam@list:map(
_pipe,
fun(A) -> gleam@float:power(A - Mean@1, 2.0) end
),
_pipe@2 = sum(_pipe@1),
_pipe@3 = (fun(A@1) ->
case (gleam@int:to_float(gleam@list:length(Arr))
- gleam@int:to_float(Ddof)) of
0.0 -> 0.0;
Gleam@denominator -> A@1
/ Gleam@denominator
end
end)(_pipe@2),
{ok, _pipe@3};
_@2 ->
{error, nil}
end
end.
-spec std(list(float()), integer()) -> {ok, float()} | {error, nil}.
std(Arr, Ddof) ->
case Arr of
[] ->
{error, nil};
_@1 ->
Var = var(Arr, Ddof),
case Var of
{ok, Var@1} ->
_pipe = Var@1,
gleam@float:square_root(_pipe);
_@2 ->
{error, nil}
end
end.
-spec moment(list(float()), integer()) -> {ok, float()} | {error, nil}.
moment(Arr, N) ->
case Arr of
[] ->
{error, nil};
_@1 ->
case N >= 0 of
true ->
case N of
0 ->
{ok, 1.0};
1 ->
{ok, 0.0};
_@2 ->
M1 = begin
_pipe = Arr,
mean(_pipe)
end,
case M1 of
{ok, M1@1} ->
_pipe@1 = Arr,
_pipe@2 = gleam@list:map(
_pipe@1,
fun(A) ->
gleam@float:power(
A
- M1@1,
gleam@int:to_float(N)
)
end
),
mean(_pipe@2);
_@3 ->
{error, nil}
end
end;
false ->
{error, nil}
end
end.
-spec skewness(list(float())) -> {ok, float()} | {error, nil}.
skewness(Arr) ->
case Arr of
[] ->
{error, nil};
_@1 ->
M2 = moment(Arr, 2),
M3 = moment(Arr, 3),
case {M2, M3} of
{{ok, M2@1}, {ok, M3@1}} ->
_pipe = case gleam@float:power(M2@1, 1.5) of
0.0 -> 0.0;
Gleam@denominator -> M3@1 / Gleam@denominator
end,
{ok, _pipe};
{_@2, _@3} ->
{error, nil}
end
end.
-spec kurtosis(list(float())) -> {ok, float()} | {error, nil}.
kurtosis(Arr) ->
case Arr of
[] ->
{error, nil};
_@1 ->
M2 = moment(Arr, 2),
M4 = moment(Arr, 4),
case {M2, M4} of
{{ok, M2@1}, {ok, M4@1}} ->
_pipe = (case gleam@float:power(M2@1, 2.0) of
0.0 -> 0.0;
Gleam@denominator -> M4@1 / Gleam@denominator
end) - 3.0,
{ok, _pipe};
{_@2, _@3} ->
{error, nil}
end
end.
-spec zscore(list(float()), integer()) -> {ok, list(float())} | {error, nil}.
zscore(Arr, Ddof) ->
case Arr of
[] ->
{error, nil};
_@1 ->
Mean = mean(Arr),
Stdev = std(Arr, Ddof),
case {Mean, Stdev} of
{{ok, Mean@1}, {ok, Stdev@1}} ->
_pipe = Arr,
_pipe@1 = gleam@list:map(_pipe, fun(A) -> case Stdev@1 of
0.0 -> 0.0;
Gleam@denominator -> (A - Mean@1) / Gleam@denominator
end end),
{ok, _pipe@1};
{_@2, _@3} ->
{error, nil}
end
end.
-spec percentile(list(float()), integer()) -> {ok, float()} | {error, nil}.
percentile(Arr, N) ->
case Arr of
[] ->
{error, nil};
_@1 ->
S = gleam@list:sort(Arr, fun gleam@float:compare/2),
R = (gleam@int:to_float(N) / 100.0) * gleam@int:to_float(
gleam@list:length(Arr)
- 1
),
F = gleam@float:truncate(R),
case {gleam@list:at(S, F), gleam@list:at(S, F + 1)} of
{{ok, Lower}, {ok, Upper}} ->
_pipe = Lower + ((Upper - Lower) * (R - gleam@int:to_float(
F
))),
{ok, _pipe}
end
end.
-spec iqr(list(float())) -> {ok, float()} | {error, nil}.
iqr(Arr) ->
case Arr of
[] ->
{error, nil};
_@1 ->
Length = gleam@list:length(Arr),
case gleam@int:is_even(Length) of
true ->
{X, Y} = begin
_pipe = Arr,
gleam@list:split(_pipe, Length div 2)
end,
case {median(Y), median(X)} of
{{ok, Val0}, {ok, Val1}} ->
{ok, Val0 - Val1}
end;
false ->
{X@1, _@2} = begin
_pipe@1 = Arr,
gleam@list:split(_pipe@1, (Length - 1) div 2)
end,
{_@3, Y@1} = begin
_pipe@2 = Arr,
gleam@list:split(_pipe@2, (Length + 1) div 2)
end,
case {median(Y@1), median(X@1)} of
{{ok, Val0@1}, {ok, Val1@1}} ->
{ok, Val0@1 - Val1@1}
end
end
end.
-spec freedman_diaconis_rule(list(float())) -> {ok, float()} | {error, nil}.
freedman_diaconis_rule(Arr) ->
case Arr of
[] ->
{error, nil};
_@1 ->
Length = gleam@int:to_float(gleam@list:length(Arr)),
Iqr = begin
_pipe = Arr,
iqr(_pipe)
end,
Lower = begin
_pipe@1 = Arr,
amin(_pipe@1)
end,
Upper = begin
_pipe@2 = Arr,
amax(_pipe@2)
end,
case {Lower, Upper, Iqr} of
{{ok, Lower@1}, {ok, Upper@1}, {ok, Iqr@1}} ->
Width = case gleam@float:power(Length, 1.0 / 3.0) of
0.0 -> 0.0;
Gleam@denominator -> (2.0 * Iqr@1) / Gleam@denominator
end,
case Width < (case Length of
0.0 -> 0.0;
Gleam@denominator@1 -> (Upper@1 - Lower@1) / Gleam@denominator@1
end) of
true ->
{error, nil};
false ->
_pipe@3 = gleam@float:ceiling(case Width of
0.0 -> 0.0;
Gleam@denominator@2 -> (Upper@1 - Lower@1) / Gleam@denominator@2
end),
{ok, _pipe@3}
end;
{_@2, _@3, _@4} ->
{error, nil}
end
end.
-spec histogram(list(float()), float()) -> {ok, list({range(), integer()})} |
{error, nil}.
histogram(Arr, Width) ->
case Arr of
[] ->
{error, nil};
_@1 ->
_pipe = create_bins(Arr, Width),
_pipe@1 = bin_elements(_pipe, Arr),
{ok, _pipe@1}
end.
-spec create_bins(list(float()), float()) -> list({range(), integer()}).
create_bins(Arr, Width) ->
Min = begin
_pipe = Arr,
amin(_pipe)
end,
Max = begin
_pipe@1 = Arr,
amax(_pipe@1)
end,
case {Min, Max} of
{{ok, Min@1}, {ok, Max@1}} ->
Inc = begin
_pipe@2 = case Width of
0.0 -> 0.0;
Gleam@denominator -> ((1.001 * Max@1) - (0.999 * Min@1)) / Gleam@denominator
end,
gleam@float:ceiling(_pipe@2)
end,
_pipe@3 = gleam@list:range(0, gleam@float:round(Inc)),
gleam@list:map(
_pipe@3,
fun(X) ->
{{range,
Width
* gleam@int:to_float(X),
Width
* gleam@int:to_float(X + 1)},
0}
end
)
end.
-spec find_bin(list({range(), integer()}), float()) -> {ok,
{range(), integer()}} |
{error, nil}.
find_bin(Bins, Key) ->
_pipe = Bins,
gleam@list:find(
_pipe,
fun(B) ->
{range, Cmin, Cmax} = gleam@pair:first(B),
(Key
>= Cmin)
andalso (Key
< Cmax)
end
).
-spec bin_elements(list({range(), integer()}), list(float())) -> list({range(),
integer()}).
bin_elements(Bins, Arr) ->
_pipe = Arr,
gleam@list:fold(
_pipe,
Bins,
fun(Acc, Key) ->
{ok, Bin@1} = case begin
_pipe@1 = Acc,
find_bin(_pipe@1, Key)
end of
{ok, Bin} -> {ok, Bin};
_try ->
erlang:error(#{gleam_error => assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _try,
module => <<"gleam_stats/stats"/utf8>>,
function => <<"bin_elements"/utf8>>,
line => 997})
end,
{ok, Kv@1} = case gleam@list:key_pop(Acc, gleam@pair:first(Bin@1)) of
{ok, Kv} -> {ok, Kv};
_try@1 ->
erlang:error(#{gleam_error => assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _try@1,
module => <<"gleam_stats/stats"/utf8>>,
function => <<"bin_elements"/utf8>>,
line => 1000})
end,
gleam@list:key_set(
gleam@pair:second(Kv@1),
gleam@pair:first(Bin@1),
gleam@pair:first(Kv@1)
+ 1
)
end
).
-spec correlation(list(float()), list(float())) -> {ok, float()} | {error, nil}.
correlation(Xarr, Yarr) ->
Xlen = gleam@list:length(Xarr),
Ylen = gleam@list:length(Yarr),
case ((Xlen
=:= Ylen)
andalso (Xlen
> 2))
andalso ((Xlen
>= 2)
andalso (Ylen
>= 2)) of
true ->
Xmean = begin
_pipe = Xarr,
mean(_pipe)
end,
Ymean = begin
_pipe@1 = Yarr,
mean(_pipe@1)
end,
case {Xmean, Ymean} of
{{ok, Xmean@1}, {ok, Ymean@1}} ->
A = begin
_pipe@2 = gleam@list:zip(Xarr, Yarr),
_pipe@3 = gleam@list:map(
_pipe@2,
fun(Z) ->
(gleam@pair:first(Z)
- Xmean@1)
* (gleam@pair:second(Z)
- Ymean@1)
end
),
sum(_pipe@3)
end,
B = begin
_pipe@4 = Xarr,
_pipe@5 = gleam@list:map(
_pipe@4,
fun(X) -> (X - Xmean@1) * (X - Xmean@1) end
),
sum(_pipe@5)
end,
C = begin
_pipe@6 = Yarr,
_pipe@7 = gleam@list:map(
_pipe@6,
fun(Y) -> (Y - Ymean@1) * (Y - Ymean@1) end
),
sum(_pipe@7)
end,
case gleam@float:square_root(B * C) of
{ok, Val0} ->
{ok, case Val0 of
0.0 -> 0.0;
Gleam@denominator -> A / Gleam@denominator
end};
_@1 ->
{error, nil}
end;
{_@2, _@3} ->
{error, nil}
end;
false ->
{error, nil}
end.
-spec trim(list(float()), integer(), integer()) -> {ok, list(float())} |
{error, nil}.
trim(Arr, Min, Max) ->
case Arr of
[] ->
{error, nil};
_@1 ->
case (Min >= 0) andalso (Max < gleam@list:length(Arr)) of
true ->
_pipe = Arr,
_pipe@1 = gleam@list:drop(_pipe, Min),
_pipe@2 = gleam@list:take(_pipe@1, (Max - Min) + 1),
{ok, _pipe@2};
false ->
{error, nil}
end
end.
-spec isclose(float(), float(), float(), float()) -> boolean().
isclose(A, B, Rtol, Atol) ->
X = gleam@float:absolute_value(A - B),
Y = Atol + (Rtol * gleam@float:absolute_value(B)),
case X =< Y of
true ->
true;
false ->
false
end.
-spec allclose(list(float()), list(float()), float(), float()) -> {ok,
list(boolean())} |
{error, nil}.
allclose(Xarr, Yarr, Rtol, Atol) ->
Xlen = gleam@list:length(Xarr),
Ylen = gleam@list:length(Yarr),
case Xlen =:= Ylen of
true ->
_pipe = gleam@list:zip(Xarr, Yarr),
_pipe@1 = gleam@list:map(
_pipe,
fun(Z) ->
isclose(
gleam@pair:first(Z),
gleam@pair:second(Z),
Rtol,
Atol
)
end
),
{ok, _pipe@1};
_@1 ->
{error, nil}
end.
-spec amax(list(float())) -> {ok, float()} | {error, nil}.
amax(Arr) ->
case Arr of
[] ->
{error, nil};
_@1 ->
case gleam@list:at(Arr, 0) of
{ok, Val0} ->
_pipe = Arr,
_pipe@1 = gleam@list:fold(
_pipe,
Val0,
fun(Acc, A) -> case A > Acc of
true ->
A;
false ->
Acc
end end
),
{ok, _pipe@1};
_@2 ->
{error, nil}
end
end.
-spec amin(list(float())) -> {ok, float()} | {error, nil}.
amin(Arr) ->
case Arr of
[] ->
{error, nil};
_@1 ->
case gleam@list:at(Arr, 0) of
{ok, Val0} ->
_pipe = Arr,
_pipe@1 = gleam@list:fold(
_pipe,
Val0,
fun(Acc, A) -> case A < Acc of
true ->
A;
false ->
Acc
end end
),
{ok, _pipe@1};
_@2 ->
{error, nil}
end
end.
-spec argmax(list(float())) -> {ok, list(integer())} | {error, nil}.
argmax(Arr) ->
case Arr of
[] ->
{error, nil};
_@1 ->
Max = begin
_pipe = Arr,
amax(_pipe)
end,
case Max of
{ok, Max@1} ->
_pipe@1 = Arr,
_pipe@2 = gleam@list:index_map(
_pipe@1,
fun(Index, A) -> case A - Max@1 of
0.0 ->
Index;
_@2 ->
-1
end end
),
_pipe@3 = gleam@list:filter(
_pipe@2,
fun(Index@1) -> case Index@1 of
-1 ->
false;
_@3 ->
true
end end
),
{ok, _pipe@3};
_@4 ->
{error, nil}
end
end.
-spec argmin(list(float())) -> {ok, list(integer())} | {error, nil}.
argmin(Arr) ->
case Arr of
[] ->
{error, nil};
_@1 ->
Min = begin
_pipe = Arr,
amin(_pipe)
end,
case Min of
{ok, Min@1} ->
_pipe@1 = Arr,
_pipe@2 = gleam@list:index_map(
_pipe@1,
fun(Index, A) -> case A - Min@1 of
0.0 ->
Index;
_@2 ->
-1
end end
),
_pipe@3 = gleam@list:filter(
_pipe@2,
fun(Index@1) -> case Index@1 of
-1 ->
false;
_@3 ->
true
end end
),
{ok, _pipe@3};
_@4 ->
{error, nil}
end
end.