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

使用R tidyverse计算长格式数据中测量2与各测量的天数差

分组计算个体重复测量的日期差值问题

问题说明

现有长格式重复测量数据集,字段包括:

  • obs_id:个体标识
  • time:测量序号(1、2、3)
  • date:测量1/2的日期(若存在)
  • date_3:测量3的单独日期(若存在)

需要生成days_2_to_time字段:

  • 存在测量2的个体,每行存储该行日期与测量2日期的天数差(测量1/2用date,测量3用date_3)
  • 无测量2的个体,该字段为缺失值

示例数据集:

library(tidyverse)
library(lubridate)
sample_data <- tribble(
  ~obs_id, ~time, ~date, ~date_3,    
  "a", 1, ymd("2017-10-28"), NA_Date_,
  "a", 2, ymd("2017-11-09"), NA_Date_,
  "a", 3, NA_Date_, ymd("2017-11-12"), 
  "b", 1, ymd("2017-10-28"), NA_Date_,
  "b", 2, ymd("2017-11-09"), NA_Date_,
  "b", 3, NA_Date_, NA_Date_,    
  "c", 1, ymd("2017-10-05"), NA_Date_,
  "c", 3, NA_Date_, ymd("2017-10-29"),
)

预期结果:

expected_result <- tribble(
  ~obs_id, ~time, ~date, ~date_3, ~days_2_to_time,
  "a", 1, ymd("2017-10-28"), NA_Date_, -12, 
  "a", 2, ymd("2017-11-09"), NA_Date_, 0, 
  "a", 3, NA_Date_, ymd("2017-11-12"), 3,
  "b", 1, ymd("2017-10-28"), NA_Date_, -12, 
  "b", 2, ymd("2017-11-09"), NA_Date_, 0,
  "b", 3, NA_Date_, NA_Date_, NA_integer_,
  "c", 1, ymd("2017-10-05"), NA_Date_, NA_integer_,
  "c", 3, NA_Date_, ymd("2017-10-29"), NA_integer_
)

原代码错误分析

尝试的代码报错:

sample_data <- sample_data |>
  group_by(obs_id) |>
  mutate(
    days_2_to_time = case_when(
      2 %in% time & time != 3 ~ as.duration(date[time == 2] %--% date)/ddays(1),
      2 %in% time & time == 3 ~ as.duration(date[time == 2] %--% date_3)/ddays(1),
      ! 2 %in% time ~ NA_integer_
    )
  ) |>
  ungroup()

错误信息:

Error in `mutate()`:
ℹ In argument: `days_2_to_time = case_when(...)`.
ℹ In group 3: `obs_id = "c"`.
Caused by error in `case_when()`:
! Can't recycle `..1 (left)` (size 2) to match `..1 (right)` (size 0).
Run `rlang::last_trace()` to see where the error occurred.

核心问题:case_when是逐行评估条件的,2 %in% time逐行判断时,每个行的time是单个值,导致:

  1. 无测量2的组中,date[time==2]返回空向量,与逐行计算的结果长度不匹配
  2. !2 %in% time会被逐行触发(比如time=1或3的行都会满足),而非针对整个组判断是否存在测量2

解决方案

方法1:先提取组内测量2的日期,再计算差值

先在分组内统一提取测量2的日期,再基于该日期计算每行的差值,同时通过组内整体判断决定是否返回NA:

sample_data <- sample_data |>
  group_by(obs_id) |>
  mutate(
    # 提取当前组中测量2的日期,无则为NA
    time2_date = first(date[time == 2]),
    # 确定当前行使用的日期字段
    current_date = case_when(
      time != 3 ~ date,
      time == 3 ~ date_3
    ),
    # 计算天数差:仅当存在测量2时计算,否则返回NA
    days_2_to_time = ifelse(is.na(time2_date), 
                            NA_integer_, 
                            as.integer(current_date - time2_date))
  ) |>
  # 可选:移除中间临时变量
  select(-time2_date, -current_date) |>
  ungroup()

方法2:简化版,无需中间变量

直接在ifelse中先判断组内是否存在测量2,再计算差值:

sample_data <- sample_data |>
  group_by(obs_id) |>
  mutate(
    days_2_to_time = ifelse(
      # 针对整个组判断是否存在测量2
      !any(time == 2),
      NA_integer_,
      # 计算当前行日期与测量2日期的天数差
      as.integer(
        case_when(
          time != 3 ~ date,
          time == 3 ~ date_3
        ) - first(date[time == 2])
      )
    )
  ) |>
  ungroup()

两种方法都能得到符合预期的结果,核心是用any(time == 2)实现针对整个分组的条件判断,而非逐行判断,同时统一提取组内测量2的日期,避免长度不匹配问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 08:17:05