R: Generate variable names, evaluate a function within a list of functions, and assign those values to the generated variable names within a loop

StackOverflow https://stackoverflow.com/questions/7943811

Question

Please excuse me if there are already answers to this, but I can't quite figure it out from the archives.

I have generated a list of very similar functions via a for-loop in R:

adoptint.fun=list()
    for(i in 1:40) {
    #function name for each column
    func.name <- paste('adoptint',i,sep='')
    #function
    func = paste('function(yearenter, adoptyear, yearleave) {ifelse(is.na(yearenter) | yearenter >', i+1905, ' | is.na(adoptyear) | yearleave > ', i+1905, ', NA, ifelse(yearenter <= ', i+1905, ' & adoptyear <= ', i+1905, ', 1, 0))}', sep='')
    adoptint.fun[[func.name]] = eval(parse(text=func))
}

I am now interested in applying this function to generate values for variables that have yet to be created in the dataframe. I want to do this using a loop or similar since the process is identical, though the specific values change, over the 40 iterations. The code would look something like:

#generate variables that will be inserted into dataframe, dfanal.reshape
var_names <- paste("dfanal.reshape$adopt", 1:40, sep="")

#run function i to obtain values for variable i, which should be appended to dataframe
for(i in 1:40){
    var_names[i] <- eval(parse(paste("adoptint.fun[[" ,i, "]](dfanal.reshape$intoobsyear,dfanal.reshape$adoptyear,dfanal.reshape$yearleave)", sep="")))
}

I have played around with mget for the var_names segment, but that doesn't seem to work and the eval segment is also not working (i.e., not assigning the values determined by the function (which works fine) to the appropriate dataframe column.

Again, apologies if this has already been answered and thanks in advance for your help.

Was it helpful?

Solution

How about adding an extra argument to your function?

func <- function(yearenter, adoptyear, yearleave,i) {
  ifelse(is.na(yearenter) | yearenter > i+1905 | is.na(adoptyear) | yearleave >  i+1905 , NA, 
         ifelse(yearenter <=  i+1905 & adoptyear <=  i+1905, 1, 0))
  }

This would allow you to do the replacement quite a lot easier, using the fact that a dataframe is a special kind of list. That was your original problem I believe :

for(i in 1:40){
  varname <- paste('adopt',i,sep='')
  dfanal.reshape[[varname]] <- 
    with(dfanal.reshape,
         func(intoobsyear,adoptyear,yearleave,i)
    )

}

Check also the help pages ?which and ?Extract

Now without reproducible example (see How to make a great R reproducible example? ), it's hard to guess what you want to do and how to do this more economical. You're still using a lot of calculation time. The following function might do what you want :

func <- function(df,j){
  out <- matrix(0,nrow=nrow(df),ncol=j)
  attach(df)
  idna <- sapply(1:j,function(i)
    is.na(yearenter) | yearenter > i+1905 | is.na(adoptyear) | yearleave >  i+1905
    )
  out[idna] <- NA
  id1 <- sapply(1:j,function(i)
    yearenter <=  i+1905 & adoptyear <=  i+1905
    )
  out[id1] <- 1
  detach(df)
  colnames(out)<- paste('adopt',1:j,sep='')
  cbind(df,out)
}

which allows you to simply do

dfanal.reshape <- func(dfanal.reshape,40)

to get the desired result. This is given that the names of your variables are yearenter, adoptyear and yearleave. As far as I can see, you have to change yearenter to intoobsyear in the function, but that's a detail.

Learning to use indices will save you a lot of frustration. And please, never ever make 40 identical functions again if adding one argument will do.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top