feat(growth): calculate z-score/centile for all indicators
The `Growth.Score.Scorer` behaviour implements the methods needed to calculate z-score and centile for any indicator.
This commit is contained in:
parent
0ac434c61d
commit
fa022b9592
80
lib/growth/score/scorer.ex
Normal file
80
lib/growth/score/scorer.ex
Normal file
@ -0,0 +1,80 @@
|
||||
defmodule Growth.Score.Scorer do
|
||||
@moduledoc """
|
||||
Behaviour defining common interface to calculate z-score and centile for a given measurement.
|
||||
"""
|
||||
|
||||
alias Growth.Calc.Centile
|
||||
alias Growth.Calc.ZScore
|
||||
|
||||
@callback measure_name() :: atom()
|
||||
|
||||
@spec results(Growth.t(), [module()]) :: Growth.t()
|
||||
@doc """
|
||||
Add z-score and centile values in growth measurements `results` for each indicator.
|
||||
"""
|
||||
def results(growth, indicators) do
|
||||
Enum.reduce(indicators, growth, &result/2)
|
||||
end
|
||||
|
||||
@spec result(module(), Growth.t()) :: Growth.t()
|
||||
@doc """
|
||||
Calculate z-score and centile values for the given indicator and add them to the growth measurement `results`.
|
||||
"""
|
||||
def result(indicator, growth) do
|
||||
result =
|
||||
growth
|
||||
|> lms(indicator)
|
||||
|> Enum.map(fn {precision, {l, m, s}} ->
|
||||
{precision, {z_score(indicator, growth, l, m, s), centile(indicator, growth, l, m, s)}}
|
||||
end)
|
||||
|
||||
%{growth | results: [Map.new([{indicator.measure_name(), result}]) | growth.results]}
|
||||
end
|
||||
|
||||
@spec lms(Growth.t(), module()) :: [{String.t(), {number(), number(), number()}}]
|
||||
@doc """
|
||||
Get the indicaator fitted values of Box-Cox transformation:
|
||||
|
||||
* power (`l`)
|
||||
* median (`m`)
|
||||
* coefficient of variation (`s`)
|
||||
|
||||
"""
|
||||
def lms(growth, indicator) do
|
||||
[
|
||||
{growth.gender, "day", growth.age_in_days},
|
||||
{growth.gender, "week", growth.age_in_weeks},
|
||||
{growth.gender, "month", growth.age_in_months}
|
||||
]
|
||||
|> Enum.map(fn {_, precision, _} = key ->
|
||||
case :ets.lookup(indicator, key) do
|
||||
[{^key, %{l: l, m: m, s: s}} | _] ->
|
||||
{precision, {l, m, s}}
|
||||
|
||||
_ ->
|
||||
nil
|
||||
end
|
||||
end)
|
||||
|> Enum.reject(&is_nil/1)
|
||||
end
|
||||
|
||||
@spec z_score(module(), Growth.t(), ZScore.l(), ZScore.m(), ZScore.s()) :: number()
|
||||
@doc """
|
||||
Check `Growth.Calc.ZScore.compute/4`.
|
||||
"""
|
||||
def z_score(indicator, growth, l, m, s) do
|
||||
growth
|
||||
|> Map.get(indicator.measure_name())
|
||||
|> ZScore.compute(l, m, s)
|
||||
end
|
||||
|
||||
@spec centile(module(), Growth.t(), ZScore.l(), ZScore.m(), ZScore.s()) :: number() | :na
|
||||
@doc """
|
||||
Check `Growth.Calc.Centile.compute/4`.
|
||||
"""
|
||||
def centile(indicator, growth, l, m, s) do
|
||||
growth
|
||||
|> Map.get(indicator.measure_name())
|
||||
|> Centile.compute(l, m, s)
|
||||
end
|
||||
end
|
Loading…
x
Reference in New Issue
Block a user