【问题标题】:R - joining more than 2^31 rows with data.tableR - 使用 data.table 连接超过 2^31 行
【发布时间】:2021-11-24 01:45:47
【问题描述】:

我有一个igraph 网络图,其中包含 103,887 个节点和 4,795,466 个关系。

这可以构造为data.table 中的边缘列表,包含近 900 万行。

按照@chinsoon12 的回答here,我可以在这个网络中找到共同的邻居。请参阅下面的示例。

这对于较小的网络非常有效,但在我的用例中遇到了问题,因为合并导致超过 2^31 行

问题:

  • 是否有有效的替代方法来处理这个问题?
  • 我可以拆分数据并逐步进行计算吗?结果将用于查询共同邻居。

示例 - 修改自 @chinsoon12 的答案:

library(data.table)
library(igraph)

set.seed(1234)
g <- random.graph.game(10, p=0.10)

adjSM <- as(get.adjacency(g), "dgTMatrix")
adjDT <- data.table(V1=adjSM@i+1, V2=adjSM@j+1)

res <- adjDT[adjDT, nomatch=0, on="V2", allow.cartesian=TRUE
][V1 < i.V1, .(Neighbours=paste(V2, collapse=",")),
  by=c("V1","i.V1")][order(V1)]

res

   V1 i.V1 Neighbours
1:  4    5          8
2:  4   10          8
3:  5   10          8

【问题讨论】:

  • 我认为您可以检查您的机器支持的最大行数,即.Machine$integer.max。在我的机器中,它是&gt; log2(.Machine$integer.max) [1] 31,这意味着我的行数不能超过2^31...
  • @ThomasIsCoding 这对我来说是一样的,但可能有一种巧妙的方法来处理这个问题,对吧?
  • 你的意思是处理我的解决方案的大小限制问题还是速度问题?如果您确实有超过 2^31 行,我认为无法解决大小限制问题,但第二个问题是可能的。
  • @ThomasIsCoding 大小限制问题对我来说是一样的。虽然我喜欢你的回答,但你的方法的性能问题禁止它在现实生活中使用。
  • 好的,我明白你的意思了。我将看看我的方法可能的改进。

标签: r merge data.table igraph


【解决方案1】:

更新

  • 如果你只是想查询共同的邻居,我不建议你建立一个巨大的查找表。相反,您可以使用以下代码来获取查询结果:
find_common_neighbors <- function(g, Vs) {
  which(colSums(distances(g, Vs) == 1) == length(Vs))
}

这样

> find_common_neighbors(g, c(4, 8))
integer(0)

> find_common_neighbors(g, c(4, 5))
[1] 8
  • 如果您需要查找表,另一种方法是使用Neighbours 作为关键字来搜索其关联节点,例如,
res <- transform(
  data.frame(Neighbours = which(degree(g) >= 2)),
  Nodes = sapply(
    Neighbours,
    function(x) toString(neighbors(g, x))
  )
)

上一个答案

我认为你可以直接使用ego over g 来生成res,例如,

setNames(
  data.frame(
    t(do.call(
      cbind,
      lapply(
        Filter(function(x) length(x) > 2, ego(g, 1)),
        function(x) {
          rbind(combn(x[-1], 2), x[1])
        }
      )
    ))
  ),
  c("V1", "V2", "Neighbours")
)

给了

  V1 V2 Neighbours
1  4  5          8
2  4 10          8
3  5 10          8

【讨论】:

  • 谢谢,托马斯。虽然这给出了正确的答案,但这种方法对于用例来说似乎太慢了。有没有办法加快速度?
  • @wake_wake 查看我的更新。
  • 我喜欢这种新方法!谢谢!是否可以将输出结构化为“节点 1、节点 2、公共邻居” - 就像您之前的建议一样?
  • @wake_wake 如果我们将结果保存在包含Node1Node2Neighbor 列的数据框中,我们可能需要一个非常大的数据框,这也是一个耗时的过程生成它,因为combn 需要很长时间。
【解决方案2】:

共同的邻居

我可以拆分数据并逐步进行计算吗?

您可以按 V1 拆分以避免遇到大合并问题:

neighDT = adjDT[, if (.N > 1) {
    cb = combn(V2, 2)
    .(a = cb[1, ], b = cb[2, ])
}, by=.(neighbor = V1)]

给了

   neighbor a  b
1:        8 4  5
2:        8 4 10
3:        8 5 10

(OP在这里发现gRbase::combnPrimcombn快。)

我们如何将同一组合的所有公共邻居(用逗号分隔)折叠到一个观察中?

neighDT_agg = neighDT[order(neighbor), 
  .(neighbors = toString(neighbor))
, keyby=.(a,b)]

order 确保字符串按字母顺序排序。 keyby 确保表格按对 {a,b} 排序,并有助于一次简单地快速查找多个对:

# single query
neighDT_agg[.(4,10), neighbors]
# [1] "8"

# multi query
pairs_queryDT = data.table(a = c(4,5,8), b = c(5,10,10))
neighDT_agg[pairs_queryDT, neighbors]
[1] "8" "8" NA

我有一个包含 103,887 个节点和 4,795,466 个关系的 igraph 网络图。

combn 的每次调用都将生成一个 2×choose(.N, 2) 矩阵。如果一个节点连接到所有其他节点,那么它是所有其他节点对的公共邻居,您将面对这些对中的choose(103887-1, 2)。我想这更多的是定义问题的方式,而不是解决问题的方法。


结果将用于查询共同邻居。

对于上述方法,您需要先计算完整的邻居表。

如果您只是有一些关于相交邻居的临时查询:

find_neighbors <- function(a, b){
    adjDT[.(c(a, b)), on=.(V1), V2[duplicated(V2)]]
}

find_neighbors(4, 10)
# [1] 8

这可以类似地包裹在 toString 中以折叠值。

【讨论】:

  • 谢谢!第一段代码运行得非常快,尤其是使用 gRbase::combnPrim。我们如何将同一组合的所有公共邻居(用逗号分隔)折叠成一个观察结果?搜索功能适用于样本数据,但我需要每周查询大约 600 万个组合。还有其他选择吗?非常感谢任何帮助!
  • @wake_wake 我认为toString 是一个很好的折叠选项,并且是一个用于一次查找多个对的键控表——如果您有要在表中查询的对,则邻居可以是在单个连接中查找。我已经编辑说明
  • 我想在获取初始 combn 时可能有必要使用 order 并且所有对都具有
  • 看起来我在运行第一块代码时遇到了类似的问题:Error in `[.data.table`(adjDT_t, , if (.N &gt; 1) { : negative length vectors are not allowed。我猜 V2 有太多可能的组合超过了 2^31 行?
  • 是的,我认为它试图构造一个包含nc = choose(.N, 2) 列的矩阵,并且 nc 有整数溢出,因此返回负数(?)。您可以使用adjDT[, .N, by=V1][, summary(N)]adjDT[, .N, by=V1][, .N, keyby=.(n_edges = N)] 对组大小进行审核
猜你喜欢
  • 1970-01-01
  • 2013-08-08
  • 2014-03-28
  • 1970-01-01
  • 2014-03-02
  • 2020-07-26
  • 2021-03-11
  • 2012-11-09
  • 2015-04-29
相关资源
最近更新 更多