R: Remodelação de dados no R: Um guia completo sobre o tidyr

Última atualização: 2026-08-26

Em projetos reais, 80% dos dados consistem em “tabelas largas” (uma entidade por linha, com campos que representam atributos), mas a análise geralmente requer “tabelas longas” (uma observação por linha). Nesta aula, aprenderemos como “transformar” dados no R — usando a reformulação do tidyr + a junção do dplyr — para converter dados de qualquer formato para o formato necessário para a análise.

Ao concluir esta lição, você será capaz de: converter entre tabelas largas e longas, dividir e unir colunas, unir várias tabelas por meio de junções e criar qualquer tipo de relatório.

1. O que você vai aprender



2. Um ponto crítico na transformação de dados

(1) Desafio: tabelas extensas são difíceis de analisar

Xiao Zhao recebeu um relatório trimestral de vendas (tabela ampla):

TEXT 📖 Somente leitura
Region    Q1    Q2    Q3    Q4
Beijing  1000  1200  1100  1500
Shanghai  1500  1800  1700  2000
Guangzhou   800   900  1000  1200

O gerente solicitou um “Gráfico de tendência de vendas trimestrais” — o ggplot2 requer uma tabela longa:

TEXT 📖 Somente leitura
Region   Quarter  Sales
Beijing   Q1    1000
Beijing   Q2    1200
...

Se levar 5 minutos para copiar e colar no Excel; Python pd.melt — uma linha; R tidyr

(2) Solução usando R

R
library(tidyr)

# Wide Table → Long Table(One line)
sales_long <- sales_wide |>
  pivot_longer(
    cols = c(Q1, Q2, Q3, Q4),
    names_to = "Quarter",
    values_to = "Sales"
  )

Converta largura em altura com apenas uma linha de código.



3. Mesas largas x mesas compridas

(1) Comparação entre as duas formas

100%
graph LR
    A[Wide Table Wide] -->|pivot_longer| B[Long Table Long]
    B -->|pivot_wider| A
    
    subgraph Width Examples
        C[Region Q1 Q2 Q3 Q4]
    end
    subgraph Long Example
        D[Region Quarter Sales]
    end

(2) Quando usar cada um?

Cenário Recomendação
Armazenamento de dados Tabela ampla (reduz o número de linhas)
Visualizar relatórios Tabela ampla (compatível com o Excel)
Gráficos com ggplot2 Tabela longa (obrigatória)
Análise com dplyr Tabela longa (compatível com group_by)
Aprendizado de máquina Tabela ampla (1 amostra por linha)


4. pivot_longer(): Largura → Comprimento

(1) Sintaxe básica

R
pivot_longer(
  data,
  cols,                 # Columns to convert
  names_to = "name",    # Newly Listed(Originally listed)
  values_to = "value",  # Newly Listed(Original value)
  names_prefix = "",    # Listed Public Prefixes
  names_sep = NULL,     # Delimiter(Split Column Names)
  names_pattern = NULL, # Regular(Extract Column Names)
  values_drop_na = FALSE
)

(2) Conversões básicas

R
library(tidyr)

# Wide Table
sales_wide <- tibble(
  region = c("Beijing", "Shanghai", "Guangzhou"),
  Q1 = c(1000, 1500, 800),
  Q2 = c(1200, 1800, 900),
  Q3 = c(1100, 1700, 1000),
  Q4 = c(1500, 2000, 1200)
)
print(sales_wide)
# # A tibble: 3 × 5
#   region    Q1    Q2    Q3    Q4
#   <chr>  <dbl> <dbl> <dbl> <dbl>
# 1 Beijing    1000  1200  1100  1500
# 2 Shanghai    1500  1800  1700  2000
# 3 Guangzhou     800   900  1000  1200

# Rotate the Long Table
sales_long <- sales_wide |>
  pivot_longer(
    cols = c(Q1, Q2, Q3, Q4),
    names_to = "quarter",
    values_to = "sales"
  )
print(sales_long)
# # A tibble: 12 × 3
#    region quarter sales
#    <chr>  <chr>   <dbl>
#  1 Beijing   Q1       1000
#  2 Beijing   Q2       1200
#  3 Beijing   Q3       1100
#  4 Beijing   Q4       1500
#  5 Shanghai   Q1       1500
#  6 Shanghai   Q2       1800
#  7 Shanghai   Q3       1700
#  8 Shanghai   Q4       2000
# ...

(3) 4 maneiras de selecionar colunas

R
# 1. Explicitly listed vectors
cols = c(Q1, Q2, Q3, Q4)

# 2. starts_with()(Most Commonly Used)
cols = starts_with("Q")

# 3. Number Range
cols = 2:5  # Columns 2-5

# 4. Listed pattern(Regular)
cols = matches("^Q\\d+$")

# 5. Exclude a specific column
cols = -region

(4) Prática: Dividindo nomes de colunas complexos

R
# Listing includes prefixes
df <- tibble(
  id = 1:3,
  sales_2022 = c(100, 200, 300),
  sales_2023 = c(150, 250, 350),
  profit_2022 = c(10, 20, 30),
  profit_2023 = c(15, 25, 35)
)

# Split sales_2022 → Type=Sales, Year=2022
df_long <- df |>
  pivot_longer(
    cols = -id,
    names_to = c("type", "year"),
    names_sep = "_",
    values_to = "value"
  )
print(df_long)
# # A tibble: 12 × 4
#       id type    year  value
#    <int> <chr>   <chr> <dbl>
#  1     1 sales   2022    100
#  2     1 sales   2023    150
#  3     1 profit  2022     10
#  4     1 profit  2023     15
#  ...

(5) Prática: Extração de dados com expressões regulares

R
# Listing Format:Variable_Year_Quarter
df <- tibble(
  id = 1:2,
  sales_2024_Q1 = c(100, 200),
  sales_2024_Q2 = c(150, 250),
  cost_2024_Q1 = c(50, 80),
  cost_2024_Q2 = c(70, 100)
)

# Extract using regular expressions
df_long <- df |>
  pivot_longer(
    cols = -id,
    names_to = c("type", "year", "quarter"),
    names_pattern = "(\\w+)_(\\d+)_(\\w+)",
    values_to = "value"
  )
print(df_long)


5. pivot_wider(): Comprimento → Largura

(1) Sintaxe básica

R
pivot_wider(
  data,
  id_cols = NULL,        # Identifier Column(Keep as a line)
  names_from = name,     # Sources of New Listings
  values_from = value,   # Source of Newly Listed Values
  values_fill = NULL,    # Missing Value Imputation
  names_prefix = ""      # New Listing Prefix
)

(2) Conversões básicas

R
# Converting a Long Table to a Wide Table
sales_wide_again <- sales_long |>
  pivot_wider(
    id_cols = region,
    names_from = quarter,
    values_from = sales
  )
print(sales_wide_again)
# # A tibble: 3 × 5
#   region    Q1    Q2    Q3    Q4
#   <chr>  <dbl> <dbl> <dbl> <dbl>
# 1 Beijing    1000  1200  1100  1500
# ...

(3) Aplicação prática: como lidar com valores ausentes

R
# There are missing data points(Not all region All of them have everything quarter)
df <- tibble(
  region = c("A", "A", "B", "C", "C"),
  quarter = c("Q1", "Q2", "Q1", "Q2", "Q3"),
  sales = c(100, 200, 150, 250, 300)
)

# Width Conversion Table(Fill in the blank 0)
df_wide <- df |>
  pivot_wider(
    id_cols = region,
    names_from = quarter,
    values_from = sales,
    values_fill = 0
  )
print(df_wide)
# # A tibble: 3 × 4
#   region    Q1    Q2    Q3
#   <chr>  <dbl> <dbl> <dbl>
# 1 A        100   200     0
# 2 B        150     0     0
# 3 C          0   250   300


6. separate() / unite(): Dividir e mesclar colunas

(1) separate() Dividir

R
# Split the Date Column
df <- tibble(date = c("2024-01-15", "2024-02-20"))

# Split by delimiter
df |> separate(date, into = c("year", "month", "day"), sep = "-")
# # A tibble: 2 × 3
#   year  month day
#   <chr> <chr> <chr>
# 1 2024  01    15
# 2 2024  02    20

# Breakdown by Location
df |> separate(date, into = c("year", "rest"), sep = 4)
# year  rest
# "2024" "-01-15"

# Split using regular expressions
df |> separate(date, into = c("year", "month", "day"),
               sep = "(-)")

(2) unite() Mesclar

R
# Merge Year/Month/Day
df_split <- tibble(
  year = c("2024", "2024"),
  month = c("01", "02"),
  day = c("15", "20")
)

df_split |> unite("date", year, month, day, sep = "-")
# # A tibble: 2 × 1
#   date
#   <chr>
# 1 2024-01-15
# 2 2024-02-20


7. dplyr: 4 tipos de junções

(1) Tabela de referência rápida para tipos de junção

100%
graph TB
    A[dplyr 4 types of join] --> B[left_join<br/>Keep the entire table]
    A --> C[right_join<br/>Keep the entire right-hand table]
    A --> D[inner_join<br/>Find the Intersection]
    A --> E[full_join<br/>Union-Disjoint Set]
    
    style A fill:#fff3cd
    style B fill:#cce5ff
    style C fill:#d4edda
    style D fill:#f8d7da
    style E fill:#e1d4ff

(2) Analogia com SQL

dplyr SQL Significado
left_join(a, b, by) LEFT JOIN Manter tudo como está
right_join(a, b, by) RIGHT JOIN Manter todos os b
inner_join(a, b, by) INNER JOIN Reservar a∩b
full_join(a, b, by) FULL OUTER JOIN Reservar a∪b

(3) Prática: Unindo duas tabelas

R
# Customer Table
customers <- tibble(
  id = 1:5,
  name = c("Alice", "Bob", "Charlie", "Diana", "Eve")
)

# Orders Table
orders <- tibble(
  customer_id = c(1, 1, 2, 4, 4, 6),
  amount = c(100, 200, 150, 300, 250, 500)
)

# 1. left_join:Retain all customers(Those with no orders are NA)
customers |> left_join(orders, by = c("id" = "customer_id"))
# # A tibble: 7 × 3  ← 5 Customer + 6 Order = 7 row(Charlie/Eve No orders)
#    id name    amount
# 1  1 Alice     100
# 2  1 Alice     200
# 3  2 Bob       150
# 4  3 Charlie    NA  ← No orders
# 5  4 Diana     300
# 6  4 Diana     250
# 7  5 Eve        NA  ← No orders

# 2. inner_join:Find the Intersection
customers |> inner_join(orders, by = c("id" = "customer_id"))
# # A tibble: 6 × 3  ← 4 A customer with an order

# 3. full_join:Union-Disjoint Set
customers |> full_join(orders, by = c("id" = "customer_id"))
# # A tibble: 7 × 3  ← Includes id=6 "No customer" Order

# 4. right_join:Keep all orders
customers |> right_join(orders, by = c("id" = "customer_id"))
# # A tibble: 6 × 3  ← 6 Orders

(4) Junção com várias chaves

R
# Sort by multiple columns join
df1 |> left_join(df2, by = c("year", "month", "id"))


8. Prática: Analisando dados de vendas em várias tabelas

A seguir, apresentamos um exemplo de um fluxo de trabalho completo que reúne todos os conceitos de refatoração abordados nesta lição.

▶ Exemplo: Análise detalhada dos dados de vendas do 4º trimestre

R 📖 Somente leitura
# ============================================
# 4 In-Depth Analysis of Quarterly Sales Data
# Features:Wide↔Long Conversion + Multiple Tables join + Reports
# ============================================

library(tidyr)
library(dplyr)

# 1. Prepare the raw data(Wide Table)
sales_wide <- tibble(
  region = c("Beijing", "Shanghai", "Guangzhou", "Shenzhen"),
  Q1 = c(1000, 1500, 800, 1200),
  Q2 = c(1200, 1800, 900, 1400),
  Q3 = c(1100, 1700, 1000, 1300),
  Q4 = c(1500, 2000, 1200, 1600)
)

# 2. Wide Table → Long Table
sales_long <- sales_wide |>
  pivot_longer(
    cols = c(Q1, Q2, Q3, Q4),
    names_to = "quarter",
    values_to = "sales"
  )

cat("=== Long Table(First 8 rows)===\n")
print(head(sales_long, 8))

# 3. Calculate the year-over-year growth
sales_growth <- sales_long |>
  group_by(region) |>
  arrange(quarter) |>
  mutate(
    prev_sales = lag(sales),
    growth = round((sales - prev_sales) / prev_sales * 100, 2)
  )

cat("\n=== Year-over-year increase ===\n")
print(sales_growth)

# 4. Long Table → Wide Table(Generate a Comparison Report)
report_wide <- sales_growth |>
  select(region, quarter, sales, growth) |>
  pivot_wider(
    id_cols = region,
    names_from = quarter,
    values_from = c(sales, growth),
    names_glue = "{quarter}_{.value}"
  )

cat("\n=== Wide-Format Reports ===\n")
print(report_wide)

# 5. Breakdown by Quarter → Quarter+Numbers
sales_long2 <- sales_long |>
  separate(quarter, into = c("q_prefix", "q_num"), sep = 1, remove = FALSE)

cat("\n=== Breakdown by Quarter ===\n")
print(sales_long2)

# 6. Multiple Tables join:Merge Customer Information
customers <- tibble(
  region = c("Beijing", "Shanghai", "Guangzhou", "Shenzhen", "Hangzhou"),
  manager = c("Alice", "Bob", "Charlie", "Diana", "Eve"),
  tier = c("A", "A", "B", "A", "C")
)

# left_join:Retain all sales regions
sales_with_manager <- sales_long |>
  left_join(customers, by = "region")

cat("\n=== Sales + Account Manager ===\n")
print(head(sales_with_manager, 8))

# 7. Grouped by Manager
manager_summary <- sales_with_manager |>
  group_by(manager, tier) |>
  summarise(
    Total Sales = sum(sales),
    Average Quarter = round(mean(sales), 2),
    Highest Quarterly = max(sales)
  ) |>
  arrange(desc(Total Sales))

cat("\n=== Manager Performance ===\n")
print(manager_summary)

# 8. Quarter-by-Quarter Comparison Report
quarter_comparison <- sales_long |>
  group_by(quarter) |>
  summarise(
    Total Sales = sum(sales),
    Average = round(mean(sales), 2),
    Maximum = max(sales),
    Minimum = min(sales)
  )

cat("\n=== Quarter-over-Quarter Comparison ===\n")
print(quarter_comparison)

# 9. Complex Wide Table(Multi-indicator)
complex_report <- sales_with_manager |>
  group_by(region) |>
  summarise(
    Total Sales = sum(sales),
    Average Quarter = round(mean(sales), 2)
  ) |>
  pivot_wider(
    names_from = region,
    values_from = c(Total Sales, Average Quarter)
  )

cat("\n=== Complex Wide Table ===\n")
print(complex_report)

# 10. Reverse Operation:Convert the width table above back to the length table(For drawing)
viz_data <- complex_report |>
  pivot_longer(
    cols = everything(),
    names_to = c("metric", "region"),
    names_sep = "_",
    values_to = "value"
  )

cat("\n=== Data Visualization ===\n")
print(viz_data)
90 linhas de lógica (limite de 40, somente leitura)

Resultado esperado (trecho):

TEXT 📖 Somente leitura
=== Manager Performance ===
# A tibble: 4 × 5
  manager tier  Total Sales Average Quarter Highest Quarterly
  <chr>   <chr>  <int>    <dbl>    <int>
1 Bob    A       7000    1750       2000
2 Alice    A       4800    1200       1500
3 Diana    A       5500    1375       1600
4 Charlie    B       3900     975       1200

=== Quarter-over-Quarter Comparison ===
# A tibble: 4 × 5
  quarter Total Sales   Average  Maximum  Minimum
  <chr>    <int>  <dbl> <int> <int>
1 Q1        4500  1125    1500   800
2 Q2        5300  1325    1800   900
3 Q3        5100  1275    1700  1000
4 Q4        6300  1575    2000  1200

❓ Perguntas Frequentes

P: Quando devo usar tabelas largas em vez de tabelas longas? R: Use tabelas largas para armazenamento e exibição (mais fáceis de usar) e tabelas longas para análise e criação de gráficos (exigidas pelo ggplot2). Todas as funções do tidyverse pressupõem tabelas longas.

P: Como se escreve pivot_longer para cols? R: Existem 4 maneiras: c(Q1, Q2, Q3, Q4) lista explícita / starts_with("Q") correspondência de prefixo / 2:5 intervalo numérico / matches("^Q\\d+$") expressão regular.

P: Qual é a diferença entre pivot_longer e gather? R: gather é a função tidyr antiga (obsoleta), enquanto pivot_longer é a nova versão. Ela é mais poderosa e consistente; o novo código usa pivot_longer.

P: Qual dos quatro tipos de junção devo usar? R: O mais utilizado é o left_join (mantém todos os dados da tabela primária). O inner_join realiza uma correspondência exata, o full_join mantém todos os dados e o right_join deve ser usado com moderação (basta trocar as tabelas da esquerda e da direita).

P: O que devo fazer se o número de linhas aumentar após uma junção? R: Verifique se a chave de junção é única. Uma junção um-para-muitos é normal, mas uma junção muitos-para-muitos resultará em um produto cartesiano. Use relationship = "many-to-many" para especificá-la explicitamente.

P: Como faço para dividir os nomes das colunas após converter uma tabela larga em uma tabela longa? R: Use names_sep = "_" para dividir (delimitador fixo) ou names_pattern = "(\\w+)_(\\d+)" para dividir (expressão regular). Declare as colunas resultantes usando names_to = c("a", "b").


📖 Resumo


📝 Exercícios

  1. Problema básico: Construa uma tabela larga (3 linhas × 4 colunas: Região/Q1/Q2/Q3/Q4), converta-a em uma tabela longa usando pivot_longer, depois converta-a de volta em uma tabela larga usando pivot_wider e verifique se os dados permanecem completos e intactos.

  2. Problema básico: Crie um tibble contendo uma coluna chamada year_month (“2024-01”); use separate() para dividi-la em duas colunas, year e month; e, em seguida, use unite() para juntá-las novamente.

  3. Exercício básico: Simule duas tabelas (Tabela de Clientes + Tabela de Pedidos) e combine-as utilizando quatro tipos diferentes de junções. Registre a diferença no número de linhas retornadas para cada tipo de junção.

  4. Exercício avançado: Crie uma tabela larga complexa (formato dos nomes das colunas: variável_ano_trimestre), use pivot_longer + names_sep para convertê-la em uma tabela longa em uma única etapa e, em seguida, use pivot_wider + names_glue para convertê-la de volta em uma tabela larga com um prefixo adicionado.

  5. Desafio: Simule um cenário completo: ① Importe uma tabela larga contendo dados de vendas de 4 filiais ao longo de 4 trimestres ② Importe uma tabela contendo informações básicas das filiais (região/gerente/departamento) ③ Faça a junção usando um LEFT JOIN ④ Converta para uma tabela longa usando pivot_longer ⑤ Agrupe e resuma por gerente ⑥ Gere um relatório resumido (tabela larga) ⑦ Use o ggplot2 para traçar um “Gráfico de Tendência de Vendas Trimestrais” (tabela longa). Salve o código de todo o processo e as capturas de tela dos resultados.

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%