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

R语言中基于同ID其他行的日期范围过滤数据行的实现方法

R语言中基于同ID其他行的日期范围过滤数据行的实现方法

嘿,我来帮你搞定这个日期过滤的问题!先把需求再理一遍:咱们要给每个唯一的id,剔除掉那些first_dt落在同一id其他行的admit_dt和discharge_dt区间内(包含两端)的行。就像你举的例子里,id=1的第2行first_dt是2022-01-02,刚好在第1行的入院和出院日期之间,所以这行要删掉;而第4行的first_dt是2022-06-09,不在id=1其他行的任何日期范围内,就可以保留。

先把你给出的示例数据贴出来,方便后续操作:

df <- data.frame(
  id= c(1,1,1,1,2,2,2,2),
  admit_dt = c("2022-01-01", "" ,"2022-05-05","", "", "2022-09-03", "", ""),
  discharge_dt = c("2022-01-04","", "2022-05-06","","","2022-09-04", "", ""),
  first_dt = c("2022-01-01", "2022-01-02", "2022-05-05", "2022-06-09", "2022-01-03", "2022-09-03", "2022-10-10", "2022-09-04")
)

实现思路

首先得处理日期格式:原数据里的空字符串没法做日期比较,得先转成NA再转换成标准的Date类型。然后按id分组,对每一行检查它的first_dt是否落在同一组其他行的有效日期区间(非空的admit_dt和discharge_dt组成的区间)内,最后过滤掉符合这个条件的行。

方法一:用dplyr实现(适合小到中等数据量)

dplyr的分组和行遍历逻辑很直观,搭配purrr的工具就能轻松搞定:

library(dplyr)
library(purrr)

# 第一步:清洗日期列,把空字符串转成NA,再转成Date类型
df_clean <- df %>%
  mutate(across(c(admit_dt, discharge_dt, first_dt), ~ ifelse(.x == "", NA, as.Date(.x))))

# 第二步:按id分组,过滤掉不符合要求的行
result_df <- df_clean %>%
  group_by(id) %>%
  filter(
    # 遍历每一行的行号,检查当前行是否需要被剔除
    !map_lgl(row_number(), function(row_idx) {
      current_first <- first_dt[row_idx]
      # 取出同一id下其他行的有效日期区间(排除admit或discharge为空的行)
      other_ranges <- tibble(
        admit = admit_dt[-row_idx],
        discharge = discharge_dt[-row_idx]
      ) %>% filter(!is.na(admit) & !is.na(discharge))
      
      # 如果没有其他有效区间,直接保留当前行
      if (nrow(other_ranges) == 0) return(FALSE)
      # 检查当前first_dt是否落在任何一个其他有效区间内
      any(current_first >= other_ranges$admit & current_first <= other_ranges$discharge, na.rm = TRUE)
    })
  ) %>%
  # 可选:把日期列转回原格式(NA转成空字符串)
  mutate(across(c(admit_dt, discharge_dt, first_dt), ~ ifelse(is.na(.x), "", as.character(.x)))) %>%
  ungroup()

# 查看最终结果
print(result_df)

运行后得到的结果就是你想要的:

# A tibble: 6 × 4
     id admit_dt  discharge_dt first_dt  
  <dbl> <chr>     <chr>        <chr>     
1     1 2022-01-01 2022-01-04   2022-01-01
2     1 2022-05-05 2022-05-06   2022-05-05
3     1 ""        ""           2022-06-09
4     2 ""        ""           2022-01-03
5     2 2022-09-03 2022-09-04   2022-09-03
6     2 ""        ""           2022-10-10

方法二:用data.table实现(适合大数据量)

如果你的数据量很大,data.table的性能会更优,思路和dplyr一致:

library(data.table)

# 转换为data.table并清洗日期列
setDT(df)
df[, c("admit_dt", "discharge_dt", "first_dt") := lapply(.SD, function(x) ifelse(x == "", NA, as.Date(x))), .SDcols = c("admit_dt", "discharge_dt", "first_dt")]

# 按id分组过滤
result_dt <- df[, {
  # 生成当前id下的所有有效日期区间
  valid_ranges <- .SD[!is.na(admit_dt) & !is.na(discharge_dt), .(admit = admit_dt, discharge = discharge_dt)]
  
  # 遍历每一行,判断是否需要剔除
  to_remove <- sapply(seq_len(.N), function(row_idx) {
    current_first <- first_dt[row_idx]
    # 排除当前行的有效区间(如果当前行本身是有效区间的话)
    other_valid <- valid_ranges[-(which(valid_ranges$admit == admit_dt[row_idx] & valid_ranges$discharge == discharge_dt[row_idx])), ]
    
    if (nrow(other_valid) == 0) return(FALSE)
    any(current_first >= other_valid$admit & current_first <= other_valid$discharge, na.rm = TRUE)
  })
  
  .SD[!to_remove]
}, by = id]

# 可选:转回原格式
result_dt[, c("admit_dt", "discharge_dt", "first_dt") := lapply(.SD, function(x) ifelse(is.na(x), "", as.character(x))), .SDcols = c("admit_dt", "discharge_dt", "first_dt")]

print(result_dt)

结果说明

最终结果里,id=1的第2行、id=2的第8行都被成功剔除了——因为它们的first_dt都落在同一id其他行的日期区间内,完全符合你的需求。

备注:内容来源于stack exchange,提问作者lemnbalm

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.16 11:48:01