每个观察我有3个离散属性。如果对于2次观察,这些属性中至少有2个采用相同的值,那么我想将它们组合在一起(实际上总是最多2个属性是相同的)。
我的想法是建立一个分组矩阵。每行和每列代表一次观察。行和列的交叉表示两个观察值的“相似性”,如果至少两个属性值相同,则应采用值TRUE
,否则FALSE
。
以下是我所做的可重现的示例(a
,b
,c
是要比较的属性:
df <- data.frame(a = c(1, 1, 2, 2, 3),
b = c(1, 2, 3, 2, 4),
c =c("a", "a", "d", "a", "c"))
grouping_matrix <- matrix(nrow = nrow(df), ncol = nrow(df))
for (i in 1:nrow(df)){
for (j in 1:nrow(df)){
if(sum(df[i, ] == df[j, ]) >= 2) {
grouping_matrix [i, j] <- TRUE
} else {
grouping_matrix [i, j] <- FALSE
}
}
}
> df
a b c
1 1 1 a
2 1 2 a
3 2 3 d
4 2 2 a
5 3 4 c
> grouping_matrix
[,1] [,2] [,3] [,4] [,5]
[1,] TRUE TRUE FALSE FALSE FALSE
[2,] TRUE TRUE FALSE TRUE FALSE
[3,] FALSE FALSE TRUE FALSE FALSE
[4,] FALSE TRUE FALSE TRUE FALSE
[5,] FALSE FALSE FALSE FALSE TRUE
这很有效。然而,即使对于几个thousend观察,它也需要永远。
我很确定有一些更有效的方式,例如一些data.table
魔法。如果问题没有明确说明,请告诉我。
答案 0 :(得分:1)
如果没有双for
循环,这样做会相同。我会做一个快速的性能测试来检查它是否相当快。
grouping_matrix <- do.call(rbind, lapply(1:nrow(df), function(x) rowSums(df[rep(x, nrow(df)),] == df) >= 2))
1 1.1 1.2 1.3 1.4
[1,] TRUE TRUE FALSE FALSE FALSE
[2,] TRUE TRUE FALSE TRUE FALSE
[3,] FALSE FALSE TRUE FALSE FALSE
[4,] FALSE TRUE FALSE TRUE FALSE
[5,] FALSE FALSE FALSE FALSE TRUE
答案 1 :(得分:1)
或者,这可以通过重塑和自联接来解决。此方法不会将每一行相互比较(如嵌套的for
循环或嵌套的lapply()
方法),而只是查找列名和值中的匹配项。
library(data.table)
mDT <- setDT(df)[, rn := .I][, melt(.SD, id.vars = "rn")]
mDT[mDT, on = .(variable, value), allow = TRUE, nomatch = 0L][
, .N >= 2L, by = .(rn, i.rn)][
, dcast(.SD, rn ~ i.rn, fill = FALSE)]
rn 1 2 3 4 5 1: 1 TRUE TRUE FALSE FALSE FALSE 2: 2 TRUE TRUE FALSE TRUE FALSE 3: 3 FALSE FALSE TRUE FALSE FALSE 4: 4 FALSE TRUE FALSE TRUE FALSE 5: 5 FALSE FALSE FALSE FALSE TRUE
首先,通过将所有列从宽格式转换为长格式(在添加了行标识mDT
之后)创建临时data.table rn
。
mDT
rn variable value 1: 1 a 1 2: 2 a 1 3: 3 a 2 4: 4 a 2 5: 5 a 3 6: 1 b 1 7: 2 b 2 8: 3 b 3 9: 4 b 2 10: 5 b 4 11: 1 c a 12: 2 c a 13: 3 c d 14: 4 c a 15: 5 c c
然后,mDT
将自己与列名和值相连接:
mDT[mDT, on = .(variable, value), allow = TRUE, nomatch = 0L]
rn variable value i.rn 1: 1 a 1 1 2: 2 a 1 1 3: 1 a 1 2 4: 2 a 1 2 5: 3 a 2 3 6: 4 a 2 3 7: 3 a 2 4 8: 4 a 2 4 9: 5 a 3 5 10: 1 b 1 1 11: 2 b 2 2 12: 4 b 2 2 13: 3 b 3 3 14: 2 b 2 4 15: 4 b 2 4 16: 5 b 4 5 17: 1 c a 1 18: 2 c a 1 19: 4 c a 1 20: 1 c a 2 21: 2 c a 2 22: 4 c a 2 23: 3 c d 3 24: 1 c a 4 25: 2 c a 4 26: 4 c a 4 27: 5 c c 5 rn variable value i.rn
参数nomatch = 0L
是在OP报告其生产数据集出现问题时尝试减少生成的行数。
现在,对rn
和i.rn
的每个组合计算匹配数,并检查数字是否大于1:
mDT[mDT, on = .(variable, value), allow = TRUE][
, .N >= 2L, by = .(rn, i.rn)]
rn i.rn V1 1: 1 1 TRUE 2: 2 1 TRUE 3: 1 2 TRUE 4: 2 2 TRUE 5: 3 3 TRUE 6: 4 3 FALSE 7: 3 4 FALSE 8: 4 4 TRUE 9: 5 5 TRUE 10: 4 2 TRUE 11: 2 4 TRUE 12: 4 1 FALSE 13: 1 4 FALSE
最后,根据要求将此结果重新整形为类似矩阵的结构。
答案 2 :(得分:1)
如果您遇到性能问题避免使用矩阵。任何需要距离矩阵的东西都需要至少O(n²)的时间,并且需要永远。在R中,因为R解释器真的慢,所以避免任何for循环。像所有快速R模块一样,用C或Fortran重写代码。纯R是sloooow。
但是你的方法有一个更普遍的问题。如果a和b应该分组,b和c应该分组,但a和c太不一样了怎么办?例如
a X Y U
b X Y Z
c V Y Z
a和b共有X Y,b和c共有Y和Z,但a和c只有Y.
如果可以对a和c进行分组,那么您正在寻找传递闭包。 不相交的集合结构可以帮助加速计算。