【问题标题】:Create a "sessionID" based on "userID" and differences in "timeStamp"根据“userID”和“timeStamp”的差异创建“sessionID”
【发布时间】:2014-03-23 18:26:22
【问题描述】:

对不起,另一个新手问题。我正在尝试根据现有 ID 或索引获取部分数据框,然后根据第二列中值的差异创建新的 ID 或索引列。

例如,在下面的示例数据中,userID 1 似乎有 2 个会话:一个从 timeStamp 1 开始到 timeStamp 6 结束,另一个从 timeStamp 40 开始到 timeStamp 47 结束。如果两个 timeStamp 之间的差是= 30),那么这被认为是一个新会话。用户 2 只有 1 个会话; User3 有 3 个。

理想情况下,我希望在 sessionID 中保留用户 ID 信息;最后两列是所需格式的示例。如果将它们设为整数更容易,我可以稍后将 userID 和 sessID 连接起来。 var1, var2, varN 只是为了表明数据框中还有其他数据。

我试图避免传统的循环并获得 R-esque。我获取了 userID 和 timeStamp 信息,并通过 userID 创建了一个list,其中 timeStamps 作为列表 1 到最后一个 userID 的向量:

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.frame 转换为 data.table。

再次感谢大家。

【问题讨论】:

    标签: r loops indexing


    【解决方案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)&gt;30 创建一个逻辑向量,其元素比组中的行数少一个,因此我们在 0 前面加上 c(0,diff(timesStamp)>30)。使用cumsum(c(0,diff(timeStamp&gt;30)) 将逻辑强制转换为整数并计算累积和。每次遇到diff &gt; 30,cumsum 都会增加1。最后,使用paste(...) 只是将用户ID 与二级索引连接起来。

    注意:您已将其设置为 sessID 是数字。如果给定用户有超过 10 个会话,这会有点冒险。 IMO 最好将字符用于sessID

    【讨论】:

      【解决方案2】:
      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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-04-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-08-31
        相关资源
        最近更新 更多