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

如何在ggplot纵向线图数据中按测试单独过滤非NA的Pre/Post时间点对?

解决方案

核心思路是把每个测试的判断独立开来——你需要的是「受试者针对某一项测试有完整的Pre/Post非NA对」,而非「受试者所有测试必须同时满足完整对」。最简洁的实现方式是先将宽格式数据转成长格式,再按「受试者+测试」分组过滤。

步骤1:宽转长格式

把多个Test列转换为「测试名称」和「测试分数」两列,让每个测试的观测单独成一行:

library(dplyr)
library(tidyr)

dat_long <- dat_all %>%
  pivot_longer(
    cols = starts_with("Test"),  # 匹配所有Test开头的列(如Test1到Test5)
    names_to = "Test",           # 新列:存储测试名称
    values_to = "Score"          # 新列:存储测试分数
  )

步骤2:按「受试者+测试」过滤

对每个受试者的每个测试,检查是否同时存在Pre和Post时间点,且两个时间点的分数都不为NA:

dat_filtered <- dat_long %>%
  group_by(SubjectID, Test) %>%
  filter(
    n_distinct(Time) == 2,  # 确保该测试包含Pre和Post两个时间点
    all(!is.na(Score))      # 两个时间点的分数均非NA
  ) %>%
  ungroup()

可选:转回宽格式

如果后续绘图需要宽格式数据,可以再转换回去:

dat_wide <- dat_filtered %>%
  pivot_wider(
    names_from = Test,
    values_from = Score
  )

为什么之前的方法不生效?

你之前的代码都是仅按SubjectID分组,相当于要求同一个受试者的所有测试必须同时满足完整对,这和你的需求不符。转成长格式后按「SubjectID+Test」分组,就能实现每个测试独立判断,保留那些某一项测试有完整对、其他测试无数据的受试者观测。

替代方案(不转长格式)

如果不想转换数据格式,可以针对每个测试单独过滤后合并:

test_names <- paste0("Test", 1:5)

# 逐个处理每个测试
filtered_list <- lapply(test_names, function(test_col) {
  dat_all %>%
    group_by(SubjectID) %>%
    filter(
      Time %in% c("Pre", "Post"),
      !is.na(.data[[test_col]]),
      n() == 2  # 确保该测试有两个非NA时间点
    ) %>%
    ungroup() %>%
    mutate(Test = test_col)  # 标记当前处理的测试
})

# 合并所有结果
dat_filtered <- bind_rows(filtered_list)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 00:47:23