R data.table按类别递增,并将NA设置为最后一个非缺失值

时间:2015-09-04 05:10:47

标签: r data.table

[背景]

我有来自一组用户的在线活动的一些数据:

  • userId表示用户的ID。
  • pageType表示用户所在的当前页面。 home表示主页,content表示内容页面。
  • 页面已按时间排序,因此第1行发生在第2行之前,第2行发生在第3行之前,...
  • 实际数据大约有200万行,有8种页面类型。 userId是一个36个字符的java.util.UUID对象。

[目标]

我想为每个pageType生成一个新列,并计算完全相同类型的先前页面浏览量(不包括当前值)。

[样本数据]

生成实际数据的样本:

library(data.table)
DT <- data.table("userId"=rep(1:3, each=10),
                 "pageType"=c("home", "content", "home", "content", "home", "home", "content", "content", "home", "home",
                              "content", "content", "home", "home", "content", "home", "home", "content", "home", "content",
                              "home", "home", "content", "content", "home", "home", "content", "content", "home", "content"))

> DT
    userId pageType
 1:      1     home
 2:      1  content
 3:      1     home
 4:      1  content
 5:      1     home
 6:      1     home
 7:      1  content
 8:      1  content
 9:      1     home
10:      1     home
...    ...      ...

[我的尝试]

我试图以两种方式解决这个问题,但两者都太慢了。我也觉得我的解决方案没有按照它的设计方式使用data.table

解决方案I

  1. pageType过滤,按userId递增。
  2. 为其他pageType设置缺失值。
  3. 以下是代码:

    FixPageView <- function(data, type) {
      val <- 0
      for (i in 1:nrow(data)) {
        if (is.na(data[[type]][i])) {
          set(data, i, type, val)
        } else {
          val <- data[[type]][i]
        }
      }
    }
    DT[pageType=="home", numHomePagesViewed:=0:(.N-1), by=userId]
    DT[pageType=="content", numContentPagesViewed:=0:(.N-1), by=userId]
    FixPageView(DT, "numHomePagesViewed")
    FixPageView(DT, "numContentPagesViewed")
    
    > DT
        userId pageType numHomePagesViewed numContentPagesViewed
     1:      1     home                  0                     0
     2:      1  content                  0                     0
     3:      1     home                  1                     0
     4:      1  content                  1                     1
     5:      1     home                  2                     1
     6:      1     home                  3                     1
     7:      1  content                  3                     2
     8:      1  content                  3                     3
     9:      1     home                  4                     3
    10:      1     home                  5                     3
    ...    ...      ...                ...                   ...
    

    解决方案II

    加倍for循环并逐行设置。

    DT[, numHomePagesViewed := 0L][, numContentPagesViewed := 0L]
    for (i in unique(DT$userId)) {
      home_inc <- -1L
      content_inc <- -1L
      for (j in 1L:nrow(DT[userId==i])) {
        if (DT$pageType[(i-1L)*10L + j] == "home") {
          home_inc <- home_inc + 1L
          set(DT, (i-1L)*10L + j, "numHomePagesViewed", home_inc)
        } else {
          set(DT, (i-1L)*10L + j, "numHomePagesViewed", max(0, home_inc))
        }
        if (DT$pageType[(i-1L)*10L + j] == "content") {
          content_inc <- content_inc + 1L
          set(DT, (i-1L)*10L + j, "numContentPagesViewed", content_inc)
        } else {
          set(DT, (i-1L)*10L + j, "numContentPagesViewed", max(0, content_inc))
        }
      }
    }
    
    > DT
        userId pageType numHomePagesViewed numContentPagesViewed
     1:      1     home                  0                     0
     2:      1  content                  0                     0
     3:      1     home                  1                     0
     4:      1  content                  1                     1
     5:      1     home                  2                     1
     6:      1     home                  3                     1
     7:      1  content                  3                     2
     8:      1  content                  3                     3
     9:      1     home                  4                     3
    10:      1     home                  5                     3
    ...    ...      ...                ...                   ...
    

    [问题]

    1. 我可以做些什么来提高速度?
    2. 还有更多&#34; data.table&#34;解决这个问题的方法?

1 个答案:

答案 0 :(得分:4)

我试试:

DT[,lapply(unique(pageType),
   function(x) pmax(cumsum(pageType==x)-1,0)),by=userId]

接下来,您必须重命名获取的列。

根据评论中的建议,您可以使用一行指定名称:

DT[, paste0("num",unique(DT$pageType),"PagesViewed") := 
      lapply(unique(pageType), function(x) pmax(cumsum(pageType==x)-1,0)), by=userId]