0

I am trying to build a list of lists in R with an apply function rather than a loop, but I'm having trouble converting a working loop into an lapply format. Allow me to explain the situation a bit more:

I have a function in R (getDetails) that retrieves details associated with parameters that I pass to the function and returns the result in a list. The function works well if I pass it a single record, and I've also built a loop that that allows me to loop through a dataframe one row at a time, and pass the elements of the dataframe, row-by-row to my function which in turn returns a list to the i-th element of my list of lists (detailsList[[i]]). I am trying to figure out how to convert my for loop into an apply function. Can you assist me with this? I've created a toy example, that if I can get working, will allow me to generalize this to my actual getDetails function.

#use cars dataset for an example
data(cars)
#get number of rows
numrows<-dim(cars)[1]
#initialize empty list
detailsList<-vector("list", numrows)

#This is a toy example of the loop I want to convert to an apply
#I'm having trouble because this builds up a list and returns the results
#to my detailsList one element at a time and I'm not sure how to do this 
#in an apply.
for (i in 1:numrows){
  detailsList[[i]]<-getDetails(cars[i,])
}

detailsList

getDetails<-function(parameters){
   valueA<-parameters[1]+45
   valueB<-parameters[1]+10
   list(valueA, valueB)
}

UPDATE:

I thought I just figured this out, but it seems when I do this, I get an third dimension in my list so when I use this:

allDetails <- lapply(1:numrows, function(i)  getDetails(cars[i,]))

the second element of the first list can only be accessed with allDetails[[1]][[1]][2] instead of with allDetails[[1]][2] as I was hoping. Does anyone know what I'm doing wrong here?

StatsStudent
  • 1,384
  • 2
  • 10
  • 28
  • 1
    Note: [*apply* functions are loops](https://stackoverflow.com/q/28983292/1422451) (just hidden ones). – Parfait Jan 15 '19 at 19:28
  • Thanks, @parfait. I understand that that the apply functions are the same as loops, but this is precesily why I'm trying to convert my loop into an apply -- the issue is, I'm not sure how to go about it in this example since I'm building a list inside of my loop and I've never really done this with an apply function before despite my having used apply functions without any difficulty in the past (because I wasn't building a list of lists inside the looping). I updated my response with what I thought was right at first, but it seems it's adding a third dimensions to the list of lists. – StatsStudent Jan 15 '19 at 19:46
  • 1
    Third element? Your function returns two elements in a list. Try replacing `list()` with `c()` assuming the value objects are single-item vectors. – Parfait Jan 15 '19 at 19:49
  • Ah yes. Sorry. I meant second. That's what I get for working with a toy example and applying it to my actual work (which does have a third element). I've updated the update. I'll try using c() as opposed to list. Thanks for the suggestion! – StatsStudent Jan 15 '19 at 20:02
  • @Parfait, you are the man (or woman)! Thank so much for your help. using just c() as opposed to list() at the end of my function did the trick. Much appreciated! – StatsStudent Jan 15 '19 at 20:11

2 Answers2

1

I think I just figured this out after a little help from @Parfait (thank, @Parfait!). In case anyone else was looking for an answer, this worked for me, but I'd welcome any other answers:

lapply(1:numrows, function(i)  getDetails(cars[i,]))

and I had to modify my function to return a vector instead of a list:

getDetails<-function(parameters){
   valueA<-parameters[1]+45
   valueB<-parameters[1]+10
   c(valueA, valueB)
}
StatsStudent
  • 1,384
  • 2
  • 10
  • 28
1

Here is your loop written with vapply

out <- vapply(1:nrow(cars), 
              function (i) {
                valA <- .subset2(cars, 1)[i] + 45L
                valB <- .subset2(cars, 1)[i] + 10L
                c(valA, valB)
              }, numeric(2))
t(out) 
#       [,1] [,2]
# [1,]   49   14
# [2,]   49   14
# [3,]   52   17
# [4,]   52   17
# [5,]   53   18
# [6,]   54   19
# [7,]   55   20
# ...
# (returns an array instead of a list, but that can be changed easily).

Btw, I don't know what your end goal is, but as far as this example goes, why any loop at all?

# vectorized
cbind(.subset2(cars, 1) + 45L, .subset2(cars, 1) + 10L)
#      [,1] [,2]
# [1,]   49   14
# [2,]   49   14
# [3,]   52   17
# [4,]   52   17
# [5,]   53   18
# [6,]   54   19
# [7,]   55   20
# ...
# or similar result with
# getDetails(cars) (version with c())
niko
  • 5,253
  • 1
  • 12
  • 32
  • Thanks @nate. the "looping" has to happen because I have to look up the details in an external 3d-party application one at a time. This was just a toy example to help me figure out to to implement in my much more complex program. Thanks for your help! – StatsStudent Jan 15 '19 at 21:10