如何用更优的R方法按行函数扩展DataFrame?
问题:
以下代码可实现需求,但是否存在更符合“R风格”的方法?我需要根据自定义函数将分组数据拆分为单独行(目前使用循环实现)。
示例:
df1 <- data.frame(group = c("A", "B", "C"), volume = c(200L, 45L, 104L) ) print(df1) #> group volume #> 1 A 200 #> 2 B 45 #> 3 C 104
我希望按group将volume拆分为多行,最终得到的DataFrame中,新列vol2的总和等于原volume。本示例中使用除数52进行整数运算,预期结果如下:
print(df3) #> group vol2 #> 1 A 52 #> 2 A 52 #> 3 A 52 #> 4 A 44 #> 21 B 45 #> 31 C 52 #> 32 C 52
当前可行方案
以下代码可得到上述预期结果:
div <- 52L df1$intgr <- df1$volume %/% div df1$remainder <- df1$volume %% div print(df1) #> group volume intgr remainder #> 1 A 200 3 44 #> 2 B 45 0 45 #> 3 C 104 2 0 df2 <- data.frame() for (r in 1:nrow(df1)){ if(df1[r,"intgr"] > 0){ for (k in 1:as.integer(df1[r,"intgr"])){ df1[r,"vol2"] <- div df2 <- rbind(df2, df1[r,]) } } if(df1[r,"remainder"]>0){ df1[r, "vol2"] <- as.integer(df1[r, "remainder"]) df2 <- rbind(df2, df1[r,]) } } print(df2) #> group volume intgr remainder vol2 #> 1 A 200 3 44 52 #> 2 A 200 3 44 52 #> 3 A 200 3 44 52 #> 4 A 200 3 44 44 #> 21 B 45 0 45 45 #> 31 C 104 2 0 52 #> 32 C 104 2 0 52 df3 <- subset(df2, select = c("group", "vol2")) print(df3) #> group vol2 #> 1 A 52 #> 2 A 52 #> 3 A 52 #> 4 A 44 #> 21 B 45 #> 31 C 52 #> 32 C 52
作为R新手,我想知道是否有更优的方法/函数实现该需求,比如通过应用自定义函数(UDF)处理DataFrame。此前搜索“expand group”多得到expand.grid,并非所需方案,期待建议!
更优的R风格实现方案
方案1:使用tidyverse生态(dplyr + tidyr)
利用管道操作和列表列展开,代码简洁易读,符合现代R的编程风格:
library(tidyverse) div <- 52L df3 <- df1 %>% mutate( intgr = volume %/% div, remainder = volume %% div, # 生成每个分组对应的vol2向量 vol2 = map2(intgr, remainder, ~c(rep(div, .x), if(.y > 0) .y else NULL)) ) %>% # 展开列表列 unnest(vol2) %>% # 保留需要的列 select(group, vol2) print(df3) #> # A tibble: 7 × 2 #> group vol2 #> <chr> <int> #> 1 A 52 #> 2 A 52 #> 3 A 52 #> 4 A 44 #> 5 B 45 #> 6 C 52 #> 7 C 52
方案2:使用base R
无需额外安装包,通过列表生成再合并的方式替代嵌套循环,避免频繁rbind带来的性能损耗:
div <- 52L df1$intgr <- df1$volume %/% div df1$remainder <- df1$volume %% div # 遍历每一行生成对应的数据框 result_list <- lapply(1:nrow(df1), function(r) { group_val <- df1$group[r] # 构造vol2的数值向量 vol_vals <- c(rep(div, df1$intgr[r]), if(df1$remainder[r] > 0) df1$remainder[r] else NULL) data.frame(group = group_val, vol2 = vol_vals) }) # 合并所有结果 df3 <- do.call(rbind, result_list) print(df3) #> group vol2 #> 1 A 52 #> 2 A 52 #> 3 A 52 #> 4 A 44 #> 5 B 45 #> 6 C 52 #> 7 C 52
内容的提问来源于stack exchange,提问作者ScottyJ
相关产品推荐
相关产品推荐

