为每列多个数据表分配唯一ID

Mah*_*oud 4 group-by r data.table

我想为每个多列值为数据表的行分配唯一的ID.让我们考虑一个简单的例子:

library(data.table)       
DT = data.table(a=c(4,2,NA,2,NA), b=c("a","b","c","b","c"), c=1:5)

    a b c
1:  4 a 1
2:  2 b 2
3: NA c 3
4:  2 b 4
5: NA c 5
Run Code Online (Sandbox Code Playgroud)

我想基于列a和b生成ID,并期望获得三个ID,其中第二和第四行ID相同,第三行和第五行也具有相同的ID.

我看过两个解决方案,但每个都略有不完整:

1)解决方案一需要排序数据表,如果我们需要为每个列生成ID(在我的实际应用中,ID是基于大约十列生成的),这非常麻烦.我们可以替换cumsum功能,因此不需要排序吗?

DT$ID1 <- cumsum(!duplicated(DT[,1:2]))
Run Code Online (Sandbox Code Playgroud)

2)解决方案二忽略NA值; 虽然我想包括NA并为他们分配一个组ID

DT <- transform(DT, ID2 = as.numeric(interaction(a,b, drop=TRUE)))
Run Code Online (Sandbox Code Playgroud)

我感谢有关如何修改任一解决方案以生成如下所示的Expected_ID的任何建议.

    a b c ID1 ID2 Expected_ID
1:  4 a 1   1   1           1
2:  2 b 2   2   2           2
3: NA c 3   3  NA           3
4:  2 b 4   3   2           2
5: NA c 5   3  NA           3
Run Code Online (Sandbox Code Playgroud)

Fra*_*ank 9

惯用的方式:

DT[, g := .GRP, by=.(a,b)]

    a b c g
1:  4 a 1 1
2:  2 b 2 2
3: NA c 3 3
4:  2 b 4 2
5: NA c 5 3
Run Code Online (Sandbox Code Playgroud)

有理由相信这不会很快,但事实证明它与竞争方法相比并不算太糟糕:

nv = 10
nu = 3
nr = 1e6

library(data.table)
set.seed(1)
DT = do.call(CJ, rep(list(seq_len(nu)), nv))[sample(1:.N, nr, replace=TRUE)]

cols = copy(names(DT))

# "idiomatic" .GRP
system.time(DT[, g := .GRP, by=cols])
#    user  system elapsed 
#    0.23    0.02    0.25 

# sort and count runs
oi = as.call(lapply(c("order", cols), as.name))
system.time(DT[eval(oi), go := rleidv(.SD, cols)])
#    user  system elapsed 
#     0.3     0.0     0.3

# paste 'em
system.time(DT[, gp := match(p <- do.call(paste, c(.SD, list(sep="_"))), unique(p)), .SDcols=cols])
#    user  system elapsed 
#    5.26    0.06    5.32 

# paste 'em, fact'em (@akrun's answer)
system.time(DT[, gpf := as.integer(factor(p <- do.call(paste, c(.SD, list(sep="_"))), levels = unique(p))), .SDcols=cols])
#    user  system elapsed 
#    4.74    0.08    4.82 

# check
identical(DT$g, DT$gp); identical(DT$g, DT$gpf)
uniqueN(DT, "g") == uniqueN(DT, c("g", "go"))
Run Code Online (Sandbox Code Playgroud)

rleidv方式创建不同的组号,但影响相同的分组.

增加问题的大小,将nr = 5e7时间提高到8s .GRP; 20s为rleidv方式; 并导致R挂在我的系统上的其他人.

对于任何感兴趣的人,可以在R FAQ中找到更多方法.如何基于数据框中的分组变量创建连续索引