2014-02-22 19 views
1

對不起,另一個新手問題。我試圖根據現有的ID或索引取出部分數據幀,然後根據第二列中的值差異創建新的ID或索引列。根據「userID」和「timeStamp」中的差異創建「sessionID」

例如,在以下示例數據中,用戶ID 1看起來有兩個會話:一個從時間戳1開始到時間戳6結束,另一個從時間戳40開始到時間戳47結束。如果兩個時間戳之間的差異是= < 30(比如分鐘),那麼這兩個時間戳被認爲是在同一個會話中。但是,當相同的用戶ID從6跳到40時,這被認爲是新的會話(差異大於30),那麼這被認爲是新會話。用戶2只有1個會話; User3有3.

理想情況下,我想保留sessionIDs中的userID信息;最後2列是所需格式的例子。如果讓它們變成整數更容易,我可以稍後連接userID和sessID。 var1,var2,varN只是爲了顯示數據框中還有其他數據。

我想避免傳統的循環,並得到R型。我把用戶ID和時間戳信息,並創建了一個list通過用戶ID與時間戳的列表1的最後一個用戶ID載體:

byUser <- with(myDF, split(timeStamp, userID)) 

一些真實的數據是這樣的:

structure(list(`1` = c(50108, 50108, 50171, 50175, 121316, 121316, 
127228), `2` = c(55145, 745210, 1407020, 2283255),... 

然後我用diff得到的每個向量中的時間戳之間的差別:

myDiff2 <- lapply(byUser, diff) 

一些真實的數據是這樣的:

structure(list(`1` = c(0, 63, 4, 71141, 0, 5912), `2` = c(690065, 
661810, 876235), `3` = c(109, 80, 98, 948417, 0), 

...現在我覺得好像應該遍歷每個列表,初始化sessID,然後如果myDiff2值> 1800秒(30分鐘),增加sessID。

這似乎很長;請告訴我怎樣才能縮短它!提前致謝!

userID timeStamp var1 var2 varN sessID1 sessID2 
1  1   1 x y N  1.0  1.1 
2  1   3 x y N  1.0  1.1 
3  1   6 x y N  1.0  1.1 
4  1  40 x y N  1.1  1.2 
5  1  42 x y N  1.1  1.2 
6  1  43 x y N  1.1  1.2 
7  1  47 x y N  1.1  1.2 
8  2   5 x y N  2.0  2.1 
9  2   8 x y N  2.0  2.1 
10  3   2 x y N  3.0  3.1 
11  3   5 x y N  3.0  3.1 
12  3  38 x y N  3.1  3.2 
13  3  39 x y N  3.1  3.2 
14  3  39 x y N  3.1  3.2 
15  3  82 x y N  3.2  3.3 
16  3  83 x y N  3.2  3.3 
17  3  90 x y N  3.2  3.3 
18  3  91 x y N  3.2  3.3 
19  3  102 x y N  3.2  3.3 

用於數據例的dput()是在這裏:

myDF <- structure(list(userID = c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 
3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L), timeStamp = c(1L, 3L, 
6L, 40L, 42L, 43L, 47L, 5L, 8L, 2L, 5L, 38L, 39L, 39L, 82L, 83L, 
90L, 91L, 102L), var1 = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L), .Label = "x", class = "factor"), 
    var2 = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
    1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L), .Label = "y", class = "factor"), 
    varN = structure(c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
    1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L), .Label = "N", class = "factor"), 
    sessID1 = c(1, 1, 1, 1.1, 1.1, 1.1, 1.1, 2, 2, 3, 3, 3.1, 
    3.1, 3.1, 3.2, 3.2, 3.2, 3.2, 3.2), sessID2 = c(1.1, 1.1, 
    1.1, 1.2, 1.2, 1.2, 1.2, 2.1, 2.1, 3.1, 3.1, 3.2, 3.2, 3.2, 
    3.3, 3.3, 3.3, 3.3, 3.3)), .Names = c("userID", "timeStamp", 
"var1", "var2", "varN", "sessID1", "sessID2"), class = "data.frame", row.names = c(NA, 
-19L)) 

=== 增編下面的答案:

在接下來的新手:

採摘一個 '。' /小數點分隔符在我的角色上可能不是很精彩:當sessID計數器從9滾動到0時,會導致一些奇怪的和非唯一的sessID。

將分隔符更改爲其他字符 - 如連字符 - - 一切都很好。

@rawr和@jlhoward - 感謝您的快速,正確和非常有幫助的迴應:兩種方法都非常有效。 @jlhoward - 特別感謝addt'l,上述呼籲的職責解釋。 (@rawr是第一個,所以我將他歸功於他的答案。)

這兩種解決方案之間的性能差別很小:data.table比較快,但需要一些額外的數據前期轉換。幀到data.table。

再次感謝所有。

回答

0
library(plyr) 

ddply(myDF, .(userID), transform, 
     sessID3 = paste(userID, 
         c(0, cumsum(sapply(1:(length(userID) - 1), 
             function(x) 
              ifelse((timeStamp[x + 1] - timeStamp[x]) > 30, 
                1, 0)))), sep = '.'), 
     sessID4 = paste(userID, 
         c(0, cumsum(sapply(1:(length(userID) - 1), 
             function(x) 
              ifelse((timeStamp[x + 1] - timeStamp[x]) > 30, 
                1, 0)))) + 1, sep = '.')) 

給我:

# userID timeStamp var1 var2 varN sessID1 sessID2 sessID3 sessID4 
# 1  1   1 x y N  1.0  1.1  1.0  1.1 
# 2  1   3 x y N  1.0  1.1  1.0  1.1 
# 3  1   6 x y N  1.0  1.1  1.0  1.1 
# 4  1  40 x y N  1.1  1.2  1.1  1.2 
# 5  1  42 x y N  1.1  1.2  1.1  1.2 
# 6  1  43 x y N  1.1  1.2  1.1  1.2 
# 7  1  47 x y N  1.1  1.2  1.1  1.2 
# 8  2   5 x y N  2.0  2.1  2.0  2.1 
# 9  2   8 x y N  2.0  2.1  2.0  2.1 
# 10  3   2 x y N  3.0  3.1  3.0  3.1 
# 11  3   5 x y N  3.0  3.1  3.0  3.1 
# 12  3  38 x y N  3.1  3.2  3.1  3.2 
# 13  3  39 x y N  3.1  3.2  3.1  3.2 
# 14  3  39 x y N  3.1  3.2  3.1  3.2 
# 15  3  82 x y N  3.2  3.3  3.2  3.3 
# 16  3  83 x y N  3.2  3.3  3.2  3.3 
# 17  3  90 x y N  3.2  3.3  3.2  3.3 
# 18  3  91 x y N  3.2  3.3  3.2  3.3 
# 19  3  102 x y N  3.2  3.3  3.2  3.3 
1

和一個 「數據表」 的方式...

library(data.table) 
myDT <- data.table(myDF) 
setkey(myDT,userID) 
myDT[,sessID3:=paste(userID,cumsum(c(0,diff(timeStamp)>30)),sep="."),by=userID] 
all.equal(myDT$sessID1,as.numeric(myDT$sessID3)) 
# [1] TRUE 

說明:

使用by=userID與數據表組中的行通過userID。使用diff(timeStamp)>30會創建一個邏輯向量,其元素數少於組中的行數,因此我們在c(0,diff(timesStamp)> 30)前加上0。使用cumsum(c(0,diff(timeStamp>30))將邏輯強制轉換爲整數並計算累計和。每次遇到diff > 30時,cumsum都會增加1.最後,使用paste(...)只是將userID與次級索引連接起來。

一個注意事項:您已將其設置爲sessID是數字。如果給定用戶的會話超過10次,這會變得有點冒險。國際海事組織更好地使用字符爲sessID