如何用tidyr::nest替代group_split实现分性别数据插值?
用tidyr::nest实现按性别插值的方案
问题背景
我有一个数据框d,其中sex取值为1/2,agedays(日龄)仅包含746(约24.5个月)和776(约25.5个月)两个值,数据结构如下:
d = structure(list(sex = c(1L, 1L, 2L, 2L), agemos = c(24.5, 25.5, 24.5, 25.5), l = c(-0.216501213, -0.239790488, -0.75220657, -0.78423366), m = c(12.74154396, 12.88102276, 12.13455523, 12.2910249), s = c(0.108166006, 0.108274706, 0.107740345, 0.10847701), agedays = c(746, 776, 746, 776)), row.names = c(NA, -4L), class = "data.frame")
需求是按性别分别对l、m、s三个变量在746到776的每一天进行线性插值。目前已通过dplyr::group_split实现,但该函数的文档提示其“不稳定,可通过处理tidyr::nest(.by =)返回的嵌套列实现类似效果”,因此希望了解nest的实现方式。
原有实现(group_split版本)
fx = function(d){ fapp <- function(v)approx(d$agedays,v,xout=746:776)$y lapply(d[,c('sex','agedays','l','m','s')],fapp); } x <- d %>% group_split(sex) |> lapply(fx); str(x) d <- bind_rows(x, .id='sex')
nest实现方案
1. 调整插值函数
先优化原函数,使其接收单性别子数据框后,直接返回插值完成的完整数据框:
fx <- function(df) { target_days <- 746:776 # 对l、m、s执行插值 interp_vals <- lapply(df[, c("l", "m", "s")], function(v) { approx(df$agedays, v, xout = target_days)$y }) # 组合成结果数据框,保留性别、日龄,可选计算对应月龄 data.frame( sex = unique(df$sex), agedays = target_days, agemos = 24.5 + (target_days - 746)/30.4375, # 按平均每月30.4375天换算月龄 l = interp_vals$l, m = interp_vals$m, s = interp_vals$s ) }
2. 嵌套+插值+展开
使用nest按性别分组嵌套,再通过map应用插值函数,最后展开嵌套结果:
library(dplyr) library(tidyr) # 执行流程 result <- d %>% nest(.by = sex) %>% # 按sex嵌套,生成包含子数据框的data列 mutate(interp_data = map(data, fx)) %>% # 对每个子数据框插值 select(-data) %>% # 移除原始嵌套数据列 unnest(interp_data) # 展开插值后的结果 # 查看结果结构 str(result)
代码说明
nest(.by = sex):将原数据按sex拆分,每个性别的数据存储在data列的列表元素中map(data, fx):遍历data列的每个子数据框,调用fx生成插值后的完整数据框unnest(interp_data):将嵌套的插值结果展开为常规数据框,最终输出与group_split版本一致
补充:base R split实现
如果需要用base R替代group_split,代码如下:
x <- split(d, d$sex) |> lapply(fx) result <- bind_rows(x, .id = "sex")
内容的提问来源于stack exchange,提问作者David F
相关产品推荐
相关产品推荐

