Aula 3: Manipulação de dados no dplyr

Profª. Edneide Ramalho

10/02/2026

Aula baseada no Capítulo 3 do livro: R for Data Science https://learning.oreilly.com/library/view/r-for-data/9781492097396/ch03.html#data-transform-prerequisites

Transformação de dados

Nem sempre os dados estão da maneira que queremos. Muitas vezes temos que:

  • criar novas variáveis

  • criar sumários para responder perguntas

  • renomear variáveis

  • reordenar observações

Por isso, vamos aprender ferramentas para fazer isso usando o pacote dplyr`.

Pré-requisitos

  • Carregar pacotes: nycflights13 e tidyverse
  • Vamos usar a base de dados flihgts para explorar os principais verbos do pacote dplyr
Mostrar/ocultar código
# install.packages("nycflights13")
library(tidyverse)
library(nycflights13)
library(gt)
Mostrar/ocultar código
dados <- flights
glimpse(dados)
# View(dados)

dplyr básico

Antes de aprender os verbos (funções), temos que entender o que tem em comum:

  • O primeiro argumento é sempre um dataframe (base de dados).

  • Os argumentos subsequentes descrevem em que colunas vamos fazer as operações, usando o nome das variáveis sem aspas

  • A saída sempre será um novo dataframe.

Para combinar múltiplos verbos, usamos o operador Pipe (|>), que basicamente encadeia as funções. (Atalho no teclado: Ctrl + Shift + M ou Cmd (⌘) + Shift + M) Vamos ver um exemplo:

(se quiser ver um pouco mais sobre os dados: ?flights)

Mostrar/ocultar código
dados |> 
  filter(dest == "IAH") |> 
  group_by(year, month, day) |> 
  summarize(
    arr_delay = mean(arr_delay, na.rm = TRUE)
  )
Mostrar/ocultar código
airports |> 
  filter(faa == "IAH") |> gt()
  • O que estamos fazendo aqui?

    • Filtramos para o destino “IAH”
    • Agrupamos por ano, mês e dia
    • Calculamos a média de tempo de atraso na chegada para cada grupo
  • Os verbos do dplyr são organizados em 4 grupos baseado em suas operações:

    • linhas

    • colunas

    • grupos

    • tabelas

Linhas

Os verbos mais importantes são:

  • filter(): escolhe que linhas são mostradas sem alterar a ordem

  • arrange(): muda a ordem das linhas sem alterar o que é apresentado.

  • distinct(): encontra linhas com valores únicos, também pode modificar colunas

filter()

  • Voos que se atrasaram em mais de duas horas:
Mostrar/ocultar código
dados |> 
  filter(dep_delay > 120)
  • Podemos usar qualquer sinal de comparação: >=, <=, <, == (sinal de igualdade); podemos usar & ou ,para “E” e | para “OU”.

  • Voos que partiram em 1 de Janeiro:

Mostrar/ocultar código
dados |> 
  filter(month == 1, day == 1)
  • Voos que partiram em Janeiro ou Fevereiro:
Mostrar/ocultar código
dados |> 
  filter(month == 1 | month == 2)
  • Também podemos usar %in% para quando o filtro está em uma lista, por exemplo:
Mostrar/ocultar código
dados |> 
  filter(month %in% c(1, 2))

arrange()

  • Muda a ordem das linhas baseada no valor das colunas:
Mostrar/ocultar código
dados |> 
  arrange(year, month, day, dep_time)
  • Se quiser que a ordenação seja em ordem decrescente, devemos usar desc():
Mostrar/ocultar código
dados |> 
  arrange(desc(dep_delay))

O tempo de atraso de saída dep_delay está ordenado do maior para o menor tempo de atraso.

distinct()

  • Encontra todas as linhas distintas no dataset, opera nas linhas. Isso ajuda a remover linhas duplicadas se houver.
Mostrar/ocultar código
dados |> 
  distinct()
  • Encontrando todos os pares distintos de origem e destino:
Mostrar/ocultar código
dados |> 
  distinct(origin, dest)

Temos 224 pares distintos de destino e chegada.

  • Se você quiser manter todas as outras colunas use .keep_all = TRUE
Mostrar/ocultar código
dados |> 
  distinct(origin, dest, .keep_all = TRUE)

Note que todos os voos estão com data 1 de janeiro, porque encontramos a primeira linha única e depois descartamos o resto.

  • Se quisermos saber o número de ocorrências para cada combinação usamos o verbo count() (que veremos mais a frente):
Mostrar/ocultar código
dados |> 
  count(origin, dest, sort = TRUE)

Exercícios

  1. In a single pipeline for each condition, find all flights that meet the condition:

    • Had an arrival delay of two or more hours

    • Flew to Houston (IAH or HOU)

    • Were operated by United, American, or Delta

    • Departed in summer (July, August, and September)

    • Arrived more than two hours late, but didn’t leave late

    • Were delayed by at least an hour, but made up more than 30 minutes in flight

  2. Sort flights to find the flights with the longest departure delays. Find the flights that left earliest in the morning.

  3. Sort flights to find the fastest flights. (Hint: Try including a math calculation inside of your function.)

  4. Was there a flight on every day of 2013?

  5. Which flights traveled the farthest distance? Which traveled the least distance?

  6. Does it matter what order you used filter() and arrange() if you’re using both? Why/why not? Think about the results and how much work the functions would have to do.

Colunas

Verbos importantes:

  • mutate()

  • select()

  • rename()

  • relocate()

mutate()

  • Adiciona novas colunas que são calculadas a partir de colunas existentes.

  • Vamos calcular o ganho e a velocidade em milhas por hora.

Mostrar/ocultar código
dados |> 
  mutate(
    ganho = dep_delay - arr_delay,
    velocidade = distance / air_time * 60
  )
  • As novas colunas são adicionadas no final da tabela. Se quiser colocá-las no início, usamos o argumento .before.
Mostrar/ocultar código
dados |> 
  mutate(
    ganho = dep_delay - arr_delay,
    velocidade = distance / air_time * 60,
    .before = 1
  )
  • O ponto antes da palavra before significa que isso é um argumento da função e não uma nova variável criada. Podemos usar .before e .after para inserir as variáveis em qualquer lugar da nossa base de dados. Por exemplo, depois da variável day :
Mostrar/ocultar código
dados |> 
  mutate(
    ganho = dep_delay - arr_delay,
    velocidade = distance / air_time * 60,
    .after = day
  )
  • Também podemos controlar que variáveis são mantidas com o argumento .keep . Um argumento particular é o used. Ele especifica que mantenhamos apenas as variáveis usadas nos cálculos.
Mostrar/ocultar código
dados |> 
  mutate(
    ganho = dep_delay - arr_delay,
    horas = air_time / 60,
    ganho_por_hora = ganho/horas,
    .keep = "used"
  )

select()

  • Permite que você selecionae um subconjunto das variáveis do seu dataset.

  • Por exemplo, selecionar o ano, mês e dia

    Mostrar/ocultar código
    dados |> 
      select(year, month, day)
  • Selecionar todas as colunas entre year e day inclusive:

Mostrar/ocultar código
dados |> 
  select(year:day)
  • Selecionar todas as colunas exceto aquelas entre year e day (inclusive):
Mostrar/ocultar código
dados |> 
  select(!year:day)
  • Selecione todas as colunas que são categóricas (character):
Mostrar/ocultar código
dados |> 
  select(where(is.character)) 
  • Há vários funções que podem ajudar na seleção de variáveis:

    • starts_with("abc")

    • ends_with("abc")

    • contains("ijk")

    • num_range("x", 1:3)

      • combina com: x1, x2, x3
  • Também podemos renomear variáveis com select() usando o “=”. O novo nome vem do lado esquerdo, e o antigo do lado direito:

Mostrar/ocultar código
dados |> 
  select(tail_num = tailnum)

rename()

  • Usado para renomear alguns nomes de variáveis:
Mostrar/ocultar código
dados |> 
  rename(tail_num = tailnum)
  • Se você quiser corrigir o nome de todas as colunas da sua base de dados, vale à pena checar a função janitor::clean_names() que fornece uma limpeza automática de maneira útil.

relocate()

  • Por padrão essa função move as variáveis para as primeiras colunas do dataset:
Mostrar/ocultar código
dados |> 
  relocate(time_hour, air_time)
  • Podemos usar os argumentos: .before() e .after() da mesma forma que fizemos com o verbo mutate().
Mostrar/ocultar código
dados |> 
  relocate(year:dep_time, .after = time_hour)
Mostrar/ocultar código
dados |> 
  relocate(starts_with("arr"), .before = dep_time) |> 
  View()

Exercises

  1. Compare dep_time, sched_dep_time, and dep_delay. How would you expect those three numbers to be related?

  2. Brainstorm as many ways as possible to select dep_time, dep_delay, arr_time, and arr_delay from flights.

  3. What happens if you specify the name of the same variable multiple times in a select() call?

  4. What does the any_of() function do? Why might it be helpful in conjunction with this vector?

    variables <- c("year", "month", "day", "dep_delay", "arr_delay")
  5. Does the result of running the following code surprise you? How do the select helpers deal with upper- and lowercase by default? How can you change that default?

    flights |> select(contains("TIME"))
  6. Rename air_time to air_time_min to indicate units of measurement and move it to the beginning of the data frame.

  7. Por que a função abaixo não funciona e o que a mensagem de erro nos diz?

    flights |> 
      select(tailnum) |> 
      arrange(arr_delay)
    #> Error in `arrange()`:
    #> ℹ In argument: `..1 = arr_delay`.
    #> Caused by error:
    #> ! object 'arr_delay' not found

O operador Pipe |>

Atalho: Ctrl/Cmd+Shift+M

Vimos alguns exemplos simples com pipe, mas seu maior uso vem quando combinamos múltiplos verbos para responder à alguma pergunta:

Por exemplo,

Note

Quais os vôos mais rápidos com destino ao aeroporto de Houston (IAH)?

  • Para responder, vamos combinar: filter(), mutate(), select(), e arrange().
Mostrar/ocultar código
dados |> 
  filter(dest == "IAH") |> 
  mutate(speed = distance/air_time * 60,
         velocidade_km_h = 1.61 * speed) |> 
  select(year:day, dep_time, carrier, flight, speed, velocidade_km_h) |> 
  arrange(desc(speed)) |> View()
  • A vantagem e usar o pipe é que ele torna o código limpo e fácil de ler, caso contrário, teríamos um encademento de funções que seria pouco legível.

Grupos

Principais verbos:

  • group_by()

  • summarize()

group_by()

Divide a sua base de dados em grupos úteis para a sua análise.

Mostrar/ocultar código
dados |> 
  group_by(month)

Fazendo somente essa operação não muda muita coisa, mas agora vemos que os dados estão agrupados, e qualquer operação que realizarmos, será feita dentro de cada grupo.

summarize()

  • A operação mais importante de um grupo é o sumário de informações dentro de cada grupo.

  • Daí, teremos uma única linha para cada grupo e seu sumário, respectivamente.

  • Calculando o atraso de partidas por mês.

Mostrar/ocultar código
dados |> 
  group_by(month) |> 
  summarize(
    atraso_medio = mean(dep_delay, na.rm = TRUE)
  )

Qual foi o mês com mais atrasos?

Mostrar/ocultar código
dados |> 
  group_by(month) |> 
  summarize(
    atraso_medio = mean(dep_delay, na.rm = TRUE)
  ) |> 
  arrange(desc(atraso_medio))
  • Vamos fazer mais um exemplo. Quantos voos foram feitos por mês e ano?
Mostrar/ocultar código
dados |> 
  group_by(year, month) |> 
  summarize(qtd_voos = n()) |> 
  ungroup() |> 
  arrange(desc(qtd_voos))
  • Juntando tudo:
Mostrar/ocultar código
sumario_voos <- dados |> 
  group_by(month) |> 
  summarize(
    qtd_voos = n(),
    atraso_medio = mean(dep_delay, na.rm = TRUE)
  )

As funções slice\_

  • df |> slice_head(n = 1) -> mostra a primeira linha de cada grupo.

  • df |> slice_tail(n = 1) -> mostra a última linha de cada grupo.

  • df |> slice_min(x, n = 1) -> Mostra a linha com o menor valor da coluna x.

  • df |> slice_max(x, n = 1) -> Mostra a linha com maior valor da coluna x.

  • df |> slice_sample(n = 1) -> Mostra uma linha aleatória.

  • Mostrando as duas primeiras linhas:

Mostrar/ocultar código
sumario_voos |> 
  slice_head(n = 2)
# A tibble: 2 × 3
  month qtd_voos atraso_medio
  <int>    <int>        <dbl>
1     1    27004         10.0
2     2    24951         10.8
  • Mostrando as duas últimas linhas:
Mostrar/ocultar código
sumario_voos |> 
  slice_tail(n = 2)
# A tibble: 2 × 3
  month qtd_voos atraso_medio
  <int>    <int>        <dbl>
1    11    27268         5.44
2    12    28135        16.6 
  • Qual o mês com menor quantidade de vôos?
Mostrar/ocultar código
sumario_voos |> 
  slice_min(qtd_voos, n = 1)
# A tibble: 1 × 3
  month qtd_voos atraso_medio
  <int>    <int>        <dbl>
1     2    24951         10.8
  • Qual o mês com maior quantidade de vôos?
Mostrar/ocultar código
sumario_voos |> 
  slice_max(qtd_voos, n = 1)
  • Uma linha aleatória:
Mostrar/ocultar código
sumario_voos |> 
  slice_sample(n = 1)
  • Encontrando os vôos mais atrasados na chegada para cada destino:
Mostrar/ocultar código
dados |> 
  group_by(dest) |> 
  slice_max(arr_delay, n = 1) |> 
  relocate(dest)

Desafios

  • Calcule a quantidade de voos e o tempo médio no ar para cada destino.
  • Que destino teve o maior número de voos?
  • Que destino teve o menor tempo no ar?

Agrupando por múltiplas variáveis

Desagrupando (ungroup)

  • Podemos usar a função ungroup(), para remover o agrupamento.

  • Isso é importante para se fazer algumas operações considerando todo o dataset ao invés somente dos grupos.

Exercises

  1. Which carrier has the worst average delays? Challenge: Can you disentangle the effects of bad airports versus bad carriers? Why/why not? (Hint: Think about flights |> group_by(carrier, dest) |> summarize(n()).)

  2. Find the flights that are most delayed upon departure from each destination.

  3. How do delays vary over the course of the day. Illustrate your answer with a plot.

  4. What happens if you supply a negative n to slice_min() and friends?

  5. Explain what count() does in terms of the dplyr verbs you just learned. What does the sort argument to count() do?

  6. Suppose we have the following tiny data frame:

    df <- tibble(
      x = 1:5,
      y = c("a", "b", "a", "a", "b"),
      z = c("K", "K", "L", "L", "K")
    )
    1. Write down what you think the output will look like; then check if you were correct and describe what group_by() does.

      df |>
        group_by(y)
    2. Write down what you think the output will look like; then check if you were correct and describe what arrange() does. Also comment on how it's different from the group_by() in part (a).

      df |>
        arrange(y)
    3. Write down what you think the output will look like; then check if you were correct and describe what the pipeline does.

      df |>
        group_by(y) |>
        summarize(mean_x = mean(x))
    4. Write down what you think the output will look like; then check if you were correct and describe what the pipeline does. Then, comment on what the message says.

      df |>
        group_by(y, z) |>
        summarize(mean_x = mean(x))
    5. Write down what you think the output will look like; then check if you were correct and describe what the pipeline does. How is the output different from the one in part (d)?

      df |>
        group_by(y, z) |>
        summarize(mean_x = mean(x), .groups = "drop")
    6. Write down what you think the outputs will look like; then check if you were correct and describe what each pipeline does. How are the outputs of the two pipelines different?

      df |>
        group_by(y, z) |>
        summarize(mean_x = mean(x))
      
      df |>
        group_by(y, z) |>
        mutate(mean_x = mean(x))