基于查找表重复值生成长数据表的R语言高效实现咨询
从宽格式查找表高效生成指定长格式数据表
需求说明
给定如下查找表df_lookup:
df_lookup = data.frame(id = c(1,2,3), one = c(10,9,7), two = c(0,1,2), three = c(0,0,1))
其结构为:
id one two three 1 1 10 0 0 2 2 9 1 0 3 3 7 2 1
需要生成的长数据表df_output共30行,规则如下:
- id=1的分组:10行bin值为1
- id=2的分组:9行bin值为1,1行bin值为2
- id=3的分组:7行bin值为1,2行bin值为2,1行bin值为3
原实现采用循环拼接的方式,但当id数量较多或需处理多个查找表时,循环+反复rbind会导致运行效率低下,现寻求更高效的实现方案。
原循环实现代码
df_lookup = data.frame(id = c(1,2,3), one = c(10,9,7), two = c(0,1,2), three = c(0,0,1)) col_names = c("one","two","three") setDT(df_lookup) df_output = data.frame() for (j in 1:length(col_names)){ temp_df = df_lookup[, .(rep(j, get(as.character(col_names[j])))),.(id)] df_output = rbind(df_output,temp_df) } names(df_output) = c("id","bin") df_output = df_output[order(df_output$id,df_output$bin),]
高效实现方案
方案1:data.table向量化实现(性能最优)
利用data.table的melt将宽表转长,结合向量化的rep操作一次性生成结果,完全避免循环和反复拼接的性能损耗:
library(data.table) # 初始化查找表为data.table df_lookup = data.table(id = c(1,2,3), one = c(10,9,7), two = c(0,1,2), three = c(0,0,1)) # 转长格式,匹配bin的数字标识,过滤计数为0的行 dt_long = melt(df_lookup, id.vars = "id", variable.name = "bin", value.name = "count") dt_long[, bin := match(bin, c("one", "two", "three"))] dt_long = dt_long[count > 0] # 按分组重复对应次数,生成最终长表并排序 df_output = dt_long[, .(bin = rep(bin, count)), by = id] setorder(df_output, id, bin)
方案2:tidyverse风格实现
如果习惯tidyverse语法,可以用pivot_longer转长,结合map+unnest_longer实现重复:
library(tidyverse) df_lookup = data.frame(id = c(1,2,3), one = c(10,9,7), two = c(0,1,2), three = c(0,0,1)) df_output = df_lookup %>% pivot_longer(cols = -id, names_to = "bin", values_to = "count") %>% mutate(bin = match(bin, c("one", "two", "three"))) %>% filter(count > 0) %>% mutate(bin = map(bin, ~rep(.x, count))) %>% unnest_longer(bin) %>% arrange(id, bin)
效率优势说明
上述两种方案均采用向量化操作,避免了循环中多次rbind的内存复制开销——当数据量较大时,这种性能差异会非常显著,能大幅提升处理速度。
内容的提问来源于stack exchange,提问作者Rohan
相关产品推荐
相关产品推荐

