根据时间间隔在R中对行进行子集

时间:2015-02-12 17:07:53

标签: r time subset intervals

我有一系列关于POSIXlt格式的GPS位置。我想要分组并保留仅花费超过5分钟(300秒)appart的位置。

我使用子集(diff())在两个连续位置之间的时间间隔(以秒为单位)。我面临的问题是我的前三个位置每个都是5分钟,所以R不会返回任何一个(同样的事情在我的数据框架中进一步发生)

我想得到的是位置#1和#3(10分钟的appart)。 我希望并放弃#2,因为距离#1只有5分钟,然后检查下一个位置是否距离我保留的最后一个位置是5分钟。

我该怎么办?

谢谢, 本

1 个答案:

答案 0 :(得分:2)

一种可能的方法是diff,删除与之前观察结果太接近的任何内容,并重复直到您不删除任何内容。然而,对于具有密集间隔观测值的大型数据集而言,这可能是非常低效的。

在一次通过中执行此操作的方法是循环观察,在跳过与最后保留的观察结果太接近的观察时累积时间差异:

# Use for loop to determine which to keep
pick.obs <- function(diffs, limit) {
  keep <- c(T, rep(F, length(diffs)))
  acc <- 0
  for (i in seq_along(diffs)) {
    acc <- acc + diffs[i]
    if (acc > limit) {
      keep[i+1] <- T
      acc <- 0
    }
  }
  return(keep)
}

# Observations at time 0, 300, 500, 700, 1700; limit 600 seconds
obs.times <- c(0, 300, 500, 700, 1700)
pick.obs(diff(obs.times), 600)
[1]  TRUE FALSE FALSE  TRUE  TRUE

这种方法的一个问题是,与向量化运算符相比,R中的for循环较慢。我们可以通过使用Rcpp包在C ++中实现for for循环(仅进行小的语法更改)来重新获得此速度:

library(Rcpp)
pick.obs2 <- cppFunction(
"LogicalVector pickObs(NumericVector diffs, const double limit) {
  int n = diffs.size();
  LogicalVector keep(n + 1, false);
  keep[0] = true;
  double acc = 0;
  for (int i=0; i < n; ++i) {
    acc += diffs[i];
    if (acc > limit) {
      keep[i+1] = true;
      acc = 0;
    }
  }
  return keep;
}")

我们可以使用microbenchmark将纯R版本的性能与Rcpp版本进行比较:

# Reproducible example of time differences (10000 observations)
set.seed(144)
diffs <- runif(10000, 0, 20)
all.equal(pick.obs(diffs, 300), pick.obs2(diffs, 300))
# [1] TRUE

# Benchmark
library(microbenchmark)
microbenchmark(pick.obs(diffs, 300), pick.obs2(diffs, 300))
# Unit: microseconds
#                   expr      min        lq       mean    median       uq       max neval
#   pick.obs(diffs, 300) 4494.029 4947.9140 6058.83941 5128.2535 6154.653 38302.461   100
#  pick.obs2(diffs, 300)   19.877   21.2015   32.02145   30.8515   34.654   178.031   100

Rcpp版本在长度10000的向量上快约200倍。此加速是否重要完全取决于问题的大小(例如,您可能不介意等待5毫秒的长度向量10,000)。