defmodule Growth.Calc.ZScore do @moduledoc """ measures = [ [30, -1.7862, 16.9392, 0.1107], [14, -1.3592, 20.4951, 0.12579], [19, -1.6318, 16.049, 0.10038] ] Enum.map(measures, &apply(Growth.Calc.ZScore, :compute, &1)) """ # TODO: (jpd) add documentation and typespecs def compute(y, l, m, s) do y |> raw(l, m, s) |> adjust(y, l, m, s) end def raw(y, l, m, s) do (:math.pow(y / m, l) - 1) / (s * l) end def adjust(zscore, y, l, m, s) when zscore > 3 do [sd2, sd3, _, _] = cutoffs(l, m, s) sd_delta = sd3 - sd2 3 + (y - sd3) / sd_delta end def adjust(zscore, y, l, m, s) when zscore < -3 do [_, _, sd2, sd3] = cutoffs(l, m, s) sd_delta = sd2 - sd3 -3 + (y - sd3) / sd_delta end def adjust(zscore, _, _, _, _) do zscore end def cutoffs(l, m, s) do Enum.map([2, 3, -2, -3], &cutoff_for_standard_deviation(&1, l, m, s)) end def cutoff_for_standard_deviation(sd, l, m, s) do m * :math.pow(1 + l * s * sd, 1 / l) end end