R语言计算数据框每行过去48小时时间窗口列最小值的简洁方法
R语言不规则时间窗口滚动最小值实现方案
你当前用purrr::pmap逐行扫描的写法逻辑正确,但属于R层逐行循环,数据量增大后性能衰减明显,下面是两种更简洁、性能更好的实现方式,输出结果和你的代码完全一致。
方案1:tidyverse生态首选(slider包)
slider是专门针对非等距滚动窗口设计的扩展包,原生支持POSIXct时间类型作为窗口索引,不需要手动计算时间上下界,代码可读性和性能都远优于逐行purrr写法:
library(tidyverse) library(slider) df <- tibble( time = as.POSIXct(c("2012-01-01 00:00:00", "2012-01-02 00:00:00", "2012-01-04 00:00:00")), value = c(0, 0.1, 0.2) ) %>% mutate( min_v_48h = slide_index_dbl( .x = value, .i = time, .f = ~ ifelse(length(.x) == 0, Inf, min(.x)), .before = hours(48), # 窗口向前覆盖48小时 .after = -1, # 不包含当前行,匹配原代码`time < upper_bound`的逻辑 .complete = FALSE ) )
运行输出:
# A tibble: 3 × 3 time value min_v_48h <dttm> <dbl> <dbl> 1 2012-01-01 00:00:00 0 Inf 2 2012-01-02 00:00:00 0.1 0 3 2012-01-04 00:00:00 0.2 0.1
这个方案的优势:
- 窗口规则直接通过参数声明,不需要手写子集判断逻辑,代码简洁不容易出错
- 底层为C实现的窗口索引计算,十万到百万级数据量下性能比逐行pmap高1~2个数量级
- 完全适配dplyr管道操作,和tidyverse生态其他函数无缝衔接
方案2:大数据量极致性能方案(data.table非等连接)
如果处理千万级以上的超大数据集,可以用data.table的非等值自连接实现,全程没有R层循环,性能是目前R语言同类场景下的第一梯队:
library(data.table) setDT(df) # 非等连接匹配48小时窗口 df[, min_v_48h := df[.(upper_t = time, lower_t = time - 48*3600), on = .(time >= lower_t, time < upper_t), min(value), by = .EACHI]$V1] # 无匹配值的填充为Inf df[is.nan(min_v_48h), min_v_48h := Inf]
注意事项
如果你的需求需要把当前行的value也纳入窗口计算,只需要把方案1的.after = -1改成.after = 0,方案2的time < upper_t改成time <= upper_t即可。
内容的提问来源于stack exchange,提问作者Ai4l2s
相关产品推荐
相关产品推荐

