2013-10-28 33 views
0

我有一個data.table和一個日期列表。我希望使用函數來過濾和修改行,以檢查列表中是否有日期。使用函數選擇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] 

但是它沒有工作:所有行的轉化(不僅範圍內的,如已經在我的醜陋的黑客案件碼)。

是否有一些使用函數選擇行的方法 - 或基於多個範圍檢查選擇行的其他方法?


更新(與感謝弗蘭克,誰發現了問題)

您可以返回一個載體或布爾函數確實進行過濾。這個錯誤是我最初的功能。

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

sapply返回一個對象,誰的classmatrix; 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 DLStest(IR1 $ tstamp,DLSlist)給出了預期的結果嗎?我認爲它應該只給出整個tstamp矢量的一個單一的真或假的值。也許如果你添加'by = 1:nrow(IR1)',它會工作...... – Frank

+2

[請參閱此鏈接](http://stackoverflow.com/questions/5963269/how-to-make-a-great-r-reproducible-example)。給出適當的可重現的例子可能會得到更好更快的答案。 –

+0

@Frank:你說得對,它只給了一個'logi'值。不幸的是,'IR1 [DLStest(tstamp,DLSlist),tstamp:= tstamp + 60 * 60,by = 1:nrow(IR1)]'不起作用。 – ricardo

回答

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.我認爲日期一目瞭然很難閱讀,如果您能提前知道您不需要它們,最好不要在您的示例中使用它們。

+0

當我嘗試使用由行應用的功能賦值失敗 - 請參閱我對原始問題的評論。你能舉個例子嗎? – ricardo

+0

@ricardo啊,你是對的,你不能只把它放在我的槽中。我編輯了答案。我認爲,您的解決方案,與適用,是最好的方式去。 – Frank

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])

我相信,你將能夠把這種邏輯的功能。

相關問題