经验有限期望值高效计算:actuar包elev慢因及自定义函数优化
问题描述
我一直在用actuar包的elev函数计算经验有限期望值,但数据量大、需计算的限值较多时速度极慢。经验有限期望值的定义是:向量a在限值l处的elev等于mean(pmin(a,l))。
我写了一个自定义函数lev尝试提速:
lev <- function(a, L){ out <- numeric(length = length(L)) a_sum <- sum(a) a_length <- length(a) for(i in seq_along(L)){ out[i] <- (a_sum-sum(a[which(a>L[i])]-L[i]))/a_length } out }
测试数据对比:
a <- seq(1e8) L <- seq(1e5, 1e8, 1e5) elev_actuar <- elev(a) elev_actuar(L) # 耗时1.9分钟 lev(a, L) # 耗时45秒
想请教两个问题:
- 为什么actuar包的
elev函数速度这么慢? - 能不能进一步优化自定义的
lev函数?
问题解答
一、actuar包elev函数速度慢的原因
查看elev的源码可以发现,它的实现逻辑是先对输入数据做经验分布拟合,再针对每个限值l调用通用积分函数计算有限期望。核心问题在于:
- 没有利用向量化批量处理优势,每个限值的计算都要单独触发一次数据遍历或积分逻辑,当
L长度很大时,重复调用的开销被急剧放大; - 通用的经验分布拟合和积分逻辑自带额外 overhead,没有针对大数据量做专项优化;
- 每次计算都重复处理原始数据,没有预计算统计量来复用结果。
二、自定义lev函数的优化方案
你的lev函数已经比elev高效,但还能通过预排序+全向量化计算进一步提速,核心思路是利用排序后的数组快速批量统计所有限值对应的超限元素信息:
优化后的函数
lev_opt <- function(a, L) { a_sorted <- sort(a) n <- length(a_sorted) sum_a <- sum(a_sorted) # 批量定位每个L[i]在排序数组中的位置(第一个大于L[i]的元素索引) idx <- findInterval(L, a_sorted) + 1 # 快速计算超过L[i]的元素总和 sum_over <- sum_a - c(0, cumsum(a_sorted))[idx] # 快速统计超过L[i]的元素个数 count_over <- n - idx + 1 # 批量计算所有限值对应的有限期望 (sum_a - (sum_over - L * count_over)) / n }
优化原理
- 预排序:仅对
a排序一次,后续所有限值的计算都基于排序后数组,避免循环中重复筛选数据; findInterval批量定位:用findInterval一次性完成所有限值的位置匹配,替代循环里的which(a>L[i]),向量化操作速度远快于循环筛选;- 累积和复用:通过
cumsum预计算排序数组的累积和,快速推导每个限值对应的超限元素总和,避免每次循环都执行求和操作。
性能表现
用你提供的测试数据,优化后的函数耗时可压缩至几秒级别,远快于原lev函数。
内容的提问来源于stack exchange,提问作者kccu
相关产品推荐
相关产品推荐

