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

如何高效将宽格式患者信息转换为患者日计数时间序列?

高效实现每日患者计数时间序列

数据说明

给定数据框每行代表一位患者信息,字段说明:

  • DateStart:患者入院日期
  • DateEnd:患者出院日期
  • Length:DateEnd与DateStart的时间差

示例数据:

DateStartDateEndLength
2020-03-022020-04-1039 days
2020-03-302020-04-0910 days
2020-03-152020-03-2813 days
2020-06-022020-06-2018 days

需要转换为每日患者计数的时间序列,示例输出:

DaysFrequency
2020-03-021
2020-03-031
2020-03-041
2020-03-051
2020-03-061
2020-03-071
2020-03-081
2020-03-091
2020-03-101
2020-03-111

问题描述

当前实现功能的CaseCountGenerator()函数采用多层嵌套循环,运行效率极低,尤其当患者数量或时间跨度较大时性能瓶颈明显,需寻求更高效的实现方案。

现有代码

library(tidyverse)
createDays=function(start,end){
  Days=seq.Date(from=start,to=end,by=1)
  return(Days)
}

CaseCountGenerator=function(Starts,Ends,Length,Days){
  Counts=rep(0,length(Days))
  
  for(j in 1:length(Starts)){
    
    for(l in 1:length(Days)){
      
      if(Days[l]==Starts[j]){
        for(m in ((1:Length[j])-1)){
          Counts[l+m]=Counts[l+m]+1
        }
        break
      }
      
      
    }
  }
  
  return(Counts)
}

示例数据:

tempdf = structure(list(DateStart = structure(c(18323, 18351, 18336, 18415, 
18417, 18418, 18428, 18439, 18439, 18438, 18440, 18439, 18444, 
18451, 18444, 18452, 18444, 18445, 18450, 18452), tzone = "Asia/Manila", class = "Date"), 
DateEnd = structure(c(18362, 18361, 18349, 18433, 18470, 
18460, 18447, 18460, 18458, 18458, 18459, 18459, 18472, 18463, 
18464, 18464, 18458, 18464, 18472, 18470), tzone = "Asia/Manila", class = "Date"), 
Length = structure(c(39, 10, 13, 18, 53, 42, 19, 21, 19, 
20, 19, 20, 28, 12, 20, 12, 14, 19, 22, 18), class = "difftime", units = "days")), row.names = c(NA, 
-20L), class = c("tbl_df", "tbl", "data.frame"))

原运行方式:

Days=createDays(min(tempdf$DateStart),
                max(tempdf$DateEnd))
  
Counts=CaseCountGenerator(tempdf$DateStart,
                          tempdf$DateEnd,
                          tempdf$Length,
                          Days)
  
result=tibble(Days,Counts)

高效实现方案

方案一:利用Tidyverse的行转序列+计数

该方案通过map2生成每个患者的住院日期序列,再展开后按日期计数,补全缺失日期的计数为0,全程使用向量化操作,避免嵌套循环:

library(tidyverse)

efficient_case_count <- function(df) {
  # 生成所有日期范围
  full_dates <- tibble(Days = seq.Date(min(df$DateStart), max(df$DateEnd), by = "day"))
  
  df %>%
    # 为每个患者生成住院期间的完整日期序列
    mutate(Days = map2(DateStart, DateEnd, ~seq.Date(.x, .y, by = "day"))) %>%
    unnest(Days) %>%
    # 按日期统计患者数
    count(Days, name = "Frequency") %>%
    # 关联完整日期范围,补全缺失日期的计数
    right_join(full_dates, by = "Days") %>%
    mutate(Frequency = replace_na(Frequency, 0)) %>%
    arrange(Days)
}

# 运行示例
result <- efficient_case_count(tempdf)

方案二:向量化区间赋值

利用日期的数值特性,直接对日期区间进行批量赋值,大幅减少循环次数:

efficient_case_count_vectorized <- function(df) {
  min_date <- min(df$DateStart)
  max_date <- max(df$DateEnd)
  # 将日期转换为数值,便于快速定位索引
  date_sequence <- seq.Date(min_date, max_date, by = "day")
  date_numeric <- as.numeric(date_sequence)
  
  start_numeric <- as.numeric(df$DateStart)
  end_numeric <- as.numeric(df$DateEnd)
  
  counts <- integer(length(date_numeric))
  
  # 遍历每个患者,直接对日期区间进行累加
  for (i in seq_along(start_numeric)) {
    start_idx <- which(date_numeric == start_numeric[i])
    end_idx <- which(date_numeric == end_numeric[i])
    counts[start_idx:end_idx] <- counts[start_idx:end_idx] + 1
  }
  
  tibble(Days = date_sequence, Frequency = counts)
}

# 运行示例
result <- efficient_case_count_vectorized(tempdf)

效率对比

原代码使用三层嵌套循环,时间复杂度为O(N*M)(N为患者数,M为天数),数据量增大时性能急剧下降。上述两种方案均通过向量化操作或减少循环层级,将时间复杂度降至O(N+M),在患者数或时间跨度较大时,效率提升可达数十倍甚至上百倍。

内容的提问来源于stack exchange,提问作者Derf

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 23:48:14