在R中没有循环的连续的列表元素对上应用函数

时间:2014-04-02 01:01:18

标签: r vectorization igraph

我试图找到一种有效的(即避免使用循环)方式来应用一个函数,该函数迭代地将列表的当前和前一个(或下一个)元素作为参数,并返回结果列表(其长度)必然会缩短1个元素)。 作为一个具体的例子,

我有一个顶点列表,用于定义某个图形中的路径

vlist <- c(1,2,7,12,17)

来自使用igraph函数“lattice”构建的点阵图

G <- graph.lattice(c(5,7))

我想在vlist上应用函数“get.edge.ids”,以便返回的列表产生连接vlist中连续元素的边的id。 例如。我想要边缘1的ids - > 2,2 - &gt; 7,7 - > 12,12 - > 17

使用for循环这是微不足道的,

    findEids <- function(G,vlist) {
        outlist=c()
        for (i in 1:(length(vlist)-1) {
            outlist=append(outlist,get.edge.ids(G,c(vlist[i],vlist[i+1])))
        }
        return(outlist)
    }

但是我想使用像apply()或reduce()这样的矢量化方法来查看是否可以让它更快地工作,因为我需要从脚本中重复调用这样的函数(例如,计算G)生成树的总伸展量。

5 个答案:

答案 0 :(得分:3)

我使用mapply。例如

a<-1:1000
mapply(function(x,y)x-y,a[-1000],a[-1])

它似乎比for循环版本略快:

> f <- function(x,y)x-y
> g <- function(){
     o<-c();
     for(i in a[-1000])o<-c(o,f(i,i+1))
> }


>
> system.time( 
+     for(i in 1:1000){
+         mapply(f,a[-1000],a[-1])
+     }
+ )
   user  system elapsed 
  2.344   0.000   2.345 


> system.time(for(i in 1:1000)g())
   user  system elapsed 
  3.399   0.000   3.425 

答案 1 :(得分:2)

这可能对您有用:

library(zoo)

findEids <- function(gr, v.list) {
  rollapply(v.list, width=2, FUN=function(x) {
    get.edge.ids(gr, x)
  })
}

findEids(G, vlist)
## [1]  1  4 13 22

答案 2 :(得分:2)

实际上,对于这个特定问题,您可以使用

一次查询整个路径
as.vector(E(G, path=vlist))
# [1]  1  4 13 22

这是非常易读的,并且似乎比任何其他解决方案都快,尽管速度可能只对你有很长的路径很重要。

v2 <- c(1,2,7,12,17,12,7,2)
vlist <- rep(v2, 100000)

system.time(get.edge.ids(G, vlist[c(1, rep(2:(length(vlist) - 1), each = 2), 
                                  length(vlist))]))
#   user  system elapsed 
#  0.218   0.014   0.232 

system.time(as.vector(E(G, path=vlist)))
#   user  system elapsed 
#  0.028   0.007   0.035 

答案 3 :(得分:1)

虽然这不是主题中的问题的直接答案,但更具体地针对您的请求

如果你在函数vp中查看参数get.edge.ids的描述,你会看到

  

<强> VP
  indicent顶点,以顶点id或符号顶点的形式给出   名。 它们是成对解释的,即第一个和第二个是   用于第一个边缘,第三个和第四个用于第二个边缘,等等。

因此,在这种情况下,您只需要从vlist创建一个新的向量,这样除了第一个和最后一个元素之外的所有元素都会重复两次。您可以使用vlist[c(1, rep(2:(length(vlist)-1), each = 2), length(vlist))]

执行此操作
c(1, rep(2:(length(vlist) - 1), each = 2), length(vlist))
## [1] 1 2 2 3 3 4 4 5
vlist[c(1, rep(2:(length(vlist) - 1), each = 2), length(vlist))]
## [1]  1  2  2  7  7 12 12 17


get.edge.ids(G, vlist[c(1, rep(2:(length(vlist) - 1), each = 2), length(vlist))])
## [1]  1  4 13 22

答案 4 :(得分:0)

我最近学会了使用dplyr,它可以通过mutate / transmute和paste来解决这个问题:

data.frame(x=vlist) %>% 
mutate(y=lead(x)) %>%
transmute(edge=paste(x,y,sep="-->")

产生

     edge
1   1-->2
2   2-->7
3  7-->12
4 12-->17
5 17-->NA