选择特定时间范围内的行

时间:2015-06-24 22:13:30

标签: r dataframe

我有一个数据框,如:

TimeStamp                    Category

2013-11-02 07:57:18 AM         0
2013-11-02 08:07:19 AM         0
2013-11-02 08:07:21 AM         0
2013-11-02 08:07:25 AM         1
2013-11-02 08:07:29 AM         0
2013-11-02 08:08:18 AM         0
2013-11-02 08:09:20 AM         0
2013-11-02 09:04:18 AM         0
2013-11-02 09:05:22 AM         0
2013-11-02 09:07:18 AM         0

我想要做的是在Category为" 1"时选择+ -10分钟的时间范围。

对于这种情况,由于category = 1位于2013-11-02 08:07:25 AM,我想选择07:57:25 AM to 08:17:25 AM中的所有行。

处理此任务的最佳方法是什么?

此外,可能有多个" 1"对于每个时间框架。 (真正的数据框架更复杂,它包含多个不同用户的TimeStamp,即另一列名为" UserID")

6 个答案:

答案 0 :(得分:10)

在基础R中,没有润滑或其他任何东西(假设您将TimeStamp转换为POSIXct对象),如:

df$TimeStamp <- as.POSIXct(TimeStamp, format = "%Y-%m-%d %I:%M:%S %p")
df[with(df, abs(difftime(TimeStamp[Category==1],TimeStamp,units="mins")) <= 10 ),]

#            TimeStamp Category
#2 2013-11-02 08:07:19        0
#3 2013-11-02 08:07:21        0
#4 2013-11-02 08:07:25        1
#5 2013-11-02 08:07:29        0
#6 2013-11-02 08:08:18        0
#7 2013-11-02 08:09:20        0

如果您有多个1,那么您必须像以下一样循环播放:

check <- with(df, 
  lapply(TimeStamp[Category==1], function(x) abs(difftime(x,TimeStamp,units="mins")) <= 10 ) 
)
df[do.call(pmax, check)==1,]

答案 1 :(得分:7)

以下是我使用data.table::foverlaps

进行此操作的方法

首先,将TimeStamp转换为正确的POSIXct

library(data.table)
setDT(df)[, TimeStamp := as.POSIXct(TimeStamp, format = "%Y-%m-%d %I:%M:%S %p")]

然后我们将创建一个临时数据集,其中Category == 1加入。我们还将通过“开始”和“结束”列创建“结束”列和key

df2 <- setkey(df[Category == 1L][, TimeStamp2 := TimeStamp], TimeStamp, TimeStamp2)

然后,我们将对df执行相同操作,但会设置10分钟间隔

setkey(df[, `:=`(start = TimeStamp - 600, end = TimeStamp + 600)], start, end)

然后,剩下要做的就是按匹配事件运行foverlaps和子集

indx <- foverlaps(df, df2, which = TRUE, nomatch = 0L)$xid
df[indx, .(TimeStamp,  Category)]
#              TimeStamp Category
# 1: 2013-11-02 08:07:19        0
# 2: 2013-11-02 08:07:21        0
# 3: 2013-11-02 08:07:25        1
# 4: 2013-11-02 08:07:29        0
# 5: 2013-11-02 08:08:18        0
# 6: 2013-11-02 08:09:20        0

答案 2 :(得分:4)

这似乎有效:

数据:

根据@DavidArenburg的评论(并在他的回答中提到),将timestamp列转换为POSIXct对象的正确方法是(如果还没有):

df$TimeStamp <- as.POSIXct(df$TimeStamp, format = "%Y-%m-%d %I:%M:%S %p")

解决方案:

library(lubridate) #for minutes
library(dplyr)     #for between
pickrows <- function(df) {
  #pick category == 1 rows
  df2 <- df[df$Category==1,]
  #for each timestamp create two variables start and end
  #for +10 and -10 minutes
  #then pick rows between them
  lapply(df2$TimeStamp, function(time) {
      start <- time - minutes(10)
      end   <- time + minutes(10)
      df[between(df$TimeStamp, start, end),]
  })
} 

#run function
pickrows(df)

输出:

> pickrows(df)
[[1]]
            TimeStamp Category
2 2013-11-02 08:07:19        0
3 2013-11-02 08:07:21        0
4 2013-11-02 08:07:25        1
5 2013-11-02 08:07:29        0
6 2013-11-02 08:08:18        0
7 2013-11-02 08:09:20        0

请记住,在多个Category==1行的情况下输出,我的函数的输出将是一个列表(在这个ocassion中它只有一个元素)所以需要do.call(rbind, pickrows(df))来组合所有内容在一个data.frame。

答案 3 :(得分:4)

使用lubridate:

df$TimeStamp <- ymd_hms(df$TimeStamp)
span10 <- (df$TimeStamp[df$Category == 1] - minutes(10)) %--% (df$TimeStamp[df$Category == 1] + minutes(10))
df[df$TimeStamp %within% span10,]
            TimeStamp Category
2 2013-11-02 08:07:19        0
3 2013-11-02 08:07:21        0
4 2013-11-02 08:07:25        1
5 2013-11-02 08:07:29        0
6 2013-11-02 08:08:18        0
7 2013-11-02 08:09:20        0

答案 4 :(得分:3)

我个人喜欢@thelatemail的基础R答案的简单性。但是为了好玩,我将在data.table中使用滚动连接提供另一个答案,而不是@DavidArenburg提供的重叠范围连接解决方案。

require(data.table)
dt_1 = dt[Category == 1L]
setkey(dt, TimeStamp)

ix1 = dt[.(dt_1$TimeStamp - 600L), roll=-Inf, which=TRUE] # NOCB
ix2 = dt[.(dt_1$TimeStamp + 600L), roll= Inf, which=TRUE] # LOCF

indices = data.table:::vecseq(ix1, ix2-ix1+1L, NULL) # not exported function
dt[indices]
#              TimeStamp Category
# 1: 2013-11-02 08:07:19        0
# 2: 2013-11-02 08:07:21        0
# 3: 2013-11-02 08:07:25        1
# 4: 2013-11-02 08:07:29        0
# 5: 2013-11-02 08:08:18        0
# 6: 2013-11-02 08:09:20        0

即使你有多个Category为1,AFAICT的单元格,这也应该可以正常工作。将此作为data.table ...

的此类操作的功能包装起来会很棒

PS:请参阅其他帖子,将TimeStamp转换为POSIXct格式。

答案 5 :(得分:1)

以下是dplyrlubridate的解决方案。以下是步骤:

使用category ==1的{​​{1}}与+进行简单-,找出lubridateminutesc(-1, 1) * minutes(10) 10分钟的位置然后使用filter基于存储在rang向量中的两个区间进行子集化。

library(lubridate)
library(dplyr)
wi1 <- which(dat$Category == 1 )
rang <- dat$TimeStamp[wi1] +  c(-1,1) * minutes(10)
dat %>% filter(TimeStamp >= rang[1] & TimeStamp <= rang[2])
            TimeStamp Category
1 2013-11-02 08:07:19        0
2 2013-11-02 08:07:21        0
3 2013-11-02 08:07:25        1
4 2013-11-02 08:07:29        0
5 2013-11-02 08:08:18        0
6 2013-11-02 08:09:20        0