基于时间变量提取各分组首个非NA值的实现方法
按分组提取各变量首个非NA值(基于时间排序)
问题背景
处理时间序列数据时,需按id分组,提取每个变量按时间排序后首个出现的非NA值。使用tidyverse的across函数尝试实现,但原代码返回了不必要的NA,未达预期效果。
可运行示例数据
library(tidyverse) df <- tibble( id = c("a","a","b","b","c","c"), time = c(2,3,1,3,3,5), val1 = c(NA,"x","y",NA,NA,"w"), val2 = c("x",NA,"y","w",NA,"q") )
原代码问题
原代码取每个组内time最小的行对应的变量值,但该行的变量可能为NA,导致结果不符合需求:
df %>% group_by(id)%>% summarise(across(starts_with('val'), ~ .x[which.min(time)]))
原代码返回结果(含NA)
# A tibble: 3 x 3 id val1 val2 <chr> <chr> <chr> 1 a NA x 2 b y y 3 c NA NA
期望结果
# A tibble: 3 x 3 id val1 val2 <chr> <chr> <chr> 1 a x x 2 b y y 3 c w q
解决方案
核心思路是先按组内时间升序排列,再对每个变量提取第一个非NA值,用across批量处理所有val开头的变量:
方法一:过滤NA后取首个值
df %>% group_by(id) %>% arrange(time, .by_group = TRUE) %>% # 按组内时间从小到大排序 summarise(across(starts_with("val"), ~ .x[!is.na(.x)][1]))
方法二:用first(na.omit(.x))简化代码
df %>% group_by(id) %>% arrange(time, .by_group = TRUE) %>% summarise(across(starts_with("val"), ~ first(na.omit(.x))))
关键说明
arrange(time, .by_group = TRUE)确保每个组内的行按时间顺序排列,time为Date或POSIXct(datetime)类型时同样适用,无需额外转换。- 两种方法都会过滤当前变量的NA值,取排序后的第一个非NA值;如果该组变量全为NA,会返回NA(符合逻辑)。
内容的提问来源于stack exchange,提问作者airj
相关产品推荐
相关产品推荐

