fix(growth): use centile and percentile correctly
1. The calculation made in `Grownth.Calc.Centile.compute/4` converts a given z-score into the expected measurement value. This can be used to get specific values for key z-scores, such as, -3, -2, 2 and 3. 2. To calculate the percentile of a given z-score the system uses the cumulative distribution function.
This commit is contained in:
parent
824a70452f
commit
5c044e69d9
@ -1,6 +1,6 @@
|
|||||||
defmodule Growth.Calc.Centile do
|
defmodule Growth.Calc.Centile do
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
Calculate percentile for a given measurement and the fitted values of Box-Cox by age/height:
|
Calculate the value of measurement at a given z-score and fitted values of Box-Cox by age/height:
|
||||||
|
|
||||||
* **power** `t:Growth.Calc.ZScore.l/0`
|
* **power** `t:Growth.Calc.ZScore.l/0`
|
||||||
* **median** `t:Growth.Calc.ZScore.m/0`
|
* **median** `t:Growth.Calc.ZScore.m/0`
|
||||||
@ -12,9 +12,9 @@ defmodule Growth.Calc.Centile do
|
|||||||
|
|
||||||
iex> measures =
|
iex> measures =
|
||||||
...> [
|
...> [
|
||||||
...> [30, -1.7862, 16.9392, 0.1107],
|
...> [-3.0, -1.7862, 16.9392, 0.1107],
|
||||||
...> [14, -1.3592, 20.4951, 0.12579],
|
...> [2, -1.3592, 20.4951, 0.12579],
|
||||||
...> [19, -1.6318, 16.049, 0.10038]
|
...> [-1.2, -1.6318, 16.049, 0.10038]
|
||||||
...> ]
|
...> ]
|
||||||
iex> Enum.map(measures, &apply(Growth.Calc.Centile, :compute, &1))
|
iex> Enum.map(measures, &apply(Growth.Calc.Centile, :compute, &1))
|
||||||
[:na, :na, 19.0]
|
[:na, :na, 19.0]
|
||||||
@ -32,17 +32,15 @@ defmodule Growth.Calc.Centile do
|
|||||||
|
|
||||||
## Examples:
|
## Examples:
|
||||||
|
|
||||||
iex> Growth.Calc.Centile.compute(19, -1.6318, 16.049, 0.10038)
|
iex> Growth.Calc.Centile.compute(0.0, -1.6318, 16.049, 0.10038)
|
||||||
19.0
|
19.0
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def compute(y, l, m, s) do
|
def compute(z_score, l, m, s) when -3 <= z_score and z_score <= 3 do
|
||||||
zscore = ZScore.raw(y, l, m, s)
|
m * :math.pow(1 + l * s * z_score, 1 / l)
|
||||||
|
end
|
||||||
|
|
||||||
if -3 <= zscore and zscore <= 3 do
|
def compute(_z_score, _l, _m, _s) do
|
||||||
m * :math.pow(1 + l * s * zscore, 1 / l)
|
:na
|
||||||
else
|
|
||||||
:na
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
30
lib/growth/calc/percentile.ex
Normal file
30
lib/growth/calc/percentile.ex
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
defmodule Growth.Calc.Percentile do
|
||||||
|
@moduledoc """
|
||||||
|
Convert the z-score of a given measurement into a cumulative percentile.
|
||||||
|
|
||||||
|
This calculation is described in the [cumulative distribution function][https://en.wikipedia.org/wiki/Error_function#Cumulative_distribution_function].
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> z_scores = [-1.0, 0.0, 1.0]
|
||||||
|
iex> Enum.map(z_scores, &apply(Growth.Calc.Percentile, :compute, :&1))
|
||||||
|
[0.15865525393145707, 0.5, 0.8413447460685429]
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Convert the z-score of a given measurement into a percentile representation, ranging from 0 to 1.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> Growth.Calc.Percentile.compute(2.0)
|
||||||
|
0.9772498680518208
|
||||||
|
iex> Growth.Calc.Percentile.compute(-2.0)
|
||||||
|
0.02275013194817921
|
||||||
|
|
||||||
|
"""
|
||||||
|
@spec compute(number()) :: number()
|
||||||
|
def compute(z_score) do
|
||||||
|
0.5 * (:math.erf(z_score / :math.sqrt(2)) + 1)
|
||||||
|
end
|
||||||
|
end
|
@ -1,9 +1,9 @@
|
|||||||
defmodule Growth.Score.Scorer do
|
defmodule Growth.Score.Scorer do
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
Behaviour defining common interface to calculate z-score and centile for a given measurement.
|
Behaviour defining common interface to calculate z-score and percentile for a given measurement.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
alias Growth.Calc.Centile
|
alias Growth.Calc.Percentile
|
||||||
alias Growth.Calc.ZScore
|
alias Growth.Calc.ZScore
|
||||||
|
|
||||||
@callback measure_name() :: atom()
|
@callback measure_name() :: atom()
|
||||||
@ -18,7 +18,7 @@ defmodule Growth.Score.Scorer do
|
|||||||
|
|
||||||
@spec result(module(), Growth.t()) :: Growth.t()
|
@spec result(module(), Growth.t()) :: Growth.t()
|
||||||
@doc """
|
@doc """
|
||||||
Calculate z-score and centile values for the given indicator and add them to the growth measurement `results`.
|
Calculate z-score and percentile values for the given indicator and add them to the growth measurement `results`.
|
||||||
"""
|
"""
|
||||||
def result(indicator, growth) do
|
def result(indicator, growth) do
|
||||||
result =
|
result =
|
||||||
@ -58,24 +58,22 @@ defmodule Growth.Score.Scorer do
|
|||||||
|> Enum.reject(&is_nil/1)
|
|> Enum.reject(&is_nil/1)
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec scores(module(), Growth.t(), ZScore.l(), ZScore.m(), ZScore.s()) ::
|
|
||||||
{Growth.measure(), number() | :na, nil}
|
|
||||||
@doc """
|
@doc """
|
||||||
Calculate the z-score and centile of an indicator measurement.
|
Calculate the z-score and percentile of an indicator measurement.
|
||||||
"""
|
"""
|
||||||
|
@spec scores(module(), Growth.t(), ZScore.l(), ZScore.m(), ZScore.s()) ::
|
||||||
|
{Growth.measure(), Growth.measure()}
|
||||||
def scores(indicator, growth, l, m, s) do
|
def scores(indicator, growth, l, m, s) do
|
||||||
measure = Map.get(growth, indicator.measure_name())
|
growth
|
||||||
|
|> Map.get(indicator.measure_name())
|
||||||
{
|
|> z_score(l, m, s)
|
||||||
z_score(measure, l, m, s),
|
|> then(fn score -> {score, percentile(score)} end)
|
||||||
centile(measure, l, m, s)
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec z_score(Growth.measure(), ZScore.l(), ZScore.m(), ZScore.s()) :: Growth.measure()
|
|
||||||
@doc """
|
@doc """
|
||||||
Check `Growth.Calc.ZScore.compute/4`.
|
Check `Growth.Calc.ZScore.compute/4`.
|
||||||
"""
|
"""
|
||||||
|
@spec z_score(Growth.measure(), ZScore.l(), ZScore.m(), ZScore.s()) :: Growth.measure()
|
||||||
def z_score(nil, _, _, _) do
|
def z_score(nil, _, _, _) do
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
@ -84,15 +82,15 @@ defmodule Growth.Score.Scorer do
|
|||||||
ZScore.compute(value, l, m, s)
|
ZScore.compute(value, l, m, s)
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec centile(Growth.measure(), ZScore.l(), ZScore.m(), ZScore.s()) :: number() | :na | nil
|
|
||||||
@doc """
|
@doc """
|
||||||
Check `Growth.Calc.Centile.compute/4`.
|
Check `Growth.Calc.Percentile.compute/1`.
|
||||||
"""
|
"""
|
||||||
def centile(nil, _, _, _) do
|
@spec percentile(Growth.measure()) :: Growth.measure()
|
||||||
|
def percentile(nil) do
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def centile(value, l, m, s) do
|
def percentile(score) do
|
||||||
Centile.compute(value, l, m, s)
|
Percentile.compute(score)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user