/// <summary> /// Builds a dictionary key using a provided sex and measurement value. /// </summary> /// <param name="measurement">The measurement in metric units</param> /// <param name="sex">Whether the child is male or female</param> /// <returns>int; represents the integer dictionary key for the given sex and measurement values</returns> internal int BuildKey(Sex sex, double measurement) { if (StatisticsHelper.IsWholeNumber(measurement)) { int sexKeyPart = sex == Sex.Male ? 1 : 2; int measurementKeyPart = (int)(measurement * 100) + sexKeyPart; return(measurementKeyPart); } return(-1); }
/// <summary> /// Calculates a z-score for a given indicator, pair of measurements (measurement1-for-measurement2, as /// in "BMI-for-Age"), and gender. /// </summary> /// <param name="indicator">The indicator to use for computing the z-score (e.g. BMI, Height-for-Age, Weight-for-Age)</param> /// <param name="measurement"> /// The first measurement value. Must be in metric units and must be greater than or equal to zero. For /// example, if the indicator is 'Height-for-Age', then measurement represents the child's height in /// centimeters. If using 'Weight-for-Age', then the measurement represents the child's weight in /// kilograms. /// </param> /// <param name="age">The age of the child in months. Must be greater than or equal to 61. Automatically rounded to 5 decimal values.</param> /// <param name="sex">Whether the child is male or female</param> /// <returns>double; the z-score for the given inputs</return> internal double CalculateZScore(Indicator indicator, double measurement, double age, Sex sex) { if (measurement < 0) { throw new ArgumentOutOfRangeException(nameof(measurement)); } if (!IsValidMeasurement(indicator, age)) { throw new ArgumentOutOfRangeException(nameof(age)); } age = Math.Round(age, 5); Dictionary <int, Lookup> reference = null; switch (indicator) { case Indicator.BodyMassIndexForAge: reference = WHO2007_BMI; break; case Indicator.WeightForAge: reference = WHO2007_WeightAge; break; case Indicator.HeightForAge: reference = WHO2007_HeightAge; break; default: throw new ArgumentOutOfRangeException(nameof(indicator)); } int key = BuildKey(sex, age); Lookup lookup = null; bool found = reference.TryGetValue(key, out lookup); if (found) { return(StatisticsHelper.CalculateZScore(measurement, lookup.L, lookup.M, lookup.S, true)); } else { var interpolatedValues = InterpolateLMS(sex, age, reference); return(StatisticsHelper.CalculateZScore(measurement, interpolatedValues.Item1, interpolatedValues.Item2, interpolatedValues.Item3, true)); } }
/// <summary> /// Calculates a z-score for the given indicator, age in months, measurement value, and gender. /// </summary> /// <param name="indicator">The indicator to use for computing the z-score (e.g. BMI-for-age, Height-for-Age, Weight-for-Age, etc.)</param> /// <param name="measurement1"> /// The first measurement value. Must be in metric units. For example, if the indicator is Height-for-Age, /// then measurement1 represents the child's height in centimeters. /// </param> /// <param name="measurement2"> /// The second measurement. Typically age of the child in months. For example, if the indicator is /// 'Height-for-Age', then measurement2 represents the child's age. If the indicator is instead /// 'Weight-for-Length' or 'Weight-for-Height' then measurement2 represents the child's length or /// height (respectively) and must be a non-zero value provided in centimeters. Automatically /// rounded to 5 decimal values. /// </param> /// <param name="sex">Whether the child is male or female</param> /// <returns>double; the calculated z-score for the given inputs</return> internal double CalculateZScore(Indicator indicator, double measurement1, double measurement2, Sex sex) { if (measurement1 < 0) { throw new ArgumentOutOfRangeException(nameof(measurement1)); } if (!IsValidMeasurement(indicator, measurement2)) { throw new ArgumentOutOfRangeException(nameof(measurement2)); } measurement2 = Math.Round(measurement2, 5); IDictionary <int, Lookup> reference = null; switch (indicator) { case Indicator.BodyMassIndexForAge: reference = CDC2000_BMI; break; case Indicator.HeadCircumferenceForAge: reference = CDC2000_HeadCircumference; break; case Indicator.LengthForAge: reference = CDC2000_LengthForAge; break; case Indicator.HeightForAge: reference = CDC2000_HeightForAge; break; case Indicator.WeightForAge: reference = CDC2000_WeightForAge; break; case Indicator.WeightForLength: reference = CDC2000_WeightForLength; break; case Indicator.WeightForHeight: reference = CDC2000_WeightForHeight; break; default: throw new ArgumentOutOfRangeException(nameof(indicator)); } int key = BuildKey(sex, measurement2); Lookup lookup = null; bool found = reference.TryGetValue(key, out lookup); if (found) { return(StatisticsHelper.CalculateZScore(measurement1, lookup.L, lookup.M, lookup.S, false)); } else { var interpolatedValues = InterpolateLMS(sex, measurement2, reference); return(StatisticsHelper.CalculateZScore(measurement1, interpolatedValues.Item1, interpolatedValues.Item2, interpolatedValues.Item3, false)); } }
/// <summary> /// Calculates a z-score for a given indicator, pair of measurements (measurement1-for-measurement2, as /// in "BMI-for-Age"), and gender. /// </summary> /// <param name="indicator">The indicator to use for computing the z-score (e.g. BMI, Height-for-Age, Weight-for-Age, etc.)</param> /// <param name="measurement1"> /// The first measurement value. Must be in metric units and must be greater than or equal to zero. For /// example, if the indicator is 'Height-for-Age', then measurement1 represents the child's height in /// centimeters. Note that subscapular skinfold and triceps skinfold require measurement1 be provided /// in millimeters. /// </param> /// <param name="measurement2"> /// The second measurement. Typically age of the child in days. For example, if the indicator is /// 'Height-for-Age', then measurement2 represents the child's age. If the indicator is instead /// 'Weight-for-Length' or 'Weight-for-Height' then measurement2 represents the child's length or /// height (respectively) and must be a non-zero value provided in centimeters. Automatically /// rounded to 5 decimal values if measuring height or length and automatically rounded to a whole /// number if measuring age in days. /// </param> /// <param name="sex">Whether the child is male or female</param> /// <returns>double; the z-score for the given inputs</return> internal double CalculateZScore(Indicator indicator, double measurement1, double measurement2, Sex sex) { if (measurement1 < 0) { throw new ArgumentOutOfRangeException(nameof(measurement1)); } if (!IsValidMeasurement(indicator, measurement2)) { throw new ArgumentOutOfRangeException(nameof(measurement2)); } measurement2 = Math.Round(measurement2, 5); Dictionary <int, Lookup> reference = null; bool shouldRound = true; switch (indicator) { case Indicator.BodyMassIndexForAge: reference = WHO2006_BMI; break; case Indicator.WeightForLength: reference = WHO2006_WeightForLength; shouldRound = false; break; case Indicator.WeightForHeight: reference = WHO2006_WeightForHeight; shouldRound = false; break; case Indicator.WeightForAge: reference = WHO2006_WeightForAge; break; case Indicator.ArmCircumferenceForAge: reference = WHO2006_ArmCircumference; break; case Indicator.HeadCircumferenceForAge: reference = WHO2006_HeadCircumference; break; case Indicator.HeightForAge: case Indicator.LengthForAge: reference = WHO2006_LengthHeightForAge; break; case Indicator.SubscapularSkinfoldForAge: reference = WHO2006_SubscapularSkinfoldForAge; break; case Indicator.TricepsSkinfoldForAge: reference = WHO2006_TricepsSkinfoldForAge; break; default: throw new ArgumentOutOfRangeException(nameof(indicator)); } if (shouldRound && !StatisticsHelper.IsWholeNumber(measurement2)) { measurement2 = Math.Round(measurement2, 0); } int key = BuildKey(sex, measurement2); Lookup lookup = null; bool found = reference.TryGetValue(key, out lookup); if (found) { return(StatisticsHelper.CalculateZScore(measurement1, lookup.L, lookup.M, lookup.S, true)); } else if (indicator == Indicator.WeightForLength || indicator == Indicator.WeightForHeight) { var interpolatedLMS = InterpolateLMS(sex, measurement2, reference); return(StatisticsHelper.CalculateZScore(measurement1, interpolatedLMS.Item1, interpolatedLMS.Item2, interpolatedLMS.Item3, true)); } else { throw new InvalidOperationException($"Could not find a lookup match for value {Math.Round(measurement2, 2).ToString("N2")}"); } }