Pediatric age in months not accurate enough

tmccormi wrote on Wednesday, December 26, 2012:

Two examples:

This patient is 2 years old, so OpenEMR should plot her growth chart on the charts for kids 2 years and older so we can see her BMI. Instead, it plots height and weight on the chart for kids under two (just a little to the right of the 24 month mark). The pt DOB is 12/09/2010, but the system calculates her age in months as 24 even, instead of 24 months + days past the DOB day.  This means the chart won’t be right until next month. 

In a similar calculation there is a patient that reports as 15 months old, but is a few day past the DOB day, it should now be reporting 16 months, or at the very least 15 months + so many days…

These are important to pediatricians.

I looked at the patient.inc calcutions for age and they are not adequate,  there are some examples of calculations that will return Years, Months, Days, Hrs.    These are the funtions that should be replaced with one more comprehensive function:

getPatientAge($dobYMD, $nowYMD=null)
getPatientAgeInDays($dobYMD, $nowYMD=null)

It is unclear why you would ever overide $NOW in a dob calculation, but we could keep that.   I suggest we make them one:

getPatientAge($dobYMD, $nowYMD=null) but return an array of:
  AgeInYears - Total Whole Years
  AgeInMonths - Total Whole  Number of Months since DOB
  MonthsPastDOBMonth - Number of whole Months past the DOB Month (ie: 4 yrs + 3 Mons)
  AgeinDays - Total Whole Number of Days since DOB
  DaysPastDOBDay - Number of days past the DOB day date (ie: 4 yrs + 3 Mon + 4 days)

I suggest we display Month + Days on anyone < 24 months old,  There may be a reason to display Years + Months old up to some age max as well.

-Tony

yehster wrote on Wednesday, December 26, 2012:

$nowYMD seems like it is an ambiguously named but needed parameter. The “use” seems to be if you wanted to know the patient’s age on a particular date.  For example, if a measurement was taken in the past but was entered “today.”  The date in the calculation would need to be overridden.  Naming the parameter “when” or “on” might be better…. "I want to know the patient’s age “on Christmas day 2012.”

I agree with you that the appropriate precision is needed.  However, I don’t think that changing the return type is the best way to do this.  If we change the return type, then we need to track down and test every instance where getPatientAge is used and adapt it to expect an array.  When acl_check was switched from “boolean” to “string” return types, we broke a lot of things and I’m not sure we’ve tracked down all the issues yet. 

One approach to consider is an optional parameter that is an array passed in by reference which can be populated with all the different formats as the appropriate way to provide the multiple possible date formats.

An optional parameter where by the calling code can explicitly request one of the five formatting options you suggest may also be a good solution.

The issue you have identified is that patient age is not “one size fits all.”  Just like there are three “rounding functions” (ceil, floor, round) for floating point numbers, there is more than one way to handle age.  However, I don’t think it’s a good idea to make an “API level change” that will be backwards incompatible.  

fencepost wrote on Wednesday, December 26, 2012:

I’d suggest naming it something like ‘asOfDate’ or ‘asOfYMD’ - ‘on’ or ‘when’ seem too generic

bradymiller wrote on Wednesday, December 26, 2012:

Hi,

There appear to be more accurate functions in the clinical rules engine:
convertDobtoAgeYearDecimal() and convertDobtoAgeMonthDecimal() in library/clinical_rules.php

Agree it’s very important to maintain backward compatibility.

-brady
OpenEMR

bradymiller wrote on Wednesday, December 26, 2012:

Hi,

just realized a good thing to consider is to place all the dob related functions in a separate library file (for example, if had a DayDecimal equivalent to above, then Tony could work these into the “main” function that you are fixing (which could also go into the library). And could place any other dob functions there, then can avoid redundant (and potentially inaccurate) dob calculation scripts).

-brady
OpenEMR

yehster wrote on Thursday, December 27, 2012:

I definitely like “asOf” for a parameter name more than my suggestions.

Age is a discrete variable, rounded to the nearest month for WHO data, and to the half month for the CDC data.

http://www.cdc.gov/growthcharts/who_charts.htm
http://www.cdc.gov/growthcharts/zscore.htm

The graphical presentation of the data and the smoothing of the  growth curves, suggests a level of precision that may not be statistically valid.  

In addition, for the 24 month old “corner case”  the fundamental issue isn’t that the data is invalid (the WHO data is valid from age 0-59).  It’s that the chart to use is automatically chosen based on age, and a clinician doesn’t have access to the data conveyed by the “other chart.”  If a 26 month old comes to visit, I’m sure that a pediatrician would still want to be able to see the birth to 24 month old data even though the newest and future data points will be plotted on the CDC graph.  However, OpenEMR doesn’t provide a way to look at the WHO graph once the patient is older than 24 months.

I previously proposed providing the information presented in these plots as numbers instead of points
https://sourceforge.net/projects/openemr/forums/forum/202506/topic/6128835?message=12389281

blankev wrote on Thursday, December 27, 2012:

A child of one month is not as old as a child born in another month! February child is younger than a December month baby. But a child of thirty days is always thirty days old. Exact graphics should be in days and not in month! If CDC or WHO make graphs in months they accept a certain inaccuracy. Our ancestors were not concerned with days after birth, but calculated with so many moons alive………

May be the OpenEMR team can convince WHO and CDC to recalculate their graphs into days to get rid of any confusion. (this conclusion was the result of a misunderstanding between a student, GP and pediatrician)

To do statistics for groups OpenEMR need graphs in days. To do a growth chart of any individual you need to follow the line and every individual need to follow their own line of it’s own growth chart.

tmccormi wrote on Sunday, February 10, 2013:

MI2 and Dr Kay are going to fund an update to resolve this issue.  Stay tuned.  Add thoughts…
-Tony

bo2999 wrote on Sunday, February 10, 2013:

There is a function in chart.php in ./vitals/growthchart/chart.php which called “get_age” that will return age in YyMmDy,( ie 10y 6m 2d ) to report in the graph.  We should use this function to replace the age function in patient.inc.  I tested the get_age function very careful, it is also corrected for the leap year calculation.  And for the graph selection based on age, it used different function for age calculation but less accurate.  That’s why when patient age passes 24 for a few days, it still shows up in the 24 month chart. We should look into that.

Bo,

bradymiller wrote on Monday, February 11, 2013:

Hi,

There are also some age functions used in the CDR engine:
library/clinical_rules.php
convertDobtoAgeYearDecimal($dob,$target)
convertDobtoAgeMonthDecimal($dob,$target)

Woulds be nice to “centralize” all these to ensure consistency between them.

-brady
OpenEMR

yehster wrote on Monday, February 11, 2013:

There are at least two separate but related issues here:
1. Age computation for use in “rules” (which growth chart to use, age appropriate immunizations, age appropriate screenings, etc…)
2. Display of age to be read by humans

I think I’ve expressed my thoughts regarding rounding modes and other technical issue previously.  What I’d like to know is what sorts of clinical scenarios is this causing the biggest problems.
Clearly the growth charts are an issue, but what about vaccinations?

If a patient comes in at age 1 month and 29 days, I’m suspect that 2 month immunizations will be offered at that time, rather than waiting until a CDR for “Patient Age >= 2 months”  actually kicks in.  However it could get very confusing/difficult to manage if we start trying to implement fuzziness like this.

bradymiller wrote on Tuesday, February 12, 2013:

Hi,
Note the CDR engine stuff can be considered “fuzzy” since can set for each rule when to remind that something is due soon.
-brady
OpenEMR

drkay wrote on Wednesday, February 13, 2013:

Here’s an example: I have a patient with a date of birth 2013-01-23. Today, on February 13, 2013, OpenEMR states that the patient is one month old, even though the patient is only 21 days old.

I searched for a standard or accepted means of determining the age for a child. I also question to the American Academy of Pediatrics COCIT (informatics special interest group) listserv. To my surpirse, there seems to be no standard way of determining a child’s age for medical purposes. For developmental screening, such as Ages and Stages, the following method is recommended:

Chronological Age
Zach was born at 28 weeks on February 27, 2009. Today’s date is 3/16/10.  His chronological age is 1 year and 19
days or 12 months and 19 days.

      Month    Day     Year
         3    16     10         (Take 30 days from the month  
       - 2    27     09     column to give to days column to subtract.  16 + 30 = 46).                   
                  
       Month   Day   Year
         2    46    10
     -   2    27    09
                19      1

If borrowing is needed during the subtraction, one month is considered to be 30 days.

Here is a link to a pdf that demonstrates this better:

http://www.nd.gov/dhs/info/pubs/docs/mhsa/2011-cmhs-ages-and-stages-questionnaire-3.pdf

James L. Kay, D.O.

yehster wrote on Wednesday, February 13, 2013:

The get_age algorithm Bo mentioned which the growth charts use produces results that are similar to the algorithm you describe.  However, instead of always using 30 days for “borrowing” it uses 28,29,30 or 31 depending on the specific month/leap year. 

The quirky part of this is that a baby born on February 1st who is 29 days old will be reported as 1 month and 1 day on March 2nd.  However, a baby born on March 1st who is 32 days old will be reported as 1 month and 1 day on April 2nd.

Tony’s original suggestion:

I suggest we display Month + Days on anyone < 24 months old, There may be a reason to display Years + Months old up to some age max as well.

We could potentially also make these cut-offs  points global configuration variables.  However the problem is that if there is code anywhere that treats the results of getPatientAge as a “value” instead of just displaying it as a string, changing the formatting would break that.  As an example, for syndromic surveillance, only one unit type is allowed.  (days, weeks, months or years)  http://phinvads.cdc.gov/vads/ViewValueSet.action?id=5002CD54-9317-E011-87A0-00188B39829B

However, just changing that display won’t  impact the growth chart issue brought up in the first post.
Some thoughts there.
Consider this scenario. Patient is 26 months old and has been followed closely due to initial low birth weight. There are vital signs about every 3 months since birth. If we just address the “age calculation” issue, then at this point OpenEMR will only display the CDC growth chart, and there is no way to access the WHO chart for reference even though the information from the WHO chart is still clinically relevant.

Similarly, if the 24 month + 2 day year old has a prior measurement at age 10 month, the data point at age 24 months is still “statistically” valid, and displaying the WHO chart will provide “trend” information which the CDC chart does not. We obviously still want to provide access to the CDC statistics so that the BMI percentile information is available. Also, the data used for generation of these tables is based on classifying patient ages to the 1/2 month. The BMI percentile calculation for a patient who is 23.8 months old using the CDC data on the 2 year-20 year chart is valid.

This reminds me of how Commander Data of Star Trek:TNG stopped reporting times to the “second” because he realized that level of precision simply served to annoy his shipmates and wasn’t actually useful.

yehster wrote on Wednesday, February 13, 2013:

http://www.cdc.gov/growthcharts/percentile_data_files.htm

Age is listed at the half month point for the entire month; for example, 1.5 months represents 1.0-1.99 months or 1.0 month up to but not including 2.0 months of age. The only exception is birth, which represents the point at birth.

yehster wrote on Wednesday, February 13, 2013:

More data for reference:
http://www.cdc.gov/nccdphp/dnpa/growthcharts/guide.htm

yehster wrote on Thursday, February 14, 2013:

https://github.com/yehster/openemr/tree/growth-chart-options
Three things going on here:
1. Allow selection of which chart to display for patients 2 years or older.
If there are appropriate data points from before Age 2, if a patient is Age 2 or older there is now a select control to choose viewing either the “Birth” chart or the “2-20” chart.

2. Configure age display to use y m d format.
New global setting under local.  age_display_format
Can choose “Years or Months” (the old way)
or “Years, Months, Days” (uses the algorithm previously included in chart.php. moved into patient.inc.php)
I have only updated the “title” frame to be aware of this (when it gets updated through demographics or demographics_full.php)

3. Bug fix in months calculation in getPatientAge
I think some of the initial confusion about which chart is being used at a given age is because there was a bug with the algorithm for calculating “age in months.”  It did not correctly take into account the numeric day of birth.
So if you were born on February 28, 2011, but came in on February 1, 2013, it would report 24 months.

yehster wrote on Thursday, February 14, 2013:

Looking at the logic in convertDobtoAgeMonthDecimal in clinical_rules.php, I think there are some big issues.

When $iDiffMonth is negative (birthday is in June, but it’s still only February)  it both reduces the iDiffYear by one and adds a negative number.  I also don’t see where the “decimal” part comes into play. 

bradymiller wrote on Thursday, February 14, 2013:

Agreed,

Not sure why it’s a decimal function. Guessing I must of started it planning to make it a decimal but never did it since wasn’t needed anywhere.

Also see your point on the negative months. I think the following needs to be placed:

    // If birthday has not happen yet for this year, subtract 1.
    if ($iDiffMonth < 0 || ($iDiffMonth == 0 && $iDiffDay < 0))
    {
        $iDiffMonth += 12;
        $iDiffYear--;
    }

Added the $iDiffMonth += 12; to above loop.

Hasn’t been noticed it appears since the monthly calulation is not used in any of the rules (so, would need to create a custom rule to see this bug). Also, noted that there is a copy of the the convertDobtoAgeYearDecimal($dob,$target) function here:
calculateAgeOnDate($date) in library/classes/rulesets/library/RsPatient.php

Things to do then:
1. Fix the monthly bug
2. Remove the decimal stuff (easy since each function only called twice)
3. Test it (may even break out some of your Testing scripts)
4. Have the calculateAgeOnDate() function call the main function

-brady
OpenEMR

bradymiller wrote on Thursday, February 14, 2013:

Actually,
in above loop, should not add 12 to iDiffMonth if $iDiffMonth is 12, so need to modify it a bit.
-brady