Stephen Tucker
2007-Jun-13 06:04 UTC
[R] passing (or obtaining) index or element name of list to FUN in lapply()
Hello everyone, I wonder if there is a way to pass the index or name of a list to a user-specified function in lapply(). For instance, my desired effect is something like the output of> L <- list(jack=4098,sape=4139) > lapply(seq(along=L),function(i,x) if(i==1) "jack" else "sape",x=L)[[1]] [1] "jack" [[2]] [1] "sape"> lapply(seq(along=L),function(i,x) if(names(x)[i]=="jack") 1 else 2,x=L)[[1]] [1] 1 [[2]] [1] 2 But by passing L as the first argument of lapply(). I thought there was a tangentially-related post on this mailing list in the past but I don't recall that it was ever addressed directly (and I can't seem to find it now). The examples above are perfectly good alternatives especially if I wrap each of the lines in "names<-"() to return lists with appropriate names assigned, but it feels like I am essentially writing a FOR-LOOP - though I was surprised to find that speed-wise, it doesn't seem to make much of a difference (unless I have not selected a rigorous test):> N <- 10000 > y <- runif(N)## looping through elements of y> system.time(lapply(y,+ function(x) { + set.seed(222) + mean(rnorm(1e4,x,1)) + })) [1] 21.00 0.17 21.29 NA NA ## looping through indices> system.time(lapply(1:N,+ function(x,y) { + set.seed(222) + mean(rnorm(1e4,y[x],1)) + },y=y)) [1] 21.09 0.14 21.26 NA NA In Python, there are methods for Lists and Dictionaries called enumerate(), and iteritems(), respectively. Example applications: ## a list L = ['a','b','c'] [x for x in enumerate(L)] ## returns index of list along with the list element [(0, 'a'), (1, 'b'), (2, 'c')] ## a dictionary D = {'jack': 4098, 'sape': 4139} [x for x in D.iteritems()] ## returns element key (name) along with element contents [('sape', 4139), ('jack', 4098)] And this is something of the effect I was looking for... Thanks to all, Stephen
Prof Brian Ripley
2007-Jun-13 07:37 UTC
[R] passing (or obtaining) index or element name of list to FUN in lapply()
On Tue, 12 Jun 2007, Stephen Tucker wrote:> Hello everyone, > > I wonder if there is a way to pass the index or name of a list to a > user-specified function in lapply(). For instance, my desired effect is > something like the output of > >> L <- list(jack=4098,sape=4139) >> lapply(seq(along=L),function(i,x) if(i==1) "jack" else "sape",x=L) > [[1]] > [1] "jack" > > [[2]] > [1] "sape"as.list(names(L))>> lapply(seq(along=L),function(i,x) if(names(x)[i]=="jack") 1 else 2,x=L) > [[1]] > [1] 1 > > [[2]] > [1] 2as.list(seq_along(L)) lapply() can be faster than a for-loop, but usually not by much: its main advantage is clarity of code. I think we need a real-life example to see what you are trying to do.> But by passing L as the first argument of lapply(). I thought there was a > tangentially-related post on this mailing list in the past but I don't recall > that it was ever addressed directly (and I can't seem to find it now). The > examples above are perfectly good alternatives especially if I wrap each of > the lines in "names<-"() to return lists with appropriate names assigned, butTry something like L[] <- lapply(seq_along(L),function(i,x) if(i==1) "jack" else "sape",x=L)> it feels like I am essentially writing a FOR-LOOP - though I was surprised to > find that speed-wise, it doesn't seem to make much of a difference (unless I > have not selected a rigorous test): > >> N <- 10000 >> y <- runif(N) > ## looping through elements of y >> system.time(lapply(y, > + function(x) { > + set.seed(222) > + mean(rnorm(1e4,x,1)) > + })) > [1] 21.00 0.17 21.29 NA NA > ## looping through indices >> system.time(lapply(1:N, > + function(x,y) { > + set.seed(222) > + mean(rnorm(1e4,y[x],1)) > + },y=y)) > [1] 21.09 0.14 21.26 NA NA > > In Python, there are methods for Lists and Dictionaries called enumerate(), > and iteritems(), respectively. Example applications: > > ## a list > L = ['a','b','c'] > [x for x in enumerate(L)] > ## returns index of list along with the list element > [(0, 'a'), (1, 'b'), (2, 'c')] > > ## a dictionary > D = {'jack': 4098, 'sape': 4139} > [x for x in D.iteritems()] > ## returns element key (name) along with element contents > [('sape', 4139), ('jack', 4098)] > > And this is something of the effect I was looking for... > > Thanks to all, > > Stephen > > ______________________________________________ > R-help at stat.math.ethz.ch mailing list > https://stat.ethz.ch/mailman/listinfo/r-help > PLEASE do read the posting guide http://www.R-project.org/posting-guide.html > and provide commented, minimal, self-contained, reproducible code. >-- Brian D. Ripley, ripley at stats.ox.ac.uk Professor of Applied Statistics, http://www.stats.ox.ac.uk/~ripley/ University of Oxford, Tel: +44 1865 272861 (self) 1 South Parks Road, +44 1865 272866 (PA) Oxford OX1 3TG, UK Fax: +44 1865 272595