You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的批量处理能力,轻松适配多列场景,比替换特定值的方法更安全(避免和数据中已有值冲突):

核心思路

  1. 先为每个数值列生成标记列,记录原始数据中NA的位置
  2. 针对每个数值列,仅在is_列为0的行(即连接产生的NA行),用最近的有效值填充
  3. 根据标记列恢复原始数据自带的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 16:10:36