You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

基于条件批量计算各ID首次连续阳性(P)测试的日期间隔天数(Python/R/SQL实现方案)

我来给你提供几种高效的批量处理方案,分别用Python(Pandas)、R(dplyr)和SQL实现,完全适配你5万+ID的大数据集需求:

Python(Pandas)实现

这个方案利用Pandas的分组聚合能力,无需手动遍历,性能出色。

思路

  1. ID分组,对每个分组的记录按Date排序(确保时间顺序正确);
  2. 找到每个分组中第一个阴性(N)结果的位置,筛选出该位置之前的所有阳性(P)记录;
  3. 对筛选后的阳性记录,计算最大日期与最小日期的间隔天数;如果只有1条记录,间隔天数为0。

代码

import pandas as pd

# 读取数据集(这里模拟你的数据,实际可替换为pd.read_csv等)
data = pd.DataFrame({
    'ID': [1,1,1,1,2,2,2,3,3,4,4,4,4],
    'Test': ['N','P','P','P','P','N','P','N','P','P','P','P','N'],
    'Date': pd.to_datetime(['2021-01-02','2021-01-08','2021-02-25','2021-03-26','2021-02-05','2021-03-04','2021-03-30','2021-01-24','2021-02-10','2021-02-15','2021-02-28','2021-03-18','2021-04-11'])
})

def calculate_first_positive_interval(group):
    # 按日期排序
    group_sorted = group.sort_values('Date')
    # 找到第一个N的索引
    first_n_idx = group_sorted[group_sorted['Test'] == 'N'].index.min()
    # 筛选第一个N之前的P记录
    if pd.notna(first_n_idx):
        pos_records = group_sorted.loc[:first_n_idx-1][group_sorted['Test'] == 'P']
    else:
        # 没有N的情况,取所有P记录
        pos_records = group_sorted[group_sorted['Test'] == 'P']
    # 计算间隔天数
    if len(pos_records) >= 2:
        return (pos_records['Date'].max() - pos_records['Date'].min()).days
    else:
        return 0

# 分组应用函数并整理结果
result = data.groupby('ID').apply(calculate_first_positive_interval).reset_index(name='Days')
print(result)

R(dplyr)实现

用dplyr的管道式操作,代码简洁且高效,适合处理大规模分组数据。

思路

  1. ID分组,对每组按Date排序;
  2. 标记出第一个N出现的位置,筛选该位置之前的P记录;
  3. 计算该组P记录的最大日期与最小日期的差值,单条记录则返回0。

代码

library(dplyr)
library(lubridate)

# 模拟数据集
data <- tibble(
  ID = c(1,1,1,1,2,2,2,3,3,4,4,4,4),
  Test = c('N','P','P','P','P','N','P','N','P','P','P','P','N'),
  Date = ymd(c('2021-01-02','2021-01-08','2021-02-25','2021-03-26','2021-02-05','2021-03-04','2021-03-30','2021-01-24','2021-02-10','2021-02-15','2021-02-28','2021-03-18','2021-04-11'))
)

result <- data %>%
  group_by(ID) %>%
  arrange(Date) %>%
  mutate(
    # 标记第一个N的位置
    first_n_row = row_number() == which(Test == 'N')[1],
    # 确定截止行:第一个N之前的所有行
    cutoff = ifelse(is.na(first_n_row), n(), which(first_n_row)[1] - 1)
  ) %>%
  filter(row_number() <= cutoff, Test == 'P') %>%
  summarise(
    Days = ifelse(n() >= 2, as.integer(max(Date) - min(Date)), 0)
  ) %>%
  ungroup()

print(result)

SQL实现

如果数据存储在数据库中,直接用SQL窗口函数处理,无需导出数据,效率最高。

思路

  1. 用窗口函数MIN()找到每个ID的第一个N的日期;
  2. 筛选出每个ID中日期早于第一个N日期的P记录;
  3. 按ID分组,计算这些P记录的最大日期与最小日期的差值,单条记录则返回0。

代码(以MySQL为例,其他数据库可微调)

WITH first_n_dates AS (
    SELECT 
        ID,
        MIN(Date) AS first_n_date
    FROM PosNeg
    WHERE Test = 'N'
    GROUP BY ID
),
filtered_pos_records AS (
    SELECT 
        p.ID,
        p.Date
    FROM PosNeg p
    LEFT JOIN first_n_dates fn ON p.ID = fn.ID
    WHERE 
        p.Test = 'P'
        AND (fn.first_n_date IS NULL OR p.Date < fn.first_n_date)
)
SELECT 
    ID,
    CASE 
        WHEN COUNT(*) >= 2 THEN DATEDIFF(MAX(Date), MIN(Date))
        ELSE 0
    END AS Days
FROM filtered_pos_records
GROUP BY ID
ORDER BY ID;

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

火山引擎 最新活动