使用sets包的可能解决方案...
给定的设置:
aa <- c(0, 0, 0, 1, 1, 0, 0)
bb <- c(1, 1, 0, 0, 1, 0, 1)
cc <- c(0, 1, 0, 0, 0, 1, 0)
d <- data.frame(aa, bb, cc)
还有准备环境...
require(sets, quietly = T)
require(data.table, quietly = T)
通过创建一组集合,以“集合”顺序创建唯一的名称列表d
。
# Created as a list so that duplicates are kept.
namesets <- sapply(seq_len(nrow(d)), function(i) {
gset(colnames(d), memberships = d[i, ])
})
# Then combine the set memberships into names and assign to the sets.
setnames <- sapply(namesets, function(s) {
ifelse(set_is_empty(s), "none", paste(as.character(s), collapse = ""))
})
names(namesets) <- setnames
# Creating set of sets from namesets orders the names and removes duplicates.
namesets <- as.set(namesets)
print(namesets)
## {none = {}, aa = {"aa"}, bb = {"bb"}, cc = {"cc"}, aabb = {"aa",
## "bb"}, bbcc = {"bb", "cc"}}
# Making it easy to create an ordered listing that we can use as a key.
setnames <- ordered(setnames, levels = names(namesets))
print(setnames)
## [1] bb bbcc none aa aabb cc bb
## Levels: none < aa < bb < cc < aabb < bbcc
转换d
为 data.table 后,我们可以以各种方式填充成员集列......
# First a simple membership to key-by
dt <- data.table(membership = setnames, d, key = "membership")
print(dt)
## membership aa bb cc
## 1: none 0 0 0
## 2: aa 1 0 0
## 3: bb 0 1 0
## 4: bb 0 1 0
## 5: cc 0 0 1
## 6: aabb 1 1 0
## 7: bbcc 0 1 1
# That might be enough for some, but the OP wants columns
# indicating a membership; so just join a matrix...
membership.map <- t(sapply(dt$membership, function(m) {
m == levels(dt$membership)
}) * 1)
colnames(membership.map) <- levels(dt$membership)
dt <- cbind(dt, split = " ==> ", membership.map)
print(dt)
## membership aa bb cc split none aa bb cc aabb bbcc
## 1: none 0 0 0 ==> 1 0 0 0 0 0
## 2: aa 1 0 0 ==> 0 1 0 0 0 0
## 3: bb 0 1 0 ==> 0 0 1 0 0 0
## 4: bb 0 1 0 ==> 0 0 1 0 0 0
## 5: cc 0 0 1 ==> 0 0 0 1 0 0
## 6: aabb 1 1 0 ==> 0 0 0 0 1 0
## 7: bbcc 0 1 1 ==> 0 0 0 0 0 1
这一切都可以包含在一个快速而肮脏的函数中,如下所示:
membership.table <- function(df) {
namesets <- sapply(seq_len(nrow(d)), function(i) {
gset(colnames(d), memberships = d[i, ])
})
setnames <- sapply(namesets, function(s) {
ifelse(set_is_empty(s), "none", paste(as.character(s), collapse = ""))
})
names(namesets) <- setnames
namesets <- as.set(namesets)
setnames <- ordered(setnames, levels = names(namesets))
dt <- data.table(membership = setnames, d, key = "membership")
membership.map <- t(sapply(dt$membership, function(m) {
m == levels(dt$membership)
}) * 1)
colnames(membership.map) <- levels(dt$membership)
cbind(dt, split = " ==> ", membership.map)
}
mt <- membership.table(d)
identical(dt, mt)
## [1] TRUE
我们现在应该在按关键字汇总成员表和从原始数据创建广义集时的成员信息时得到匹配结果。
mt[, lapply(.SD, sum), by = membership, .SDcols = seq(3 + ncol(d), ncol(mt))]
## membership none aa bb cc aabb bbcc
## 1: none 1 0 0 0 0 0
## 2: aa 0 1 0 0 0 0
## 3: bb 0 0 2 0 0 0
## 4: cc 0 0 0 1 0 0
## 5: aabb 0 1 1 0 1 0
## 6: bbcc 0 0 1 1 0 1
as.list(as.gset(d))
## $`3`
## (aa = 0, bb = 0, cc = 0)
##
## $`6`
## (aa = 0, bb = 0, cc = 1)
##
## $`1`
## (aa = 0, bb = 1, cc = 0)
##
## $`2`
## (aa = 0, bb = 1, cc = 1)
##
## $`4`
## (aa = 1, bb = 0, cc = 0)
##
## $`5`
## (aa = 1, bb = 1, cc = 0)
##
## attr(,"memberships")
##
## 1 2 3 4 5 6
## 1 1 2 1 1 1
请注意,在成员表中bb
有一个总和2
,并且广义集合列表中的第三项(表示bb
)也显示了 2 个这样的集合。
如果将相同的算法应用于 Hong 的示例,则结果为:
## membership a b c d e f split a bc ce abd acd ade abef acdef abcdef
## 1: a 1 0 0 0 0 0 ==> 1 0 0 0 0 0 0 0 0
## 2: bc 0 1 1 0 0 0 ==> 0 1 0 0 0 0 0 0 0
## 3: ce 0 0 1 0 1 0 ==> 0 0 1 0 0 0 0 0 0
## 4: abd 1 1 0 1 0 0 ==> 0 0 0 1 0 0 0 0 0
## 5: acd 1 0 1 1 0 0 ==> 0 0 0 0 1 0 0 0 0
## 6: ade 1 0 0 1 1 0 ==> 0 0 0 0 0 1 0 0 0
## 7: abef 1 1 0 0 1 1 ==> 0 0 0 0 0 0 1 0 0
## 8: acdef 1 0 1 1 1 1 ==> 0 0 0 0 0 0 0 1 0
## 9: abcdef 1 1 1 1 1 1 ==> 0 0 0 0 0 0 0 0 1
## 10: abcdef 1 1 1 1 1 1 ==> 0 0 0 0 0 0 0 0 1
虽然这个解决方案做得更多(如排序和排序),但与 Hong 的解决方案相比,时间并不算太糟糕;但比起托马斯...
## Unit: milliseconds
## expr min lq median uq max neval
## hf 241.810 246.411 253.634 262.544 290.345 10
## mt 128.105 137.931 142.966 154.244 210.276 10
## tf 1.754 1.768 1.806 2.312 3.487 10
## plain.gset 1.220 1.330 1.386 1.475 1.644 10
...两种解决方案都很慢。毫无疑问,如果您只需要使用套装,那么对于更大的会员资格来说,在套装小插曲中花一点时间可能是值得的。