长表让每行表示一条观测,每列表示一个变量;宽表则可能把同一个变量的不同条件分散在多列。两种形状没有绝对优劣,关键是当前操作需要什么结构。
宽表转长表
library(dplyr)
library(tidyr)
wide <- tibble(
id = 1:3,
gene_A = c(10, 20, 30),
gene_B = c(15, 25, 35)
)
long <- wide |>
pivot_longer(
cols = starts_with("gene_"),
names_to = "gene",
values_to = "expression"
)
结果中,一个 id 对应多行,每行保存一个基因及其表达量。
选择需要拉长的列
cols 使用 tidyselect 语法:
wide |> pivot_longer(gene_A:gene_B)
wide |> pivot_longer(-id)
wide |> pivot_longer(where(is.numeric) & !id)
应明确哪些列是标识变量,哪些列名实际上编码了变量值。误把 ID 或固定协变量拉长,会改变观测单位。
从列名中提取信息
列名 A_1、A_2、B_1 同时编码类型和重复编号:
wide2 <- tibble(
id = 1:2,
A_1 = c(5, 6),
A_2 = c(7, 8),
B_1 = c(9, 10)
)
wide2 |>
pivot_longer(
cols = -id,
names_to = c("type", "replicate"),
names_sep = "_",
values_to = "value"
)
结构更复杂时使用正则捕获组:
wide2 |>
pivot_longer(
cols = -id,
names_to = c("type", "replicate"),
names_pattern = "([A-Z])_(\\d+)",
values_to = "value"
)
如果只需要移除固定前缀:
wide |>
pivot_longer(
starts_with("gene_"),
names_to = "gene",
names_prefix = "gene_",
values_to = "expression"
)
缺失值与不存在的组合
wide2 |>
pivot_longer(
cols = -id,
names_to = c("type", "replicate"),
names_sep = "_",
values_to = "value",
values_drop_na = TRUE
)
values_drop_na = TRUE 会删除值为 NA 的结果行。这适合宽表中的 NA 代表组合不存在的情况;如果它代表一次真实但缺失的测量,删除后会隐藏缺失信息。
长表转宽表
long2 <- tibble(
id = c(1, 1, 2, 2),
key = c("A", "B", "A", "B"),
value = c(10, 20, 30, 40)
)
long2 |>
pivot_wider(
names_from = key,
values_from = value
)
names_from 的取值成为新列名,values_from 提供单元格值,其余列共同构成行标识。
填充缺失组合
scores <- tibble(
name = c("Alice", "Alice", "Bob"),
test = c("Math", "Science", "Math"),
score = c(80, 90, 70)
)
scores |>
pivot_wider(
names_from = test,
values_from = score,
values_fill = NA
)
只有当缺失组合在业务上确实代表零时,才使用 values_fill = 0。没有记录和观测值为零是不同状态。
同时展开多个值列
measurements <- tibble(
id = c(1, 1, 2, 2),
visit = c("baseline", "followup", "baseline", "followup"),
weight = c(70, 68, 82, 79),
pressure = c(130, 124, 145, 136)
)
measurements |>
pivot_wider(
names_from = visit,
values_from = c(weight, pressure),
names_sep = "_"
)
输出列类似 weight_baseline、pressure_followup。
重复键
若同一个行标识与列标识对应多个值,宽表中的一个单元格无法容纳它们:
duplicated <- tibble(
id = c(1, 1),
key = c("A", "A"),
value = c(10, 12)
)
先检查重复来自录入问题、重复测量还是缺少标识列:
duplicated |> count(id, key) |> filter(n > 1)
确实需要汇总时,显式指定规则:
duplicated |>
pivot_wider(
names_from = key,
values_from = value,
values_fn = mean
)
不要为了消除重复警告就随意取均值;聚合规则会改变数据含义。
能否往返转换
pivot_longer() 与 pivot_wider() 在键唯一、类型兼容且没有删除缺失的情况下可以互相还原。实际工作中,往返失败通常揭示重复键、隐含标识列或缺失组合。
长宽转换不是单纯排版。它重新定义“每行是什么观测、每列是什么变量”,因此应先写清楚数据的观测单位和唯一键。