【问题标题】:Count how many vertices in a vertex's neighbourhood have an attribute in igraph for R计算顶点邻域中有多少顶点在 igraph 中具有 R 属性
【发布时间】:2017-06-24 09:25:34
【问题描述】:

我在 igraph 中有一个大图(实际上是几个)——大约有 100,000 个顶点——每个顶点都有一个属性,它是 truefalse。对于每个顶点,我想计算直接连接到它的顶点中有多少具有该属性。我目前的解决方案是以下函数,它以图形为参数。

attrcount <- function(g) {
  nb <- neighborhood(g,order=1)
  return(sapply(nb,function(x) {sum(V(g)$attr[x]}))
}

这会返回一个计数向量,对于具有该属性的顶点,该向量偏移 1,但我可以轻松调整它。

问题在于它运行得非常慢,而且似乎应该有一种快速的方法来做到这一点,因为例如,使用degree(g) 计算每个顶点的度数实际上是即时的。

我这样做是不是很愚蠢?

例如,假设这是我们的图表。

set.seed(42)
g <- erdos.renyi.game(169081, 178058, type="gnm")
V(g)$att <- as.logical(rbinom(vcount(g), 1, 0.5))

【问题讨论】:

    标签: r igraph


    【解决方案1】:

    使用get.adjlist 查询所有相邻顶点,然后在此列表中使用sapply(或tapply 可能更快)以获取计数。将属性存储在向量中也是值得的,因为这样您就不需要一直提取它。

    用 sapply

    system.time({
      al <- get.adjlist(g)
      att <- V(g)$att
      res <- sapply(al, function(x) sum(att[x]))
    })
    #   user  system elapsed 
    #  0.571   0.005   0.576 
    

    带点击

    system.time({
      al <- get.adjlist(g)
      alv <- unlist(al)
      alf <- factor(rep(seq_along(al), sapply(al, length)),
                    levels=seq_along(al))
      att <- V(g)$att
      res2 <- tapply(att[alv], alf, sum)
      res2[is.na(res2)] <- 0
    })
    #   user  system elapsed 
    #  1.121   0.020   1.144 
    
    all(res == res2)
    # TRUE
    

    让我有些意外,但tapply 解决方案实际上更慢。

    如果这还不够,那么我想你仍然可以通过用 C/C++ 编写来使其更快。

    【讨论】:

    • 谢谢,我正试图弄清楚该怎么做。我编辑了问题以包含一些可以使用的东西
    • 我现在正在尝试这个sapply(get.adjlist(g),function(x) sum(V(g)$att[x])),它适用于玩具示例,但在整个图表上仍然非常缓慢。
    • 此图太小,无法进行基准测试。请添加一个与您拥有的图表相当的图表,例如它具有相同的顺序。见degree.sequence.game
    • 我不确定如何包含该数据。我应该放置一个指向文本文件的 Dropbox 链接还是什么?
    • 不,只需设置随机种子并创建与您的度数序列相同的随机图。请参阅degreedegree.sequence.game
    【解决方案2】:

    为了更快的计算,使用get.adjacency拉取邻接矩阵,然后使用%*%将矩阵乘以属性向量:

    library(igraph)
    set.seed(42)
    g <- erdos.renyi.game(1000, 1000, type = "gnm")
    V(g)$att <- as.logical(rbinom(vcount(g), 1, 0.5))
    
    system.time({
      ma   <- get.adjacency(g)
      att  <- V(g)$att
      res1 <- as.numeric(ma %*% att)
    })
    #  user  system elapsed 
    # 0.003   0.000   0.003 
    

    与使用get.adjlistsapply 相比:

    system.time({
      al   <- get.adjlist(g)
      att  <- V(g)$att
      res2 <- sapply(al, function(x) sum(att[x]))
    })
    #   user  system elapsed 
    #  9.733   0.243  10.107
    

    修改res1的类后,结果向量相同:

    res1 <- as.numeric(res1)
    identical(res1, res2)
    # [1] TRUE
    

    【讨论】:

    • 感谢 igraph 将邻接矩阵存储为 dgCMatrix-class 对象,这些对象是稀疏的,因此在 object.size 中相对较小,这种方法也应该适用于更大的图而不需要太多开销。
    最近更新 更多