如何在R中按行获取每位患者的最后有效观测值
在R中按行获取每行最后一个非NA观测值
以下是几种针对你的需求的实现方案,基于你提供的示例数据集:
首先重现你的数据集:
ID <- c(1:5) Age <- c(23,34,26,18,35) day1 <- c(NA,NA,NA,4,2) day2 <- c(NA,3,NA,NA,NA) day3 <- c(2,NA,3,NA,4) mydata <- data.frame(ID, Age, day1, day2, day3)
方法1:基础R(apply函数)
适合小数据集,代码简洁直观:
# 提取所有以day开头的列,按行处理 mydata$last_value <- apply(mydata[, grep("^day", names(mydata))], 1, function(x) { non_na_vals <- x[!is.na(x)] # 处理整行全为NA的情况,返回NA if (length(non_na_vals) == 0) NA else tail(non_na_vals, 1) })
运行后得到的结果:
ID Age day1 day2 day3 last_value 1 1 23 NA NA 2 2 2 2 34 NA 3 NA 3 3 3 26 NA NA 3 3 4 4 18 4 NA NA 4 5 5 35 2 NA 4 4
方法2:dplyr(tidyverse风格)
适合习惯tidyverse语法的用户,可读性强:
library(dplyr) mydata <- mydata %>% rowwise() %>% # 反转day列顺序后用coalesce取第一个非NA(对应原顺序的最后一个) mutate(last_value = coalesce(!!!rev(c_across(starts_with("day"))))) %>% ungroup()
如果需要处理全NA的行,可调整为:
mydata <- mydata %>% rowwise() %>% mutate(last_value = { vals <- c_across(starts_with("day"))[!is.na(c_across(starts_with("day")))] if (length(vals) == 0) NA else tail(vals, 1) }) %>% ungroup()
方法3:data.table(高效处理大数据)
当数据集规模较大时,data.table的性能优于apply和dplyr的rowwise模式:
library(data.table) setDT(mydata) # 指定day开头的列,按行计算 mydata[, last_value := apply(.SD, 1, function(x) { non_na_vals <- x[!is.na(x)] if (length(non_na_vals) == 0) NA else tail(non_na_vals, 1) }), .SDcols = starts_with("day")]
更高效的无apply版本(避免逐行循环):
mydata[, last_value := { # 生成每行非NA值的位置矩阵 na_mat <- !is.na(.SD) # 获取每行最后一个非NA值的列索引 last_col_idx <- max.col(na_mat, ties.method = "last") # 根据索引提取对应值 .SD[cbind(seq_len(.N), last_col_idx)] }, .SDcols = starts_with("day")]
内容的提问来源于stack exchange,提问作者Seydou GORO
相关产品推荐
相关产品推荐

