使用在每个子组内重新启动的多个变量创建计数器

Create counter with multiple variables that restart within each subgroup

我有一个包含两列(标识和值)的数据框。我想创建一个计数器,每次 ident 值更改时以及每个 ident 中的值更改时都会重新启动。下面举个例子来说明一下。

# ident value counter
#-------------------- 
#  1     0       1
#  1     0       2
#  1     1       1
#  1     1       2
#  1     1       3
#  1     0       1
#  1     1       1
#  1     1       2
#  2     1       1
#  2     0       1
#  2     0       2
#  2     0       3

我试过 plyr 包

ddply(mydf, .(ident, value), transform, .id = seq_along(ident))

与 data.frame 包相同的结果。

我们可以paste这两个值放在一起,利用rlelength属性来得到连续数的长度。然后我们使用 sequence 来生成计数器。

df$counter <- sequence(rle(paste0(df$dent, df$value))$lengths)
df
#   dent value counter
#1     1     0       1
#2     1     0       2
#3     1     1       1
#4     1     1       2
#5     1     1       3
#6     1     0       1
#7     1     1       1
#8     1     1       2
#9     2     1       1
#10    2     0       1
#11    2     0       2
#12    2     0       3

A data.table 使用 rleid/rowid 函数的替代方法。使用 rleid 可以为连续值创建一个 运行 长度 id,它可以用作一个组。 1:.Nrowid 可用于创建计数器。代码:

library(data.table)
# option 1:
setDT(d)[, counter := 1:.N, by = .(ident,rleid(value))]
# option 2:
setDT(d)[, counter := rowid(ident, rleid(value))]

两者都给出:

> d
    ident value counter
 1:     1     0       1
 2:     1     0       2
 3:     1     1       1
 4:     1     1       2
 5:     1     1       3
 6:     1     0       1
 7:     1     1       1
 8:     1     1       2
 9:     2     1       1
10:     2     0       1
11:     2     0       2
12:     2     0       3

使用 dplyr 就没那么简单了:

library(dplyr)
d %>% 
  group_by(ident, val.gr = cumsum(value != lag(value, default = first(value)))) %>% 
  mutate(counter = row_number()) %>% 
  ungroup() %>% 
  select(-val.gr)

作为 cumsum 函数的替代方法,您可以从 data.table.


已用数据:

d <- structure(list(ident = c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L), 
                    value = c(0L, 0L, 1L, 1L, 1L, 0L, 1L, 1L, 1L, 0L, 0L, 0L)), 
               .Names = c("ident", "value"), class = "data.frame", row.names = c(NA, -12L))