R语言:保留原始NA,填充连接操作产生的NA值
问题描述
现有两个tibble数据集:
weights <- tibble(Time = c(as.POSIXct("1900-01-01 10:00:00"), as.POSIXct("1900-01-01 13:00:00"), as.POSIXct("1900-01-01 14:00:00")), weight = c(3, NA, 4), is_weight = c(1, 1, 1)) heights <- tibble(Time = c(as.POSIXct("1900-01-01 11:00:00"), as.POSIXct("1900-01-01 12:00:00"), as.POSIXct("1900-01-01 15:00:00")), height = c(4, NA, 5), is_height = c(1, 1, 1))
通过Time字段全连接并填充is_列的NA后得到数据集df:
df <- full_join(weights, heights, by = "Time") %>% arrange(Time) %>% mutate(is_weight = replace(is_weight, is.na(is_weight), 0)) %>% mutate(is_height = replace(is_height, is.na(is_height), 0)) df # A tibble: 6 x 5 Time weight is_weight height is_height <dttm> <dbl> <dbl> <dbl> <dbl> 1 1900-01-01 10:00:00 3 1 NA 0 2 1900-01-01 11:00:00 NA 0 4 1 3 1900-01-01 12:00:00 NA 0 NA 1 4 1900-01-01 13:00:00 NA 1 NA 0 5 1900-01-01 14:00:00 4 1 NA 0 6 1900-01-01 15:00:00 NA 0 5 1
当前数据存在两种NA:
- 原始数据自带的
NA(如第3行的height) - 连接操作产生的
NA(如第4行的height)
需求:保留原始NA,仅将连接产生的NA填充为最近的有效值(例如当is_weight=0时,复制最近is_weight=1行的weight值),最终结果如下:
# A tibble: 6 x 5 Time weight is_weight height is_height <dttm> <dbl> <dbl> <dbl> <dbl> 1 1900-01-01 10:00:00 3 1 NA 0 2 1900-01-01 11:00:00 3 0 4 1 3 1900-01-01 12:00:00 3 0 NA 1 4 1900-01-01 13:00:00 NA 1 NA 0 5 1900-01-01 14:00:00 4 1 NA 0 6 1900-01-01 15:00:00 4 0 5 1
需要找到通用方法,可同时处理10个类似列。本人思路是先将原始NA替换为特定值(如1234),使用fill方法后再替换回NA,寻求更优方案。
解决方案
可以通过标记原始NA→填充连接产生的NA→恢复原始NA的流程实现,结合dplyr的批量处理能力,轻松适配多列场景,比替换特定值的方法更安全(避免和数据中已有值冲突):
核心思路
- 先为每个数值列生成标记列,记录原始数据中NA的位置
- 针对每个数值列,仅在
is_列为0的行(即连接产生的NA行),用最近的有效值填充 - 根据标记列恢复原始数据自带的NA
完整代码(适配多列)
library(dplyr) library(tidyr) library(stringr) # 生成初始df df <- full_join(weights, heights, by = "Time") %>% arrange(Time) %>% mutate(across(starts_with("is_"), ~ replace(., is.na(.), 0))) # 定义需要处理的列组:数值列名称 + 对应的is_列名称 cols_to_process <- list( list(val_col = "weight", flag_col = "is_weight"), list(val_col = "height", flag_col = "is_height") # 如需处理更多列,按相同格式添加即可,比如: # list(val_col = "temp", flag_col = "is_temp"), # list(val_col = "pressure", flag_col = "is_pressure") ) # 执行处理流程 final_df <- df %>% # 步骤1:标记原始NA的位置 mutate( across( map_chr(cols_to_process, "val_col"), ~ paste0(cur_column(), "_original_na") := is.na(.), .names = "{.col}_original_na" ) ) %>% # 步骤2:填充连接产生的NA mutate( across( map_chr(cols_to_process, "val_col"), ~ case_when( # 原始数据行(is_=1)保留原数值 get(str_replace(cur_column(), "(.*)", "is_\\1")) == 1 ~ ., # 连接产生的行(is_=0)用最近有效值填充 TRUE ~ fill(., .direction = "downup")[cur_group_rows()] ) ) ) %>% # 步骤3:恢复原始NA mutate( across( map_chr(cols_to_process, "val_col"), ~ ifelse(get(paste0(cur_column(), "_original_na")), NA, .) ) ) %>% # 移除临时标记列 select(-ends_with("_original_na")) # 查看结果 final_df
说明
fill(., .direction = "downup")会同时向下、向上查找最近的有效值,确保所有连接产生的NA都被覆盖- 只需扩展
cols_to_process列表,即可批量处理任意数量的类似列,无需重复编写代码
内容的提问来源于stack exchange,提问作者Ai4l2s
相关产品推荐
相关产品推荐

