如何用rowSums()统计每行非缺失且唯一的MRI扫描次数?
问题:统计每行非缺失且唯一的MRI扫描日期次数
现有如下R语言数据框df,每行代表一名患者,各列记录其MRI扫描日期:
> df mr_daterd mr_daterd_fu1 mr_daterd_fu2 1 2018-03-05 2018-03-05 <NA> 2 2019-05-04 <NA> 2020-03-05 3 2020-01-03 2020-06-06 2021-04-02
需求为统计每行的MRI扫描次数,即统计每行非缺失值的数量,但需要排除重复日期的情况。原使用rowSums()的代码:
df_new <- df %>% mutate( n_mri = rowSums(!is.na(select(., contains('mr_daterd')))) )
该代码会将第一行的重复日期统计为2次,但实际应为1次。期望输出如下:
> df_new mr_daterd mr_daterd_fu1 mr_daterd_fu2 n_mri 1 2018-03-05 2018-03-05 <NA> 1 2 2019-05-04 <NA> 2020-03-05 2 3 2020-01-03 2020-06-06 2021-04-02 3
数据框结构定义:
df <- structure(list(mr_daterd = structure(c(17595, 18020, 18264), class = "Date"), mr_daterd_fu1 = structure(c(17595, NA, 18419), class = "Date"), mr_daterd_fu2 = structure(c(NA, 18326, 18719), class = "Date")), class = "data.frame", row.names = c(NA, -3L))
解决方案
方法1:使用dplyr的rowwise()结合n_distinct()
可读性强,适配tidyverse工作流:
library(dplyr) df_new <- df %>% rowwise() %>% mutate( n_mri = n_distinct(c_across(contains('mr_daterd')), na.rm = TRUE) ) %>% ungroup()
rowwise():指定按行处理数据c_across(contains('mr_daterd')):提取所有含mr_daterd的列并合并为向量n_distinct(..., na.rm = TRUE):计算向量中非缺失的唯一值数量
方法2:基础R的apply()实现
无需加载额外包,适合轻量场景:
df$n_mri <- apply(df[, grepl('mr_daterd', colnames(df))], 1, function(x) { length(unique(na.omit(x))) })
apply(..., 1):按行执行自定义函数na.omit(x):移除当前行的缺失值unique()+length():统计去重后的有效日期数量
方法3:tidyr长格式转换统计
适合后续需扩展分析的复杂场景:
library(dplyr) library(tidyr) df_new <- df %>% mutate(id = row_number()) %>% pivot_longer(cols = contains('mr_daterd'), names_to = 'scan', values_to = 'date') %>% drop_na(date) %>% distinct(id, date) %>% count(id, name = 'n_mri') %>% right_join(df, by = 'id') %>% select(-id) %>% relocate(n_mri, .after = last_col())
内容的提问来源于stack exchange,提问作者cmirian
相关产品推荐
相关产品推荐

