Public-Health-Scotland / phsmethods

An R package to standardise methods used in Public Health Scotland (https://public-health-scotland.github.io/phsmethods/)
https://public-health-scotland.github.io/phsmethods/
54 stars 13 forks source link

New Function: `age_at_date` #65

Closed Moohan closed 2 years ago

Moohan commented 3 years ago

As per this chat on Teams, there are numerous possible options for calculating age.

It would be good to have a simple function that takes dob and a date and returns the age in years. I could be wrong but I don't think there's a need for age in months, days or factional years? Definitely, whole years should be the default, if not the only option.

The function would need to be tested against all edge cases (combination of leap year dates etc.) to validate the methodology and tested against existing options for speed. It's possible just a wrapper to simplify an existing function would be the most efficient, e.g.


age_at_date <- function(dob, date) {
  #Test for date type supplied...

  #Return age at date in years
  age <- lubridate::as.period(lubridate::interval(dob, date))$year
  return(age)
}
davidc92 commented 3 years ago

I'm disinclined to greenlight this when age_calc from eeptools already does this in days, months and years. It might be a little slower when a vector is supplied rather than a single date, but we are talking milliseconds rather than minutes or even hours. I don't think that speed gain is worth the effort this would take to do properly. Especially when this function already handles leap years quite nicely.

Nic-Chr commented 3 years ago

I like the idea of a having a function to calculate age, especially using lubridate functions. The one thing I'm not a big of fan of with eeptools::age_calc are how you sometimes get a returned numeric class and other times a difftime class (see below for an example).

> my_date <- lubridate::dmy("29022020")
> eeptools::age_calc(my_date, units = "days")
Time difference of 489 days
> eeptools::age_calc(my_date, units = "years")
[1] 1.336986

The second thing I don't like is how units more granular than days aren't supported, but I'm sure this was intentional.

> eeptools::age_calc(my_date, units = "hours")
Error in eeptools::age_calc(my_date, units = "hours") : 
  Unrecognized units. Please choose years, months, or days.

Perhaps we could use something like below which has similar general functionality as eeptools::age_calc though I do appreciate that the functionality of having granular time units may not be necessary.

Edited to use periods instead of durations.

> age_at_date <- function(dob, end_date = Sys.time(), units = "years", round_down = TRUE) {
+   unit_period <- lubridate::period(num = 1, units = units)
+   age_interval <- lubridate::interval(dob, end_date)
+   if (round_down) {
+     age_interval %/% unit_period
+   } else {
+     age_interval / unit_period
+   }
+ }
> 
> age_at_date(my_date, units = "years")
[1] 1
> age_at_date(my_date, units = "months")
[1] 16
> age_at_date(my_date, units = "days")
[1] 489
> age_at_date(my_date, units = "hours")
[1] 11746
> age_at_date(my_date, units = "minutes")
[1] 704765
> age_at_date(my_date, units = "seconds")
[1] 42285930
> age_at_date(my_date, units = "years", round_down = FALSE)
[1] 1.340878
> age_at_date(my_date, units = "months", round_down = FALSE)
[1] 16.11402
> age_at_date(my_date, units = "days", round_down = FALSE)
[1] 489.4205
> age_at_date(my_date, units = "hours", round_down = FALSE)
[1] 11746.09
> age_at_date(my_date, units = "minutes", round_down = FALSE)
[1] 704765.5
> age_at_date(my_date, units = "seconds", round_down = FALSE)
[1] 42285930
Moohan commented 3 years ago

That's quite a nice implementation Nic.

@davidc92 I'd say the main reason would be to provide an obvious function to use i.e. "Use the one in phsmethods" whilst eeptools does do what we want I wasn't aware of it before now so I'd guess other people aren't either (seems to be the case from the original Teams thread too).