Lubridate: datas no R

Introdução

Trabalhar com datas no R pode ser uma chateação. As funções do R base são, em geral, contraintuitivas e podem mudar de acordo com o tipo do objeto que você está usando. Além disso, características como fusos horários, anos bissextos, horários de verão, entre outras, podem não estar bem especificadas pelos métodos criados ou nem mesmo sendo levadas em conta.

O pacote lubridate, criado por Garrett Grolemund e Hadley Wickham, lida com esses problemas, fazendo o trabalho com datas ser muito mais fácil.

Para instalar, siga uma das opções abaixo:

# Instale ao lado de outros pacotes do tidyverse.
install.packages("tidyverse")

# Instale apenas o lubridate.
install.packages("lubridate")

# Instale a versão de desenvolvimento direto do Github.
devtools::install_github("tidyverse/lubridate")

# Se você não tiver o pacote devtools instalado...
install.packages("devtools")

Para carregar o pacote, basta rodar

library(lubridate)
## Warning: package 'lubridate' was built under R version 3.4.4
library(magrittr) # para ter o operador %>%
library(tibble) # para ter a função as_tibble
library(dplyr) # para ter a função mutate
## Warning: package 'dplyr' was built under R version 3.4.4

Neste seção, falaremos sobre:

  • transformar e extrair datas;
  • algumas funções úteis para trabalhar com datas;
  • trabalhar com fusos horários; e
  • operações aritméticas com datas.

A classe “date”

Datas no R são tratadas como um tipo especial de objeto, com classe date. Há várias formas de criar objetos dessa classe:

data_string <- "21-10-2015"
class(data_string)
## [1] "character"

data_date <- date(data_string)
class(data_date)
## [1] "Date"

data_as_date <- as_date(data_string)
## Warning: All formats failed to parse. No formats found.
class(data_as_date)
## [1] "Date"

data_mdy <- dmy(data_string)
class(data_mdy)
## [1] "Date"

Veja que as funções date() e as_date() converteram a string para data, mas não devolveram o valor esperado. A função dmy() resolve esse problema estabelecendo explicitamente a ordem dia-mês-ano. Existem funções para todas as ordens possíveis: dmy(), mdy(), myd(), ymd(), ydm() etc.

As funções date() e as_date() assumem que a ordem segue o padrão da língua inglesa: ano-mês-dia (ymd).

date("2015-10-21")
## [1] "2015-10-21"
as_date("2015-10-21")
## [1] "2015-10-21"

Uma grande facilidade que essas funções trazem é poder criar objetos com classe date a partir de números e strings em diversos formatos.

dmy(21102015)
## [1] "2015-10-21"
dmy("21102015")
## [1] "2015-10-21"
dmy("21/10/2015")
## [1] "2015-10-21"
dmy("21.10.2015")
## [1] "2015-10-21"

Se além da data você precisar especificar o horário, basta usar as funções do tipo ymd_h(), ymd_hm(), ymd_hms(), sendo que também há uma função para cada formato de dia-mês-ano.

ymd_hms(20151021165411)
## [1] "2015-10-21 16:54:11 UTC"

Funções úteis

O lubridate traz diversas funções para extrair os componentes de um objeto da classe date.

  • second() - extrai os segundos.
  • minute() - extrai os minutos.
  • hour() - extrai a hora.
  • wday() - extrai o dia da semana.
  • mday() - extrai o dia do mês.
  • month() - extrai o mês.
  • year() - extrai o ano.

Os nomes são tão óbvios que a explicação do que cada função faz é praticamente desnecessária.

bday <- ymd_hms(19890729030142)
bday
## [1] "1989-07-29 03:01:42 UTC"

second(bday)
## [1] 42
day(bday)
## [1] 29
month(bday)
## [1] 7
year(bday)
## [1] 1989

wday(bday)
## [1] 7
wday(bday, label = TRUE)
## [1] Sat
## Levels: Sun < Mon < Tue < Wed < Thu < Fri < Sat

Você também pode usar essas funções para atribuir esses componentes a uma data:

data <- dmy(01041991)
data
## [1] "1991-04-01"

hour(data) <- 20
data
## [1] "1991-04-01 20:00:00 UTC"

Também existem funções para extrair a data no instante da execução.

today() 
## [1] "2018-08-17"
now()
## [1] "2018-08-17 20:10:05 -03"

# Data e horário do dia em que essa página foi editada pela última vez.

Fusos horários

Uma característica inerente das datas é o fuso horário. Se você estiver trabalhando com datas de eventos no Brasil e na Escócia, por exemplo, é preciso verificar se os valores seguem o mesmo fuso horário. Além disso, quando a data exata de um evento for relevante, você pode precisar converter horários para outros fusos para comunicar seus resultados em outros países.

Para fazer essas coisas, o lubridate fornece as funções with_tz() e force_tz(). Veja um exemplo de como usá-las.

estreia_GoT <- ymd_hms("2017-07-16 22:00:00", tz = "America/Sao_Paulo")
estreia_GoT
## [1] "2017-07-16 22:00:00 -03"

# Devolve qual seria a data em outro fuso

with_tz(estreia_GoT, tzone = "GMT")
## [1] "2017-07-17 01:00:00 GMT"
with_tz(estreia_GoT, tzone = "US/Alaska")  
## [1] "2017-07-16 17:00:00 AKDT"

# Altera o fuso sem mudar a hora

force_tz(estreia_GoT, tzone = "GMT")
## [1] "2017-07-16 22:00:00 GMT"

Operações com datas

O pacote lubridate possui ainda funções para calcular intervalos e fazer operações aritméticas com datas.

Intervalos

Intervalos podem ser salvos em objetos com classe interval.

inicio <- dmy("01-04-1991")
evento <- dmy("31-10-1993")

sobrev <- interval(inicio, evento)
sobrev
## [1] 1991-04-01 UTC--1993-10-31 UTC
class(sobrev)
## [1] "Interval"
## attr(,"package")
## [1] "lubridate"

Você pode verificar se dois intervalos tem intersecção com a função int_overlaps().

# Outra forma de definir um intervalo: o operador %--%
intervalo_1 <- dmy("01-02-2003") %--% dmy("02-03-2005")  

intervalo_2 <- dmy("04-05-2004") %--% dmy("12-03-2012")  

int_overlaps(intervalo_1, intervalo_2)
## [1] TRUE

Aritmética com datas

Veja alguns exemplos de operações aritméticas que você pode fazer utilizando funções do lubridate:


# Somando datas

data <- dmy(31012000)

data + ddays(1)
## [1] "2000-02-01"
data + dyears(1)
## [1] "2001-01-30"

data + months(1)  # Operações que resultam em datas inválidas geram NAs.
## [1] NA

# Criando datas recorrentes

reuniao <- dmy("18-03-2017")
reunioes <- reuniao + weeks(0:10)
reunioes
##  [1] "2017-03-18" "2017-03-25" "2017-04-01" "2017-04-08" "2017-04-15"
##  [6] "2017-04-22" "2017-04-29" "2017-05-06" "2017-05-13" "2017-05-20"
## [11] "2017-05-27"

# Duração de um intervalo 

intervalo <-dmy("01-03-2003") %--% dmy("31-03-2003") 

intervalo / ddays(1)               # Número de dias
## [1] 30
intervalo / dminutes(1)            # Número de minutos
## [1] 43200

as.period(intervalo)
## [1] "30d 0H 0M 0S"

Para mais informações sobre olubridate, visite o vignette do pacote.

Exercícios

Nos exercícios a seguir, vamos utilizar a base lakers, que contém estatísticas jogo a jogo do Los Angeles Lakers na temporada 2008-2009.

lakers %>% as_tibble()
## # A tibble: 34,624 x 13
##      date opponent game_type time  period etype team  player result points
##     <int> <chr>    <chr>     <chr>  <int> <chr> <chr> <chr>  <chr>   <int>
##  1 2.01e7 POR      home      12:00      1 jump… OFF   ""     ""          0
##  2 2.01e7 POR      home      11:39      1 shot  LAL   Pau G… missed      0
##  3 2.01e7 POR      home      11:37      1 rebo… LAL   Vladi… ""          0
##  4 2.01e7 POR      home      11:25      1 shot  LAL   Derek… missed      0
##  5 2.01e7 POR      home      11:23      1 rebo… LAL   Pau G… ""          0
##  6 2.01e7 POR      home      11:22      1 shot  LAL   Pau G… made        2
##  7 2.01e7 POR      home      11:22      1 foul  POR   Greg … ""          0
##  8 2.01e7 POR      home      11:22      1 free… LAL   Pau G… made        1
##  9 2.01e7 POR      home      11:00      1 foul  LAL   Vladi… ""          0
## 10 2.01e7 POR      home      10:53      1 shot  POR   LaMar… made        2
## # ... with 34,614 more rows, and 3 more variables: type <chr>, x <int>,
## #   y <int>

1.

Repare que a coluna date no data.frame é um vetor de inteiros. Transforme essa coluna em um vetor de valores com classe date.


2.

Crie uma coluna que junte as informações de data e tempo de jogo (colunas date e time) em objetos da classe date.


3.

Crie as colunas dia, mês e ano com as respectivas informações sobre a data do jogo.


4.

Em média, quanto tempo o Lakers demora para arremessar a primeira bola no primeiro período?

Dica: arremessos são representados pela categoria shot da coluna etype.


5.

Em média, quanto tempo demora para sair a primeira cesta de três pontos? Considere toda a base, e cestas de ambos os times.


6.

Construa boxplots do tempo entre pontos consecultivos para cada períodos. Considere toda a base de dados e apenas pontos do Lakers.


7.

Qual foi o dia e mês do jogo que o Lakers demorou mais tempo para fazer uma cesta? Quanto tempo foi?


Respostas

Nota

Não há apenas uma maneira de resolver os exercícios. Você pode encontrar soluções diferentes das nossas, algumas vezes mais eficientes, outras vezes menos. Quando estiver fazendo suas análises, tente buscar o equilíbrio entre eficiência e praticidade. Economizar 1 hora com a execução do código pode não valer a pena se você demorou 2 horas a mais para programá-lo.


1.

Crie uma nova coluna no data.frame com as datas no formato dia-mês-ano.

lakers %>% 
  mutate(date = ymd(date))

Repare que as_date() não funciona neste caso.

lakers %>%
  mutate(date = as_date(date))

Para entender porque a função devolveu um dia do ano 56949, rode os códigos abaixo.

as_date(0)
as_date(-3:3)
as_date(20081027:20081029)

2.

Crie uma coluna que junte as informações de data e hora (colunas date e time) em objetos da classe date.

lakers %>% 
  mutate(date_time = paste0(date, " 00:", time) %>% ymd_hms) %>% 
  select(date_time)

3.

Crie as colunas dia, mes e ano com as respectivas informações sobre a data do jogo.

lakers %>%
  mutate(date = ymd(date),
         dia = day(date),
         mes = month(date),
         ano = year(date)) %>% 
  select(date, dia, mes, ano)

4.

Em média, quanto tempo o Lakers demora para arremessar a primeira bola no primeiro período?

Dicas: arremessos são representados pela categoria shot da coluna etype e cada período tem 12 minutos.

lakers %>% 
  dplyr::filter(etype == "shot", period == 1, team == "LAL") %>% 
  dplyr::mutate(time = hms(paste0("00:", time)),
                cronometro = 12*60 - minute(time)*60 - second(time)) %>% 
  dplyr::group_by(date) %>% 
  dplyr::filter(cronometro == min(cronometro)) %>% 
  dplyr::ungroup() %>% 
  dplyr::summarise(media = mean(cronometro))

O que foi feito:

  • Primeiro filtramos a base para arremessos, etype == "shot", do primeiro período, period ==, que fossem do Lakers, team == "LAL".

  • Em seguida, mudamos a classe da coluna time, de character para period, e criamos a coluna cronometro, que contém o tempo passado (em segundos) até o instante do evento.

  • Então agrupamos a base pelo dia e a filtramos apenas para o primeiro evento de cada dia, isto é, o evento que tem o menor valor na coluna cronometro. Assim, a coluna cronometro da base resultante terá o tempo do primeiro arremesso de cada jogo.

  • Por fim, desagrupamos a base e calculamos a média da coluna cronometro.


5.

Em média, quanto tempo demora para sair a primeira cesta de três pontos em cada um dos quatro períodos? Considere toda a base, e cestas de ambos os times.

lakers %>% 
  dplyr::filter(etype == "shot", period %in% 1:4, points == "3") %>% 
  dplyr::mutate(time = hms(paste0("00:", time)),
                cronometro = 12*60 - minute(time)*60 - second(time)) %>% 
  dplyr::group_by(date, period) %>% 
  dplyr::filter(cronometro == min(cronometro)) %>% 
  dplyr::ungroup() %>%
  dplyr::group_by(period) %>% 
  dplyr::summarise(media = mean(cronometro))

A resolução desse exercício é análoga ao anterior, só mudamos o filtro inicial e o agrupamento. Se você ficou com dúvidas, consulte o passo a passo na resolução do exercício 4.

Repare que não precisarímos filtrar por etype == "shot", já que o único evento que gera 3 pontos é a cesta de 3 pontos.


6.

Construa boxplots do tempo entre pontos consecultivos para cada períodos. Considere toda a base de dados e apenas pontos do Lakers.


7.

Qual foi o dia e mês do jogo que o Lakers demorou mais tempo para fazer uma cesta? Quanto tempo foi?


Nossa Newsletter

Uma vez por semana enviamos um e-mail para você não perder nenhum post da Curso-R. Avisamos também sempre que abrimos uma nova turma.