La version originale de cette astuce a été publiée par Joseph Larmarange sur http://joseph.larmarange.net/?Calculer-proprement-un-age-sous-R.

Le calcul d’un âge sous R n’est pas forcément aussi trivial qu’il n’y parait.

Rappel sur les âges

Il convient en premier lieu de rappeler les principaux âges utilisés les démographes :

L’âge – on précise parfois âge chronologique – est une des caractéristiques fondamentales de la structure des populations. On l’exprime généralement en années, ou en années et mois, voire en mois et jours, pour les enfants en bas âge ; parfois en années et fractions décimales d’année. Les démographes arrondissent d’ordinaire l’âge à l’unité inférieure, l’exprimant ainsi en années révolues, ou années accomplies, le cas échéant en mois révolus, ou mois accomplis. Cet âge est aussi l’âge au dernier anniversaire. On trouve aussi, dans les statistiques, l’âge atteint dans l’année, qui est égal à la différence de millésimes entre l’année considérée et l’année de naissance. […] On est parfois conduit à préciser que l’on considère un âge exact, pour éviter toute confusion avec un âge en années révolues, qui représente en fait une classe d’âges exacts.

Le package lubridate

Le package lubridate est spécialement développé pour faciliter la manipulation et le calcul autour des dates. La version de développement intègre depuis peu une fonction time_length adaptée au calcul des âges exacts.

La fonction time_length étant récente, elle n’est pas encore disponible dans la version stable du package. Pour installer la version de développement de lubridate, on aura recours au package devtools :

library(devtools)
install_github("tidyverse/lubridate")

Nous noterons naiss la date de naissance et evt la date à laquelle nous calculerons l’âge.

Calcul d’un âge exact

On appelle âge exact l’expression d’un âge avec sa partie décimale.

Une approche simple consiste à calculer une différence en jours puis à diviser par 365. Or, le souci c’est que toutes les années n’ont pas le même nombre de jours. Regardons par exemple ce qui se passe si l’on calcule l’âge au 31 décembre 1999 d’une personne née le 1er janvier 1900.

library(lubridate)

Attaching package: 'lubridate'
The following object is masked from 'package:base':

    date
naiss <- ymd("1900-01-01")
evt <- ymd("1999-12-31")
time_length(interval(naiss, evt), "days")
[1] 36523
time_length(interval(naiss, evt), "days")/365
[1] 100.063

Or, au 31 décembre 1999, cette personne n’a pas encore fêté son centième anniversaire. Le calcul précédent ne prend pas en compte les années bissextiles. Une approche plus correcte serait de considérer que les années durent en moyenne 365,25 jours.

time_length(interval(naiss, evt), "days")/365.25
[1] 99.99452

Si cette approche semble fonctionner avec cet exemple, ce n’est plus le cas dans d’autres situations.

evt <- ymd("1903-01-01")
time_length(interval(naiss, evt), "days")/365.25
[1] 2.997947

Or, à la date du premier janvier 1903, cette personne a bien fêté son troisième anniversaire.

Pour calculer proprement un âge en années (ou en mois), il est dès lors nécessaire de prendre en compte la date anniversaire et le fait que la durée de chaque année (ou mois) est variable. C’est justement ce que fait la fonction time_length appliquée à un objet de type Interval. On détermine le dernier et le prochain anniversaire et l’on rajoute, à l’âge atteint au dernier anniversaire, le ratio entre le nombre de jours entre l’événement et le dernier anniversaire par le nombre de jours entre le prochain et le dernier anniversaire.

naiss <- ymd("1900-01-01")
evt <- ymd("1999-12-31")
time_length(interval(naiss, evt), "years")
[1] 99.99726
evt <- ymd("1903-01-01")
time_length(interval(naiss, evt), "years")
[1] 3
evt <- ymd("1918-11-11")
time_length(interval(naiss, evt), "years")
[1] 18.86027

Attention, cela n’est valable que si l’on présente à la fonction time_length un objet de type Interval (pour lequel on connait dès lors la date de début et la date de fin). Si l’on passe une durée (objet de type Duration) à la fonction time_length, le calcul s’effectuera alors en prenant en compte la durée moyenne d’une année (plus précisément 365 jours).

naiss <- ymd("1900-01-01")
evt <- ymd("1999-12-31")
time_length(interval(naiss, evt), "years")
[1] 99.99726
time_length(evt - naiss, "years")
[1] 100.063
time_length(as.duration(interval(naiss, evt)), "years")
[1] 100.063

Cas particulier des personnes nées un 29 février

Pour les personnes nées un 29 février, il existe un certain flou concernant leur date d’anniversaire pour les années non bissextiles. Doit-on considérer qu’il s’agit du 28 février ou du 1er mars ?

Au sens strict, on peut considérer que leur anniversaire a lieu entre le 28 février soir à minuit et le 1er mars à 0 heure du matin, autrement dit que le 28 février ils n’ont pas encore fêté leur anniversaire. C’est la position adoptée par la fonction time_length.

naiss <- ymd("1992-02-29")
evt <- ymd("2014-02-28")
time_length(interval(naiss, evt), "years")
[1] 21.99726
evt <- ymd("2014-03-01")
time_length(interval(naiss, evt), "years")
[1] 22

Cette approche permets également d’être cohérent avec la manière dont les dates sont prises en compte informatiquement. On considère en effet que lorsque seule la date est précisée (sans mention de l’heure), l’heure correspondante est 0:00. Autrement dit, "2014-03-01" est équivalent à "2014-03-01 00:00:00". L’approche adoptée permet donc d’être cohérent lorsque l’anniversaire est calculé en tenant compte des heures.

naiss <- ymd("1992-02-29")
evt <- ymd_hms("2014-02-28 23:00:00")
time_length(interval(naiss, evt), "years")
[1] 21.99989
evt <- ymd_hms("2014-03-01 00:00:00")
time_length(interval(naiss, evt), "years")
[1] 22
evt <- ymd_hms("2014-03-01 01:00:00")
time_length(interval(naiss, evt), "years")
[1] 22.00011
naiss <- ymd_hms("1992-02-29 12:00:00")
evt <- ymd_hms("2014-03-01 01:00:00")
time_length(interval(naiss, evt), "years")
[1] 22.00011

Âge révolu ou âge au dernier anniversaire

Une fois que l’on sait calculer un âge exact, le calcul d’un âge révolu ou âge au dernier anniversaire est assez simple. Il suffit de ne garder que la partie entière de l’âge exact (approche conseillée).

naiss <- ymd("1980-01-09")
evt <- ymd("2015-01-01")
time_length(interval(naiss, evt), "years")
[1] 34.97808
trunc(time_length(interval(naiss, evt), "years"))
[1] 34

Une autre approche consiste à convertir l’intervalle en objet de type Period et à ne prendre en compte que les années.

as.period(interval(naiss, evt))
[1] "34y 11m 23d 0H 0M 0S"
as.period(interval(naiss, evt))@year
[1] 34

Âge par différence de millésimes

L’âge par différence de millésimes, encore appelé âge atteint dans l’année, s’obtient tout simplement en soustrayant l’année de naissance à l’année de l’événement.

naiss <- ymd("1980-01-09")
evt <- ymd("2015-01-01")
year(evt) - year(naiss)
[1] 35

Calcul d’un âge moyen

Le calcul d’un âge moyen s’effectue normalement à partir d’âges exacts. Il arrive fréquemment que l’on ne dispose dans les données d’enquêtes que de l’âge révolu. Auquel cas, il faut bien penser à rajouter 0,5 au résultat obtenu. En effet, un âge révolu peut être vu comme une classe d’âges exacts : les individus ayant 20 ans révolus ont entre 20 et 21 ans exacts, soit en moyenne 20,5 ans !

Notes

L’ensemble des fonctions présentées peuvent être appliquées à des vecteurs et, par conséquent, aux colonnes d’un tableau de données (data.frame).

Le package eeptools fournit de son côté une fonction age_calc1 qui permet le calcul des âges exacts et révolus.

library(eeptools)
Loading required package: ggplot2
naiss <- as.Date("1980-01-09")
evt <- as.Date("2015-01-01")
age_calc(naiss, evt, units = "years", precise = TRUE)
[1] 34.97814
time_length(interval(naiss, evt), "years")
[1] 34.97808

La méthode utilisée par age_calc donne des résultats légèrement différent de ceux de time_length. Il est donc conseillé d’utiliser de préférence le package lubridate.

En l’absence du package lubridate, il reste facile de calculer une durée en jours avec les fonctions de base de R :

naiss <- as.Date("1900-01-01")
evt <- as.Date("1999-12-31")
evt - naiss
Time difference of 36523 days
as.integer(evt - naiss)
[1] 36523