按分组线性插值填补纵向数据中的NA值
分组线性插值填补NA值解决方案
示例数据
df <- data.frame( country = c("country1", "country2", "country3", "country1", "country2"), measurement = c("m1", "m1", "m1", "m2", "m2"), y2015 = c(NA, 15, 19, 13, 55), y2016 = c(NA, 17, NA, 10, NA), y2017 = c(14, NA, NA, 9, 45), y2018 = c(18, 22, 16, NA, 40) )
需求说明
需按country和measurement分组,基于时间维度的斜率进行线性插值,填补连续多个NA:
- 第5行(country2+m2)填补后为
c(55, *50*, 45, *40*) - 第1行(country1+m1)填补后为
c(*6*, *10*, 14, 18) - 第3行(country3+m1)填补后为
c(19, *18*, *17*, 16)
方法1:使用tidyverse分组处理
步骤1:宽表转长表
将年份列转为时间-值的长格式,方便按时间插值:
library(tidyverse) df_long <- df %>% pivot_longer(cols = starts_with("y"), names_to = "year", names_prefix = "y", values_to = "value") %>% mutate(year = as.integer(year))
步骤2:分组线性插值
用approx函数实现线性插值,rule=2用于延伸填补首尾的连续NA:
df_interpolated <- df_long %>% group_by(country, measurement) %>% mutate(value_interpolated = approx(x = year, y = value, xout = year, rule = 2)$y) %>% ungroup()
步骤3:转回宽表
还原为原始宽表格式:
df_final <- df_interpolated %>% pivot_wider(names_from = year, names_prefix = "y", values_from = value_interpolated) # 查看结果 print(df_final)
方法2:基础R循环实现
无需依赖tidyverse,用循环逐个处理分组:
# 获取所有唯一分组 groups <- unique(df[, c("country", "measurement")]) # 初始化结果数据框 df_final_loop <- df # 遍历每个分组 for(i in 1:nrow(groups)){ # 定位当前分组行 group_idx <- df$country == groups$country[i] & df$measurement == groups$measurement[i] # 提取当前分组的年份数据 year_cols <- df[group_idx, starts_with("y")] years <- as.integer(sub("y", "", colnames(year_cols))) values <- as.numeric(year_cols) # 执行线性插值 interpolated_vals <- approx(x = years, y = values, xout = years, rule = 2)$y # 替换回原数据框 df_final_loop[group_idx, starts_with("y")] <- interpolated_vals } # 查看结果 print(df_final_loop)
说明
spline包的非线性插值不符合需求,而approx的线性插值正好匹配“按时间斜率填补”的要求,配合分组逻辑即可解决连续NA的填补问题。
内容的提问来源于stack exchange,提问作者bee_47
相关产品推荐
相关产品推荐

