基于data.table优化map2中rmultinomial的大样本运行效率
优化多项分布航班分配代码(处理千万级数据)
问题描述
我们需要通过多项分布将每行的总航班数分配到V1、V2、V3三个层级,但现有基于purrr::map2的代码在处理1000万行数据时耗时极长,希望优化实现(比如用data.table),核心要求是逐行对应不同的概率向量。
初始数据
| number_total_flights |
|---|
| 3 |
| 4 |
| 5 |
预期结果
| number_total_flights | V1 | V2 | V3 |
|---|---|---|---|
| 3 | 0 | 0 | 3 |
| 4 | 2 | 1 | 1 |
| 5 | 1 | 1 | 3 |
原代码及问题
原代码使用map2逐行调用rmultinom,但逐行操作在千万级数据下效率极低,且多次转换数据结构(列表转数据框再合并)进一步拖慢速度:
library(dplyr) library(data.table) library(purrr) # 初始数据 base <- structure(list( number_total_flights = c(3L, 4L, 5L)), row.names = c(NA, 3L), class = "data.frame") # 每行对应的概率向量列表 proba <- list( structure(c(0.1, 0.4, 0.5), .Dim = c(1L, 3L)), structure(c(0.5, 0.2, 0.3), .Dim = c(1L, 3L)), structure(c(0.2, 0.2, 0.6), .Dim = c(1L, 3L))) # 逐行生成多项分布结果的函数 distrib_for_each_level <- function(nb_flights, prob){ level <- t(rmultinom(n=1, size=nb_flights, prob=prob)) } # 整合结果的函数 adding_levels <- function (base, proba){ list_levels <- map2(base$number_total_flights, proba, distrib_for_each_level) %>% map(as.data.frame) %>% rbindlist() base <- base %>% cbind(list_levels) } # 生成结果 base_with_levels <- adding_levels(base, proba)
优化方案:基于data.table的矢量化实现
核心思路是避免逐行调用rmultinom,改用批量/矢量化操作,同时用data.table直接处理减少数据转换开销:
方案1:利用apply批量处理+data.table合并
# 将概率列表转为矩阵 proba_mat <- do.call(rbind, proba) # 转为data.table base_dt <- as.data.table(base) # 批量生成多项分布结果,比map2效率更高 multinom_dt <- as.data.table(t( apply(cbind(base_dt$number_total_flights, proba_mat), 1, function(x) { rmultinom(n=1, size=x[1], prob=x[2:4]) }) )) # 重命名列 setnames(multinom_dt, c("V1", "V2", "V3")) # 合并结果 base_with_levels_dt <- cbind(base_dt, multinom_dt)
方案2:用专用包实现高效批量生成(推荐千万级数据)
使用extraDistr包的rmnom函数,原生支持批量输入概率矩阵,性能远超逐行调用:
library(extraDistr) library(data.table) base_dt <- as.data.table(base) proba_mat <- do.call(rbind, proba) # 直接生成所有行的多项分布结果 multinom_fast <- rmnom(n = nrow(base_dt), size = base_dt$number_total_flights, prob = proba_mat) # 合并到原数据 base_with_levels_fast <- cbind(base_dt, as.data.table(multinom_fast))
优化效果说明
- 避免了
map2的逐行迭代开销,批量操作在千万级数据下速度可提升数十倍 data.table的合并和转换操作内存效率更高,减少中间数据占用extraDistr::rmnom是专为批量多项分布设计的函数,处理大规模数据时性能最优
内容的提问来源于stack exchange,提问作者Daoudi Karim
相关产品推荐
相关产品推荐

