【问题标题】:FAST way to iterate over vertices and compute new attributes based on that of neighbors快速迭代顶点并根据邻居计算新属性的方法
【发布时间】:2015-10-16 16:45:21
【问题描述】:

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

  1. 使用 ad_adj_list 创建一个 adj 列表,然后遍历每个元素;
  2. 使用 sapply 直接迭代每个顶点。

但是,对于我的数据量(30 万个顶点和 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

方法一的代码是:

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

方法二的代码是:

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.tableby语法进行计算,如下:

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 方式快得多。但是,它的结果与前两种方法的结果完全相同。另外,看到这么简单的任务还得依赖另外一个包,我也很失望,我认为这应该是igraph-R的强项。希望我错了!

【问题讨论】:

  • 也许考虑使用data.table / findinterval
  • @MichaelChirico,啊,我想我明白你的意思了,你的意思是先将图形结构导入data.table,然后使用data.table的快速分组功能进行计算?我试过了,它比 igraph 方式快得多。然而,这对我来说并不优雅:快速迭代顶点应该是任何图形包的基本功能。遗憾的是,特定 SNA 包的用户不得不求助于其他包来进行一些基本的 SNA 计算!
  • 不能说我不同意。 R 的美妙之处在于您可以编写自己的包! ;-)

标签: r igraph sna


【解决方案1】:

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

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

...都是一样的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-02-23
    • 1970-01-01
    • 1970-01-01
    • 2017-01-03
    • 1970-01-01
    • 2012-03-18
    • 1970-01-01
    相关资源
    最近更新 更多