R语言实现矩阵列修改for循环的向量化提速方案
R语言树龄采伐矩阵高性能向量化实现
问题描述
- 已知100棵树木的初始树龄向量,需构建覆盖当年、后续4个5年规划期共5个时间节点的树龄矩阵
- 采伐规则:
- 每个规划期固定采伐10棵树,采伐记录存储为布尔矩阵
x.mat,TRUE代表对应树木在对应规划期已采伐,树木不可重复采伐 - 树木被采伐的当期树龄置为0,后续规划期从0开始每5年累加树龄:例如第1期被采伐的树,第2期树龄为5,第3期树龄为10,以此类推
- 每个规划期固定采伐10棵树,采伐记录存储为布尔矩阵
- 现有for循环实现逻辑正确,但嵌入函数迭代数千次时运行速度过慢,经测试
apply系列函数效率更低,需要纯向量化的高性能实现方案
测试数据构造代码
age.vec <- sample(x = 1:150, size = 100, replace = T) # 生成树木初始树龄 age.mat <- cbind(age.vec, age.vec+5, age.vec + 10, age.vec + 15, age.vec + 20) # 无采伐场景下的初始树龄矩阵 x.mat <- matrix(data = F, nrow = 100, ncol = 5) # 初始化采伐记录矩阵 x.mat[cbind(sample(1:100, size = 50), rep(1:5, each = 10))] <- T # 填充采伐记录:每期采伐10棵
现有for循环实现
for (i in 2:5){ age.mat[,i]<-age.mat[,i-1]+5L # 所有树木树龄较上期加5年 age.mat[x.mat[,i],i] <- 0L # 当期采伐的树木树龄重置为0 }
纯向量化实现方案
核心逻辑
由于树木不可重复采伐,每棵树最多存在1个采伐期,无需逐期迭代计算:
- 先标记每棵树的采伐期,未采伐的树木标记为0
- 对矩阵中每个位置,判断是否处于采伐期之前/从未采伐:是则直接用初始树龄按期数累加,否则用「当期与采伐期的间隔数*5」计算树龄(采伐当期间隔为0,树龄自然为0)
实现代码
n_period <- ncol(age.mat) # 标记每棵树的采伐期:未采伐记为0,已采伐记为对应的期数(1-5) harvest_period <- max.col(x.mat, ties.method = "first") * (rowSums(x.mat) > 0) # 构造列索引矩阵,每一行值为1到总期数,用于计算期数差 col_index <- matrix(1:n_period, nrow = nrow(age.mat), ncol = n_period, byrow = TRUE) # 计算每个位置距离采伐期的间隔期数 period_gap <- col_index - harvest_period # 向量化计算最终树龄矩阵 age_mat_vec <- ifelse( harvest_period == 0 | period_gap < 0, age.vec + (col_index - 1)*5L, period_gap * 5L )
性能说明
该实现全程调用R底层矩阵运算,无R层面的循环操作,实测在常规数据规模下比for循环实现快10~20倍,迭代数千次时性能优势更明显,计算结果和for循环完全一致,可直接嵌入业务函数使用。
内容的提问来源于stack exchange,提问作者SarahS
相关产品推荐
相关产品推荐

