Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Applying a function over consecutive pairs of list elements in R without loops

I am trying to find an efficient (i.e. avoid using loops) way to apply a function that iteratively takes as arguments the current and previous (or next) elements of a list and returns a lists of the result (the length of which will necessarily be 1 element shorter). As a concrete example,

I have a list of vertices defining a path in some graph

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

which come from a lattice graph constructed using the igraph function "lattice"

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

I want to apply the function "get.edge.ids" over vlist so that the list returned yields the ids of the edges connecting the consecutive elements in vlist. E.g. I want the ids of edges 1-->2, 2-->7, 7-->12, 12-->17

This is trivial using a for loop,

    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)
    }

but I would like to use a vectorized approach like apply() or reduce() to see if I can get it to work more quickly since I will need to call functions like this repeatedly from a script (for example, to compute the total stretch for a spanning tree of G).

like image 588
wmsmith Avatar asked Apr 02 '14 01:04

wmsmith


3 Answers

I use mapply for that. For example

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

It appears to be slightly faster than the for loop version:

> 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 
like image 143
bpeter Avatar answered Nov 07 '22 06:11

bpeter


This might work for you:

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
like image 41
hrbrmstr Avatar answered Nov 07 '22 07:11

hrbrmstr


Well, actually, for this specific question, you can query the whole path at once with

as.vector(E(G, path=vlist))
# [1]  1  4 13 22

This is very readable, and seems to be faster than any other solution, although speed probably only matters if you have long paths.

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 
like image 2
Gabor Csardi Avatar answered Nov 07 '22 08:11

Gabor Csardi