【问题标题】:How count common elements for every combination of rows in a matrix?如何计算矩阵中每个行组合的公共元素?
【发布时间】:2017-07-07 06:55:03
【问题描述】:

我正在尝试计算数据集中每个行组合中的公共元素。我设法使用嵌套循环来做到这一点,该循环适用于小型数据集,但对于真正的数据集(1380 * 1380 组合)需要太长时间。我想知道是否有一种直接且计算量较小的方法来做到这一点。

a <- c(1,5,6,8,9) 
b <- c(4,3,6,8,2)
c <- c(4,3,6,1,9)
df <- rbind(a,b,c)

结果应该是这样的

data.frame(p1= c('a','a','a','b','b','b','c','c','c'), 
           p2= c('a','b','c','a','b','c','a','b','c'), 
           res= c(5,2,2,2,5,3,2,3,5))

如果去掉self之间的比较就更好了。 非常感谢您的帮助!

【问题讨论】:

  • 你能详细说明一下这个例子吗?我不明白为什么输出的第一行是1 1 5:前两列包含数据集不同元素的笛卡尔积,对吧?但是我真的看不懂5...
  • @RobertoB 你运行你的循环并计时了吗?我的意思是无论如何这都是一个相当繁重的操作,所以也许一些基准会很好?
  • 对不起,如果不清楚@Bruno,5 代表两行之间共有的元素数(在这种情况下为 1 和 1)。
  • @friep 我对数据集的前 13 行做了一些基准测试,结果如下:option 1 (my loop) - rep: 100, elapsed: 4.43; option 2 (as suggested by@Sotos -rep: 100; elapsed: 16.58

标签: r dataframe


【解决方案1】:

您可以使用data.table 对所有组合(包括 x a)快速完成此操作:

library(data.table)

# Set up data.table
DT <- data.table(p = c("a","b","c"), vec = c(list(a), list(b), list(c)))
DT[, JA := 1]
DT <- merge(DT, DT, by = "JA", allow.cartesian = TRUE)

# calculate intersection
DT[, length(intersect(unlist(vec.x),unlist(vec.y))), by = .(p.x, p.y)]
   p.x p.y V1
1:   a   a  5
2:   a   b  2
3:   a   c  3
4:   b   a  2
5:   b   b  5
6:   b   c  3
7:   c   a  3
8:   c   b  3
9:   c   c  5

您的数据可能比较棘手的一件事是使用列表列初始化 data.table,但这取决于您的 1380 个向量当前的存储方式。

要过滤掉 a x a,只需使用:

DT[p.x != p.y]

这个解决方案非常快,在大约 12 秒内处理 100 万行

DT2 <- data.table(p.x = 1:1000000, p.y = 1:1000000)
DT2[, vec.x := rep(list(a), 1e6)]
DT2[, vec.y := rep(list(b), 1e6)]

system.time(DT2[, length(intersect(unlist(vec.x),unlist(vec.y))), by = .(p.x, p.y)])
user  system elapsed 
11.80    0.03   12.00

编辑:重新阅读您的示例后,兴趣可能不是您所需要的。如果元素的顺序很重要,请改用以下内容:

DT[, length(which(unlist(vec.x) == unlist(vec.y))), by = .(p.x, p.y)]
   p.x p.y V1
1:   a   a  5
2:   a   b  2
3:   a   c  2
4:   b   a  2
5:   b   b  5
6:   b   c  3
7:   c   a  2
8:   c   b  3
9:   c   c  5

【讨论】:

  • 谢谢,这似乎确实是一个不错的解决方案。我正在研究它,因为正如您所说,棘手的部分是获取数据作为列列表
  • 如果每个向量中的数据长度不同,这个就不行了。
【解决方案2】:

如果您不需要比较相同的行(即 a 与 a),那么 combn 可以如下工作,

combn(1:nrow(df), 2, FUN = function(i) sum(df[i[1],] - df[i[2],]==0))
#[1] 2 2 3

# or add the names of combinations as well,

setNames(combn(1:3, 2, FUN = function(i) sum(df[i[1],] - df[i[2],]==0)), 
         combn(rownames(df), 2, toString))
#a, b a, c b, c 
#   2    2    3 

【讨论】:

  • 我得到了一个类似的解决方案(我不会发布,因为你的基本上是一样的)但也许添加df1 &lt;- df1[df1$Var1 != df1$Var2, ] 来取出OP谈到的“自我比较”?对于这个小例子,它并不重要,但有 1200 行它需要进行大约 60000 次比较。 :)
  • @friep 我错过了“取出自身之间的比较”。感谢您指出。我会修改:)
  • 我认为您可能希望length(which(df[i[1],] == df[i[2],])) 与他的示例保持一致,因为值的位置很重要。我在下面犯了同样的错误。
  • @Chris 带有combn 的那个已经对齐了...不是吗? (顺便说一句,您提供的答案非常好!!!)
  • 是的,很抱歉一个是对的——我的意思是你提供的第二个例子
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多