[WIP] Implement growth assessment using WHO indicators #80
40
lib/growth/calc/age.ex
Normal file
40
lib/growth/calc/age.ex
Normal file
@ -0,0 +1,40 @@
|
||||
defmodule Growth.Calc.Age do
|
||||
@moduledoc """
|
||||
Calculate the age in months.
|
||||
"""
|
||||
|
||||
# NOTE: (jpd): based on [WHO instructions][0]
|
||||
# [0]: https://cdn.who.int/media/docs/default-source/child-growth/child-growth-standards/indicators/instructions-en.pdf
|
||||
@day_in_month 30.4375
|
||||
|
||||
@day_in_week 7.0
|
||||
|
||||
@doc """
|
||||
Calculate the age with the precision of day, or week, or month. For age in weeks or months, considers completed ones, removing decimal value.
|
||||
|
||||
If only date of birth is available, assumes that measurement date is today.
|
||||
"""
|
||||
@spec calculate(:day | :week | :month, Date.t(), Date.t()) :: pos_integer()
|
||||
|
||||
def calculate(precision, date_of_birth) do
|
||||
calculate(precision, date_of_birth, Date.utc_today())
|
||||
end
|
||||
|
||||
def calculate(:month, date_of_birth, date_of_measurement) do
|
||||
:day
|
||||
|> calculate(date_of_birth, date_of_measurement)
|
||||
|> Kernel./(@day_in_month)
|
||||
|> floor()
|
||||
end
|
||||
|
||||
def calculate(:week, date_of_birth, date_of_measurement) do
|
||||
:day
|
||||
|> calculate(date_of_birth, date_of_measurement)
|
||||
|> Kernel./(@day_in_week)
|
||||
|> floor()
|
||||
end
|
||||
|
||||
def calculate(:day, date_of_birth, date_of_measurement) do
|
||||
Date.diff(date_of_measurement, date_of_birth)
|
||||
end
|
||||
end
|
29
lib/growth/calc/bmi.ex
Normal file
29
lib/growth/calc/bmi.ex
Normal file
@ -0,0 +1,29 @@
|
||||
defmodule Growth.Calc.BMI do
|
||||
@moduledoc """
|
||||
Calculate body mass index
|
||||
"""
|
||||
|
||||
@inch_to_centimeter 2.54
|
||||
@pound_to_kilogram 0.453592
|
||||
|
||||
@doc """
|
||||
Calculate the body mass index for a given weight and height.
|
||||
|
||||
Measurements taken in english unit (pounds and inches) are converted to their metric equivalents (kilograms and centimeters).
|
||||
"""
|
||||
@spec calculate(:english | :metric, number, number) :: number
|
||||
|
||||
def calculate(:english, weight, height) do
|
||||
metric_weight = pound_to_kilogram(weight)
|
||||
metric_height = inches_to_centimeters(height)
|
||||
calculate(:metric, metric_weight, metric_height)
|
||||
end
|
||||
|
||||
def calculate(:metric, weight, height) do
|
||||
weight / :math.pow(height / 100.0, 2)
|
||||
end
|
||||
|
||||
defp inches_to_centimeters(height), do: height * @inch_to_centimeter
|
||||
|
||||
defp pound_to_kilogram(weight), do: weight * @pound_to_kilogram
|
||||
end
|
25
lib/growth/calc/centile.ex
Normal file
25
lib/growth/calc/centile.ex
Normal file
@ -0,0 +1,25 @@
|
||||
defmodule Growth.Calc.Centile 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.Centile, :compute, &1))
|
||||
"""
|
||||
|
||||
# TODO: (jpd) add documentation and typespecs
|
||||
|
||||
def compute(y, l, m, s) do
|
||||
zscore = Growth.Calc.ZScore.raw(y, l, m, s)
|
||||
|
||||
cond do
|
||||
-3 <= zscore and zscore <= 3 ->
|
||||
m * :math.pow(1 + l * s * zscore, 1 / l)
|
||||
|
||||
true ->
|
||||
:na
|
||||
end
|
||||
end
|
||||
end
|
47
lib/growth/calc/z_score.ex
Normal file
47
lib/growth/calc/z_score.ex
Normal file
@ -0,0 +1,47 @@
|
||||
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
|
Loading…
x
Reference in New Issue
Block a user