6  Manipulação de datas e textos

Autoria

No curso Introdução à análise de dados com R exploramos algumas funções de transformação de dados. Nesta aula, vamos explorar funções específicas para manipulação de datas e textos.

Caso você tenha dúvidas sobre o tema, recomendamos revisar as seguintes aulas:

6.1 Dados

Antes de iniciar o conteúdo, vamos baixar a base de dados que utilizaremos nesta aula. A base de dados voos_dez-2024-alterado.csv apresenta informações sobre voos realizados em dezembro de 2024 realizados no Brasil. Esses dados foram obtidos com o pacote {flightsbr}, e foram alterados para fins didáticos.

download.file(
  url = "https://github.com/ipeadata-lab/curso_r_intermediario_202501/raw/refs/heads/main/dados/voos_dez-2024-alterado.csv",
  destfile = "dados/voos_dez-2024-alterado.csv",
  mode = "wb")
1
URL do arquivo a ser baixado.
2
Caminho onde o arquivo que será salvo.

Vamos carregar o pacote {tidyverse}:

library(tidyverse)

Vamos importar a base de dados voos_dez-2024-alterado.csv e ver o resultado da função glimpse():

voos <- read_csv2("dados/voos_dez-2024-alterado.csv") 
glimpse(voos)
Rows: 82,003
Columns: 26
$ id_basica             <dbl> 33821878, 33972533, 33819746, 33819747, 33914237…
$ id_empresa            <dbl> 1002650, 1001070, 1007570, 1007570, 1000896, 100…
$ nm_empresa            <chr> "DHL AERO EXPRESO", "AEROVÍAS DE MÉXICO S.A DE C…
$ dt_referencia         <date> 2024-12-01, 2024-12-01, 2024-12-01, 2024-12-01,…
$ ds_di                 <chr> "INCLUSÃO DE ETAPA EM UM VOO PREVISTO EM HOTRAN"…
$ ds_grupo_di           <chr> "REGULAR", "REGULAR", "REGULAR", "REGULAR", "REG…
$ ds_natureza_etapa     <chr> "INTERNACIONAL", "INTERNACIONAL", "INTERNACIONAL…
$ hr_partida_real       <time> 13:35:00, 10:15:00, 05:34:00, 19:45:00, 20:24:0…
$ dt_partida_real       <chr> "01/12/2024", "01/12/2024", "01/12/2024", "30/11…
$ id_aerodromo_origem   <dbl> 467, 301, 301, 609, 66, 66, 301, 500, 301, 301, …
$ nm_aerodromo_origem   <chr> "MIAMI INTERNATIONAL AIRPORT", "GUARULHOS - GOVE…
$ nm_municipio_origem   <chr> "MIAMI, FLORIDA", "GUARULHOS", "GUARULHOS", "PUN…
$ sg_uf_origem          <chr> NA, "SP", "SP", NA, NA, NA, "SP", NA, "SP", "SP"…
$ nm_regiao_origem      <chr> NA, "SUDESTE", "SUDESTE", NA, NA, NA, "SUDESTE",…
$ nm_pais_origem        <chr> "ESTADOS UNIDOS DA AMÉRICA", "BRASIL", "BRASIL",…
$ nm_continente_origem  <chr> "AMÉRICA DO NORTE", "AMÉRICA DO SUL", "AMÉRICA D…
$ hr_chegada_real       <time> 22:46:00, 19:01:00, 11:42:00, 03:28:00, 05:42:0…
$ dt_chegada_real       <dbl> 45627, 45627, 45627, 45627, 45628, 45627, 45628,…
$ id_aerodromo_destino  <dbl> 162, 466, 609, 301, 301, 301, 500, 301, 66, 66, …
$ nm_aerodromo_destino  <chr> "VIRACOPOS", "BENITO JUAREZ INTL", "PUNTA CANA",…
$ nm_municipio_destino  <chr> "CAMPINAS", "MEXICO", "PUNTA CANA", "GUARULHOS",…
$ sg_uf_destino         <chr> "SP", NA, NA, "SP", "SP", "SP", NA, "SP", NA, NA…
$ nm_regiao_destino     <chr> "SUDESTE", NA, NA, "SUDESTE", "SUDESTE", "SUDEST…
$ nm_pais_destino       <chr> "BRASIL", "MÉXICO", "REPÚBLICA DOMINICANA", "BRA…
$ nm_continente_destino <chr> "AMÉRICA DO SUL", "AMÉRICA DO NORTE", "AMÉRICA C…
$ dt_sistema            <dttm> 2025-01-04 01:00:02, 2025-01-10 01:00:09, 2025-…

6.2 Manipulação de datas

6.2.1 Tipo Date

Quando escrevemos datas em português, é comum utilizarmos o formato dia/mês/ano (por exemplo: 20/01/2025). Ao criar uma variável com esta no R, teremos um objeto do tipo character (texto):

class("20/01/2025")
[1] "character"

Porém no R existe um tipo específico para datas, o Date. O tipo Date é útil em vários contextos: permite realizar operações específicas para datas, como calcular a diferença entre duas datas, extrair o dia da semana, alterar a formatação de datas em gráficos, entre outros.

Para que o R reconheça um objeto com o tipo Date, precisamos salvar as datas no formato ano-mês-dia (por exemplo: 2025-01-20), e utilizar a função as.Date() para transformar a variável do tipo character em Date.

Exemplo:

data_inicio_curso <- as.Date("2025-01-20")
data_inicio_curso
[1] "2025-01-20"
class(data_inicio_curso)
[1] "Date"

Porém, tome cuidado! Se você tentar transformar uma data no formato dia/mês/ano diretamente para Date, o R não irá reconhecer a data corretamente (e também não irá retornar um erro):

as.Date("20/01/2025")
[1] "20-01-20"

A função Sys.Date() retorna a data atual do sistema, com o tipo Date:

Sys.Date()
[1] "2025-02-23"
class(Sys.Date())
[1] "Date"

O tipo Date armazena o quantidade de dias desde 1 de janeiro de 1970. Podemos verificar isso utilizando a função as.numeric() para obter o número armazenado em uma variável do tipo Date. Por exemplo, a data 1 de janeiro de 1970:

data_1970 <- as.Date("1970-01-01")
data_1970
[1] "1970-01-01"
as.numeric(data_1970)
[1] 0

Um número negativo indica uma data anterior a 1 de janeiro de 1970, e um número positivo indica uma data posterior a essa data. Por exemplo, considerando a data de nascimento de Julia Lopes de Almeida (24 de setembro de 1862):

nascimento_julia_lopes <- as.Date("1862-09-24")

as.numeric(nascimento_julia_lopes)
[1] -39180

6.2.1.1 Exercício

  1. Crie uma variável chamada data_nascimento com sua data de nascimento e transforme-a para o tipo Date.

  2. Crie uma variável chamada data_atual com a data atual do sistema.

  3. Calcule a diferença entre a data atual e a sua data de nascimento. O que é retornado?

  1. Utilize a função Sys.time() e guarde o resultado em uma variável.
    1. O que essa função retorna?
    2. Qual é o tipo da variável?

6.2.2 Tipo POSIXct e POSIXt

O tipo POSIXct é uma extensão do tipo Date, que inclui informações de data, hora e fuso horário (em UTC: Coordinated Universal Time). O formato padrão para datas e horas é ano-mês-dia hora:minuto:segundo fuso_horario (por exemplo: 2025-01-20 15:30:00 -03).

Sys.time()
[1] "2025-02-23 01:19:32 UTC"
class(Sys.time())
[1] "POSIXct" "POSIXt" 

Esse tipo armazena o número de segundos desde 1 de janeiro de 1970, no fuso horário UTC +0 (que é igual ao GMT).

Nesta aula, vamos focar no tipo Date, mas é importante saber que o R possui funções específicas para manipulação de datas e horas (caso você precise trabalhar com isso).

6.2.2.1 Exercício

  1. Utilizando a base de dados voos (importada no início da aula), explore:
    1. Quais são as colunas que armazenam alguma data?
    2. Qual é o tipo de cada uma dessas colunas?
    3. Quais dessas precisariam ser transformadas para o tipo Date? Como você faria isso?

6.2.3 Tipo Date e bases de dados

Até agora, exploramos como criar variáveis do tipo Date a partir de valores que digitamos diretamente no código. Porém, em muitos casos, as datas estão armazenadas em bases de dados.

A rotina mais comum é importar a base de dados, e caso a variável de data esteja no formato character (texto), utilizamos alguma função para transformá-la em Date.

Vamos explorar trabalhar com as datas da base de dados voos. As colunas que armazenam datas iniciam com o prefixo dt_. Vamos selecionar essas colunas e observar as primeiras linhas:

voos_datas <- voos |> 
  select(starts_with("dt_"))

voos_datas |> 
  head()
1
Selecionamos apenas as colunas que iniciam com o prefixo dt_.
2
Exibimos as primeiras linhas da tabela.
# A tibble: 6 × 4
  dt_referencia dt_partida_real dt_chegada_real dt_sistema         
  <date>        <chr>                     <dbl> <dttm>             
1 2024-12-01    01/12/2024                45627 2025-01-04 01:00:02
2 2024-12-01    01/12/2024                45627 2025-01-10 01:00:09
3 2024-12-01    01/12/2024                45627 2025-01-02 01:00:59
4 2024-12-01    30/11/2024                45627 2025-01-02 01:00:59
5 2024-12-01    01/12/2024                45628 2025-01-09 01:00:08
6 2024-12-01    01/12/2024                45627 2025-01-09 01:00:08
  • Exercício: identifique as colunas que armazenam datas na base de dados voos.

Com o resultado, vamos observar quais colunas são datas e quais não são:

  • dt_referencia: O readr identificou que a coluna é uma data e já importou como <date>.

  • dt_partida_prevista: está como tipo <character>. O formato é dia/mês/ano. Precisamos transformar para <date>.

  • dt_chegada_real: está como <double>, em um formato numérico. Precisamos transformar para <date>.

  • dt_sistema: está como <dttm>, ou seja, do tipo POSIXct. Vamos manter como está.

6.2.3.1 Transformando variáveis de texto em datas

Como vimos anteriormente, existem funções para transformar variáveis de texto em datas. Vamos transformar a coluna dt_partida_real para o tipo Date.

Se a gente tentar transformar a coluna dt_partida_real diretamente para Date, o R não irá reconhecer a data corretamente (e também não irá retornar um erro):

voos_datas |> 
  select(dt_partida_real) |> 
  mutate(
    dt_partida_real_date = as.Date(dt_partida_real)
  )
# A tibble: 82,003 × 2
   dt_partida_real dt_partida_real_date
   <chr>           <date>              
 1 01/12/2024      1-12-20             
 2 01/12/2024      1-12-20             
 3 01/12/2024      1-12-20             
 4 30/11/2024      30-11-20            
 5 01/12/2024      1-12-20             
 6 01/12/2024      1-12-20             
 7 01/12/2024      1-12-20             
 8 01/12/2024      1-12-20             
 9 01/12/2024      1-12-20             
10 01/12/2024      1-12-20             
# ℹ 81,993 more rows

Então é importante lembrar que o R espera que a data esteja no formato ano-mês-dia para transformar em Date. Para isso, vamos utilizar a função as.Date() e especificar o formato da data, usando o argumento format:

voos_datas |> 
  select(dt_partida_real) |> 
  mutate(
    dt_partida_real_date = as.Date(dt_partida_real, format = "%d/%m/%Y")
  )
# A tibble: 82,003 × 2
   dt_partida_real dt_partida_real_date
   <chr>           <date>              
 1 01/12/2024      2024-12-01          
 2 01/12/2024      2024-12-01          
 3 01/12/2024      2024-12-01          
 4 30/11/2024      2024-11-30          
 5 01/12/2024      2024-12-01          
 6 01/12/2024      2024-12-01          
 7 01/12/2024      2024-12-01          
 8 01/12/2024      2024-12-01          
 9 01/12/2024      2024-12-01          
10 01/12/2024      2024-12-01          
# ℹ 81,993 more rows

No argumento format, precisamos apresentar como a data está formatada. Ou seja: em "%d/%m/%Y", %d representa o dia, %m representa o mês e %Y representa o ano.

Existe uma função do pacote lubridate que facilita a conversão de datas: a função dmy() (que significa “dia-mês-ano”) e similares, mas vamos apresentá-las mais adiante.

A função readr::parse_date() tem o mesmo propósito, porém ela apresenta alguns argumentos extras (como o locale). A documentação dessa função é muito útil para entender como formatar datas.

voos_datas |> 
  select(dt_partida_real) |> 
  mutate(
    dt_partida_real_date = parse_date(dt_partida_real, format = "%d/%m/%Y")
  )
# A tibble: 82,003 × 2
   dt_partida_real dt_partida_real_date
   <chr>           <date>              
 1 01/12/2024      2024-12-01          
 2 01/12/2024      2024-12-01          
 3 01/12/2024      2024-12-01          
 4 30/11/2024      2024-11-30          
 5 01/12/2024      2024-12-01          
 6 01/12/2024      2024-12-01          
 7 01/12/2024      2024-12-01          
 8 01/12/2024      2024-12-01          
 9 01/12/2024      2024-12-01          
10 01/12/2024      2024-12-01          
# ℹ 81,993 more rows

Exemplo de um uso onde o locale é necessário: quando a data está escrita por extenso, em um idioma diferente do inglês:

# Com as.Date(), fev não é reconhecido, pois em inglês é feb
as.Date("03/fev/2025", format = "%d/%b/%Y")
[1] "2025-02-03"
# Com o parse_date(), podemos informar o locale:
readr::parse_date("03/fev/2025",
                  format = "%d/%b/%Y",
                  locale = locale("pt"))
[1] "2025-02-03"

6.2.3.2 Transformando datas do Excel para Date

A coluna dt_chegada_real foi importada como número (Double).

Será que conseguimos transformar essa coluna para Date apenas usando a função as.Date()?

voos_datas |> 
  select(dt_chegada_real) |> 
  mutate(
    dt_chegada_real_date = as.Date(dt_chegada_real)
  )
# A tibble: 82,003 × 2
   dt_chegada_real dt_chegada_real_date
             <dbl> <date>              
 1           45627 2094-12-03          
 2           45627 2094-12-03          
 3           45627 2094-12-03          
 4           45627 2094-12-03          
 5           45628 2094-12-04          
 6           45627 2094-12-03          
 7           45628 2094-12-04          
 8           45627 2094-12-03          
 9           45628 2094-12-04          
10           45627 2094-12-03          
# ℹ 81,993 more rows

As datas convertidas estão no futuro (o que não faz sentido, já que os voos ocorreram em dezembro de 2024). O que aconteceu?

Essa coluna está salva em um padrão comum de dados do Excel: o número de dias desde 01/01/1900. Para transformar essa coluna em Date, precisamos informar ao R que a data de referência é 30/12/1899 (um dia antes de 01/01/1900), utilizando o argumento origin:

voos_datas |> 
  select(dt_chegada_real) |> 
  mutate(
    dt_chegada_real_date = as.Date(dt_chegada_real, origin = "1899-12-30")
  )
# A tibble: 82,003 × 2
   dt_chegada_real dt_chegada_real_date
             <dbl> <date>              
 1           45627 2024-12-01          
 2           45627 2024-12-01          
 3           45627 2024-12-01          
 4           45627 2024-12-01          
 5           45628 2024-12-02          
 6           45627 2024-12-01          
 7           45628 2024-12-02          
 8           45627 2024-12-01          
 9           45628 2024-12-02          
10           45627 2024-12-01          
# ℹ 81,993 more rows

Não é muito prático ter que lembrar a data de referência para transformar datas do Excel em Date. Por isso, o janitor possui a função excel_numeric_to_date() que faz essa conversão automaticamente:

voos_datas |> 
  select(dt_chegada_real) |> 
  mutate(
    dt_chegada_real_date = janitor::excel_numeric_to_date(dt_chegada_real)
  )
# A tibble: 82,003 × 2
   dt_chegada_real dt_chegada_real_date
             <dbl> <date>              
 1           45627 2024-12-01          
 2           45627 2024-12-01          
 3           45627 2024-12-01          
 4           45627 2024-12-01          
 5           45628 2024-12-02          
 6           45627 2024-12-01          
 7           45628 2024-12-02          
 8           45627 2024-12-01          
 9           45628 2024-12-02          
10           45627 2024-12-01          
# ℹ 81,993 more rows

6.2.3.3 Exercícios

  1. Observe os valores abaixo, e identifique em qual formato de data estão. Como você transformaria esses valores para o tipo Date?
    1. "2025-02-03"
    2. "03/02/2025"
    3. 20122
    4. 45691

6.2.4 Pacote lubridate

O pacote lubridate faz parte do pacote {tidyverse} do R, e tem funções que facilitam a manipulação de datas. Ele possui funções específicas para extrair informações de datas, como dia, mês, ano, semana, entre outras.

library(lubridate)

6.2.4.1 Funções de parse

Como citado anteriormente, quando queremos transformar uma data no formato dia/mês/ano para Date, podemos utilizar a função dmy() (que significa “dia-mês-ano”):

voos_datas |> 
  select(dt_partida_real) |> 
  mutate(
    dt_partida_real_date = dmy(dt_partida_real)
  )
# A tibble: 82,003 × 2
   dt_partida_real dt_partida_real_date
   <chr>           <date>              
 1 01/12/2024      2024-12-01          
 2 01/12/2024      2024-12-01          
 3 01/12/2024      2024-12-01          
 4 30/11/2024      2024-11-30          
 5 01/12/2024      2024-12-01          
 6 01/12/2024      2024-12-01          
 7 01/12/2024      2024-12-01          
 8 01/12/2024      2024-12-01          
 9 01/12/2024      2024-12-01          
10 01/12/2024      2024-12-01          
# ℹ 81,993 more rows

O lubridate também possui funções para transformar datas que estejam em outros formatos. O importante é lembrar que y representa o ano, m representa o mês e d representa o dia, e assim podemos combinar essas letras para formar o nome da função que precisamos usar para transformar a data. Se quiser saber mais sobre esse grupo de funções, acesse a documentação oficial.

6.2.4.2 Extrair informações de datas

O lubridate possui funções para extrair informações de datas. No exemplo abaixo, vamos extrair o dia, mês e ano da coluna dt_referencia:

voos_datas |> 
  select(dt_referencia) |> 
  mutate(
    dia = day(dt_referencia),
    mes = month(dt_referencia),
    ano = year(dt_referencia)
  )
# A tibble: 82,003 × 4
   dt_referencia   dia   mes   ano
   <date>        <int> <dbl> <dbl>
 1 2024-12-01        1    12  2024
 2 2024-12-01        1    12  2024
 3 2024-12-01        1    12  2024
 4 2024-12-01        1    12  2024
 5 2024-12-01        1    12  2024
 6 2024-12-01        1    12  2024
 7 2024-12-01        1    12  2024
 8 2024-12-01        1    12  2024
 9 2024-12-01        1    12  2024
10 2024-12-01        1    12  2024
# ℹ 81,993 more rows

Algumas funções tem argumentos que permitem personalizar a saída. Por exemplo, a função month() tem o argumento label que permite retornar o mês por extenso (em vez de numérico), o argumento abbr que permite retornar o mês abreviado, e o argumento locale que permite retornar o mês em outro idioma:

voos_datas |>
  select(dt_referencia) |>
  mutate(
    mes_curto = month(dt_referencia, label = TRUE),
    
    mes = month(dt_referencia, label = TRUE, abbr = FALSE),
    
    mes_pt = month(
      dt_referencia,
      label = TRUE,
      abbr = FALSE,
      locale = "pt_BR"
    )
  )
Warning: There was 1 warning in `mutate()`.
ℹ In argument: `mes_pt = month(dt_referencia, label = TRUE, abbr = FALSE,
  locale = "pt_BR")`.
Caused by warning in `Sys.setlocale()`:
! SO informa que a requisição para definir o locale como 'pt_BR' não pode ser honrada
# A tibble: 82,003 × 4
   dt_referencia mes_curto mes      mes_pt  
   <date>        <ord>     <ord>    <ord>   
 1 2024-12-01    dez       dezembro dezembro
 2 2024-12-01    dez       dezembro dezembro
 3 2024-12-01    dez       dezembro dezembro
 4 2024-12-01    dez       dezembro dezembro
 5 2024-12-01    dez       dezembro dezembro
 6 2024-12-01    dez       dezembro dezembro
 7 2024-12-01    dez       dezembro dezembro
 8 2024-12-01    dez       dezembro dezembro
 9 2024-12-01    dez       dezembro dezembro
10 2024-12-01    dez       dezembro dezembro
# ℹ 81,993 more rows

A função wday() retorna o dia da semana, e também possui argumentos para personalizar a saída:

voos_datas |>
  select(dt_referencia) |>
  mutate(
    dia_semana_numero = wday(dt_referencia),
    
    dia_semana = wday(dt_referencia, label = TRUE),
    
    dia_semana_abbr = wday(dt_referencia, label = TRUE, abbr = FALSE),
    
    dia_semana_pt = wday(
      dt_referencia,
      label = TRUE,
      abbr = FALSE,
      locale = "pt_BR"
    )
  )
# A tibble: 82,003 × 5
   dt_referencia dia_semana_numero dia_semana dia_semana_abbr dia_semana_pt
   <date>                    <dbl> <ord>      <ord>           <ord>        
 1 2024-12-01                    1 dom        domingo         domingo      
 2 2024-12-01                    1 dom        domingo         domingo      
 3 2024-12-01                    1 dom        domingo         domingo      
 4 2024-12-01                    1 dom        domingo         domingo      
 5 2024-12-01                    1 dom        domingo         domingo      
 6 2024-12-01                    1 dom        domingo         domingo      
 7 2024-12-01                    1 dom        domingo         domingo      
 8 2024-12-01                    1 dom        domingo         domingo      
 9 2024-12-01                    1 dom        domingo         domingo      
10 2024-12-01                    1 dom        domingo         domingo      
# ℹ 81,993 more rows

6.2.4.3 Arredondando datas

O lubridate também possui funções para arredondar datas. Por exemplo, a função floor_date() arredonda a data para o início de um intervalo de tempo (por exemplo, o início do mês):

voos_datas |> 
  select(dt_referencia) |> 
  mutate(
    mes_arredondado = floor_date(dt_referencia, unit = "month"),
  )
# A tibble: 82,003 × 2
   dt_referencia mes_arredondado
   <date>        <date>         
 1 2024-12-01    2024-12-01     
 2 2024-12-01    2024-12-01     
 3 2024-12-01    2024-12-01     
 4 2024-12-01    2024-12-01     
 5 2024-12-01    2024-12-01     
 6 2024-12-01    2024-12-01     
 7 2024-12-01    2024-12-01     
 8 2024-12-01    2024-12-01     
 9 2024-12-01    2024-12-01     
10 2024-12-01    2024-12-01     
# ℹ 81,993 more rows

6.2.4.4 Exercícios

  1. Utilizando a base de dados voos, tente gerar a tabela abaixo utilizando a coluna dt_partida_real:
# A tibble: 82,003 × 5
   dt_partida_real dia_partida mes_partida mes_partida_date dia_semana_partida
   <date>                <int> <ord>       <date>           <ord>             
 1 2024-12-01                1 dez         2024-12-01       dom               
 2 2024-12-01                1 dez         2024-12-01       dom               
 3 2024-12-01                1 dez         2024-12-01       dom               
 4 2024-11-30               30 nov         2024-11-01       sáb               
 5 2024-12-01                1 dez         2024-12-01       dom               
 6 2024-12-01                1 dez         2024-12-01       dom               
 7 2024-12-01                1 dez         2024-12-01       dom               
 8 2024-12-01                1 dez         2024-12-01       dom               
 9 2024-12-01                1 dez         2024-12-01       dom               
10 2024-12-01                1 dez         2024-12-01       dom               
# ℹ 81,993 more rows

6.2.5 Datas em gráficos

Vamos criar um gráfico com o pacote ggplot2 utilizando a base de dados voos. Vamos criar um gráfico de barras com a quantidade de voos por dia de partida, que partiram do aeroporto de Brasília - DF: PRESIDENTE JUSCELINO KUBITSCHEK.

voos_brasilia <- voos |>
  filter(nm_aerodromo_origem == "PRESIDENTE JUSCELINO KUBITSCHEK")

Como essa coluna está originalmente salva como texto, o ggplot2 entenderá que cada data é uma categoria (e não uma data), e o gráfico não será exibido corretamente:

quantidade_voos_por_partida <- voos_brasilia |> 
  count(dt_partida_real) 

quantidade_voos_por_partida |> 
  ggplot(aes(x = dt_partida_real, y = n)) +
  geom_col() +
  labs(
    x = "Data",
    y = "Quantidade de voos",
    title = "Quantidade de voos por dia de partida"
  )

Para que o ggplot2 entenda que a variável é uma data, precisamos transformá-la para o tipo Date (como visto anteriormente):

quantidade_voos_partida_data <- voos_brasilia |>
  mutate(
    dt_partida_real_date = dmy(dt_partida_real)
  ) |>
  count(dt_partida_real_date)

quantidade_voos_partida_data
# A tibble: 31 × 2
   dt_partida_real_date     n
   <date>               <int>
 1 2024-12-01             142
 2 2024-12-02             158
 3 2024-12-03             163
 4 2024-12-04             160
 5 2024-12-05             164
 6 2024-12-06             157
 7 2024-12-07             126
 8 2024-12-08             146
 9 2024-12-09             160
10 2024-12-10             159
# ℹ 21 more rows

Agora vamos criar o gráfico com a data transformada. Observe que agora o ggplot2 entende que a variável é uma data, e o gráfico é exibido corretamente:

 grafico_voos <- quantidade_voos_partida_data |> 
  ggplot(aes(x = dt_partida_real_date, y = n)) +
  geom_col() +
  labs(
    x = "Data de partida",
    y = "Quantidade de voos",
    title = "Quantidade de voos por dia de partida",
    subtitle = "Voos que partiram do aeroporto de Brasília, em dezembro de 2024"
  ) +
  coord_trans(expand = FALSE) +
  theme_minimal()

grafico_voos

O pacote ggplot2 possui funções específicas para trabalhar com datas em gráficos. Por exemplo, a função scale_x_date() permite personalizar o eixo x de um gráfico com datas. Essa função possui argumentos para personalizar o formato das datas (date_labels), e o intervalo entre as datas (date_breaks).

grafico_voos +
  scale_x_date(date_labels = "%d", date_breaks = "1 day") 

6.2.5.1 Exercícios

  1. Utilizando o gráfico criado anteriormente, personalize o eixo x para exibir o dia/mês de cada data (como 12/dez), e o intervalo entre as datas seja de 7 dias.
  1. Os nomes dos meses estão em inglês ou em português? Caso esteja em inglês, como você faria para exibir os nomes dos meses em português?
Dica

Dica: a função Sys.setlocale() permite alterar o locale utilizado pelo R.

Sys.setlocale("LC_ALL", "pt_br.utf-8") 

6.3 Manipulação de textos (strings)

Nesta seção, vamos explorar funções para manipulação de textos (strings). Isso é útil para extrair informações de textos, fazer limpeza de dados, realizar filtros, entre outros.

6.3.1 O pacote stringr

O pacote stringr é um pacote do tidyverse que facilita a manipulação de strings. Ele possui funções para detectar padrões em strings, extrair informações, substituir textos, tratar textos, entre outros.

A maioria das funções do pacote tem o prefixo str_, o que facilita a identificação das funções que trabalham com strings. De início, vamos explorar algumas funções básicas para detectar e extrair padrões em strings:

Essas funções seguem a mesma sintaxe: o primeiro argumento é a string, e o segundo argumento é o padrão que queremos buscar.

Para entender como trabalhar com esses padrões, precisamos conhecer o conceito de expressões regulares (regex). Vamos explorar isso a seguir, e depois voltamos para o stringr!

6.3.2 Introdução à Expressões regulares (regex)

As expressões regulares (ou regex) são padrões utilizados para encontrar e substituir textos em strings. Elas são muito úteis para realizar buscas complexas em textos, e são utilizadas em várias linguagens de programação (ou seja, não são exclusivas do R).

Existem alguns caracteres especiais que são utilizados nas expressões regulares. Alguns exemplos:

  • . (qualquer caractere)
  • ^ (início da string)
  • $ (fim da string)
  • *, +, ? (quantificadores)
  • [] (conjunto de caracteres)
  • {} (quantificadores numéricos)
  • \d (dígitos: 0-9)
  • \w (letras e dígitos)

O site regexer é uma ferramenta muito útil para testar expressões regulares. Você pode inserir um texto, e testar diferentes expressões regulares para encontrar padrões no texto. O ChatGPT (e outras ferramentas similares) também podem ser usados para interpretar expressões regulares.

Cuidado

Existem alguns caracteres que precisamos “escapar” com \ para que sejam interpretados como caracteres literais.

  • No R, a barra invertida (\) é um caractere de escape. Quando queremos utilizar uma barra invertida em uma expressão regular, precisamos “escapar” a barra invertida com outra barra invertida.

Por exemplo, se quisermos encontrar um dígito (\d), precisamos escrever \\d. Caso você encontre um erro como o abaixo, provavelmente é porque você esqueceu de escapar um caractere especial:

str_extract("123", "\d")
Error: '\d' is an unrecognized escape in character string (<input>:1:22)
str_extract("123", "\\d")
[1] "1"

Vamos tentar interpretar o que cada um dos padrões a seguir faz (tente responder antes de abrir a resposta):

Esse padrão é utilizado para encontrar arquivos que terminam com a extensão .csv. O * significa que pode ter qualquer caractere antes de .csv, e o $ indica que .csv deve ser o final da string.

Isso é útil para encontrar arquivos em um diretório, por exemplo:

fs::dir_ls("dados", regexp = ".csv$")
dados/mtcars.csv                 dados/sidrar_4092_bruto.csv      
dados/sidrar_4092_bruto_2.csv    dados/sigbm_20252118.csv         
dados/uf_regiao.csv              dados/voos_dez-2024-alterado.csv 
dados/voos_dez-2024.csv          

Esse padrão é utilizado para encontrar dois dígitos em uma string. O \\d representa um dígito, e {2} indica que devem ser dois dígitos.

Este exemplo é útil para compreender o exemplo a seguir!

Esse padrão busca por datas no formato dia/mês/ano. O \\d representa um dígito, e {2} indica que devem ser dois dígitos. As barras (/) são caracteres literais, e o {4} indica que o ano deve ter quatro dígitos.

Isso é útil para extrair datas de um texto, por exemplo:

texto <- "Neste ano, o carnaval será entre os dias 03/03/2025 e 05/03/2025 (quarta-feira de cinzas)."

str_extract_all(texto, "\\d{2}/\\d{2}/\\d{4}")
[[1]]
[1] "03/03/2025" "05/03/2025"

6.3.2.1 Exercícios

  1. O código abaixo deveria retornar todos os elementos do vetor que estão com a palavra “R$”. Porém, ele retorna um caractere vazio. Por que isso acontece? Como você faria para corrigir?
valores <- c("R$ 100", "USD 200", "300€", "R$ 200")
str_subset(valores, "R$") 
character(0)
  1. Considerando o vetor palavras_pro:
palavras_pro <- c("processo", "projeto", "produto",
                  "improviso", "aprovar", "professora",
                  "floresta", "prédio", "prova", "problema")
  1. Qual é a diferença entre os padrões a seguir? O que você acha que cada um deles faz? Quais palavras você acha que serão retornadas em cada caso?
str_subset(palavras_pro, "^pro")

str_subset(palavras_pro, "pro")

6.3.3 Manipulação de strings com stringr

Nos exemplos anteriores, utilizamos funções básicas do stringr para detectar e extrair padrões em strings.

Podemos também utilizar essas funções juntamente com o dplyr para realizar transformações em bases de dados.

6.3.3.1 Filtros com str_detect()

No curso introdutório, aprendemos a utilizar a função filter() do dplyr para filtrar linhas de uma base de dados. Porém o filter() busca por valores exatos, e não por padrões em strings.

Se pesquisarmos por voos que saíram de Congonhas, podemos usar o filter() para encontrar exatamente “CONGONHAS”:

voos |> 
  filter(nm_aerodromo_origem == "CONGONHAS")
# A tibble: 8,112 × 26
   id_basica id_empresa nm_empresa               dt_referencia ds_di ds_grupo_di
       <dbl>      <dbl> <chr>                    <date>        <chr> <chr>      
 1  33973670    1000854 GOL LINHAS AÉREAS S.A. … 2024-12-01    REGU… REGULAR    
 2  33973818    1000854 GOL LINHAS AÉREAS S.A. … 2024-12-01    REGU… REGULAR    
 3  33974325    1000854 GOL LINHAS AÉREAS S.A. … 2024-12-01    REGU… REGULAR    
 4  33974449    1000854 GOL LINHAS AÉREAS S.A. … 2024-12-01    REGU… REGULAR    
 5  33974542    1000854 GOL LINHAS AÉREAS S.A. … 2024-12-01    REGU… REGULAR    
 6  33974581    1000854 GOL LINHAS AÉREAS S.A. … 2024-12-01    REGU… REGULAR    
 7  33974592    1000854 GOL LINHAS AÉREAS S.A. … 2024-12-01    REGU… REGULAR    
 8  33974692    1000854 GOL LINHAS AÉREAS S.A. … 2024-12-01    REGU… REGULAR    
 9  33975020    1000854 GOL LINHAS AÉREAS S.A. … 2024-12-01    REGU… REGULAR    
10  33975462    1000854 GOL LINHAS AÉREAS S.A. … 2024-12-01    REGU… REGULAR    
# ℹ 8,102 more rows
# ℹ 20 more variables: ds_natureza_etapa <chr>, hr_partida_real <time>,
#   dt_partida_real <chr>, id_aerodromo_origem <dbl>,
#   nm_aerodromo_origem <chr>, nm_municipio_origem <chr>, sg_uf_origem <chr>,
#   nm_regiao_origem <chr>, nm_pais_origem <chr>, nm_continente_origem <chr>,
#   hr_chegada_real <time>, dt_chegada_real <dbl>, id_aerodromo_destino <dbl>,
#   nm_aerodromo_destino <chr>, nm_municipio_destino <chr>, …

Porém caso queiramos encontrar voos que saíram de Guarulhos, não basta usar o filter() com ==, pois o nome do aeroporto está escrito de forma diferente (“GUARULHOS - GOVERNADOR ANDRÉ FRANCO MONTORO”):

voos |> 
  filter(nm_aerodromo_origem == "GUARULHOS")
# A tibble: 0 × 26
# ℹ 26 variables: id_basica <dbl>, id_empresa <dbl>, nm_empresa <chr>,
#   dt_referencia <date>, ds_di <chr>, ds_grupo_di <chr>,
#   ds_natureza_etapa <chr>, hr_partida_real <time>, dt_partida_real <chr>,
#   id_aerodromo_origem <dbl>, nm_aerodromo_origem <chr>,
#   nm_municipio_origem <chr>, sg_uf_origem <chr>, nm_regiao_origem <chr>,
#   nm_pais_origem <chr>, nm_continente_origem <chr>, hr_chegada_real <time>,
#   dt_chegada_real <dbl>, id_aerodromo_destino <dbl>, …

A função str_detect() retorna um vetor lógico (TRUE ou FALSE) indicando se um padrão está presente em uma string. Isso é muito útil para usar com a função filter() (do dplyr), para filtrar strings que contêm um padrão específico. Neste caso, podemos usar a função str_detect() para filtrar strings que contêm um padrão específico:

voos |> 
  filter(str_detect(nm_aerodromo_origem, "GUARULHOS"))
# A tibble: 11,726 × 26
   id_basica id_empresa nm_empresa               dt_referencia ds_di ds_grupo_di
       <dbl>      <dbl> <chr>                    <date>        <chr> <chr>      
 1  33972533    1001070 AEROVÍAS DE MÉXICO S.A … 2024-12-01    REGU… REGULAR    
 2  33819746    1007570 ARAJET S.A.              2024-12-01    REGU… REGULAR    
 3  33914269    1000896 DELTA AIR LINES INC.     2024-12-01    REGU… REGULAR    
 4  33914319    1000896 DELTA AIR LINES INC.     2024-12-01    REGU… REGULAR    
 5  33914324    1000896 DELTA AIR LINES INC.     2024-12-01    REGU… REGULAR    
 6  33820286    1007303 FB LÍNEAS AÉREAS S.A (F… 2024-12-01    REGU… REGULAR    
 7  33820290    1007303 FB LÍNEAS AÉREAS S.A (F… 2024-12-01    REGU… REGULAR    
 8  33972705    1001220 BOA BOLIVIANA DE AVIACI… 2024-12-01    REGU… REGULAR    
 9  33972718    1001220 BOA BOLIVIANA DE AVIACI… 2024-12-01    REGU… REGULAR    
10  33972965    1000854 GOL LINHAS AÉREAS S.A. … 2024-12-01    REGU… REGULAR    
# ℹ 11,716 more rows
# ℹ 20 more variables: ds_natureza_etapa <chr>, hr_partida_real <time>,
#   dt_partida_real <chr>, id_aerodromo_origem <dbl>,
#   nm_aerodromo_origem <chr>, nm_municipio_origem <chr>, sg_uf_origem <chr>,
#   nm_regiao_origem <chr>, nm_pais_origem <chr>, nm_continente_origem <chr>,
#   hr_chegada_real <time>, dt_chegada_real <dbl>, id_aerodromo_destino <dbl>,
#   nm_aerodromo_destino <chr>, nm_municipio_destino <chr>, …

Imagine que queremos descobrir quais são os destinos possíveis a partir dos aeroportos de Guarulhos e Congonhas (localizados na Região Metropolitana de São Paulo). Podemos utilizar a função str_detect() + filter() para filtrar estas origens. Observe que utilizamos o operador | para indicar que queremos encontrar voos que saíram de Guarulhos ou Congonhas:

voos |> 
  filter(str_detect(nm_aerodromo_origem, "GUARULHOS|CONGONHAS")) |> 
  count(nm_aerodromo_origem, nm_municipio_destino, nm_pais_destino, sort = TRUE)
# A tibble: 235 × 4
   nm_aerodromo_origem                nm_municipio_destino nm_pais_destino     n
   <chr>                              <chr>                <chr>           <int>
 1 CONGONHAS                          RIO DE JANEIRO       BRASIL           1757
 2 CONGONHAS                          BRASÍLIA             BRASIL            700
 3 CONGONHAS                          CONFINS              BRASIL            654
 4 CONGONHAS                          PORTO ALEGRE         BRASIL            583
 5 GUARULHOS - GOVERNADOR ANDRÉ FRAN… RECIFE               BRASIL            563
 6 CONGONHAS                          SÃO JOSÉ DOS PINHAIS BRASIL            557
 7 GUARULHOS - GOVERNADOR ANDRÉ FRAN… RIO DE JANEIRO       BRASIL            534
 8 CONGONHAS                          FLORIANÓPOLIS        BRASIL            481
 9 CONGONHAS                          SALVADOR             BRASIL            476
10 GUARULHOS - GOVERNADOR ANDRÉ FRAN… CONFINS              BRASIL            425
# ℹ 225 more rows

6.3.3.2 mutate() e stringr

A função mutate() do dplyr é muito útil para criar novas variáveis em uma base de dados. Podemos utilizar o mutate() juntamente com o stringr para realizar vários tipos de transformações em strings. Existem muitas funções do stringr que podem ser utilizadas com o mutate(), como:

voos |> 
  mutate(
    nm_aerodromo_origem = str_squish(nm_aerodromo_origem),
    nm_aerodromo_origem = str_to_title(nm_aerodromo_origem),
    nm_aerodromo_origem = str_replace_all(nm_aerodromo_origem, " Do ", " do "),
    nm_aerodromo_origem = str_replace_all(nm_aerodromo_origem, " De ", " de "),
    nm_aerodromo_origem = str_replace_all(nm_aerodromo_origem, " Da ", " da "),
  ) |> 
  count(nm_aerodromo_origem, sort = TRUE)
# A tibble: 382 × 2
   nm_aerodromo_origem                                                         n
   <chr>                                                                   <int>
 1 Guarulhos - Governador André Franco Montoro                             11706
 2 Congonhas                                                                8112
 3 Viracopos                                                                5210
 4 Tancredo Neves                                                           4788
 5 Aeroporto Internacional do Rio de Janeiro (Galeão) - Antonio Carlos Jo…  4683
 6 Presidente Juscelino Kubitschek                                          4531
 7 Guararapes - Gilberto Freyre                                             3506
 8 Deputado Luís Eduardo Magalhães                                          2430
 9 Santos Dumont                                                            2395
10 Afonso Pena                                                              2121
# ℹ 372 more rows

6.3.3.3 Reclassificando categorias com {stringr} e case_when()

Imagine que queremos criar uma tabela com as principais empresas aéreas que realizaram voos domésticos em dezembro de 2024. Para isso, precisamos filtrar os vôos domésticos, e contar quantos voos cada empresa realizou:

voos_brasil <- voos |> 
  filter(ds_natureza_etapa == "DOMÉSTICA") 

voos_brasil |> 
  count(nm_empresa, sort = TRUE)
# A tibble: 158 × 2
   nm_empresa                                              n
   <chr>                                               <int>
 1 AZUL LINHAS AÉREAS BRASILEIRAS S/A                  24643
 2 TAM LINHAS AÉREAS S.A.                              20428
 3 GOL LINHAS AÉREAS S.A. (EX- VRG LINHAS AÉREAS S.A.) 18463
 4 AZUL CONECTA LTDA. (EX TWO TAXI AEREO LTDA)          1677
 5 PASSAREDO TRANSPORTES AÉREOS S.A.                     996
 6 SIDERAL LINHAS AÉREAS LTDA.                           422
 7 TAM LINHAS ASUL                                       303
 8 TOTAL LINHAS AÉREAS S.A.                              157
 9 ABSA - AEROLINHAS BRASILEIRAS S.A.                    135
10 MAP TRANSPORTES AÉREOS LTDA.                          124
# ℹ 148 more rows

A tabela resultante apresenta nomes de empresas de forma diferente (por exemplo, "AZUL LINHAS AÉREAS BRASILEIRAS S/A" e "AZUL CONECTA LTDA. (EX TWO TAXI AEREO LTDA)"). Vamos consultar, por exemplo, quais nomes de empresas começam com AZUL:

empresas_azul <- voos_brasil |> 
  filter(
    str_detect(nm_empresa, "^AZUL ")
  ) |> 
  count(nm_empresa, sort = TRUE)

empresas_azul
# A tibble: 28 × 2
   nm_empresa                                      n
   <chr>                                       <int>
 1 AZUL LINHAS AÉREAS BRASILEIRAS S/A          24643
 2 AZUL CONECTA LTDA. (EX TWO TAXI AEREO LTDA)  1677
 3 AZUL LINHAS AÉREAS BRASNDRÉ FRANC               5
 4 AZUL LINS - GOVERNADOR ANDRÉ FRANC              4
 5 AZUL LIN LUÍS EDUARDO MAGALHÃES                 2
 6 AZUL LINAS AÉREAS S.A. (EX- VRG LI              2
 7 AZUL LINHAS AÉREAS BRASILEIRAS ANC              2
 8 AZUL LINIONAL DE BELÉM/VAL DE CANS              2
 9 AZUL INHAS AÉREAS S.A. (EX- VRG LI              1
10 AZUL LDO LUÍS EDUARDO MAGALHÃES                 1
# ℹ 18 more rows

São muitas formas de escrever, e parece que a maioria são erros de digitação.

Como queremos buscar as principais empresas aéreas, vamos padronizar os nomes das empresas. Vamos criar uma nova variável nm_empresa_reclassificado com os nomes das empresas padronizados:

voos_brasil |> 
  mutate(nm_empresa_reclassificado = case_when(
    str_detect(nm_empresa, "^GOL") ~ "GOL",
    str_detect(nm_empresa, "^AZUL") ~ "AZUL",
    str_detect(nm_empresa, "^TAM|^LATAM") ~ "LATAM",
    .default = "OUTROS"
  )) |>
  count(nm_empresa_reclassificado, sort = TRUE) |> 
  mutate(porc = n / sum(n) * 100) 
# A tibble: 4 × 3
  nm_empresa_reclassificado     n  porc
  <chr>                     <int> <dbl>
1 AZUL                      26365 38.7 
2 LATAM                     20851 30.6 
3 GOL                       18485 27.2 
4 OUTROS                     2345  3.45

6.3.3.4 Joins (unindo duas tabelas)

Os joins são operações que combinam duas ou mais tabelas em uma única tabela. No curso Introdução à análise de dados no R, conhecemos algumas funções de join do pacote dplyr.

Para fazer joins, precisamos ter uma variável em comum entre as tabelas (chamada de chave). Porém, em alguns casos as chaves não são exatamente iguais, e precisamos utilizar funções do pacote {stringr} para transformar as colunas, de forma que os valores fiquem correspondentes; ou comparar strings de forma mais flexível.

6.3.3.5 Extra: exemplo de join com {geobr}

Imagine que queremos fazer um mapa com a quantidade de voos por município de origem. Para isso, precisamos unir a base de dados voos com uma base de dados georreferenciada, que contém informações sobre os municípios do Brasil. Com o pacote {geobr}, podemos importar essa base de dados!

library(sf)
geo_brasil_completo <- geobr::read_state()
geo_brasil_municipios <- geobr::read_municipality()
glimpse(geo_brasil_municipios)
1
Delimitação dos estados do Brasil.
2
Delimitação dos municípios do Brasil.
Rows: 5,565
Columns: 5
$ code_muni    <dbl> 1100015, 1100023, 1100031, 1100049, 1100056, 1100064, 110…
$ name_muni    <chr> "Alta Floresta D'oeste", "Ariquemes", "Cabixi", "Cacoal",…
$ code_state   <chr> "11", "11", "11", "11", "11", "11", "11", "11", "11", "11…
$ abbrev_state <chr> "RO", "RO", "RO", "RO", "RO", "RO", "RO", "RO", "RO", "RO…
$ geom         <MULTIPOLYGON [°]> MULTIPOLYGON (((-62.2462 -1..., MULTIPOLYGON…

Observe que na base de dados geo_brasil_municipios temos o código IBGE do município, o nome do município e a sigla do estado. Porém a base de dados voos não possui o código do município, e sim o nome do município de origem.

Para que essas colunas funcionem como chaves, precisamos que o nome do município de origem da base de dados voos_brasil esteja igual ao nome do município da base de dados geo_brasil_municipios.

voos_brasil |> 
  select(nm_municipio_origem, sg_uf_origem) |> 
  head()
# A tibble: 6 × 2
  nm_municipio_origem sg_uf_origem
  <chr>               <chr>       
1 RECIFE              PE          
2 JUAZEIRO DO NORTE   CE          
3 RIBEIRÃO PRETO      SP          
4 RIO DE JANEIRO      RJ          
5 GUARULHOS           SP          
6 RIO DE JANEIRO      RJ          
geo_brasil_municipios |> 
  select(name_muni, abbrev_state) |> 
  head()
Simple feature collection with 6 features and 2 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: -63.61822 ymin: -13.6937 xmax: -60.33317 ymax: -9.66916
Geodetic CRS:  SIRGAS 2000
              name_muni abbrev_state                           geom
1 Alta Floresta D'oeste           RO MULTIPOLYGON (((-62.2462 -1...
2             Ariquemes           RO MULTIPOLYGON (((-63.13712 -...
3                Cabixi           RO MULTIPOLYGON (((-60.52408 -...
4                Cacoal           RO MULTIPOLYGON (((-61.42679 -...
5            Cerejeiras           RO MULTIPOLYGON (((-61.41347 -...
6     Colorado Do Oeste           RO MULTIPOLYGON (((-60.66352 -...

Ops, na primeira base de dados os nomes dos municípios estão em caixa alta (ex: “SÃO PAULO”), já na segunda base de dados apenas a primeira letra dos nomes estão em caixa alta (ex: “São Paulo”). Vamos utilizar o {stringr} para deixar os nomes dos municípios padronizados.

Aviso

Este processo de padronizar uma coluna de duas tabelas diferentes para utilizar como chave é iterativo, e pode ser necessário fazer mais ajustes conforme for unindo as bases de dados (e encontrando problemas). Tenha paciência!

Primeiro, vamos transformar os nomes dos municípios para minúsculas e remover acentos:

rm_accent <- function(x){
  # função do pacote abjutils
  stringi::stri_trans_general(x, "Latin-ASCII")
}
geo_brasil_municipios_preparado <- geo_brasil_municipios |> 
  mutate(
    sigla_estado = abbrev_state,
    nome_municipio = str_to_lower(name_muni),
    nome_municipio = rm_accent(nome_municipio)
  )

voos_brasil_preparado <- voos_brasil |>
  mutate(
    sigla_estado = sg_uf_origem,
    nome_municipio = str_to_lower(nm_municipio_origem),
    nome_municipio = rm_accent(nome_municipio)
  )

A função anti_join() é utilizada para retornar as linhas da primeira tabela que não estão presentes na segunda tabela, então podemos utilizar essa função para verificar onde o left_join() não daria certo (ou seja, onde os nomes dos municípios estão diferentes):

join_nao_funcionou <- anti_join(voos_brasil_preparado, geo_brasil_municipios_preparado)
Joining with `by = join_by(sigla_estado, nome_municipio)`
nrow(voos_brasil_preparado)
[1] 68046
join_nao_funcionou |> 
  count(nome_municipio, sigla_estado, sort = TRUE)
# A tibble: 50 × 3
   nome_municipio       sigla_estado     n
   <chr>                <chr>        <int>
 1 sao jose doul        PR              22
 2 sao gonco sul        RN              14
 3 sao jose dsul        PR              12
 4 sao jose dsul        SP               6
 5 domestica            SP               4
 6 america do sul       PR               3
 7 sao jose dos         PR               3
 8 sao jose do�reas s.a PR               3
 9 domestic             DF               2
10 domestica            SC               2
# ℹ 40 more rows

É possível perceber que neste caso temos muitos municípios que estão com erro de digitação. Poderíamos corrigir manualmente (com case_when()), mas neste caso, os erros representam menos de 1% dos dados (na verdade, apenas 0.17 %). Vamos seguir com o join!

A função inner_join() é utilizada para retornar as linhas que estão presentes nas duas tabelas (ou seja, os nomes que estão incorretos ficam de fora!).

Como queremos criar um mapa apenas com a quantidade de voos por município de origem, vamos contar a quantidade de voos por município e salvar em um objeto:

quantidade_voos_por_muni <- voos_brasil_preparado |> 
  count(nome_municipio, sigla_estado, sort = TRUE)

Com este objeto, podemos fazer o join com a base de dados geo_brasil_municipios_preparado. Atenção: para que a base de dados resultante mantenha as propriedades espaciais, precisamos utilizar a tabela geo_brasil_municipios como base para o join (ou seja, utilizamos ela primeiro):

join_brasil <- inner_join(geo_brasil_municipios_preparado,
                          quantidade_voos_por_muni) 
Joining with `by = join_by(sigla_estado, nome_municipio)`

Agora podemos apresentar o mapa!

ggplot() +
  geom_sf(data = geo_brasil_completo, fill = "white") +
  geom_sf(data = join_brasil, aes(fill = n)) +
  scale_fill_viridis_c(direction = -1) +
  gghighlight::gghighlight(n > 3000) +
  labs(
    x = "Quantidade de voos",
    y = "Município",
    title = "Quantidade de voos por município de origem",
    subtitle = "Voos domésticos realizados em dezembro de 2024"
  ) +
  theme_void()
1
Iniciando o gráfico.
2
Adicionando a base de dados com os limites dos estados do Brasil.
3
Adicionando a base de dados com a quantidade de voos por município.
4
Personalizando as cores do mapa: usando a paleta viridis.
5
Destacando os municípios com mais de 3000 voos.
6
Personalizando os textos.
7
Personalizando o tema.

6.3.3.6 Extra - Joins avançados

O pacote {fuzzyjoin} permite fazer joins entre tabelas considerando similaridade entre textos, utilizando técnicas de fuzzy join, que permite unir tabelas com base em similaridade parcial entre as chaves, em vez de exigir correspondência exata. Isso é útil quando trabalhamos com bases de dados que podem ter inconsistências, como erros de digitação em nomes de pessoas ou pequenas variações em endereços.

6.4 Material complementar