我有一个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
会返回class
为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
答案 0 :(得分: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])
我相信你可以将这个逻辑作为一个函数。
答案 1 :(得分: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
请注意,我将它放在data.table的j
位置以返回结果。通常,要通过表达式进行子集,可将其置于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
可以返回不同类型的结果。
P.S。我认为日期很难一目了然,如果你能提前知道你不需要它们,最好不要在你的例子中使用它们。