R,dplyr:n_distinct的累积版本

时间:2014-08-28 15:55:24

标签: r dplyr cumsum

我有一个如下数据帧。它按列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值,即直到每个组time 中的那个点{{1} }。这与我使用grp时的情况略有不同。

预期产出 -

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

4 个答案:

答案 0 :(得分:6)

{@ 1}}解决方案的灵感来自@ akrun的答案 -

逻辑基本上是为每个组dplyr设置var11的每个唯一值的第一次出现并休息到0然后应用grp在它上面 -

cumsum

答案 1 :(得分:5)

假设东西已经由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))

答案 2 :(得分:2)

尝试:

更新

使用新数据集,基础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

答案 3 :(得分:1)

这是另一种使用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))