在Tidyverse中如何递归应用函数填充数据列缺失值?
递归填充分组数据中的缺失值(自定义规则)
原始数据
data <- data.frame( group = c(rep("A", 10), rep("B", 10)), year = rep(2016:2025, 2), value = c(10, 20, 30, 40, NA, NA, NA, NA, NA, NA, 70, 80, NA, NA, NA, NA, NA, NA, NA, NA) )
数据结构如下:
group year value 1 A 2016 10 2 A 2017 20 3 A 2018 30 4 A 2019 40 5 A 2020 NA 6 A 2021 NA 7 A 2022 NA 8 A 2023 NA 9 A 2024 NA 10 A 2025 NA 11 B 2016 70 12 B 2017 80 13 B 2018 NA 14 B 2019 NA 15 B 2020 NA 16 B 2021 NA 17 B 2022 NA 18 B 2023 NA 19 B 2024 NA 20 B 2025 NA
需求
对每个分组,保留非NA的value值,从第一个NA值开始,递归应用自定义函数(示例为每年增加10,即valueₜ = valueₜ₋₁ + 10)填充后续缺失值。
尝试过的方法及局限性
dplyr::lag方法:仅能填充第一个NA,无法实现递归填充,因为它是向量化操作,不依赖前一次计算的结果:
library(dplyr) data %>% group_by(group) %>% mutate(value_fix = dplyr::lag(value) + 10)
输出仅能填充第一个NA,后续NA仍为空。
tidyr::fill方法:只能用前一个非NA值填充,不支持自定义递归计算逻辑:
data %>% group_by(group) %>% tidyr::fill(value)
- 已有的繁琐解决方案:通过额外生成
last_value列再结合purrr::accumulate实现,但代码冗余可读性差:
data %>% group_by(group) %>% mutate(last_value = case_when( value == dplyr::last(na.omit(value)) ~ value, TRUE ~ NA_real_ )) %>% mutate(value_fix = purrr::accumulate( .x = last_value, .f = ~ coalesce(.x + 10, .y) ))
更简洁的Tidyverse解决方案
可以直接用purrr::accumulate结合coalesce实现,代码简洁且逻辑清晰:
library(tidyverse) data %>% group_by(group) %>% mutate(value_fix = accumulate(value, ~ coalesce(.y, .x + 10))) %>% ungroup()
输出结果
# A tibble: 20 × 4 group year value value_fix <chr> <int> <dbl> <dbl> 1 A 2016 10 10 2 A 2017 20 20 3 A 2018 30 30 4 A 2019 40 40 5 A 2020 NA 50 6 A 2021 NA 60 7 A 2022 NA 70 8 A 2023 NA 80 9 A 2024 NA 90 10 A 2025 NA 100 11 B 2016 70 70 12 B 2017 80 80 13 B 2018 NA 90 14 B 2019 NA 100 15 B 2020 NA 110 16 B 2021 NA 120 17 B 2022 NA 130 18 B 2023 NA 140 19 B 2024 NA 150 20 B 2025 NA 160
逻辑说明
accumulate是递归迭代函数,每次处理一个元素时:
- 如果当前
value(.y)非NA,直接保留该值; - 如果当前
value是NA,就用上一次迭代的结果(.x)加上10作为填充值。
整个过程完全符合需求,且无需额外中间列,代码简洁易读。
内容的提问来源于stack exchange,提问作者elikesprogramming
相关产品推荐
相关产品推荐

