em1 <- read.csv("escolha_modal.csv")
em2 <- read_csv("escolha_modal.csv")3 Manipulação de Dados Tabulares
Nos dois capítulos anteriores conhecemos a linguagem R e aprendemos a calcular estatísticas sobre os dados que temos em mãos. Antes de chegar a esse ponto, porém, quase nenhuma base de dados real está pronta para ser resumida. Este capítulo trata do trabalho que antecede e sustenta toda análise, que é deixar os dados no formato em que a pergunta possa ser respondida. Começaremos pela leitura de arquivos em diferentes formatos e pelo operador que encadeia operações no R. Em seguida percorreremos os verbos que formam o vocabulário básico da manipulação, com os quais se filtram linhas, selecionam colunas, ordenam registros, criam variáveis, calculam resumos por grupo e combinam tabelas diferentes. Ao final, veremos como encadear tudo em uma única sequência legível, como guardar o resultado sem perder informação e como reorganizar uma tabela entre os formatos long e wide, decisão que determina o quão fácil ou será realizar certos tipos de tarefa de exploração de dados que veremos nos capítulos mais aplicados deste livro.
Neste capítulo, trabalharemos com dois conjuntos de dados: escolha_modal.csv e velocidades.xlsx. Faça o download de ambos pelos links abaixo e salve-os em uma pasta onde você irá criar o projeto no RStudio no qual executará esta aula. Assim que salvá-los, crie um novo script do R para iniciarmos o estudo.
3.1 Aspectos Introdutórios
3.1.1 O tidyverse
O tidyverse é uma coleção de pacotes do R desenvolvida principalmente pela equipe da Posit e projetada para facilitar a análise de dados. Todos os pacotes compartilham uma filosofia de design comum e estruturas de dados coerentes, o que torna a experiência de trabalho mais fluída e previsível. A documentação completa está disponível em tidyverse.org.
Ao instalar o tidyverse com install.packages("tidyverse"), você obtém um conjunto de pacotes para as principais etapas da análise de dados:
ggplot2: criação de gráficos declarativos baseados na Grammar of Graphicsdplyr: manipulação de dados tabulares (filtrar, selecionar, transformar, sumarizar e combinar tabelas)tidyr: reestruturação de tabelas para o formato tidy (cada variável em uma coluna, cada observação em uma linha)readr: leitura de arquivos de texto (CSV, TSV etc.) de forma rápida e previsívelpurrr: programação funcional, facilitando a aplicação de funções a listas e vetorestibble: versão modernizada do data frame, com comportamento mais consistente e impressão mais legívelstringr: manipulação de textos e expressões regularesforcats: manipulação de variáveis categóricas (factors)lubridate: manipulação de datas e horários
Neste capítulo, o foco está nos pacotes dplyr e readr, que cobrem as operações mais frequentes na etapa de manipulação de dados tabulares.
3.1.2 Leitura de arquivos CSV com read_csv()
Conforme já vimos anteriormente, o R base oferece a função read.csv() para leitura de arquivos CSV. O pacote readr, parte do tidyverse, oferece a alternativa read_csv(). O código abaixo mostra como usar cada uma delas para ler o arquivo escolha_modal.csv:
Embora ambas façam a leitura do mesmo arquivo, o comportamento ao imprimir os objetos é diferente. em1 é um data frame convencional e, caso a base de dados tenha muitas linhas, ao digitá-lo no console, o R imprimirá centenas delas, fazendo com que tenhamos de subir bastante a barra de rolagem à direita para verificar o nome das suas colunas. Já em2 é do tipo tibble. Ao imprimi-lo, aparecem apenas as primeiras dez linhas e apenas as colunas que cabem na largura do console, além dos tipos de cada variável logo abaixo dos nomes das colunas (como <chr> para texto, <dbl> para número real e <int> para inteiro). Observe abaixo:
em2# A tibble: 1,000 × 7
sexo idade renda posse_auto modo_viagem dist_viagem tempo_viagem
<chr> <dbl> <dbl> <chr> <chr> <dbl> <dbl>
1 F 34 3797 N "Carro ou App" 14.3 27
2 F 43 3515 N "Carro ou App" 36.1 55
3 F 55 4791 S "TP" 26.9 64
4 F 45 2749 N "A p\xe9" 2.9 26
5 M 45 2929 N "Carro ou App" 20.6 44
6 M 36 2011 N "TP" 22.6 58
7 F 18 1084 N "TP" 29.6 83
8 F 31 2338 N "TP" 26.9 63
9 M 53 7473 S "Carro ou App" 18.3 39
10 M 40 2786 N "Carro ou App" 36.8 57
# ℹ 990 more rows
Por padrão, tibbles imprimem apenas as 10 primeiras linhas e data frames imprimem até 1000 elementos. É possível alterar esses comportamentos com a função options(), que configura parâmetros globais da sessão R. As opções mais úteis são:
options(max.print = n)— define o número máximo de elementos impressos (n) para data frames e outros objetos R base. O padrão é 1000.options(tibble.print_max = n)— define o número máximo de linhas exibidas em tibbles. Quando o objeto tem mais linhas do que esse valor, somente as primeirasnsão impressas.options(tibble.print_min = n)— define quantas linhas são exibidas quando o limite máximo é ultrapassado.options(tibble.width = Inf)— exibe todas as colunas, mesmo que o tibble seja mais largo do que a janela do console.
Para sempre exibir todas as linhas de um tibble, use options(tibble.print_max = Inf). Para restaurar os padrões, use options(max.print = 1000, tibble.print_max = 10, tibble.print_min = 10).
Ao imprimir em2, a coluna modo_viagem pode exibir o valor "A p\xe9" no lugar de "A pé". Isso ocorre porque o arquivo foi gravado com codificação Latin-1 (também chamada de ISO-8859-1), enquanto o R, por padrão, tenta ler arquivos em UTF-8. O caractere "é" não ocupa o mesmo byte nos dois sistemas de codificação, resultando na sequência de escape \xe9.
Para corrigir esse problema, é preciso informar a codificação do arquivo na leitura. Com read.csv(), usa-se o argumento fileEncoding:
em1 <- read.csv("escolha_modal.csv", fileEncoding = "Latin1")Com read_csv(), usa-se o argumento locale:
em2 <- read_csv("escolha_modal.csv", locale = locale(encoding = "Latin1"))Após essa correção, os acentos aparecerão corretamente. Podemos verificar a classe dos dois objetos com a função class():
class(em1)
class(em2)O resultado será "data.frame" para em1 e c("tbl_df", "tbl", "data.frame") para em2, confirmando que o tibble é uma extensão do data frame.
3.1.3 Leitura de arquivos Excel com readxl
Para ler arquivos no formato Excel (.xlsx ou .xls), usamos o pacote readxl. Ele não faz parte do núcleo carregado por library(tidyverse), mas é instalado junto com o tidyverse. Para usá-lo, instale-o (se ainda não estiver instalado) e depois carregue-o:
install.packages("readxl")
library(readxl)O pacote oferece as funções read_excel(), que detecta o formato automaticamente, e read_xlsx() e read_xls, específicas para .xlsx e .xls, respectivamente. Ambas opções abaixo valem para lermos nosso arquivo “velocidades.xlsx”:
vel <- read_excel("velocidades.xlsx")
vel <- read_xlsx("velocidades.xlsx")Outro aspecto interessante quando queremos utilizar funções de bibliotecas já instaladas, mas não carregadas com o library() no R, é que podemos executá-las usando a notação pacote::função(). Observe abaixo:
vel <- readxl::read_xlsx("velocidades.xlsx")Essa notação é útil quando se quer usar uma função de um pacote de forma pontual, sem carregar o pacote inteiro na sessão.
3.1.4 O operador pipe (|>)
O operador pipe é uma das ferramentas mais úteis do ecossistema tidyverse. Ele permite encadear operações de forma legível, passando o resultado de uma expressão como o primeiro argumento da expressão seguinte.
Durante muito tempo, o pipe mais usado no R foi o %>%, proveniente do pacote magrittr. A partir do R 4.1.0, lançado em 2021, o R passou a oferecer um pipe nativo, |>, que funciona de forma similar sem exigir nenhum pacote adicional. Neste livro, usamos o pipe nativo |>.
No RStudio, o atalho de teclado Ctrl+Shift+M (Windows e Linux) ou Cmd+Shift+M (Mac) insere o pipe automaticamente. Para garantir que o atalho insira |> e não %>%, vá em Tools > Global Options > Code e marque a opção Use native pipe operator.
A lógica do pipe é simples: em vez de escrever f(x), escrevemos x |> f(). Veja alguns exemplos com a base vel:
vel |>
head()# A tibble: 6 × 3
faixa tipo_veic velocidade
<chr> <chr> <dbl>
1 Cen Passeio 54
2 Cen Caminhão/Ônibus 50
3 Cen Passeio 56
4 Dir Moto 43
5 Esq SUV/Pickup 57
6 Cen Caminhão/Ônibus 50
vel$velocidade |>
mean()[1] 53.804
No primeiro exemplo, vel é passado como argumento para head(), que retorna as primeiras seis linhas. No segundo, o vetor vel$velocidade é passado para mean(), que calcula a média. O pipe se torna ainda mais poderoso quando encadeamos várias operações, como veremos nas seções mais adiante.
3.1.5 A função summary() e variáveis factor
Vamos agora fazer a leitura definitiva da base escolha_modal.csv, informando a codificação correta:
em <- read_csv("escolha_modal.csv", locale = locale(encoding = "Latin1"))Podemos obter um resumo rápido de todas as variáveis com a função summary():
em |>
summary() sexo idade renda posse_auto
Length :1000 Min. :18.00 Min. : 327 Length :1000
N.unique : 2 1st Qu.:32.00 1st Qu.: 2142 N.unique : 2
N.blank : 0 Median :43.00 Median : 3017 N.blank : 0
Min.nchar: 1 Mean :42.84 Mean : 3993 Min.nchar: 1
Max.nchar: 1 3rd Qu.:54.00 3rd Qu.: 5398 Max.nchar: 1
Max. :80.00 Max. :14049
modo_viagem dist_viagem tempo_viagem
Length :1000 Min. : 0.10 Min. : 9.00
N.unique : 4 1st Qu.:10.70 1st Qu.: 34.00
N.blank : 0 Median :22.15 Median : 44.00
Min.nchar: 2 Mean :19.65 Mean : 45.96
Max.nchar: 12 3rd Qu.:27.80 3rd Qu.: 56.00
Max. :45.20 Max. :105.00
Para variáveis numéricas, summary() retorna mínimo, primeiro quartil, mediana, média, terceiro quartil e máximo, além do número de valores ausentes. Para variáveis do tipo character (texto), porém, o resultado é pouco informativo, pois o R indica apenas o comprimento e a classe, sem qualquer informação sobre os valores que aparecem.
Para variáveis categóricas, é mais adequado usar o tipo factor. Um factor é uma representação especial para variáveis categóricas no R que armazena os valores distintos como níveis (levels) e permite que o summary() mostre a contagem de cada categoria, entre outras vantagens que veremos mais adiante.
Vamos transformar as variáveis sexo, posse_auto e modo_viagem em factor:
em <- em |>
mutate(
sexo = factor(sexo),
posse_auto = factor(posse_auto),
modo_viagem = factor(modo_viagem)
)Agora summary() mostrará a contagem de cada nível:
em |>
summary() sexo idade renda posse_auto modo_viagem
F:478 Min. :18.00 Min. : 327 N:613 A pé :150
M:522 1st Qu.:32.00 1st Qu.: 2142 S:387 Bicicleta :141
Median :43.00 Median : 3017 Carro ou App:380
Mean :42.84 Mean : 3993 TP :329
3rd Qu.:54.00 3rd Qu.: 5398
Max. :80.00 Max. :14049
dist_viagem tempo_viagem
Min. : 0.10 Min. : 9.00
1st Qu.:10.70 1st Qu.: 34.00
Median :22.15 Median : 44.00
Mean :19.65 Mean : 45.96
3rd Qu.:27.80 3rd Qu.: 56.00
Max. :45.20 Max. :105.00
Para verificar os níveis de um factor, usamos a função levels():
levels(em$modo_viagem)[1] "A pé" "Bicicleta" "Carro ou App" "TP"
levels(em$sexo)[1] "F" "M"
3.2 Filtragem de Observações
A filtragem de observações consiste na seleção de um subconjunto de linhas de uma tabela com base em algum critério. O dplyr oferece duas funções principais para isso.
A função slice() seleciona linhas por posição. Para obter as cinco primeiras linhas de vel:
vel |>
slice(1:5)# A tibble: 5 × 3
faixa tipo_veic velocidade
<chr> <chr> <dbl>
1 Cen Passeio 54
2 Cen Caminhão/Ônibus 50
3 Cen Passeio 56
4 Dir Moto 43
5 Esq SUV/Pickup 57
A função filter() realiza a filtragem com base em condições lógicas. Os exemplos abaixo usam a base vel com diferentes operadores condicionais sobre a variável velocidade:
vel |>
filter(velocidade >= 50)# A tibble: 383 × 3
faixa tipo_veic velocidade
<chr> <chr> <dbl>
1 Cen Passeio 54
2 Cen Caminhão/Ônibus 50
3 Cen Passeio 56
4 Esq SUV/Pickup 57
5 Cen Caminhão/Ônibus 50
6 Esq Passeio 55
7 Esq SUV/Pickup 64
8 Esq Passeio 53
9 Cen Passeio 58
10 Esq Passeio 53
# ℹ 373 more rows
vel |>
filter(velocidade < 45)# A tibble: 32 × 3
faixa tipo_veic velocidade
<chr> <chr> <dbl>
1 Dir Moto 43
2 Dir Moto 42
3 Dir Caminhão/Ônibus 42
4 Dir Caminhão/Ônibus 44
5 Dir Moto 43
6 Dir Caminhão/Ônibus 41
7 Dir Caminhão/Ônibus 40
8 Dir Caminhão/Ônibus 44
9 Dir Moto 43
10 Dir Caminhão/Ônibus 43
# ℹ 22 more rows
Para combinar condições, usamos os operadores & (E lógico) e | (OU lógico). O exemplo abaixo seleciona registros com velocidade acima de 50 km/h e do tipo de veículo “Moto”:
vel |>
filter(velocidade > 50 & tipo_veic == "Moto")# A tibble: 7 × 3
faixa tipo_veic velocidade
<chr> <chr> <dbl>
1 Cen Moto 51
2 Cen Moto 52
3 Cen Moto 55
4 Esq Moto 51
5 Dir Moto 55
6 Cen Moto 51
7 Cen Moto 54
Já o exemplo seguinte seleciona registros com velocidade entre 40 e 50 km/h:
vel |>
filter(velocidade > 40 & velocidade < 50)# A tibble: 114 × 3
faixa tipo_veic velocidade
<chr> <chr> <dbl>
1 Dir Moto 43
2 Dir Moto 42
3 Dir Moto 46
4 Dir Caminhão/Ônibus 48
5 Dir Caminhão/Ônibus 47
6 Dir Moto 46
7 Dir Caminhão/Ônibus 42
8 Dir Moto 46
9 Dir Passeio 46
10 Dir Caminhão/Ônibus 47
# ℹ 104 more rows
Na base em, podemos combinar filtros em variáveis diferentes. O exemplo abaixo seleciona viagens realizadas por pessoas do sexo feminino que usaram transporte público (“TP”) ou foram a pé. Note que o operador %in% verifica se o valor está contido em um vetor de opções, sendo uma alternativa mais limpa a múltiplas condições com | quando temos dados do tipo character.
em |>
filter(sexo == "F" & modo_viagem %in% c("TP", "A pé"))# A tibble: 277 × 7
sexo idade renda posse_auto modo_viagem dist_viagem tempo_viagem
<fct> <dbl> <dbl> <fct> <fct> <dbl> <dbl>
1 F 55 4791 S TP 26.9 64
2 F 45 2749 N A pé 2.9 26
3 F 18 1084 N TP 29.6 83
4 F 31 2338 N TP 26.9 63
5 F 32 1170 N TP 33.7 79
6 F 20 1292 N TP 31.8 70
7 F 34 2695 N A pé 2.7 29
8 F 66 7295 S TP 28.4 62
9 F 25 1963 N TP 28.2 62
10 F 45 3037 N TP 23.7 67
# ℹ 267 more rows
3.3 Seleção de Colunas
A seleção de colunas permite manter apenas as variáveis relevantes para a análise, facilitando a leitura do código e reduzindo o uso de memória. No dplyr, usamos a função select().
Para selecionar apenas as colunas tipo_veic e velocidade de vel:
vel |>
select(tipo_veic, velocidade)# A tibble: 500 × 2
tipo_veic velocidade
<chr> <dbl>
1 Passeio 54
2 Caminhão/Ônibus 50
3 Passeio 56
4 Moto 43
5 SUV/Pickup 57
6 Caminhão/Ônibus 50
7 Passeio 55
8 SUV/Pickup 64
9 Passeio 53
10 Passeio 58
# ℹ 490 more rows
Para remover uma coluna, basta preceder o nome com -:
vel |>
select(-velocidade)# A tibble: 500 × 2
faixa tipo_veic
<chr> <chr>
1 Cen Passeio
2 Cen Caminhão/Ônibus
3 Cen Passeio
4 Dir Moto
5 Esq SUV/Pickup
6 Cen Caminhão/Ônibus
7 Esq Passeio
8 Esq SUV/Pickup
9 Esq Passeio
10 Cen Passeio
# ℹ 490 more rows
O select() também pode ser usado para reordenar colunas. Ao listar as colunas na nova ordem desejada, o resultado terá as colunas nessa sequência:
vel |>
select(velocidade, tipo_veic, faixa)# A tibble: 500 × 3
velocidade tipo_veic faixa
<dbl> <chr> <chr>
1 54 Passeio Cen
2 50 Caminhão/Ônibus Cen
3 56 Passeio Cen
4 43 Moto Dir
5 57 SUV/Pickup Esq
6 50 Caminhão/Ônibus Cen
7 55 Passeio Esq
8 64 SUV/Pickup Esq
9 53 Passeio Esq
10 58 Passeio Cen
# ℹ 490 more rows
Eventualmente, nós podemos obter os nomes das variáveis de interesse a serem analisadas a partir de alguma operação anterior que as retorne em formato character. Como a função select() não aceita nomes de colunas nesse formato, podemos usamos a função auxiliar all_of(), conforme o exemplo abaixo:
colunas <- c("tipo_veic", "velocidade")
vel |>
select(all_of(colunas))# A tibble: 500 × 2
tipo_veic velocidade
<chr> <dbl>
1 Passeio 54
2 Caminhão/Ônibus 50
3 Passeio 56
4 Moto 43
5 SUV/Pickup 57
6 Caminhão/Ônibus 50
7 Passeio 55
8 SUV/Pickup 64
9 Passeio 53
10 Passeio 58
# ℹ 490 more rows
3.4 Ordenação de Linhas
A ordenação de linhas reordena as observações de uma tabela com base nos valores de uma ou mais colunas. No dplyr, usamos a função arrange().
Para ordenar vel pela velocidade em ordem crescente:
vel |>
arrange(velocidade)# A tibble: 500 × 3
faixa tipo_veic velocidade
<chr> <chr> <dbl>
1 Dir Caminhão/Ônibus 38
2 Dir Caminhão/Ônibus 40
3 Dir Moto 40
4 Dir Caminhão/Ônibus 41
5 Dir Moto 41
6 Dir Caminhão/Ônibus 41
7 Dir Moto 42
8 Dir Caminhão/Ônibus 42
9 Dir Caminhão/Ônibus 42
10 Dir Moto 42
# ℹ 490 more rows
Para ordenar em ordem decrescente, usamos a função auxiliar desc():
vel |>
arrange(desc(velocidade))# A tibble: 500 × 3
faixa tipo_veic velocidade
<chr> <chr> <dbl>
1 Esq SUV/Pickup 66
2 Esq Passeio 65
3 Esq Passeio 65
4 Esq Passeio 65
5 Esq SUV/Pickup 64
6 Esq Passeio 64
7 Esq Passeio 64
8 Esq Passeio 63
9 Esq Passeio 63
10 Esq Passeio 63
# ℹ 490 more rows
Para variáveis numéricas, uma alternativa mais compacta é usar o sinal de negação diretamente:
vel |>
arrange(-velocidade)# A tibble: 500 × 3
faixa tipo_veic velocidade
<chr> <chr> <dbl>
1 Esq SUV/Pickup 66
2 Esq Passeio 65
3 Esq Passeio 65
4 Esq Passeio 65
5 Esq SUV/Pickup 64
6 Esq Passeio 64
7 Esq Passeio 64
8 Esq Passeio 63
9 Esq Passeio 63
10 Esq Passeio 63
# ℹ 490 more rows
O resultado é idêntico. Atente-se, no entanto, que a notação com - não funciona para variáveis de texto ou factor, situações em que desc() é obrigatório.
A ordenação também é possível por múltiplas colunas. O código abaixo ordena primeiro por tipo_veic (em ordem alfabética) e, dentro de cada tipo, por velocidade em ordem decrescente:
vel |>
arrange(tipo_veic, desc(velocidade))# A tibble: 500 × 3
faixa tipo_veic velocidade
<chr> <chr> <dbl>
1 Cen Caminhão/Ônibus 56
2 Cen Caminhão/Ônibus 55
3 Cen Caminhão/Ônibus 54
4 Cen Caminhão/Ônibus 53
5 Esq Caminhão/Ônibus 53
6 Cen Caminhão/Ônibus 52
7 Cen Caminhão/Ônibus 52
8 Dir Caminhão/Ônibus 52
9 Cen Caminhão/Ônibus 52
10 Cen Caminhão/Ônibus 52
# ℹ 490 more rows
Observe que a ordenação de tipo_veic segue a ordem alfabética, o que não reflete uma sequência lógica em termos de porte do veículo. A ordem natural seria Moto, Passeio, SUV/Pickup e Caminhão/Ônibus. Para impor essa ordem, convertemos tipo_veic em factor com os níveis na sequência desejada:
vel <- vel |>
mutate(
tipo_veic = factor(
tipo_veic,
levels = c("Moto", "Passeio", "SUV/Pickup", "Caminhão/Ônibus")
)
)Quando a variável categórica tem uma ordem intrínseca com significado (como o porte do veículo), podemos declará-la explicitamente com o argumento ordered = TRUE, fazendo com que esta se torne uma variável categórica ordinal:
vel <- vel |>
mutate(
tipo_veic = factor(
tipo_veic,
levels = c("Moto", "Passeio", "SUV/Pickup", "Caminhão/Ônibus"),
ordered = TRUE
)
)Um factor ordenado permite comparações lógicas entre categorias. Por exemplo, tipo_veic > "Passeio" retornará TRUE para “SUV/Pickup” e “Caminhão/Ônibus” e FALSE para “Moto” e “Passeio”. Ao ordenar novamente por tipo_veic, o resultado seguirá a ordem dos níveis definidos:
vel |>
arrange(tipo_veic)# A tibble: 500 × 3
faixa tipo_veic velocidade
<chr> <fct> <dbl>
1 Dir Moto 43
2 Dir Moto 42
3 Dir Moto 46
4 Dir Moto 46
5 Dir Moto 46
6 Cen Moto 51
7 Dir Moto 43
8 Dir Moto 45
9 Cen Moto 48
10 Dir Moto 43
# ℹ 490 more rows
Podemos confirmar os níveis com levels():
levels(vel$tipo_veic)[1] "Moto" "Passeio" "SUV/Pickup" "Caminhão/Ônibus"
3.5 Transformação e Criação de Variáveis
A função mutate() do dplyr permite criar novas variáveis ou modificar variáveis existentes. Ela adiciona (ou substitui) colunas na tabela sem alterar o número de linhas.
A variável tempo_viagem na base em está originalmente em minutos. Vamos convertê-la para horas, sobrescrevendo a coluna original:
em <- em |>
mutate(tempo_viagem = tempo_viagem / 60)Com o tempo agora em horas, podemos criar uma nova variável com a velocidade média da viagem, dividindo a distância pelo tempo:
em <- em |>
mutate(vel_media_viagem = dist_viagem / tempo_viagem)Para criar variáveis dicotômicas, a função ifelse() é útil. O código abaixo cria uma variável lógica indicando se o veículo estava acima de 60 km/h:
vel <- vel |>
mutate(acim60 = ifelse(velocidade > 60, TRUE, FALSE))Para classificações com mais de duas categorias, costumamos utilizar o ifelse() de forma aninhada, ou, de maneira mais simples, usando a função case_when(). O exemplo abaixo cria a variável veic_pesado, listando explicitamente quais categorias pertencem a cada grupo por meio do operador %in%:
vel <- vel |>
mutate(
veic_pesado = case_when(
tipo_veic %in% c("Caminhão/Ônibus") ~ "S",
tipo_veic %in% c("Moto", "Passeio", "SUV/Pickup") ~ "N"
)
)Quando nem todas as combinações precisam ser enumeradas, podemos usar .default para definir o valor dos casos restantes, tornando o código mais conciso:
vel <- vel |>
mutate(
veic_pesado = case_when(
tipo_veic == "Caminhão/Ônibus" ~ "S",
.default = "N"
)
)A sintaxe do case_when() segue o padrão condição ~ valor, com .default definindo o valor para os casos que não atendem a nenhuma condição explícita. Uma alternativa equivalente ao .default é usar TRUE ~ "N", notação mais antiga que ainda é amplamente encontrada em códigos R.
Há situações em que o interesse não está em acrescentar colunas à base original, mas em produzir uma tabela nova contendo apenas as variáveis derivadas. Para isso existe o transmute(), que calcula exatamente como o mutate() e descarta todas as colunas que não forem mencionadas. O exemplo abaixo monta uma tabela compacta com o modo de transporte, a distância em quilômetros, o tempo convertido de volta para minutos e a velocidade média, deixando de fora as demais variáveis de em.
em |>
transmute(
modo_viagem,
dist_km = dist_viagem,
tempo_min = tempo_viagem * 60,
vel_kmh = vel_media_viagem
)# A tibble: 1,000 × 4
modo_viagem dist_km tempo_min vel_kmh
<fct> <dbl> <dbl> <dbl>
1 Carro ou App 14.3 27 31.8
2 Carro ou App 36.1 55 39.4
3 TP 26.9 64 25.2
4 A pé 2.9 26 6.69
5 Carro ou App 20.6 44 28.1
6 TP 22.6 58 23.4
7 TP 29.6 83 21.4
8 TP 26.9 63 25.6
9 Carro ou App 18.3 39 28.2
10 Carro ou App 36.8 57 38.7
# ℹ 990 more rows
Observe que colunas já existentes podem ser mantidas apenas citando seu nome, como modo_viagem, ou renomeadas no caminho, como dist_viagem, que passou a se chamar dist_km. Note também que nada foi atribuído a um objeto, de modo que a tabela em original permanece intacta e o resultado apenas foi impresso no console.
A partir da versão 1.1.0 do dplyr, o transmute() passou à condição de superseded, ou seja, continua funcionando e não será removido, mas deixou de receber novos desenvolvimentos. A recomendação atual é obter o mesmo resultado com o argumento .keep = "none" do próprio mutate(). A única diferença de escrita está nas colunas já existentes, que precisam ser atribuídas a si mesmas.
em |>
mutate(
modo_viagem = modo_viagem,
dist_km = dist_viagem,
tempo_min = tempo_viagem * 60,
vel_kmh = vel_media_viagem,
.keep = "none"
)Vale conhecer as duas formas, já que o transmute() aparece com frequência em códigos escritos antes dessa mudança.
3.6 Sumarização de Dados
A sumarização reduz uma tabela a um conjunto de estatísticas calculadas a partir dos valores das colunas. No dplyr, a combinação de group_by() e summarize()1 é o principal mecanismo para isso.
Vamos calcular a velocidade média de viagem por modo de transporte na base em:
em |>
group_by(modo_viagem) |>
summarize(vel_media = mean(vel_media_viagem, na.rm = TRUE))# A tibble: 4 × 2
modo_viagem vel_media
<fct> <dbl>
1 A pé 4.19
2 Bicicleta 15.2
3 Carro ou App 35.0
4 TP 24.7
O argumento na.rm = TRUE instrui a função a ignorar os valores ausentes (NA) no cálculo. Sem ele, qualquer NA na coluna faria com que o resultado fosse NA para o grupo inteiro.
Podemos calcular várias estatísticas de uma vez no mesmo summarize():
em |>
group_by(modo_viagem) |>
summarize(
vel_media = mean(vel_media_viagem, na.rm = TRUE),
vel_mediana = median(vel_media_viagem, na.rm = TRUE),
q1 = quantile(vel_media_viagem, 0.25, na.rm = TRUE),
q3 = quantile(vel_media_viagem, 0.75, na.rm = TRUE),
desvio_pad = sd(vel_media_viagem, na.rm = TRUE)
)# A tibble: 4 × 6
modo_viagem vel_media vel_mediana q1 q3 desvio_pad
<fct> <dbl> <dbl> <dbl> <dbl> <dbl>
1 A pé 4.19 4.25 3.39 4.97 1.13
2 Bicicleta 15.2 15.1 13.0 17.2 3.55
3 Carro ou App 35.0 34.8 32.3 37.7 4.32
4 TP 24.7 24.7 22.7 26.8 3.19
Um outro uso bastante interessante é o de contagem de observações em níveis diferentes de variáveis categóricas. Para tanto, usamos a função n() dentro de summarize(), que não recebe argumentos e conta o número de linhas em cada grupo. O exemplo abaixo conta o número de viagens por combinação de sexo e posse de automóvel:
em |>
group_by(sexo, posse_auto) |>
summarize(quant = n())`summarise()` has regrouped the output.
ℹ Summaries were computed grouped by sexo and posse_auto.
ℹ Output is grouped by sexo.
ℹ Use `summarise(.groups = "drop_last")` to silence this message.
ℹ Use `summarise(.by = c(sexo, posse_auto))` for per-operation grouping
(`?dplyr::dplyr_by`) instead.
# A tibble: 4 × 3
# Groups: sexo [2]
sexo posse_auto quant
<fct> <fct> <int>
1 F N 328
2 F S 150
3 M N 285
4 M S 237
Para esse tipo de contagem simples, o dplyr oferece a função count() como atalho conveniente:
em |>
count(sexo, posse_auto)# A tibble: 4 × 3
sexo posse_auto n
<fct> <fct> <int>
1 F N 328
2 F S 150
3 M N 285
4 M S 237
Os valores são equivalentes aos do exemplo anterior, mas há uma diferença importante no objeto retornado. O group_by() seguido de summarize() retorna um tibble ainda agrupado, com o agrupamento por sexo permanecendo ativo após o summarize(). Já o count() retorna um tibble comum, sem agrupamento. Essa distinção é relevante quando se encadeiam operações após a contagem. Com o resultado do group_by() |> summarize(), um mutate() subsequente opera dentro dos grupos remanescentes. Já com o resultado do count(), ele opera sobre o tibble inteiro.
Esse é o caso de quando queremos analisar a proporção dentro de cada grupo, em vez de números absolutos, o que pode ser mais informativo. O código abaixo calcula a proporção de cada combinação dentro de cada sexo, encadeando um mutate() após o summarize():
em |>
group_by(sexo, posse_auto) |>
summarize(quant = n()) |>
mutate(prop = quant / sum(quant))`summarise()` has regrouped the output.
ℹ Summaries were computed grouped by sexo and posse_auto.
ℹ Output is grouped by sexo.
ℹ Use `summarise(.groups = "drop_last")` to silence this message.
ℹ Use `summarise(.by = c(sexo, posse_auto))` for per-operation grouping
(`?dplyr::dplyr_by`) instead.
# A tibble: 4 × 4
# Groups: sexo [2]
sexo posse_auto quant prop
<fct> <fct> <int> <dbl>
1 F N 328 0.686
2 F S 150 0.314
3 M N 285 0.546
4 M S 237 0.454
O mutate() é aplicado com o agrupamento por sexo ainda ativo, então sum(quant) é calculado dentro de cada categoria de sexo, gerando proporções que somam 1 dentro de cada grupo. Repare que o dplyr imprime uma mensagem informativa antes do resultado, avisando que a saída permaneceu agrupada por sexo. Não é erro nem sinal de problema, é o pacote tornando explícito um comportamento que passaria despercebido. A mensagem, porém, denuncia algo incômodo, pois o resultado depende de uma regra que não está escrita em lugar nenhum do código.
Para contrastar, veja o que acontece ao usar count() e encadear o mesmo mutate():
em |>
count(sexo, posse_auto) |>
mutate(prop = n / sum(n))# A tibble: 4 × 4
sexo posse_auto n prop
<fct> <fct> <int> <dbl>
1 F N 328 0.328
2 F S 150 0.15
3 M N 285 0.285
4 M S 237 0.237
Como count() retorna um tibble sem agrupamento, sum(n) é calculado sobre todas as linhas da tabela, gerando proporções em relação ao total geral, não dentro de cada sexo. As quatro proporções agora somam 1 entre si.
Nenhum dos dois resultados está errado, eles simplesmente respondem a perguntas diferentes. O problema do primeiro é de outra natureza. Quem lê aquele código precisa saber de cor que o summarize() descarta apenas o último nível de agrupamento para conseguir prever o que o mutate() seguinte vai fazer. Para este cálculo de proporção dentro de cada grupo existe uma terceira forma, que produz exatamente os mesmos números da primeira sem depender dessa memória e sem gerar mensagem nenhuma. Ela consiste em contar com count(), que devolve a tabela já limpa, e declarar explicitamente o agrupamento desejado antes do mutate():
em |>
count(sexo, posse_auto) |>
group_by(sexo) |>
mutate(prop = n / sum(n))# A tibble: 4 × 4
# Groups: sexo [2]
sexo posse_auto n prop
<fct> <fct> <int> <dbl>
1 F N 328 0.686
2 F S 150 0.314
3 M N 285 0.546
4 M S 237 0.454
Os valores são idênticos aos da primeira versão. A diferença está em que o agrupamento por sexo aparece escrito no código, em vez de sobrar de uma operação anterior. Quem lê não precisa reconstruir mentalmente o que restou do group_by() inicial, e quem escreve pode trocar group_by(sexo) por group_by(posse_auto) para inverter a pergunta sem mexer em mais nada. É a forma que recomendamos como padrão sempre que o objetivo for calcular proporções dentro de grupos.
O resultado continua agrupado por sexo, como indica a linha # Groups: sexo [2] logo abaixo do cabeçalho. Quando as etapas seguintes não dependerem desse agrupamento, encerre a cadeia com ungroup() para devolver um tibble comum.
em |>
count(sexo, posse_auto) |>
group_by(sexo) |>
mutate(prop = n / sum(n)) |>
ungroup()# A tibble: 4 × 4
sexo posse_auto n prop
<fct> <fct> <int> <dbl>
1 F N 328 0.686
2 F S 150 0.314
3 M N 285 0.546
4 M S 237 0.454
A própria mensagem do summarize() sugere dois caminhos para silenciá-la. O argumento .groups controla o que acontece com o agrupamento na saída, sendo .groups = "drop" a opção que remove todos os níveis e .groups = "drop_last" a que apenas confirma o comportamento padrão de descartar o último. Já o argumento .by permite declarar o agrupamento dentro da própria função, como em summarize(quant = n(), .by = c(sexo, posse_auto)), dispensando o group_by() anterior e devolvendo sempre um resultado sem agrupamento. Vale conhecer as duas alternativas, porque aparecem com frequência, mas nenhuma delas deixa o código mais legível do que a sequência com count() apresentada acima.
3.7 Junção de Tabelas
Em muitas situações, as informações relevantes para uma análise estão distribuídas em tabelas diferentes, que precisam ser combinadas. Essa operação é chamada de junção (join) e é realizada com base em uma ou mais colunas que identificam a correspondência entre as linhas das tabelas.
Suponha que, além dos dados de velocidade, tenhamos uma tabela com o peso médio de cada tipo de veículo:
pesos <- tibble(
tipo_veic = c("Moto", "Passeio", "SUV/Pickup", "Caminhão/Ônibus"),
peso_kg = c(120, 1300, 2000, 16000)
)
pesos# A tibble: 4 × 2
tipo_veic peso_kg
<chr> <dbl>
1 Moto 120
2 Passeio 1300
3 SUV/Pickup 2000
4 Caminhão/Ônibus 16000
Para incorporar essa informação à base vel, usamos left_join(), função do dplyr que mantém todas as linhas da tabela à esquerda (vel) e adiciona as colunas da tabela à direita (pesos) para as linhas com correspondência:
vel |>
left_join(pesos, by = "tipo_veic")# A tibble: 500 × 6
faixa tipo_veic velocidade acim60 veic_pesado peso_kg
<chr> <chr> <dbl> <lgl> <chr> <dbl>
1 Cen Passeio 54 FALSE N 1300
2 Cen Caminhão/Ônibus 50 FALSE S 16000
3 Cen Passeio 56 FALSE N 1300
4 Dir Moto 43 FALSE N 120
5 Esq SUV/Pickup 57 FALSE N 2000
6 Cen Caminhão/Ônibus 50 FALSE S 16000
7 Esq Passeio 55 FALSE N 1300
8 Esq SUV/Pickup 64 TRUE N 2000
9 Esq Passeio 53 FALSE N 1300
10 Cen Passeio 58 FALSE N 1300
# ℹ 490 more rows
O argumento by indica a coluna usada como chave de junção. Neste caso, tipo_veic está presente nas duas tabelas e identifica cada tipo de veículo. Para cada linha de vel, o left_join() busca o peso correspondente em pesos e adiciona a coluna peso_kg ao resultado.
O dplyr oferece outros tipos de junção para diferentes situações. O right_join() mantém todas as linhas da tabela à direita. O inner_join() mantém apenas as linhas com correspondência nas duas tabelas. O full_join() mantém todas as linhas de ambas as tabelas.
Na prática, é comum que as duas tabelas usem nomes diferentes para a mesma coluna-chave. Suponha que a tabela pesos tivesse a coluna chamada veiculo em vez de tipo_veic:
pesos2 <- tibble(
veiculo = c("Moto", "Passeio", "SUV/Pickup", "Caminhão/Ônibus"),
peso_kg = c(120, 1300, 2000, 16000)
)Nesse caso, o argumento by recebe um vetor nomeado, onde o nome é a coluna da tabela da esquerda e o valor é a coluna da tabela da direita:
vel |>
left_join(pesos2, by = c("tipo_veic" = "veiculo"))# A tibble: 500 × 6
faixa tipo_veic velocidade acim60 veic_pesado peso_kg
<chr> <chr> <dbl> <lgl> <chr> <dbl>
1 Cen Passeio 54 FALSE N 1300
2 Cen Caminhão/Ônibus 50 FALSE S 16000
3 Cen Passeio 56 FALSE N 1300
4 Dir Moto 43 FALSE N 120
5 Esq SUV/Pickup 57 FALSE N 2000
6 Cen Caminhão/Ônibus 50 FALSE S 16000
7 Esq Passeio 55 FALSE N 1300
8 Esq SUV/Pickup 64 TRUE N 2000
9 Esq Passeio 53 FALSE N 1300
10 Cen Passeio 58 FALSE N 1300
# ℹ 490 more rows
Uma sintaxe alternativa e mais legível é usar a função join_by(), disponível a partir do dplyr 1.1.0. Ela recebe uma expressão de igualdade entre os nomes das colunas:
vel |>
left_join(pesos2, by = join_by(tipo_veic == veiculo))# A tibble: 500 × 6
faixa tipo_veic velocidade acim60 veic_pesado peso_kg
<chr> <chr> <dbl> <lgl> <chr> <dbl>
1 Cen Passeio 54 FALSE N 1300
2 Cen Caminhão/Ônibus 50 FALSE S 16000
3 Cen Passeio 56 FALSE N 1300
4 Dir Moto 43 FALSE N 120
5 Esq SUV/Pickup 57 FALSE N 2000
6 Cen Caminhão/Ônibus 50 FALSE S 16000
7 Esq Passeio 55 FALSE N 1300
8 Esq SUV/Pickup 64 TRUE N 2000
9 Esq Passeio 53 FALSE N 1300
10 Cen Passeio 58 FALSE N 1300
# ℹ 490 more rows
O resultado é idêntico. A vantagem do join_by() é que os nomes das colunas são tratados como variáveis, sem necessidade de aspas, tornando o código mais próximo da sintaxe usada no restante do dplyr.
3.8 Fazendo tudo de uma só vez
Nas seções anteriores, cada operação foi apresentada de forma independente. Em uma análise real, essas operações quase sempre aparecem encadeadas, e é exatamente para isso que o operador pipe (|>) foi projetado.
Partindo do resultado do left_join() entre vel e pesos, queremos executar quatro operações em sequência. Primeiro, criaremos uma nova variável convertendo o peso de quilogramas para toneladas, o que torna os valores mais legíveis. Depois, filtraremos os registros para manter apenas veículos leves, excluindo caminhões e ônibus. Em seguida, selecionaremos apenas as colunas relevantes para a análise final. Por último, ordenaremos os registros pela velocidade em ordem decrescente.
Sem o pipe, seria necessário criar um objeto intermediário a cada passo:
vel_com_peso <- left_join(vel, pesos, by = "tipo_veic")
vel_com_peso <- mutate(vel_com_peso, peso_ton = peso_kg / 1000)
vel_com_peso <- filter(vel_com_peso, tipo_veic != "Caminhão/Ônibus")
vel_com_peso <- select(vel_com_peso, tipo_veic, velocidade, peso_ton)
vel_com_peso <- arrange(vel_com_peso, desc(velocidade))
vel_com_pesoO resultado é correto, mas o código é repetitivo, com o nome vel_com_peso aparecendo dez vezes. Cada linha reatribui o objeto ao mesmo nome, e qualquer erro em uma etapa intermediária exige depurar toda a cadeia manualmente. Com o pipe, o mesmo resultado é obtido de forma muito mais clara:
vel |>
left_join(pesos, by = "tipo_veic") |>
mutate(peso_ton = peso_kg / 1000) |>
filter(tipo_veic != "Caminhão/Ônibus") |>
select(tipo_veic, velocidade, peso_ton) |>
arrange(desc(velocidade))# A tibble: 424 × 3
tipo_veic velocidade peso_ton
<chr> <dbl> <dbl>
1 SUV/Pickup 66 2
2 Passeio 65 1.3
3 Passeio 65 1.3
4 Passeio 65 1.3
5 SUV/Pickup 64 2
6 Passeio 64 1.3
7 Passeio 64 1.3
8 Passeio 63 1.3
9 Passeio 63 1.3
10 Passeio 63 1.3
# ℹ 414 more rows
O raciocínio é linear: começamos com vel, adicionamos o peso com left_join(), convertemos para toneladas com mutate(), excluímos os veículos pesados com filter(), mantemos apenas as colunas de interesse com select() e ordenamos os registros por velocidade decrescente com arrange(). Cada linha descreve um passo da análise, e a leitura de cima para baixo espelha a sequência lógica do raciocínio.
Para armazenar o resultado em um novo objeto, basta atribuir a cadeia completa:
vel_leve <- vel |>
left_join(pesos, by = "tipo_veic") |>
mutate(peso_ton = peso_kg / 1000) |>
filter(tipo_veic != "Caminhão/Ônibus") |>
select(tipo_veic, velocidade, peso_ton) |>
arrange(desc(velocidade))3.9 Salvando Arquivos em Formato .rds
O formato .rds é específico do R e permite salvar um único objeto com todos os seus metadados preservados. Isso inclui os tipos das variáveis e a ordem dos níveis de factors, características que se perderiam ao salvar em formatos como .csv ou .xlsx.
Por exemplo, ao longo deste capítulo transformamos tipo_veic em um factor com os níveis na ordem “Moto”, “Passeio”, “SUV/Pickup” e “Caminhão/Ônibus”. Se salvarmos vel como .csv e depois lermos novamente o arquivo, essa ordem será perdida e tipo_veic voltará a ser uma variável character.
Para salvar vel no formato .rds:
saveRDS(vel, "vel.rds")Para carregar o arquivo em uma sessão futura:
vel <- readRDS("vel.rds")Ao carregar com readRDS(), o objeto retorna exatamente no estado em que foi salvo, com todos os tipos e ordenamentos preservados. Isso torna o .rds especialmente útil para armazenar bases de trabalho intermediárias, evitando reprocessar as transformações a cada sessão.
3.10 Dados em formato long e wide
Uma mesma informação pode ser organizada de maneiras diferentes em uma tabela, e a escolha entre elas determina quais operações ficam fáceis e quais ficam trabalhosas. As duas organizações mais comuns são o formato wide (largo) e o formato long (longo). No formato wide, cada unidade de análise ocupa uma única linha e as demais colunas guardam suas medições. No formato long, cada linha corresponde a uma combinação entre a unidade de análise e uma medição, de modo que a tabela fica mais alta e mais estreita.
Para tornar a diferença concreta, vamos criar uma pequena tabela fictícia com o número de viagens diárias por modo de transporte em três municípios.
viagens <- tibble(
municipio = c("mun_a", "mun_b", "mun_c"),
a_pe = c(120, 90, 60),
bicicleta = c( 30, 45, 20),
carro = c(200, 150, 260),
tp = c(150, 110, 80)
)
viagens# A tibble: 3 × 5
municipio a_pe bicicleta carro tp
<chr> <dbl> <dbl> <dbl> <dbl>
1 mun_a 120 30 200 150
2 mun_b 90 45 150 110
3 mun_c 60 20 260 80
Essa é a apresentação wide, e ela é confortável para leitura humana. Relatórios impressos e planilhas de divulgação quase sempre chegam nesse formato, porque a comparação entre municípios se faz percorrendo a coluna e a comparação entre modos se faz percorrendo a linha. Do ponto de vista da manipulação, porém, há um problema. Os nomes a_pe, bicicleta, carro e tp não são variáveis, são valores de uma variável que poderíamos chamar de modo de transporte. O número de viagens, única medição de fato registrada, está espalhado por quatro colunas.
Foi essa distinção que motivou o princípio tidy2 mencionado no início do capítulo, segundo o qual cada variável deve ocupar uma coluna e cada observação deve ocupar uma linha. A tabela acima viola esse princípio, e a consequência prática é que operações simples ficam trabalhosas. Calcular o total de viagens por município exige somar quatro colunas nomeadas uma a uma, e a inclusão de um quinto modo na pesquisa obrigaria a reescrever esse código.
A função pivot_longer(), do pacote tidyr, reorganiza a tabela para o formato long. Geralmente, são necesários informar 3 aspectos:
- Quais colunas devem ser empilhadas (
cols). - Qual será o nome da nova coluna que receberá os nomes antigos (
names_to), que corresponderão aos níveis dessa nova variável. - Qual será o nome da nova coluna que receberá os valores (
values_to) que existiam na tabela original.
Observe no exemplo para os nossos dados abaixo:
viagens_long <- viagens |>
pivot_longer(
cols = c(a_pe, bicicleta, carro), # poderia ser somente cols = -municipio
names_to = "modo_viagem",
values_to = "viagens"
)
viagens_long# A tibble: 9 × 4
municipio tp modo_viagem viagens
<chr> <dbl> <chr> <dbl>
1 mun_a 150 a_pe 120
2 mun_a 150 bicicleta 30
3 mun_a 150 carro 200
4 mun_b 110 a_pe 90
5 mun_b 110 bicicleta 45
6 mun_b 110 carro 150
7 mun_c 80 a_pe 60
8 mun_c 80 bicicleta 20
9 mun_c 80 carro 260
As três linhas com quatro colunas de medição viraram doze linhas. Nenhuma informação foi criada ou perdida, apenas redistribuída. Agora existe uma coluna modo_viagem que guarda a categoria e uma coluna viagens que guarda a medição, e ambas podem ser usadas diretamente pelas funções estudadas nas seções anteriores.
Veja que o ganho aparece de imediato. O total de viagens por modo, que na tabela wide exigiria somar colunas manualmente, sai de um group_by() com summarize().
viagens_long |>
group_by(modo_viagem) |>
summarize(total = sum(viagens))# A tibble: 3 × 2
modo_viagem total
<chr> <dbl>
1 a_pe 270
2 bicicleta 95
3 carro 610
Da mesma forma, a repartição modal de cada município sai de um group_by() com mutate(), exatamente como fizemos na seção de sumarização.
viagens_long |>
group_by(municipio) |>
mutate(prop = viagens / sum(viagens))# A tibble: 9 × 5
# Groups: municipio [3]
municipio tp modo_viagem viagens prop
<chr> <dbl> <chr> <dbl> <dbl>
1 mun_a 150 a_pe 120 0.343
2 mun_a 150 bicicleta 30 0.0857
3 mun_a 150 carro 200 0.571
4 mun_b 110 a_pe 90 0.316
5 mun_b 110 bicicleta 45 0.158
6 mun_b 110 carro 150 0.526
7 mun_c 80 a_pe 60 0.176
8 mun_c 80 bicicleta 20 0.0588
9 mun_c 80 carro 260 0.765
Nenhum desses dois códigos menciona os nomes dos modos. Se a pesquisa passasse a registrar viagens de motocicleta, bastaria acrescentar as linhas correspondentes e ambos continuariam funcionando sem qualquer alteração. Essa independência em relação aos valores das categorias é a principal razão pela qual o formato long domina o trabalho de análise no R. É também o formato esperado pelo ggplot2 para a elaboração de gráficos, o que veremos em detalhe no próximo capítulo.
A operação inversa é feita por pivot_wider(), que espalha uma coluna de categorias por várias colunas novas. Os argumentos centrais são names_from, que indica de qual coluna sairão os nomes das novas colunas, e values_from, que indica de qual coluna sairão os valores que as preencherão. Aplicada sobre viagens_long, a função devolve exatamente a tabela original.
viagens_long |>
pivot_wider(
names_from = modo_viagem,
values_from = viagens
)# A tibble: 3 × 5
municipio tp a_pe bicicleta carro
<chr> <dbl> <dbl> <dbl> <dbl>
1 mun_a 150 120 30 200
2 mun_b 110 90 45 150
3 mun_c 80 60 20 260
Nem toda tabela em formato long contém todas as combinações possíveis. Quando uma combinação está ausente, o pivot_wider() preenche a célula correspondente com NA. Se o valor ausente significar zero, como costuma ocorrer em contagens, o argumento values_fill = 0 resolve a questão em uma linha.
A alternância entre os dois formatos reaparecerá com frequência na segunda parte do livro. As tabelas do Censo Demográfico costumam ser divulgadas em formato wide, com uma coluna para cada faixa etária, cada classe de rendimento ou cada tipo de domicílio, e a primeira providência ao analisá-las quase sempre é empilhá-las com pivot_longer().
3.11 Exercícios
Antes de iniciar os exercícios, baixe os arquivos abaixo para uma pasta onde você irá processá-los. Crie um projeto do R dentro dessa pasta e um novo script .R dentro desse projeto para responder às questões abaixo.
Leia o arquivo
velocidades.xlsxcomo umtibblee armazene-o em um objeto denominadovele converta as variáveisfaixaetipo_veicpara factor. Em seguida, obtenha as quantidades e proporções para cada combinação de níveis defaixaetipo_veic. Na sequência filtre todas as observações com proporção acima de 0.1 e ordene-as de forma decrescente de acordo com a frequência absoluta de observações.Crie uma nova variável na base
velchamadacat_velque classifique as velocidades em três níveis usandocase_when():"Lenta": velocidade menor que 40 km/h"Normal": velocidade entre 40 e 60 km/h (incluindo esses limites)"Alta": velocidade acima de 60 km/h
Leia o arquivo
escolha_modal.csvcomo umtibble(com a codificação de caracteres correta) e armazene-o em um objeto denominadoem. Posteriormente, crie um dataframe com valores médios de emissão de CO2 por km rodado para cada um dos modos presentes na colunamodo_viagem(A pé = 0 g/km, Bicicleta = 0 g/km, Carro ou App = 120 g/km e TP = 50 g/km). Na sequência faça um left_join deemcom essa nova tabela para incorporar os valores de emissão aem.Salve os objetos
veleemapós todas as operações realizadas em formato .rds para entrega.