如何用Tidyverse工具(如purrr::map)替代循环填充n_j列NA值?
递推填充缺失值的Tidyverse解决方案
问题描述
现有数据集df,其中n_j列仅第一行有值,其余均为NA。需要通过公式n_j(当前行) = n_j(前一行) - (d_j(前一行) + c_j(前一行))填充所有缺失的n_j值。目前已通过for循环实现需求,希望改用purrr::map或其他Tidyverse函数完成。
数据创建代码
df = structure(list(time_intervals = structure(1:8, levels = c("[0,12)", "[12,24)", "[24,36)", "[36,48)", "[48,60)", "[60,72)", "[72,84)", "[84,96]"), class = "factor"), d_j = c(16L, 10L, 1L, 3L, 2L, 2L, 0L, 2L), c_j = c(4L, 4L, 0L, 1L, 2L, 0L, 1L, 0L), n_j = c(48L, NA, NA, NA, NA, NA, NA, NA)), row.names = c(NA, -8L), class = c("tbl_df", "tbl", "data.frame"))
已实现的for循环代码
for (i in 1:nrow(df)) { df <- df |> mutate( n_j = ifelse(is.na(n_j), lag(n_j)- (lag(d_j)+lag(c_j)), n_j) ) }
Tidyverse替代方案
方法1:使用purrr::accumulate(推荐)
accumulate是purrr包中专门用于递推计算的函数,完美适配这种依赖前序结果的场景:
library(tidyverse) df <- df |> mutate( n_j = accumulate( .init = first(n_j), 1:(nrow(df)-1), ~ .x - (d_j[.y] + c_j[.y]) ) )
.init指定初始值为n_j的第一个有效值- 迭代范围
1:(nrow(df)-1)对应需要计算的7行数据 - 公式
~ .x - (d_j[.y] + c_j[.y])中,.x代表前一行计算得到的n_j值,.y代表当前迭代的索引,取对应行的d_j和c_j求和后做减法
方法2:使用dplyr的累积求和
通过先计算每行的增量,再结合初始值做累积求和,同样可以实现需求:
library(dplyr) df <- df |> mutate( # 计算每行的增量:前一行d_j+c_j的负值 delta = -(d_j + c_j), # 初始值加上前n-1个增量的累积和 n_j = first(n_j) + c(0, cumsum(delta[-nrow(df)])) ) |> select(-delta) # 移除临时增量列
说明
两种方法均避免了原for循环中反复赋值整个数据集的低效操作,属于Tidyverse生态内的向量化/递推计算方式,运行效率更高且代码更简洁。
内容的提问来源于stack exchange,提问作者Abdullah Abdelaziz
相关产品推荐
相关产品推荐

