嵌套与展开数据框
使用 nest() 和 unnest() 处理整洁工作流中的列表列
嵌套与展开数据框 是 CoddyKit 上的免费 R Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 R Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 R Academy 课程共包含 4 节课。
列表列和嵌套数据
列表列是数据框中的一列,其中每个单元格都包含一个 R 对象(向量、数据框、模型或列表)。这样可以将结构化的子数据与其他列存放在一起,从而支持强大的分组工作流。
library(tidyr)
library(dplyr)
# A simple data frame with a list-column
df <- data.frame(
group = c('A','B','C'),
n = c(3, 2, 4)
)
df$values <- list(c(1,2,3), c(4,5), c(6,7,8,9))
print(df)
print(df$values[[1]]) # Access the first list elementnest() — 创建嵌套数据框
nest(df, data = c(col1, col2)) 按未嵌套的列对行进行分组,并将指定列打包到一个由数据框组成的列表列中。每个组占一行,每行包含一个小型数据框。
library(tidyr)
library(dplyr)
df <- data.frame(
region = c('East','East','East','West','West'),
month = c(1,2,3,1,2),
sales = c(100,120,110,200,190)
)
nested <- df %>%
nest(data = c(month, sales))
print(nested)
print(nested$data[[1]]) # East region's data将 nest() 与 group_by() 结合使用
最常见的模式是:先使用 group_by(),再使用 nest(),为每个组创建一行,并将其余所有列打包到 data 列表列中。在较新版本的 tidyr 中,这等价于 nest(.by = group_col)。
library(tidyr)
library(dplyr)
df <- data.frame(
category = c('A','A','A','B','B','C','C','C','C'),
x = c(1,2,3,4,5,6,7,8,9),
y = c(2,4,3,8,7,5,6,9,8)
)
nested <- df %>%
group_by(category) %>%
nest()
print(nested)
cat('Rows per category B:', nrow(nested$data[[2]]))使用 map() 处理嵌套数据框
数据嵌套后,可以使用 purrr::map() 对每个小型数据框应用函数。该函数每次接收一个数据框,处理结果会成为新的列表列,通常是一个拟合好的模型。
library(tidyr)
library(dplyr)
library(purrr)
df <- data.frame(
group = c('A','A','A','B','B','B'),
x = c(1,2,3,1,2,3),
y = c(2.1,4.0,5.9,3.2,5.8,9.1)
)
models <- df %>%
group_by(group) %>%
nest() %>%
mutate(model = map(data, ~lm(y ~ x, data = .x)))
print(models)使用 map() 提取模型结果
按组拟合模型后,可以将 map() 与 broom::tidy() 或 coef() 等函数结合使用,从每个模型中提取结果。输出会成为另一个列表列,之后可以将其展开。
library(tidyr)
library(dplyr)
library(purrr)
df <- data.frame(
group = c('A','A','A','B','B','B'),
x = c(1,2,3,1,2,3),
y = c(2.1,4.0,5.9,3.2,5.8,9.1)
)
df %>%
group_by(group) %>%
nest() %>%
mutate(
model = map(data, ~lm(y ~ x, data = .x)),
r_squared = map_dbl(model, ~summary(.x)$r.squared),
intercept = map_dbl(model, ~coef(.x)[1]),
slope = map_dbl(model, ~coef(.x)[2])
) %>%
select(group, r_squared, intercept, slope)unnest() — 展开列表列
unnest(df, cols = data) 是 nest() 的逆操作。它会将列表列展开为常规列,并根据需要重复标识列。使用 cols 指定要展开的列表列。
library(tidyr)
library(dplyr)
nested <- data.frame(
region = c('East','West')
)
nested$data <- list(
data.frame(month=1:3, sales=c(100,120,110)),
data.frame(month=1:2, sales=c(200,190))
)
unnested <- unnest(nested, cols = data)
print(unnested)unnest_longer() — 将列表展开为行
unnest_longer(df, col) 会展开列表列,使列表中的每个元素成为一行。与要求列表元素为数据框的 unnest() 不同,unnest_longer() 适用于由向量或标量组成的列表。
library(tidyr)
df <- data.frame(
person = c('Alice','Bob','Carol')
)
df$skills <- list(
c('R','Python','SQL'),
c('Excel','Tableau'),
c('R','Julia','Stan','BUGS')
)
unnest_longer(df, skills)unnest_wider() — 将列表展开为列
unnest_wider(df, col) 会将每个列表元素展开为一组新列。列表必须包含命名元素,这些名称会成为列名。它适用于类似嵌套 JSON 的结构。
library(tidyr)
df <- data.frame(id = 1:3)
df$info <- list(
list(name='Alice', age=30, city='NYC'),
list(name='Bob', age=25, city='LA'),
list(name='Carol', age=35, city='Chicago')
)
unnest_wider(df, info)完整的嵌套工作流模式
完整的嵌套数据框工作流为:group_by() + nest() → 使用 map() 应用函数 → 提取结果 → 使用 unnest() 恢复为扁平数据框。这种模式支持按组执行任意分析。
library(tidyr)
library(dplyr)
library(purrr)
df <- data.frame(
region = c('East','East','East','West','West','West'),
month = c(1,2,3,1,2,3),
revenue = c(100,120,115,200,195,210)
)
df %>%
group_by(region) %>%
nest() %>%
mutate(
mean_rev = map_dbl(data, ~mean(.x$revenue)),
trend = map_dbl(data, ~coef(lm(revenue~month, data=.x))[2])
) %>%
select(region, mean_rev, trend)嵌套多个列
您可以有选择地指定哪些列应放入嵌套数据框中。在 nest(data = c(...)) 中明确指定这些列。未指定的列会作为外层数据框中的常规列,与列表列并存。
library(tidyr)
library(dplyr)
df <- data.frame(
region = c('East','East','West','West'),
year = c(2023,2024,2023,2024),
q1 = c(100,110,200,210),
q2 = c(105,115,195,215),
target = c(105,112,198,212)
)
# Only nest quarterly data, keep target as outer column
nested <- df %>%
group_by(region, year) %>%
nest(quarters = c(q1, q2))
print(nested)何时使用嵌套数据框
在以下场景中,嵌套数据框非常适用:对不同组拟合同一个模型、运行按组模拟、对数据子集应用复杂转换,或处理类似 JSON 的层次化数据。它们可以将组专属数据与组级元数据整齐地组织在一起。
library(tidyr)
library(dplyr)
library(purrr)
# Bootstrap confidence interval per group
df <- data.frame(
group = c('A','A','A','A','B','B','B','B'),
value = c(10,12,11,13,20,22,19,21)
)
set.seed(42)
df %>%
group_by(group) %>%
nest() %>%
mutate(
boot_means = map(data, function(d) {
replicate(100, mean(sample(d$value, replace=TRUE)))
}),
ci_low = map_dbl(boot_means, ~quantile(.x, 0.025)),
ci_high = map_dbl(boot_means, ~quantile(.x, 0.975))
) %>%
select(group, ci_low, ci_high)快速检查
如何将由数据框组成的列表列正确展开回扁平数据框?
总结:嵌套与展开
嵌套数据框工作流的要点:
nest(data = c(cols))— 将列打包到由数据框组成的列表列中,每个组一个数据框group_by() + nest()— 按组嵌套的标准模式map(nested$data, fn)— 对每个嵌套数据框应用任意函数unnest(cols = data)— 将由数据框组成的列表列展开回扁平格式unnest_longer(col)— 将由向量或标量组成的列表展开为行unnest_wider(col)— 将命名列表展开为列- 适用于按组建模、自助法和 JSON 数据处理
library(tidyr)
library(dplyr)
library(purrr)
data.frame(
team = c('A','A','B','B'),
x = c(1,2,1,2),
y = c(2,4,3,6)
) %>%
group_by(team) %>%
nest() %>%
mutate(
model = map(data, ~lm(y ~ x, data = .x)),
slope = map_dbl(model, ~coef(.x)['x'])
) %>%
select(team, slope)常见问题解答
「嵌套与展开数据框」课时是免费的吗?
是的 — 「嵌套与展开数据框」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 R Academy 课程的其余内容,请升级到 CoddyKit PRO。 R Academy 课程共包含 4 节课。
「嵌套与展开数据框」这节课中我会学到什么?
使用 nest() 和 unnest() 处理整洁工作流中的列表列 你通过在浏览器中直接运行的动手代码来练习 R Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 R Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 R Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。
「嵌套与展开数据框」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 R Academy 课中编写并运行代码吗?
能。每节 R Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。