我想为每个多列值为数据表的行分配唯一的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,并期望获得三个ID,其中第二和第四行ID相同,第三行和第五行也具有相同的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
Frank..
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
有理由相信这不会很快,但事实证明它与竞争方法相比并不算太糟糕:
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
时间提高到8s .GRP
; 20s为rleidv方式; 并导致R挂在我的系统上的其他人.
对于任何感兴趣的人,可以在R FAQ中找到更多方法.如何基于数据框中的分组变量创建连续索引
1> Frank..:
惯用的方式:
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
时间提高到8s .GRP
; 20s为rleidv方式; 并导致R挂在我的系统上的其他人.
对于任何感兴趣的人,可以在R FAQ中找到更多方法.如何基于数据框中的分组变量创建连续索引