为数据表的多列分配唯一的ID

时间:2018-10-04 17:12:10

标签: r group-by 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

我想基于a和b列生成ID,并希望获得3个ID,其中第2行和第4行ID相同,而第3行和第5行也具有相同ID。

我已经看到了两种解决方案,但是每种解决方案都不太完善:

1)解决方案一需要排序的数据表,如果我们需要每许多列生成ID(在我的实际应用程序中,ID是基于大约十列生成的),这将非常麻烦。我们可以替换cumsum函数,所以不需要排序吗?

DT$ID1 <- cumsum(!duplicated(DT[,1:2]))

2)解决方案二忽略NA值;而我想加入NA并为其分配一个组ID

DT <- transform(DT, ID2 = as.numeric(interaction(a,b, drop=TRUE)))

对于任何有关如何修改任一解决方案以生成如下所示的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

2 个答案:

答案 0 :(得分:8)

惯用方式:

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

有理由相信这不会很快,但是事实证明,与竞争方法相比,这还算不错:

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"))

rleidv方法创建不同的组号,但影响相同的分组。

将问题的大小增加到nr = 5e7会使.GRP方法的时间增加到8s; rleidv方式为20秒;并导致R为我系统上的其他用户挂断电话。

对于任何感兴趣的人,可以在R FAQ How to create a consecutive index based on a grouping variable in a dataframe

中找到更多方法。

答案 1 :(得分:0)

我们可以使用

DT[, Expected_ID := as.numeric(factor(paste(a, b), levels = unique(paste(a, b))))]