defmodule Growth.Calc.Centile do @moduledoc """ Calculate percentile for a given measurement and the fitted values of Box-Cox by age/height: * **power** `t:Growth.Calc.ZScore.l/0` * **median** `t:Growth.Calc.ZScore.m/0` * **coefficientof variation** `t:Growth.Calc.ZScore.s/0` This calculation is described in the [instructions provided by the World Health Organization](https://cdn.who.int/media/docs/default-source/child-growth/growth-reference-5-19-years/computation.pdf). ## Examples: iex> measures = ...> [ ...> [30, -1.7862, 16.9392, 0.1107], ...> [14, -1.3592, 20.4951, 0.12579], ...> [19, -1.6318, 16.049, 0.10038] ...> ] iex> Enum.map(measures, &apply(Growth.Calc.Centile, :compute, &1)) [:na, :na, 19.0] """ alias Growth.Calc.ZScore @spec compute(number(), ZScore.l(), ZScore.m(), ZScore.s()) :: number() | :na | nil @doc """ Compute the centile for a given measurement (`y`) and the Box-Cox values: power (`l`), median (`m`), and coefficient of variation (`s`). Returns the centile based on the z-score when -3 <= z-score <= 3; otherwise, returns `:na.` ## Examples: iex> Growth.Calc.Centile.compute(19, -1.6318, 16.049, 0.10038) 19.0 """ def compute(nil, _, _, _) do nil end def compute(y, l, m, s) do zscore = ZScore.raw(y, l, m, s) if -3 <= zscore and zscore <= 3 do m * :math.pow(1 + l * s * zscore, 1 / l) else :na end end end