快速迭代顶点并根据邻居计算新属性的方法

时间:2015-10-16 16:45:21

标签: r igraph sna

我正在执行一项简单的任务:迭代所有顶点并根据其邻居计算新属性。我搜索SO,到目前为止我知道至少有三种方法可以做到:

  1. 使用ad_adj_list创建一个adj列表,然后迭代每个元素;
  2. 使用sapply直接迭代每个顶点。
  3. 然而,这两种方法对于我的数据量级(300k顶点和800万个边缘)花费的时间太长。是否有任何快速循环顶点的方法?谢谢!

    对于基准测试,请说我有以下示例数据:

    set.seed <- 42
    g <- sample_gnp(10000, 0.1)
    V(g)$name <- seq_len(gorder(g)) # add a name attribute for data.table merge
    V(g)$attr <- rnorm(gorder(g))
    V(g)$mean <- 0 # "mean" is the attribute I want to compute
    

    方法1的代码是:

    al <- as_adj_list(g)
    attr <- V(g)$attr
    V(g)$mean <- sapply(al, function(x) mean(attr[x])) 
    # took 28s
    # most of the time is spent on creating the adj list
    

    方法2的代码是:

    compute_mean <- function(v){
        mean(neighbors(g, v)$attr)
    }
    V(g)$mean <- sapply(V(g), compute_mean)  # took 33s
    

    我相信igraph-R在交互顶点时不应该这么慢,否则,这将使大型图形的分析不可能达到数百万,我觉得这个任务应该对R用户来说非常普遍!

    更新

    根据@ MichaelChirico的评论,现在我提出了第三种方法:将图形结构导入data.table并使用data.table by语法进行计算,如下所示:

    gdt.v <- as_data_frame(g, what = "vertices") %>% setDT() # output the vertices
    gdt.e <- as_data_frame(g, what = "edges") %>% setDT() # output the edges
    gdt <- gdt.e[gdt.v, on = c(to = "name"), nomatch = 0] # merge vertices and edges data.table
    mean <- gdt[, .(mean = mean(attr)), keyby = from][, mean]
    V(g)$mean <- mean 
    # took only 0.74s !!
    

    data.table方式 MUCH 更快。但是,其结果 NOT 与前两种方法完全相同。此外,我非常失望地看到我必须依靠另一个包来完成这么简单的任务,我认为应该是igraph-R的强项。希望我错了!

1 个答案:

答案 0 :(得分:0)

我不确定实际问题在哪里......当我重新运行你的代码时:

library(microbenchmark)
library(data.table)
library(igraph)
set.seed <- 42
g <- sample_gnp(10000, 0.1)
V(g)$name <- seq_len(gorder(g)) # add a name attribute for data.table merge
V(g)$attr <- rnorm(gorder(g))
V(g)$mean <- 0 # "mean" is the attribute I want to compute
gg <- g

...并比较表达式e1e2

中的两种方法
e1 <- expression({
  al <- as_adj_list(gg)
  attr <- V(gg)$attr
  V(gg)$mean <- sapply(al, function(x) mean(attr[x]))  
})

e2 <- expression({
  gdt.v <- as_data_frame(g, what = "vertices") %>% setDT() # output the vertices
  gdt.e <- as_data_frame(g, what = "edges") %>% setDT() # output the edges
  gdt <- gdt.e[gdt.v, on = c(to = "name"), nomatch = 0] # merge vertices and edges data.table
  mean <- gdt[, .(mean = mean(attr)), keyby = from][, mean]
  V(g)$mean <- mean 
})

时间安排如下:

microbenchmark(e1, e2)

## Unit: nanoseconds
##  expr min lq  mean median uq max neval cld
##    e1  47 47 51.42     48 48 338   100   a
##    e2  47 47 59.98     48 48 956   100   a

非常相似,结果

all.equal(V(g)$mean, V(gg)$mean)

## [1] TRUE

......是一样的。