42

I would like to use lubridate to calculate age in years given their date of birth and today's date. Right now I have this:

library(lubridate)
today<-mdy(08312015)
dob<-mdy(09071982)
today-dob

which gives me their age in days.

Ignacio
  • 7,646
  • 16
  • 60
  • 113
  • Is dividing by 365.25 not accurate enough? – lenz Aug 31 '15 at 13:56
  • 1
    Dividing by 365.25. Or maybe use `year(today)-year(dob)`. But this just subtracts year 1 minus year 2. – phiver Aug 31 '15 at 13:57
  • Yes, but `(today-dob)/365.25` gives me `Time difference of 32.98015 days` instead of years – Ignacio Aug 31 '15 at 13:58
  • 6
    what you're seeing is really just a label. I often find it easier to change the class of the result: `as.numeric((today-dob)/365.25)`. And for a very minor increase in precision, divide by 365.2425. – Benjamin Aug 31 '15 at 14:06
  • 2
    Note that using `today - dob` is not really the lubridate way to go, but uses basic R functionality (`difftime`). See my answer for a `lubridate` approach. – Paul Hiemstra Aug 31 '15 at 14:29

6 Answers6

64

This is the lubridate approach I would take:

interval(dob, today) / years(1)

Yields the answer of 32 years.

Note that the function will complain that it cannot express the remainder of the fraction of the year. This is because year is not a fixed concept, i.e. 366 in leap years and 365 in non-leap years. You can get an answer with more detail in regard to the number of weeks and days:

interval_period = interval(dob, today)
full_year = interval_period %/% years(1)
remaining_weeks = interval_period %% years(1) %/% weeks(1)
remaining_days = interval_period %% years(1) %% weeks(1) %/% days(1)
sprintf('Your age is %d years, %d weeks and %d days', full_year, remaining_weeks, remaining_days)
# [1] "Your age is 32 years, 51 weeks and 1 days"

Note that I use %/% for division and %% as modulo to get the remaining weeks/days after subtracting the full years/weeks.

llrs
  • 3,308
  • 35
  • 68
Paul Hiemstra
  • 59,984
  • 12
  • 142
  • 149
  • 2
    Thanks, this does what I want. Note `new_interval` is deprecated, use `interval` instead – dpel Feb 26 '16 at 10:02
  • Please also note 'new_interval' is deprecated; use 'interval' instead. Deprecated in version '1.5.0'. – amonk Jun 02 '17 at 15:13
  • What's the difference between new_interval(dob, today) and dob-today and as.period(today - dob, unit = "years")? – skan Dec 20 '17 at 15:34
  • 1
    `new_interval` is deprecated since v1.5.0, now it's just `interval`. – MS Berends Oct 31 '18 at 14:44
13

This is an old question, but I still missing the following clean approach. (Tidyverse is only necessary for the %>% operator.)

library(tidyverse)
library(lubridate)

today<-mdy(08312015)
dob<-mdy(09071982)

interval(dob, today) %>%
  as.numeric('years')

# 32.98015 - you have to decide how to deal with the fraction of a year
ah bon
  • 9,293
  • 12
  • 65
  • 148
ACNB
  • 816
  • 9
  • 18
  • This is really neat. Would you mind explaining how `as.numeric("years")` work over the output of `interval()`? – Emman Nov 02 '21 at 11:59
6
as.duration(interval(dob,today)) %/% as.duration(years(1))

should do the job without errors.

Shota Shimizu
  • 121
  • 2
  • 8
  • 2
    Thank you for this code snippet, which may provide some immediate help. A proper explanation would [greatly improve](https://meta.stackexchange.com/questions/114762/explaining-entirely-code-based-answers) its educational value by showing why this is a good solution to the problem, and would make it more useful to future readers with similar, but not identical, questions. Please edit your answer to add explanation, and give an indication of what limitations and assumptions apply. – basvk Jul 04 '17 at 11:41
4
as.period(today - dob, unit = "years")

This will give a message that it's only an estimate because it doesn't take into account the exact starting date and end date.

misspelled
  • 306
  • 1
  • 5
3

Another Tidyverse approach (with the shortest amount of code) would be

library(tidyverse)
library(lubridate)

today<-mdy(08312015)
dob<-mdy(09071982)

dob %--% today / ddays(365.25)

James
  • 459
  • 2
  • 14
  • `dob %--% today / ddays(365))` is 33.00274, which is inaccurate (32 years, 52 weeks and 1 day). `dob %--% today / years(1)` is 32.98082 which is accurate (and also shorter code :-) ). Also you have a extra parenthesis in you example. – Jeff Parker Jun 15 '21 at 21:35
  • Thanks for the paranthesis...I've removed...so it depends how you define a year: in a leap year, you'll have 366 days; in a normal year, you'll have 365.25. I added the .25 in which case, we get the same answer. I do like the years approach though...and yes, I guess, technically shorter:) – James Jun 15 '21 at 21:43
0

Another answer, it's much faster. See speed test below

as.numeric(today - dob) / 365.25

Comparing all the answers

library(dplyr)
library(lubridate)

today<-mdy(08312015)
dob<-mdy(09071982)

interval(dob, today) / years(1)
> 32.98082

as.duration(interval(dob,today)) %/% as.duration(years(1))
> 32

interval(dob, today) %>% as.numeric('years')
> 32.98015

dob %--% today / ddays(365.25)
> 32.98015

as.numeric(today - dob) / 365.25
> 32.98015

I'm not sure whether 32.98082 or 32.98015 is more correct. See https://stackoverflow.com/a/32313487/4745348

Speed test

microbenchmark::microbenchmark(
  interval(dob, today) / years(1),
  as.duration(interval(dob,today)) %/% as.duration(years(1)),
  interval(dob, today) %>% as.numeric('years'),
  dob %--% today / ddays(365.25),
  as.numeric(today - dob) / 365.25
)

> Unit: microseconds
>                                                       expr      min        lq       mean    median       uq      max neval
>                              interval(dob, today)/years(1) 1913.601 1996.1510 2172.96001 2059.1005 2102.851 6037.201   100
>  as.duration(interval(dob, today))%/%as.duration(years(1))  749.700  799.1010  912.30394  823.1510  863.751 5078.601   100
>               interval(dob, today) %>% as.numeric("years")  439.701  464.0510  485.31708  480.3010  501.101  591.000   100
>                               dob %--% today/ddays(365.25)  394.501  427.5510  450.37502  443.7010  463.301  620.601   100
>                             as.numeric(today - dob)/365.25   17.400   25.9005   30.66293   32.7515   36.151   52.700   100
Anthony Ebert
  • 675
  • 14
  • 25