2016-04-28 52 views
16

我有一個data.table(〜30萬行)在POSIXct格式由datetime柱,一個id柱和其他一些列(在本例中,我剛離開一個不相干列x來證明存在需要保留的其他列)。 A dput位於帖子的底部。子集的意見,即由至少30分鐘時間不同

head(DT) 
#    datetime   x id 
#1: 2016-04-28 16:20:18 0.02461368 1 
#2: 2016-04-28 16:41:34 0.88953932 1 
#3: 2016-04-28 16:46:07 0.31818101 1 
#4: 2016-04-28 17:00:56 0.14711365 1 
#5: 2016-04-28 17:09:11 0.54406602 1 
#6: 2016-04-28 17:39:09 0.69280341 1 

問:對於每個id,我需要子集只有那些超過30分鐘的時間裏不同的意見。什麼可能是有效的data.table方法來做到這一點(如果可能,沒有廣泛的循環)?

的邏輯也可以描述爲(像我下面的評論):

每個ID的第一行始終保持。下一行至少應在第一行之後至少保留30分鐘。讓我們假設行 保持爲4行。然後,計算4行和 行5之間的時間差:n和保持第一,通過超過30分鐘的不同等 上

在dput下面,我添加了一個列表keep來指示在這個例子中應該保留哪些行,因爲它們與每個id保留的先前觀察值相差超過30分鐘。困難在於似乎有必要迭代地計算時間差(或者至少我現在不能想到更有效的方法)。

library(data.table) 
DT <- structure(list(
    datetime = structure(c(1461853218.81561, 1461854494.81561, 
    1461854767.81561, 1461855656.81561, 1461856151.81561, 1461857949.81561, 
    1461858601.81561, 1461858706.81561, 1461859078.81561, 1461859103.81561, 
    1461852799.81561, 1461852824.81561, 1461854204.81561, 1461855331.81561, 
    1461855633.81561, 1461856311.81561, 1461856454.81561, 1461857177.81561, 
    1461858662.81561, 1461858996.81561), class = c("POSIXct", "POSIXt")), 
    x = c(0.0246136845089495, 0.889539316063747, 0.318181007634848, 
    0.147113647311926, 0.544066024711356, 0.6928034061566, 0.994269776623696, 
    0.477795971091837, 0.231625785352662, 0.963024232536554, 0.216407935833558, 
    0.708530468167737, 0.758459537522867, 0.640506813768297, 0.902299045119435, 
    0.28915973729454, 0.795467417687178, 0.690705278422683, 0.59414202044718, 
    0.655705799115822), 
    id = c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L), 
    keep = c(TRUE, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, 
      FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, TRUE)), 
    .Names = c("datetime", "x", "id", "keep"), 
    row.names = c(NA, -20L), 
    class = c("data.table", "data.frame")) 

setkey(DT, id, datetime) 
DT[, difftime := difftime(datetime, shift(datetime, 1L, NA,type="lag"), units = "mins"), 
    by = id] 
DT[is.na(difftime), difftime := 0] 
DT[, difftime := cumsum(as.numeric(difftime)), by = id] 

keep列的說明:

  • 行2:3相差不到30分鐘從第1行 - >刪除
  • 第4點的不同通過從第1行超過30分鐘 - >保持
  • 行5個dufferes通過小於30分鐘,從4行 - >從4行刪除
  • 第6行相差超過30分鐘 - >保持
  • ...

所需的輸出:

desiredDT <- DT[(keep)] 

感謝我收到了三個專家解答。我測試了1千萬行數據。這是基準的摘錄。

一)一個百萬行

microbenchmark(frank(DT_Frank), roland(DT_Roland), eddi1(DT_Eddi1), eddi2(DT_Eddi2), 
       times = 3L, unit = "relative") 
#Unit: relative 
#    expr  min  lq  mean median  uq  max neval 
# frank(DT_Frank) 1.286647 1.277104 1.185216 1.267769 1.140614 1.036749  3 
# roland(DT_Roland) 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000  3 
# eddi1(DT_Eddi1) 11.748622 11.697409 10.941792 11.647320 10.587002 9.720901  3 
# eddi2(DT_Eddi2) 9.966078 9.915651 9.210168 9.866330 8.877769 8.070281  3 

B)10萬行

microbenchmark(frank(DT_Frank), roland(DT_Roland), eddi1(DT_Eddi1), eddi2(DT_Eddi2), 
       times = 3L, unit = "relative") 
#Unit: relative 
#    expr  min  lq  mean median  uq  max neval 
# frank(DT_Frank) 1.019561 1.025427 1.026681 1.031061 1.030028 1.029037  3 
# roland(DT_Roland) 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000  3 
# eddi1(DT_Eddi1) 11.567302 11.443146 11.301487 11.323914 11.176515 11.035143  3 
# eddi2(DT_Eddi2) 9.796800 9.693823 9.526193 9.594931 9.398969 9.211019  3 

顯然,@弗蘭克的data.table方法,並@基於羅蘭的RCPP的解決方案是在與具有RCPP性能相似輕微的優勢,而@ eddi的方法仍然很快,但不像其他人一樣表現良好。

然而,當我檢查瞭解決方案的平等,我發現@羅蘭的做法有一個稍微不同的結果比別人:

一)一個百萬行

all.equal(frank(DT_Frank), roland(DT_Roland)) 
#[1] "Component 「datetime」: Numeric: lengths (982228, 982224) differ" 
#[2] "Component 「id」: Numeric: lengths (982228, 982224) differ"  
#[3] "Component 「x」: Numeric: lengths (982228, 982224) differ" 
all.equal(frank(DT_Frank), eddi1(DT_Eddi1)) 
#[1] TRUE 
all.equal(frank(DT_Frank), eddi2(DT_Eddi2)) 
#[1] TRUE 

B)千萬行

all.equal(frank(DT_Frank), roland(DT_Roland)) 
#[1] "Component 「datetime」: Numeric: lengths (9981898, 9981891) differ" 
#[2] "Component 「id」: Numeric: lengths (9981898, 9981891) differ"  
#[3] "Component 「x」: Numeric: lengths (9981898, 9981891) differ"  
all.equal(frank(DT_Frank), eddi1(DT_Eddi1)) 
#[1] TRUE 
all.equal(frank(DT_Frank), eddi2(DT_Eddi2)) 
#[1] TRUE 

我現在的假設是,這種差異可能與該differnce是> 30分鐘或>時間= 30分鐘,雖然我不知道這一點呢。最終想法:我決定與@弗蘭克的解決方案一起去,原因有兩個:1.它的表現非常好,幾乎等於Rcpp解決方案,2.它不需要另一個我並不十分喜歡的包熟悉卻又(我使用data.table反正)

+1

這些都是種我認爲一個好的C/C++解決方案是非常有價值的任務。沒有一個明顯的R向量化的方式,寫出你描述的條件在C或C++中應該非常簡單。如果你知道如何編寫可從R調用的C/C++函數,我會建議這條路線。 – nicola

+0

應該很容易與重疊連接,只准備*從*和*到*每個'ID'的日期 – jangorecki

+0

@jangorecki我希望這樣的解決方案存在,但我不知道從和日期apriori。或者你有一個想法如何計算它們? –

回答

11

這裏是我會做什麼:

setDT(DT, key=c("id","datetime")) # invalid selfref with the OP's example data 

s = 0L 
w = DT[, .I[1L], by=id]$V1 

while (length(w)){ 
    s = s + 1L 
    DT[w, tag := s] 

    m = DT[w, .(id, datetime = datetime+30*60)] 
    w = DT[m, which = TRUE, roll=-Inf] 
    w = w[!is.na(w)] 
} 

這給

   datetime   x id keep tag 
1: 2016-04-28 10:20:18 0.02461368 1 TRUE 1 
2: 2016-04-28 10:41:34 0.88953932 1 FALSE NA 
3: 2016-04-28 10:46:07 0.31818101 1 FALSE NA 
4: 2016-04-28 11:00:56 0.14711365 1 TRUE 2 
5: 2016-04-28 11:09:11 0.54406602 1 FALSE NA 
6: 2016-04-28 11:39:09 0.69280341 1 TRUE 3 
7: 2016-04-28 11:50:01 0.99426978 1 FALSE NA 
8: 2016-04-28 11:51:46 0.47779597 1 FALSE NA 
9: 2016-04-28 11:57:58 0.23162579 1 FALSE NA 
10: 2016-04-28 11:58:23 0.96302423 1 FALSE NA 
11: 2016-04-28 10:13:19 0.21640794 2 TRUE 1 
12: 2016-04-28 10:13:44 0.70853047 2 FALSE NA 
13: 2016-04-28 10:36:44 0.75845954 2 FALSE NA 
14: 2016-04-28 10:55:31 0.64050681 2 TRUE 2 
15: 2016-04-28 11:00:33 0.90229905 2 FALSE NA 
16: 2016-04-28 11:11:51 0.28915974 2 FALSE NA 
17: 2016-04-28 11:14:14 0.79546742 2 FALSE NA 
18: 2016-04-28 11:26:17 0.69070528 2 TRUE 3 
19: 2016-04-28 11:51:02 0.59414202 2 FALSE NA 
20: 2016-04-28 11:56:36 0.65570580 2 TRUE 4 

它背後的想法是由OP描述:

每個ID第一行始終保留。在第一行之後至少30分鐘的下一行也將被保留。讓我們假設該行必須保持爲4排然後,計算4行間和行5時差:n和保持這種相差超過30分鐘等

+1

謝謝弗蘭克,這看起來非常有前途。(+1) –

9

第一使用RCPP:

library(Rcpp) 
library(inline) 
cppFunction(
    'LogicalVector selecttimes(const NumericVector x) { 
    const int n = x.length(); 
    LogicalVector res(n); 
    res(0) = true; 
    double testval = x(0); 
    for (int i=1; i<n; i++) { 
    if (x(i) - testval > 30 * 60) { 
     testval = x(i); 
     res(i) = true; 
    } 
    } 
    return res; 
    }') 

DT[, keep1 := selecttimes(datetime), by = id] 

DT[, all(keep == keep1)] 
#[1] TRUE 

應該做一些額外的測試,它需要輸入驗證,並且時間差可以作爲參數。

+0

感謝Roland,這看起來也是一個非常好的方法!現在我有了兩個解決方案,我將在明天測試真實數據(還沒有掌握home)+1 –

+0

次要的事情:你可以定義'testval = x(i)+ 30 * 60'並測試'x(i)> testval'以減少計算量。 (不知道我是否缺少一些邊緣案例。) – Frank

7
# create an index column 
DT[, idx := 1:.N, by = id] 

# find the indices of the matching future dates 
DT[, fut.idx := DT[.(id = id, datetime = datetime+30*60), on = c('id', 'datetime') 
        , idx, roll = -Inf]] 
#    datetime   x id keep   difftime idx fut.idx 
# 1: 2016-04-28 09:20:18 0.02461368 1 TRUE 0.0000000 mins 1  4 
# 2: 2016-04-28 09:41:34 0.88953932 1 FALSE 21.2666667 mins 2  6 
# 3: 2016-04-28 09:46:07 0.31818101 1 FALSE 25.8166667 mins 3  6 
# 4: 2016-04-28 10:00:56 0.14711365 1 TRUE 40.6333333 mins 4  6 
# 5: 2016-04-28 10:09:11 0.54406602 1 FALSE 48.8833333 mins 5  7 
# 6: 2016-04-28 10:39:09 0.69280341 1 TRUE 78.8500000 mins 6  NA 
# 7: 2016-04-28 10:50:01 0.99426978 1 FALSE 89.7166667 mins 7  NA 
# 8: 2016-04-28 10:51:46 0.47779597 1 FALSE 91.4666667 mins 8  NA 
# 9: 2016-04-28 10:57:58 0.23162579 1 FALSE 97.6666667 mins 9  NA 
#10: 2016-04-28 10:58:23 0.96302423 1 FALSE 98.0833333 mins 10  NA 
#11: 2016-04-28 09:13:19 0.21640794 2 TRUE 0.0000000 mins 1  4 
#12: 2016-04-28 09:13:44 0.70853047 2 FALSE 0.4166667 mins 2  4 
#13: 2016-04-28 09:36:44 0.75845954 2 FALSE 23.4166667 mins 3  6 
#14: 2016-04-28 09:55:31 0.64050681 2 TRUE 42.2000000 mins 4  8 
#15: 2016-04-28 10:00:33 0.90229905 2 FALSE 47.2333333 mins 5  9 
#16: 2016-04-28 10:11:51 0.28915974 2 FALSE 58.5333333 mins 6  9 
#17: 2016-04-28 10:14:14 0.79546742 2 FALSE 60.9166667 mins 7  9 
#18: 2016-04-28 10:26:17 0.69070528 2 TRUE 72.9666667 mins 8  10 
#19: 2016-04-28 10:51:02 0.59414202 2 FALSE 97.7166667 mins 9  NA 
#20: 2016-04-28 10:56:36 0.65570580 2 TRUE 103.2833333 mins 10  NA 


# at this point the problem is "solved", but you still have to extract the solution 
# and that's the more complicated part 
DT[, keep.new := FALSE] 

# iterate over the matching indices (jumping straight to the correct one) 
DT[, { 
     next.idx = 1 

     while(!is.na(next.idx)) { 
     set(DT, .I[next.idx], 'keep.new', TRUE) 
     next.idx = fut.idx[next.idx] 
     } 
    }, by = id] 

DT[, identical(keep, keep.new)] 
#[1] TRUE 

或者最後一步,你可以做(​​這將遍歷整個事情,但我不知道速度的影響會是什麼):

DT[, keep.3 := FALSE] 
DT[DT[, .I[na.omit(Reduce(function(x, y) fut.idx[x], c(1, fut.idx), accumulate = T))] 
     , by = id]$V1 
    , keep.3 := TRUE] 

DT[, identical(keep, keep.3)] 
#[1] TRUE 
+0

在1.9.7上,'DT [,idx:= rowid(id)]'我猜。是的,'DT [,{... set(DT,...)...}]'對我來說看起來很奇怪。 – Frank

+0

@Frank我不這麼認爲 - 我找不到一個沒有簡單循環的跳躍方式 – eddi

+0

是的,我剛剛注意到我的猜測是錯誤的..但似乎應該有另一種方式。 – Frank

相關問題