0

我有一个data.table, 和一个日期列表。我希望使用检查日期是否与列表相符的函数来过滤和修改行。

# example data
set.seed(1)
tt <- sample(
             seq(as.POSIXct("2011-10-02"), as.POSIXct("2014-04-06"),
                 by = "day"), 10)
IR1 <- data.table(tstamp = sort(tt), dLoc = 1L:10L)

日期列表:

DLSlist <- lapply(
                  list(dls11t12 = c("2011-10-02", "2012-04-01"), 
                       dls12t13 = c("2012-10-07", "2013-04-07"), 
                       dls13t14 = c("2013-10-06", "2014-04-06"),
                       dls14t15 = c("2014-10-05", "2015-04-05"),
                       dls15t16 = c("2015-10-04", "2016-04-03"),
                       dls16t17 = c("2016-10-02", "2017-04-02")
                       ), 
                  function(X) as.POSIXct(X)
                  )

如果dLoc 落在DLSlist. 我可以做到以下几点:

IR1[tstamp > DLSlist[[1]][1] & tstamp < DLSlist[[1]][2], tstamp := tstamp + 60*60]
IR1[tstamp > DLSlist[[2]][1] & tstamp < DLSlist[[2]][2], tstamp := tstamp + 60*60]
IR1[tstamp > DLSlist[[3]][1] & tstamp < DLSlist[[3]][2], tstamp := tstamp + 60*60] 

然而,这似乎很容易出错:一个函数适合这个任务......我的没有工作。

DLStest <- function(dd, DLSobj) {
    any(sapply(DLSobj, function(X) dd %between% X))
}

我将其应用于:

IR1[DLStest(tstamp, DLSlist), tstamp := tstamp + 60*60]

但是它不起作用:所有行都被转换了(不仅是范围内的行,就像我丑陋的黑客代码中的情况一样)。

是否有一些使用函数选择行的方法 - 或其他一些基于多个范围检查选择行的方法?


更新(感谢发现问题的 Frank)

您确实可以使用返回向量或布尔值的函数进行过滤。错误都与我的初始功能有关。

DLStest_old <- function(dd, DLSobj) {
    any(sapply(DLSobj, function(X) dd %between% X))
}

sapply返回一个对象 who's classis matrix; any检查整个. _ matrix如果有任何真值,则评估为单个TRUE. 如果不是,则评估为单个FALSE.

使用测试数据:

(IR1[DLStest_old(tstamp, DLSlist), dLoc := dLoc + 1000L])

                 tstamp dLoc
 1: 2011-11-27 01:00:00 1001
 2: 2012-04-03 00:00:00 1002
 3: 2012-06-01 00:00:00 1003
 4: 2012-09-06 00:00:00 1004
 5: 2013-03-09 01:00:00 1005
 6: 2013-04-25 00:00:00 1006
 7: 2013-05-25 00:00:00 1007
 8: 2013-12-29 01:00:00 1008
 9: 2014-01-09 01:00:00 1009
10: 2014-02-08 01:00:00 1010

解决方法是分别测试矩阵的每一行,使用apply.

DLStest <- function(dd, DLSobj) {
    apply(sapply(DLSobj, function(X) dd %between% X), 1, any)
}

这现在有效:

> (IR1[DLStest(tstamp, DLSlist), dLoc := dLoc + 1000L])
                 tstamp dLoc
 1: 2011-11-27 01:00:00 1001
 2: 2012-04-03 00:00:00    2
 3: 2012-06-01 00:00:00    3
 4: 2012-09-06 00:00:00    4
 5: 2013-03-09 01:00:00 1005
 6: 2013-04-25 00:00:00    6
 7: 2013-05-25 00:00:00    7
 8: 2013-12-29 01:00:00 1008
 9: 2014-01-09 01:00:00 1009
10: 2014-02-08 01:00:00 1010
4

2 回答 2

1

您的数据看起来在 DLSlist 中没有重叠范围,在这种情况下应该可以使用 -

library(data.table)

#creating the data
DLSlist <- data.table(read.csv(textConnection('
                  "2011-10-02", "2012-04-01" 
                  "2012-10-07", "2013-04-07" 
                  "2013-10-06", "2014-04-06"
                  "2014-10-05", "2015-04-05"
                  "2015-10-04", "2016-04-03"
                  "2016-10-02", "2017-04-02"'), header = FALSE))

IR1 <- data.table(
   tstamp = c("2011-10-01", "2012-10-06", "2014-10-07","2016-10-03")
)

#fixing data type       
IR1[,tstamp := as.Date(tstamp,"%Y-%m-%d")]
DLSlist[,V1 := as.Date(V1,"%Y-%m-%d")]
DLSlist[,V2 := as.Date(V2,"%Y-%m-%d")]
DLSlist[,tstamp := V1]

#setting a key for data.table to find the closest match
setkey(IR1,tstamp)
setkey(DLSlist,tstamp)

#roll = Inf finds the closest match for the key
IR2 <- DLSlist[IR1, roll = Inf]

#Doing the operation where condition is satisfied
IR2[tstamp > V1 & tstamp < V2 , tstamp2 := tstamp + 60*60]

输出

> IR2
       tstamp         V1         V2    tstamp2
1: 2011-10-01       <NA>       <NA>       <NA>
2: 2012-10-06 2011-10-02 2012-04-01       <NA>
3: 2014-10-07 2014-10-05 2015-04-05 2024-08-15
4: 2016-10-03 2016-10-02 2017-04-02 2026-08-12

如果您确实有重叠的范围,那么您可以创建一组执行此操作的所有日期,并将其合并回来以IR1查看哪些日期属于该集合。您可以获得执行此操作的所有日期的列表,如下所示 -

DLSlist2 <- unique(DLSlist[,list(DatesToFix = seq.Date(V1, V2, by = "day")), by = "V1"][,V1 := NULL])

我相信你将能够把这个逻辑作为一个函数。

于 2013-10-28T11:45:30.790 回答
1

您想使用逻辑向量进行子集化。在您的初始公式中,该函数仅返回单个值(而不是向量),导致您的分配影响所有行或不影响所有行。

IR <- copy(IR1)
DLStest_old <- function(dd, DLSobj) {
    any(sapply(DLSobj, function(X) dd %between% X))
}

# on the whole tstamp vector at once
  IR[,DLStest_old(tstamp, DLSlist)]
  # TRUE

一种解决方案是使用您的功能,但“按行”应用它:

# by row
  IR[,DLStest_old(tstamp, DLSlist),by=1:nrow(IR)]$V1
  # TRUE FALSE FALSE FALSE  TRUE FALSE FALSE  TRUE  TRUE  TRUE

请注意,我将其放在jdata.table 的位置以返回结果。通常,要按表达式进行子集化,可以将其放入该i位置(在第一个逗号之前),但是“by”不适用于i表达式,因此对于这种方法,最好保存逻辑向量,然后按它进行子集化:

 # by row, for use in i
    change_em <- IR[,DLStest_old(tstamp, DLSlist),by=1:nrow(IR)]$V1
    IR[change_em,tstamp:=tstamp+1e15][]

我破坏了您的日期以使更改更加清晰,结果是:

                 tstamp dLoc
 1: ))0'-06-03 15:45:52    1
 2: 2012-04-03 00:00:00    2
 3: 2012-06-01 00:00:00    3
 4: 2012-09-07 00:00:00    4
 5: ))0'-06-03 15:45:52    5
 6: 2013-04-26 00:00:00    6
 7: 2013-05-25 00:00:00    7
 8: ))0'-06-03 15:45:52    8
 9: ))0'-06-03 15:45:52    9
10: ))0'-06-03 15:45:52   10

您找到的另一个解决方案是使用*apply家庭中的一些东西:

DLStest_apply <- function(dd, DLSobj) {
    apply(sapply(DLSobj, function(X) dd %between% X), 1, any)
}

# apply "any" on the margin of the sapply result
  IR[,DLStest_apply(tstamp, DLSlist)]
  # TRUE FALSE FALSE FALSE  TRUE FALSE FALSE  TRUE  TRUE  TRUE

apply是为矩阵和数组制作的,结果sapply是一个矩阵,

class(sapply(DLSlist, function(X) IR$tstamp %between% X))
# "matrix"

所以这应该很快。一般来说,sapply可以返回不同类型的结果。


PS我认为日期很难一目了然,如果你能提前告诉你不需要它们,最好不要在你的例子中使用它们。

于 2013-10-28T19:53:11.913 回答