如何从R数据框指定列中提取最新非NA值生成新变量
R提取指定列最新非NA值生成新列的高效实现
现有如下R数据框df_test:
df_test<- data.frame(id = c("1a", "2a", "3a", "4a", "5a", "6a", "7a", "8a", "9a", "10a"), baseline= c(-1,-1,-1,-1,-1,-1,-1,-1,-1,-1), checkin1= c(0,NA,1, 2, NA, NA,NA, 1, NA, NA), checkin2= c(-1,NA, NA,4,NA,NA,NA,NA,NA,NA), checkin3= c(-1, 0, 1,1,1,2,2,NA,NA,2), checkin4= c(NA,NA,NA,2,NA,NA,NA,NA,NA,1))
需要生成新列end,提取第3至6列(checkin1至checkin4,其中checkin4为最新记录、checkin1为最旧)中的最新非NA值;若该行无有效非NA值(如第9行),则新列值为NA,期望结果如下:
result<- data.frame(id = c("1a", "2a", "3a", "4a", "5a", "6a", "7a", "8a", "9a", "10a"), baseline= c(-1,-1,-1,-1,-1,-1,-1,-1,-1,-1), checkin1= c(0,NA,1, 2, NA, NA,NA, 1, NA, NA), checkin2= c(-1,NA, NA,4,NA,NA,NA,NA,NA,NA), checkin3= c(-1, 0, 1,1,1,2,2,NA,NA,2), checkin4= c(NA,NA,NA,2,NA,NA,NA,NA,NA,1), end= c(-1,0,1,2,1,2,2,1,NA,1))
已知可通过ifelse与mutate组合实现,但以下是更简洁直接的高效实现方式:
方法1:dplyr行处理 + last()
利用dplyr的行级处理能力,结合last()直接提取每行指定列的最后一个非NA值:
library(dplyr) df_test %>% rowwise() %>% mutate(end = last(na.omit(c(checkin1, checkin2, checkin3, checkin4)), default = NA)) %>% ungroup()
- 逻辑:
na.omit()过滤当前行指定列的NA值,last()取过滤后的最后一个值(对应最新的checkin记录),default = NA确保无有效值时返回NA。
方法2:base R原生apply()实现
无需额外加载包,直接用base R的行应用函数完成:
df_test$end <- apply(df_test[, 3:6], 1, function(x) { valid_vals <- na.omit(x) if (length(valid_vals) == 0) NA else tail(valid_vals, 1) })
- 逻辑:
apply()按行遍历第3-6列,na.omit()清理NA值,tail(valid_vals,1)取最后一个有效记录,无有效值时返回NA。
方法3:tidyr重塑数据提取
通过宽转长的方式筛选最新记录,再合并回原数据框:
library(dplyr) library(tidyr) df_test %>% pivot_longer(cols = checkin1:checkin4, names_to = "checkin", values_to = "value", values_drop_na = TRUE) %>% mutate(checkin_order = as.integer(sub("checkin", "", checkin))) %>% group_by(id) %>% filter(checkin_order == max(checkin_order)) %>% select(id, end = value) %>% right_join(df_test, by = "id") %>% select(names(df_test), end)
- 逻辑:将checkin列转为长格式后提取每个checkin的序号,按id分组筛选序号最大的记录(最新),最后合并回原数据框补全缺失行。
内容的提问来源于stack exchange,提问作者pww_qs2
相关产品推荐
相关产品推荐

