重启的组计数器(使用R data.table)

时间:2018-06-03 20:38:55

标签: r data.table

这与Create sequential counter that restarts on a condition within panel data groupsdata.table "key indices" or "group counter"有些相关,但不完全相同。

# data table:
    x y i d
 1: A B 1 1
 2: A B 1 1
 3: A C 2 2
 4: A D 3 3
 5: B A 1 4
 6: B A 1 4 
 7: C A 1 4
 8: C A 1 4 
 9: C B 2 5
10: C C 3 6
11: C C 3 6
12: C D 4 7

使用dt[, d:= .GRP, by = .(x,y)]可以生成最后一列。然而,我正在寻找一个在每个x组内重启的计数器。请参阅列i以获取所需结果。

3 个答案:

答案 0 :(得分:3)

您可以使用rleidy列上的x功能来实现这一目标。 rleid是一种计数器,每次发生变化时都会增加,否则保持不变

library(data.table)
tab <- fread("
x y i d
A B 1 1
A B 1 1
A C 2 2
A D 3 3
B A 1 4
B A 1 4 
C A 1 4
C A 1 4 
C B 2 5
C C 3 6
C C 3 6
C D 4 7")

dt <- tab[, .(x, y, i)]
dt[, d:= rleid(y), by = .(x)]
dt
#>     x y i d
#>  1: A B 1 1
#>  2: A B 1 1
#>  3: A C 2 2
#>  4: A D 3 3
#>  5: B A 1 1
#>  6: B A 1 1
#>  7: C A 1 1
#>  8: C A 1 1
#>  9: C B 2 2
#> 10: C C 3 3
#> 11: C C 3 3
#> 12: C D 4 4

reprex package(v0.2.0)创建于2018-06-03。

答案 1 :(得分:2)

如果您的数据未在y x内排序,则可以

df[, i := .SD[, rep(.GRP, .N), y]$V1, x]

df[, i := {ord <- order(y); rleid(y[ord])[order(ord)]}, x]

但是,如果订单不重要,那么在计算y之前按i排序会更快

setorder(df, y) 
df[, i := rleid(y), x]

比较

df <- df[sample(nrow(df), 1e7, T)]

grp <- function(df) df[, i := .SD[, rep(.GRP, .N), y]$V1, x]
rleid.alone <- function(df) 
  df[, i := rleid(y), x]
setord.rleid <- function(df) {
  setorder(df, y); df[, i := rleid(y), x]}
ord.rleid <- function(df){ 
    df[, i := {ord <- order(y); rleid(y[ord])[order(ord)]}, x]}
microbenchmark(
  rleid.alone(df),
  setord.rleid(df),
  ord.rleid(df),
  grp(df),
  times = 10
)

# Unit: milliseconds
# expr                   min        lq      mean    median        uq        max neval
# rleid.alone(df)   196.5973  201.1499  237.3837  234.6709  262.0397   292.0986    10
# setord.rleid(df)  215.6894  248.7814  285.1045  273.7231  316.5271   382.6173    10
# ord.rleid(df)    7610.9995 7767.9028 8137.2361 7820.5919 8055.2610 10034.9907    10
# grp(df)           336.3208  357.3206  439.5327  394.6960  517.3482   719.8893    10

答案 2 :(得分:1)

我发现先考虑算法,然后再考虑data.table(或base R或dplyr)应用程序。似乎有几种可能的算法来创建所需的计数器。我有

f0 = function(x) match(x, unique(x))

或者如果以某种方式对x的值进行排序

f1 = function(x) match(x, sort(unique(x)))

这些与基于x

中的运行的索引不同
f2 = function(x) { r = rle(x); r$values = seq_along(r$values); inverse.rle(r) }

我们有其他答案

f3 = function(x) { o <- order(x); rleid(x[o])[order(o)] }

data.table::rleid()

以下是不同功能的快速比较

> set.seed(123); x = sample(5, 20, TRUE)
> f0(x); f1(x); f2(x); f3(x); rleid(x)
 [1] 1 2 3 4 4 5 3 4 3 3 4 3 2 3 5 4 1 5 1 4
 [1] 2 4 3 5 5 1 3 5 3 3 5 3 4 3 1 5 2 1 2 5
 [1]  1  2  3  4  4  5  6  7  8  8  9 10 11 12 13 14 15 16 17 18
 [1] 2 4 3 5 5 1 3 5 3 3 5 3 4 3 1 5 2 1 2 5
 [1]  1  2  3  4  4  5  6  7  8  8  9 10 11 12 13 14 15 16 17 18

澄清实现f0-f2各自不同,f2()rleid()似乎至少对于f的域是相同的,并且f1()似乎是@瑞恩的解决方案f3()

有趣的是,问题中提供的数据并没有区分这些实现(我正在做data.table一步吗?)

> dt = tab[, .(x, y, i)]
> (dt[, .(y = y, f0 = f0(y), f1 = f1(y), f2 = f2(y), rleid = rleid(y)), by = .(x)])
    x y f0 f1 f2 rleid
 1: A B  1  1  1     1
 2: A B  1  1  1     1
 3: A C  2  2  2     2
 4: A D  3  3  3     3
 5: B A  1  1  1     1
 6: B A  1  1  1     1
 7: C A  1  1  1     1
 8: C A  1  1  1     1
 9: C B  2  2  2     2
10: C C  3  3  3     3
11: C C  3  3  3     3
12: C D  4  4  4     4

建立了不同的算法后,可能可以比较性能以区分其他实现。

> x = sample(100, 10000, TRUE)
> microbenchmark(f0(x), f1(x), f2(x), f3(x), rleid(x))
Unit: microseconds
     expr      min        lq      mean   median        uq      max neval
    f0(x)  818.773  856.5275  926.5475  880.014  906.6040 5273.431   100
    f1(x) 1026.094 1084.1425 1112.1629 1101.626 1133.4100 1384.260   100
    f2(x) 1362.461 1428.8665 1595.0777 1622.881 1672.9835 4253.685   100
    f3(x)  823.653  862.5090  893.1710  894.268  914.1290 1050.157   100
 rleid(x)  236.590  245.0090  252.4963  251.158  257.7365  309.326   100