feat(growth): add weight-for-height calculation
All checks were successful
continuous-integration/drone/pr Build is passing
All checks were successful
continuous-integration/drone/pr Build is passing
This commit is contained in:
@@ -1,5 +1,95 @@
|
||||
defmodule Growth.Score.WeightForHeight do
|
||||
@moduledoc """
|
||||
Calculate z-score for weight for height.
|
||||
|
||||
This module calculates the z-score for weight relative to height, which is an important
|
||||
indicator for assessing whether a child's weight is appropriate for their height,
|
||||
regardless of age.
|
||||
|
||||
**Limitation**: the memasurements do not differentiate between length and height, and
|
||||
always assume height.
|
||||
"""
|
||||
|
||||
@behaviour Growth.Score.Scorer
|
||||
|
||||
alias Growth.Score.Scorer
|
||||
|
||||
@impl Scorer
|
||||
@spec measure_name() :: atom()
|
||||
@doc """
|
||||
Name of the measurement used in weight for height indicator.
|
||||
"""
|
||||
def measure_name do
|
||||
:weight_for_height
|
||||
end
|
||||
|
||||
@doc """
|
||||
Custom implementation for weight-for-height lookup, as it requires both weight and height.
|
||||
|
||||
This overrides the default implementation in the Scorer module.
|
||||
"""
|
||||
@spec lms(Growth.t(), module()) :: [{String.t(), {number(), number(), number()}}]
|
||||
def lms(%Growth{gender: gender, weight: weight, height: height}, _indicator)
|
||||
when is_number(weight) and is_number(height) do
|
||||
# For weight-for-height, we use height as the lookup value instead of age
|
||||
key = {{gender, :height, height}, :_}
|
||||
|
||||
case :ets.match_object(__MODULE__, key) do
|
||||
[{{^gender, _, ^height}, %{l: l, m: m, s: s}} | _] ->
|
||||
[{"height", {l, m, s}}]
|
||||
|
||||
_ ->
|
||||
# Try to find the closest height value
|
||||
find_closest_height(gender, height)
|
||||
end
|
||||
end
|
||||
|
||||
def lms(_growth, _indicator) do
|
||||
[]
|
||||
end
|
||||
|
||||
@doc """
|
||||
Find the closest height value in the ETS table when an exact match isn't found.
|
||||
"""
|
||||
@spec find_closest_height(Growth.gender(), number()) :: [
|
||||
{String.t(), {number(), number(), number()}}
|
||||
]
|
||||
def find_closest_height(gender, height) do
|
||||
# Fetch all entries for the given gender and height measurement
|
||||
matcher = [
|
||||
{
|
||||
{{:"$1", :"$2", :"$3"}, :_},
|
||||
[
|
||||
{:andalso,
|
||||
{:andalso, {:andalso, {:==, :"$1", gender}, {:==, :"$2", :height}},
|
||||
{:>, :"$3", height - 1.0}}, {:<, :"$3", height + 1.0}}
|
||||
],
|
||||
[:"$_"]
|
||||
}
|
||||
]
|
||||
|
||||
gender_height_entries = :ets.select(__MODULE__, matcher)
|
||||
|
||||
case gender_height_entries do
|
||||
[] ->
|
||||
# No entries found for this gender
|
||||
[]
|
||||
|
||||
entries ->
|
||||
# Find the entry with the height closest to the target height
|
||||
closest_entry =
|
||||
entries
|
||||
|> Enum.filter(fn {{_, _, entry_height}, _lms_data} ->
|
||||
abs(entry_height - height) <= 0.5
|
||||
end)
|
||||
|> Enum.min_by(fn {{_, _, entry_height}, _lms_data} ->
|
||||
abs(entry_height - height)
|
||||
end)
|
||||
|
||||
# Extract the LMS data from the closest entry
|
||||
{_, %{l: l, m: m, s: s}} = closest_entry
|
||||
|
||||
[{"height", {l, m, s}}]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
Reference in New Issue
Block a user