【问题标题】:Cumulative sum with a threshold window in R data.tableR data.table中具有阈值窗口的累积和
【发布时间】:2021-04-05 00:48:15
【问题描述】:

我想计算我的数据集中 n 行的滚动总和,其中窗口大小“n”取决于总和本身。例如,我想在滚动时间总和超过 5 分钟时滑动窗口。基本上,我想计算这个人在最后 5 分钟内行进了多少距离,但时间步长并不相等。为了清楚起见,这是一个虚拟的 data.table(最后两列是必需的):

我正在寻找 R 中的 data.table 解决方案

输入数据表:

ID Distance Time
1 2 2
1 4 1
1 2 1
1 2 2
1 3 3
1 6 3
1 1 1

期望的输出:

ID Distance Time 5.min.rolling.distance 5.min.rolling.time
1 2 2 NA NA
1 4 1 NA NA
1 2 1 NA NA
1 2 2 10 6
1 3 3 5 5
1 6 3 9 6
1 1 1 10 7

【问题讨论】:

  • 你可能会在?frollsum 中发现adaptive 参数很有用
  • @jangorecki 谢谢,但自适应只允许每行有不同的窗口大小,但窗口大小是固定向量。

标签: r data.table threshold rolling-sum


【解决方案1】:

这是一个适用于double 时间单位的解决方案,以及一个适用于integer 时间单位的更简单的解决方案。我在 10,000 条记录上测试了 double 解决方案,并在我 2015 年的笔记本电脑上立即执行。我无法保证 40 GB 数据的性能。

如果您想概括此代码,我会查看 RcppRoll package 并学习如何在 R 中实现 c++ 代码。

double 时间单位的解决方案

我把它分解成两个问题。首先,通过回顾至少 5 分钟(或用完数据)来计算窗口大小。其次,取当前观察到回溯单元的距离和时间之和。

R 中的错​​误循环代码通常会尝试“增长”一个向量,预先分配向量长度然后更改其中的元素会大大提高效率。

input <- data.frame(
  dist = c(2, 4, 2, 2, 3, 6, 1),
  time = c(2, 1, 1, 2, 3, 3, 1)
)

var_window_cumsum <- function(input, MIN_TIME) {
  
  if(is.null(input$time) | is.null(input$dist)) {
    stop("input must have variables time and dist that record the row's duration and distance traveled.")
  }
  
  n <- nrow(input)
  
  # First, figure out how far we need to look back to, this vector will store
  # the position of the first record that gets our target record up to 5 min or
  # more. If we cant look back to 5 min, we leave it as NA.
  time_indx = rep(NA_integer_, length = n) # always preallocate your vector!
  for(time in (1:n)) {
    prior = time # start at self in case observation is already >= MIN_TIME
    while(sum(input$time[time:prior]) < MIN_TIME & prior > 1) {
      prior = prior - 1
    }
    
    # if we cant look back to our minimum time, leave the indx as NA
    if (sum(input$time[time:prior]) >= MIN_TIME) {
      time_indx[time] = prior 
    }
  }
  
  # Now that we know how far to look back, its easy to find out the total distance
  # and total time.
  dist5 =  rep(NA_integer_, n)
  time5 =  rep(NA_integer_, n)
  for (i in 1:n) {
    dist5[i] <- ifelse(!is.na(time_indx[i]), 
                       sum(input$dist[i:time_indx[i]]),
                       NA)
    time5[i] <- ifelse(!is.na(time_indx[i]), 
                       sum(input$time[i:time_indx[i]]),
                       NA)
  }
  
  cbind(input, 
        window_dist = dist5, 
        window_time = time5, 
        window_start = time_indx)
}

# output looks good 
# Warning: example data does not include exhaustive cases 
# I have not setup thorough testing
var_window_cumsum(input, 5)
# Test on a larger dataset, 10k records
set.seed(1234)
n <- 10000
med_input <- data.frame(
  dist = sample(1:5, n, replace = TRUE),
  time = sample(1:60, n, replace = TRUE) / 10
)

# you should inspect this to make sure there are no errors
med_output <- var_window_cumsum(med_input, 5)

integer 时间单位的解决方案

如果您的时间单位是整数并且您的数据不是太大,它可能适用于complete 您的数据集。这有点小技巧,但在这里我创建了一个连续的timeid 变量,该变量从开始时间到最大时间,并为每个整数时间单位创建一行。从那里很容易计算最后五个时间单位的滚动累积和。最后,我们删除了我们添加的所有假行(您要确保这样做,因为它们将包含无效的累积总和数据。另外,重要的是要注意我使用 roll_sumr 而不是 roll_sum;@987654333 @ 在前 4 个单元的输出向量的左侧包含 4 个填充 NA。

library(tidyverse)
library(RcppRoll)

input <- data.frame(
  dist = c(2, 4, 2, 2, 3, 6, 1),
  time = c(2, 1, 1, 2, 3, 3, 1)
)

desired_dist5 <- c(NA, NA, NA, 10, 5, 9, 10)
desired_time5 <- c(NA, NA, NA, 6, 5, 6, 7)

output <- input %>% 
  mutate(timeid = cumsum(time), 
         realrow = TRUE) %>% 
  complete(timeid = 1:max(timeid)) %>% 
  mutate(dist5 = roll_sumr(dist, 5, na.rm = T),
         time5 = roll_sumr(time, 5, na.rm = T)) %>% 
  filter(realrow) %>% 
  select(-c(realrow, timeid))

# Check against example table
output$dist5 == desired_dist5
output$time5 == desired_time5

【讨论】:

  • 感谢您的回答。但 dist5 在我的数据中不可用。我也需要计算一下。为了清楚起见,更新了我的问题
  • 当然,之前很清楚。我的代码没有实质性改变,但我对其进行了编辑以明确表明您所需的输出正在用于检查解决方案的有效性。
  • 感谢更新。这是一个巧妙的解决方案。它就像一个魅力,但问题是我有 40 GB 的数据,时间单位是双倍的。所以我有时间作为 1.2、3.6 等,但这个解决方案不起作用。如果您能帮我找出非整数时间单位的解决方案,那就太好了。
  • 为双单元添加了另一种解决方案。无法保证使用这么多数据的性能时间。
  • 非常感谢 :)
猜你喜欢
  • 2016-02-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-17
  • 2019-02-24
  • 2017-09-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多