【问题标题】:R, dplyr: cumulative version of n_distinctR、dplyr:n_distinct的累积版本
【发布时间】:2014-10-22 13:40:13
【问题描述】:

我有一个如下的数据框。它按列time 排序。

输入 -

df = data.frame(time = 1:20,
            grp = sort(rep(1:5,4)),
            var1 = rep(c('A','B'),10)
            )

head(df,10)
   time grp var1
1   1   1    A
2   2   1    B
3   3   1    A
4   4   1    B
5   5   2    A
6   6   2    B
7   7   2    A
8   8   2    B
9   9   3    A
10 10   3    B

我想创建另一个变量 var2,它计算到目前为止没有不同的 var1 值,即 直到每个组 grp 中的 time 中的那个点。这与我使用 n_distinct 时得到的有点不同。

预期输出 -

   time grp var1 var2
1   1   1    A    1
2   2   1    B    2
3   3   1    A    2
4   4   1    B    2
5   5   2    A    1
6   6   2    B    2
7   7   2    A    2
8   8   2    B    2
9   9   3    A    1
10 10   3    B    2

我想为此创建一个函数 cum_n_distinct 并将其用作 -

d_out = df %>%
  arrange(time) %>%
  group_by(grp) %>%
  mutate(var2 = cum_n_distinct(var1))

【问题讨论】:

    标签: r dplyr cumsum


    【解决方案1】:

    受@akrun 回答启发的dplyr 解决方案 -

    这个逻辑基本上是将var1的每个唯一值的第一次出现设置为1,然后为每个组grp设置0,然后在其上应用cumsum -

    df = df %>%
      arrange(time) %>%
      group_by(grp,var1) %>%
      mutate(var_temp = ifelse(row_number()==1,1,0)) %>%
      group_by(grp) %>%
      mutate(var2 = cumsum(var_temp)) %>%
      select(-var_temp)
    
    head(df,10)
    
    Source: local data frame [10 x 4]
    Groups: grp
    
       time grp var1 var2
    1     1   1    A    1
    2     2   1    B    2
    3     3   1    A    2
    4     4   1    B    2
    5     5   2    A    1
    6     6   2    B    2
    7     7   2    A    2
    8     8   2    B    2
    9     9   3    A    1
    10   10   3    B    2
    

    【讨论】:

      【解决方案2】:

      假设东西已经由time 订购,首先定义一个累积不同的函数:

      dist_cum <- function(var)
        sapply(seq_along(var), function(x) length(unique(head(var, x))))
      

      然后是使用ave 创建组的基本解决方案(注意,假设var1 是因子),然后将我们的函数应用于每个组:

      transform(df, var2=ave(as.integer(var1), grp, FUN=dist_cum))
      

      一个data.table的解决方案,基本上做同样的事情:

      library(data.table)
      (data.table(df)[, var2:=dist_cum(var1), by=grp])
      

      还有dplyr,同样的事情:

      library(dplyr)
      df %>% group_by(grp) %>% mutate(var2=dist_cum(var1))
      

      【讨论】:

      • @DavidArenburg, j 只是典型的第二个参数的正式名称,所以DT[, x:=1] 可以等效地写为DT[j=x:=1]
      • 哦,我明白了,那为什么不直接data.table(df)[, var2:=dist_cum(var1), by=grp]?或data.table(df)[, var2:=dist_cum(var1), grp]
      • @DavidArenburg,没有充分的理由,也许这会更好,因为大多数人都习惯了你的版本;我会更新的。
      【解决方案3】:

      试试:

      更新

      使用您的新数据集,基础 R 中的一种方法

        df$var2 <-  unlist(lapply(split(df, df$grp),
                    function(x) {x$var2 <-0
                     indx <- match(unique(x$var1), x$var1)
                     x$var2[indx] <- 1
                     cumsum(x$var2) }))
      
        head(df,7)
        #   time grp var1 var2
        # 1    1   1    A    1
        # 2    2   1    B    2
        # 3    3   1    A    2
        # 4    4   1    B    2
        # 5    5   2    A    1
        # 6    6   2    B    2
        # 7    7   2    A    2
      

      【讨论】:

      • 谢谢。相当不错的把戏!这适用于我之前提到的示例,但在未订购 var1 时可能不起作用。我已经修改了示例以包含更一般的情况。
      【解决方案4】:

      这是另一个使用 data.table 的解决方案,非常快。

      通用函数

      cum_n_distinct <- function(x, na.include = TRUE){
        # Given a vector x, returns a corresponding vector y
        # where the ith element of y gives the number of unique
        # elements observed up to and including index i
        # if na.include = TRUE (default) NA is counted as an 
        # additional unique element, otherwise it's essentially ignored
      
        temp <- data.table(x, idx = seq_along(x))
        firsts <- temp[temp[, .I[1L], by = x]$V1]
        if(na.include == FALSE) firsts <- firsts[!is.na(x)]
        y <- rep(0, times = length(x))
        y[firsts$idx] <- 1
        y <- cumsum(y)
      
        return(y)
      }
      

      使用示例

      cum_n_distinct(c(5,10,10,15,5))  # 1 2 2 3 3
      cum_n_distinct(c(5,NA,10,15,5))  # 1 2 3 4 4
      cum_n_distinct(c(5,NA,10,15,5), na.include = FALSE)  # 1 1 2 3 3
      

      您的问题的解决方案

      d_out = df %>%
        arrange(time) %>%
        group_by(grp) %>%
        mutate(var2 = cum_n_distinct(var1))
      

      【讨论】:

        猜你喜欢
        • 2014-06-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-10-02
        • 2014-09-29
        • 2020-08-27
        相关资源
        最近更新 更多