2014-01-29 71 views
4

我有一個有點大的數據集(784,932行/項目,27,492唯一ID)。在每個ID每個項目,我試圖創建一個虛擬變量等於1,如果日期之間的差小於60秒。R:如何快速轉換子集的子集(優化或替代`ddply`)?

程式化的數據和代碼:

ID <- c(1,1,1,1,1,1,3,3,3,3,3,3) 
Item <- c(10,10,10,20,20,20,10,20,10,10,10,20) 
Date <- c("19/11/13 18:58:00","19/11/13 18:58:21","19/11/13 20:58:00","19/11/13 18:58:00","19/11/13 18:58:00","19/11/13 18:58:00","19/11/13 18:58:00","19/11/13 18:58:00","19/11/13 18:58:00","19/11/13 18:58:00","19/11/13 18:58:00","19/11/13 19:58:00") 
df <- data.frame(ID, Item, Date) 
df <- df[order(ID, Date), ] 
df[, "Date"] = lapply(df["Date"],function(x){strptime(x, "%d/%m/%y %H:%M:%S")}) 

# less than 60 sec difference = 1 (first item set to 999 -> 0) 
fnDummy <- function(date) { ifelse(c(999, diff(date))<60, 1, 0) } 

library(plyr) 
ddply(df, .(ID, Item), transform, Dummy=fnDummy(Date)) 

輸出:

ID Item    Date Dummy 
1 1 10 2013-11-19 18:58:00  0 
2 1 10 2013-11-19 18:58:21  1 
3 1 10 2013-11-19 20:58:00  0 
4 1 20 2013-11-19 18:58:00  0 
5 1 20 2013-11-19 18:58:00  1 
6 1 20 2013-11-19 18:58:00  1 
7 3 10 2013-11-19 18:58:00  0 
8 3 10 2013-11-19 18:58:00  1 
9 3 10 2013-11-19 18:58:00  1 
10 3 10 2013-11-19 18:58:00  1 
11 3 20 2013-11-19 18:58:00  0 
12 3 20 2013-11-19 19:58:00  1 

從你看到第一行和第二行有共同的編號和項目的輸出,在日期的差別只有21秒,所以僞是1。第二排和第三排也有共同的編號和項目,但這裏日期的差值小於60秒大得多,所以假爲0

我設法日我想要的輸出,但操作很慢。對於1000行,大約需要40秒(參見下面的system.time結果)。這對應於約。 180分鐘的整個數據集(我的電腦耗盡內存並在此之前很久崩潰)。

user system elapsed 
36.485 3.328 39.800 

如何使此操作更快?我可以使用data.table完成相同的輸出嗎?速度更快嗎?

+1

爲什麼你使用'ifelse'? 'c(FALSE,diff(date)<60)'應該就足夠了。如果你真的需要0和1,請使用'as.integer'。 – Roland

+0

我意識到你已經得到了答案,但我有點困惑。你問題中的代碼甚至不會爲我運行。我在'ddply'行發生錯誤。 – joran

回答

7

你建議你可以使用data.table。你必須轉換你的POSIXltPOSIXct雖然:

library(data.table) 
df$Date <- as.POSIXct(df$Date) 
DT <- as.data.table(df) 

DT[, dummy_date := fnDummy(Date), by=c('ID', 'Item')] 

然而,經濟放緩的很大一部分可能是在ifelse功能,你並不真的需要它,因爲你要創建一個布爾值:

as.integer(c(FALSE, diff(date) < 60)) 
+1

小提示:我會使用'as.data.table(。)'而不是'data.table(。)'。前者要快得多。 – Arun

+1

@阿倫正確!但是當回答問題時,三個額外的字符太多了...... – Justin

+0

非常感謝。現在在整個數據集上運行該操作需要14秒。這很漂亮! – bonna

2

你也可以使用dplyr來解決這個問題:

ID <- c(1,1,1,1,1,1,3,3,3,3,3,3) 
Item <- c(10,10,10,20,20,20,10,20,10,10,10,20) 
Date <- c("19/11/13 18:58:00","19/11/13 18:58:21","19/11/13 20:58:00","19/11/13 18:58:00","19/11/13 18:58:00","19/11/13 18:58:00","19/11/13 18:58:00","19/11/13 18:58:00","19/11/13 18:58:00","19/11/13 18:58:00","19/11/13 18:58:00","19/11/13 19:58:00") 
df <- data.frame(ID, Item, Date = as.POSIXct(Date)) 

library(dplyr) 

df %.% 
    group_by(ID, Item) %.% 
    mutate(
    lagged = lag(Date, order_by = Date, default = 999), 
    dummy = Date - lagged < 60 
) 

lag()有效可以推廣diff()允許您指定第一個元素的排序值和默認值。 (這也目前失去了載體的屬性,但是將被固定在未來的版本。在跟蹤進度https://github.com/hadley/dplyr/issues/166