Voilà déjà 2-3 ans que je recommande à qui veut m'entendre le package lubridate
pour travailler avec des dates. En effet, travailler avec des dates, à la base, ce n'est pas évident. Des opérations a priori simples, comme ordonner les vecteurs, calculer des différences, arrondir des valeurs, peuvent devenir compliquées dès que le vecteur en question est de classe date
. Cela avait d'ailleurs fait l'objet de mon tout premier billet de blog (émotion! nostalgie!) que j'avais rédigé du temps où je ne connaissais pas encore lubridate
-ni ggplot2
, du reste-. Autant vous dire qu'à cette époque, travailler avec des dates, c'était carrément la louse.
Heureusement, le progrès ayant parfois du bon, le tidyverse
et lubridate
sont passés par là: maintenant, travailler avec des dates, c'est fingers in the nose. D'autant que RStudio a publié récemment une cheatsheet lubridate, qui, comme toujours, est très bien construite et illustrée.
Allez hop, commençons donc par charger le package:
library(lubridate)
Transformer une chaîne de caractères en date
Avant toute autre chose, les fonctions du package lubridate
facilitent de manière déconcertante la transformation d'un vecteur de classe chaîne de caractères en un vecteur de classe date.
Pour ce faire, il suffit d'indiquer quels sont les éléments renseignés dans la chaîne de caractères (et dans quel ordre). C'est le nom de la fonction qui remplit cet office.
Par exemple, je veux faire comprendre à R les éléments qui composent la chaîne de caractères "11 avril 2019": je vais donc lui indiquer que les éléments sont le jour (d comme day) le mois (m comme month) et l'année (y comme year):
"11 avril 2019"
## [1] "11 avril 2019"
class("11 avril 2019")
## [1] "character"
jourJ <- dmy("11 avril 2019")
class(jourJ)
## [1] "Date"
jourJ
## [1] "2019-04-11"
Vous avez vu? la transformation s'est faite sans problème (et à partir d'une date en français, s'il-vous-plaît!). Par ailleurs, la fonction dmy
aurait très bien pu s'accomoder de mises en formes différentes (par exemple "11th of April, 2019" ou "11/04/2019"), pour aboutir au même résultat.
Sur ce même principe, le package lubridate
comprend un grand nombre de fonctions capables de prendre en entrée dates et heures, selon des mises en formes variées. Note pour la suite: quand on a des vecteurs qui regroupent à la fois la date et l'heure (comme "11/04/2019 14h37" par exemple) on parle de "date-time".
Quelques exemples:
ymd("2019/04_11")
## [1] "2019-04-11"
ymd_hm("2019.04.11 14h37")
## [1] "2019-04-11 14:37:00 UTC"
ymd_hms("20190407143752")
## [1] "2019-04-07 14:37:52 UTC"
hms("14h37min52s")
## [1] "14H 37M 52S"
Récupérer les éléments des dates
Maintenant que l'on sait faire comprendre à R de quelle manière interpréter une chaîne de caractères en tant que date-time, on va pouvoir réaliser quelques opérations simples.
Pour commencer, on peut essayer d'isoler un des éléments de la date-time (juste l'année, ou juste le mois, ou juste l'heure, etc.).
Là encore, c'est à travers le nom de la fonction utilisée que l'on va spécifier quel est l'élément qui nous intéresse.
t <- ymd_hms("2019.04.11 14h37min52s")
date(t)
## [1] "2019-04-11"
hour(t)
## [1] 14
minute(t)
## [1] 37
second(t)
## [1] 52
Arrondir
On peut également arrondir une date, vers le haut (ceiling_date()
), vers le bas (floor_date()
), ou vers le plus proche (round_date()
):
t <- ymd_hms("2019.04.11 14h37min52s")
ceiling_date(t,"hour")
## [1] "2019-04-11 15:00:00 UTC"
floor_date(t,"hour")
## [1] "2019-04-11 14:00:00 UTC"
round_date(t,"hour")
## [1] "2019-04-11 15:00:00 UTC"
Evidemment, on peut choisir à quelle unité se fait cet arrondi:
t <- ymd_hms("2019.04.11 14h37min52s")
round_date(t,"minute")
## [1] "2019-04-11 14:38:00 UTC"
round_date(t,"hour")
## [1] "2019-04-11 15:00:00 UTC"
round_date(t,"day")
## [1] "2019-04-12 UTC"
round_date(t,"month")
## [1] "2019-04-01 UTC"
round_date(t,"year")
## [1] "2019-01-01 UTC"
Périodes ou durées
t1 <- dmy("17/07/2018")
t2 <- dmy("17/04/2019")
diff <- t2-t1
L'objet diff
nous renseigne sur la "différence de temps" entre t1 et t2. Il s'agit d'un objet de classe difftime
(classe qui n'est pas spécifiquement liée à l'usage de lubridate
).
Cette "différence de temps" peut être traitée de différentes manières par lubridate
. On peut en effet considérer cette différence en terme de période ou en terme de durée.
On le spécifie de la manière suivante:
as.duration(diff)
## [1] "23673600s (~39.14 weeks)"
as.period(diff)
## [1] "274d 0H 0M 0S"
Bon, à ce stade, à part en terme d'affichage, on ne voit pas forcément bien la nuance entre les deux... L'idée, c'est que la durée correspond plutôt à une différence de temps "physique" (c'est à dire le nombre exact de secondes correspondant à un intervalle de temps) tandis que la période correspond à une différence de temps "sociale".
Par exemple, quand Patrick Bruel dit "On s'était dit rendez-vous dans 10 ans, même jour même heure, même pomme", on peut supposer qu'il parle d'un écoulement "social" du temps.
Ainsi, partant d'une prise de rendez-vous le 4/02/1991, on arriverait à un prochain rendez-vous le:
t0=dmy_h("4/02/1991 21h")
t0+years(10)
## [1] "2001-02-04 21:00:00 UTC"
soit, également, un 4/02. Alors que si on avait voulu que 10 ans stricto sensu se soient écoulés, cela aurait abouti à un rendez-vous quelques jours plus tôt, du fait des années bissextiles ayant eu lieu dans l'intervalle:
t0+dyears(10)
## [1] "2001-02-01 21:00:00 UTC"
Calculs arithmétiques avec des périodes ou durées
Dans la partie précédente, nous avons déjà vu qu'il était possible de réaliser des opérations arithmétiques sur des dates. Quelle que soit l'opération réalisée, il faut bien garder en tête la distinction entre période et durée! Les périodes correspondent au fonctions xxx()
(par exemple days()
ou months()
) tandis que les durées correspondent aux fonctions dxxx()
(par exemple ddays()
ou dyears()
)
t1+months(9) # t1 + 9 mois
## [1] "2019-04-17"
t1+ddays(268) # t1 + exactement 268 jours
## [1] "2019-04-11"
ddays(268)/dweeks(1) # combien de semaines (exactement) pour 268 jours?
## [1] 38.28571
t2-dweeks(3) # t2 - (exactement) 3 semaines
## [1] "2019-03-27"
Notez que ces fonctions vous permettent également de créer des séries à intervalle de temps régulier:
t1+months(1:9)
## [1] "2018-08-17" "2018-09-17" "2018-10-17" "2018-11-17" "2018-12-17" "2019-01-17" "2019-02-17" "2019-03-17" "2019-04-17"
now()+minutes(seq(0,30,by=10))
## [1] "2018-09-10 11:29:33 CEST" "2018-09-10 11:39:33 CEST" "2018-09-10 11:49:33 CEST" "2018-09-10 11:59:33 CEST"
Intervalles de temps
Une autre manière d'envisager l'analyse d'un jeu de données comprenant des dates est de travailler sur des intervalles de temps.
Pour transformer deux dates en intervalle de temps avec lubridate
, on a deux solutions (la fonction interval()
, ou l'opérateur %--%
. Dans les deux cas, on obtient le même résultat:
itv <- interval(t1,t2)
itv <- t1 %--% t2
itv
## [1] 2018-07-17 UTC--2019-04-17 UTC
Disposer d'un intervalle, cela permet de réaliser certaines opérations, comme (par exemple) déterminer si une date (ou une "date-time") donnée fait partie de l'intervalle:
Noel <- dmy("25/12/2018")
Noel %within% itv
## [1] TRUE
sitv=int_diff(t1+months(1:9))
Une des opérations à mon sens les plus utiles en lien avec ces intervalles, c'est ainsi de permettre de replacer l'occurrence d'un ou plusieurs événements dans des intervalles de temps:
sitv
## [1] 2018-08-17 UTC--2018-09-17 UTC 2018-09-17 UTC--2018-10-17 UTC 2018-10-17 UTC--2018-11-17 UTC 2018-11-17 UTC--2018-12-17 UTC 2018-12-17 UTC--2019-01-17 UTC 2019-01-17 UTC--2019-02-17 UTC 2019-02-17 UTC--2019-03-17 UTC 2019-03-17 UTC--2019-04-17 UTC
Noel %within% sitv
## [1] FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE
Conclusion
Je suis loin d'avoir couvert toutes les possibilités de lubridate
(par exemple je n'ai rien dit sur les fuseaux horaires, sur les formats date
et time
qui correspondent à un nombre de jours), mais j'espère néanmoins vous avoir convaincu (si le besoin s'en faisait ressentir) qu'on ne saurait s'en passer pour travailler sur des données temporelles!
9 Comments
PB
Super! Je me sentirai moins seul!
Gina
il y a un message codé dans l'illustration ? 😉
lvaudor
Haha, chut! Personne n'a l'air d'avoir tilté (enfin, parmi ceux qui peuvent lire le blog mais ne peuvent pas me voir de profil)! C'était un test pour voir si mes lecteurs me lisaient attentivement (il y a des indices aussi dans le contenu du billet lui-même). Hé ben, pas bravo les lecteurs 😛 !
Nono
Bonjour,
Lorsque que je manipule des dates avec R, il ne me donne pas celles-ci sous le bon format :
"0015-01-06 LMT"
Je ne sais pas ce que signifie "LMT" et comment le modifier (en UTC).
Car les années, comme vous pouvez le voir, sont "0015" au lieu de 2015.
Le tout correctement formaté dans le fichier source puisque sur d'autre machine cela fonctionne.
Pouvez-vous m'aider à rectifier ce formatage ?
lvaudor
Bonjour,
Il me manque des éléments importants pour pouvoir peut-être répondre. Est-ce que le "mauvais format" apparaît au moment de l'import de la table? Au moment où vous utilisez une fonction de lubridate? Comment sont-elles dans le fichier source?
Ben
Bonjour,
Merci pour vos explications.
Question: Je veux coder la variable saison de l'année à partir des dates ( Jour et mois).
Exemple: hiver: 21-12 à 19-03
Comment puis-je procéder puisque je ne peux pas extraire simultanément le jour et le mois?
lvaudor
Bonjour,
Pour une date en format jour-mois vous pouvez voir des solutions ici https://stackoverflow.com/questions/62269705/extract-only-day-and-month-with-lubridate-in-r-and-conserve-the-date-format. Ensuite il faudra définir un facteur "saison" à partir de ces dates en format jour-mois.
Pour une définition des saisons un peu plus "lâche" (i.e. à partir du mois seulement: janvier-février-mars=hiver, etc.) vous pouvez voir ici que cela a fait l'objet de discussions pour l'équipe de développement de lubridate (il y a des "solutions" dans le thread). https://github.com/tidyverse/lubridate/issues/611.
cozette
Bonjour,
Merci pour cette page à laquelle je me réfère dès que j'ai besoin du package !
Petite question, je souhaite ajouter des durées
ex : 2 jours, 3 heures, 4 minutes + 5 jours, 8 heures, 2 minutes + etc
Je souhaite additionner les durées lorsqu'elles concernent le même mois ...
Est-ce possible ?
lvaudor
👍