2

I would like to iterate through vectors of values and calculate something for every value while being within a function environment in R. For example:

# I have costs for 3 companies
c <- c(10, 20, 30)
# I have the same revenue across all 3 
r <- 100
#  I want to obtain the profits for all 3 within one variable
result <- list()

# I could do this in a for loop
for(i in 1:3){
    result[i] <- r - c[i]
}

Now lets assume I have a model that is very long and I define everything as a function which is to be solved with various random draws for the costs.

# Random draws
n  <- 1000
r  <- rnorm(n, mean = 100,  sd = 10)
c1 <- rnorm(n, mean = 10,  sd = 1)
c2 <- rnorm(n, mean = 20,  sd = 2)
c3 <- rnorm(n, mean = 30,  sd = 3)
X  <- data.frame(r, c1, c2, c3)

fun <- function(x){
       r  <- x[1]
       c  <- c(x[2], x[3], x[4])

       for(i in 1:3){
           result[i] <- r - c[i]
       }
  return(result)
}

I could then evaluate the result for all draws by iterating through the rows of randomly sampled input data.

for(j in 1:n){
  x <- X[j,]
  y <- fun(x)
}

In this example, the output variable y would entail the nested result variable which comprises of the results for all 3 companies. However, my line of thinking results in an error and I think it has to do with the fact that I try to return a nested variable? Hence my question how you guys would approach something like this.

6
  • 1
    You should not use c as an object name. Commented Sep 11, 2018 at 10:43
  • Well thanks for the information I guess. Commented Sep 11, 2018 at 10:47
  • Sorry, an explanation: c, as well as quite a list of other letters or words, is a base R function. One should avoid using the names of base functions for object naming. Regarding your question: At least the example you gave could be solved by vectorized functions (example: x <- r - c returns the same as for(i in 1:3){x[i] <- r - c[i]}. In many situations, R is capable of operating with vectors directly without the need for a loop. Commented Sep 11, 2018 at 10:50
  • t(apply(X, 1, function(x) x[1] - x[2:4])) Commented Sep 11, 2018 at 10:52
  • I did not think of not using c. I tried to make this example as easy as possible because usually I do not obtain any relevant answer on this website but rather comments of complains regarding formatting or what the desired result should look like. Hence, I went for c like cost and made the example as easy as possible. The actual model does not have c as an object. However, I have objects that are lists of lists which entail matrices. This made the monte carlo implementation weirdly difficult Commented Sep 11, 2018 at 10:53

2 Answers 2

2

I would suggest rethinking your coding approach. This is a very un-R-like way of doing things.

For example, the first for loop can be written much more succinctly as

x <- c(10, 20, 30)
r <- 100
result <- lapply(-x, `+`, r)

Then fun becomes something like

fun <- function(x) lapply(-x[-1], `+`, x[1])

To then operate over the rows of a data.frame (which is what you seem to do in the last step), you can use something like

apply(X, 1, fun)

where the MARGIN = 1 argument in apply ensures that you are applying a function per row (as opposed to per column).

Sign up to request clarification or add additional context in comments.

9 Comments

Thank you for the answer. I am new to coding in general and in particular to R. I am certain that there are more computationally efficient means of writing it. Do you have any recommendations in line with the bad way of doing it, as I do not have time to rewrite the entire model
The for-loop (and your lapply example) could also be written as result <- as.list(r - x).
@Kevin It seems that in your example you can replace nearly all for loops with lapply/sapplys. I would say that familiarising yourself with the *apply family of functions is a one of the key components when learning R. They offer elegant (and fast) ways to operate over the elements of a list (or vector, data.frame, etc.). Under the hood they are nothing but optimised loops.
@MauritsEvers okay, I will do so. Will this resolve the issue of returning vector objects from functions or just make the code run faster?
@Kevin I'm not sure I understand what you mean by "returning vector objects". lapply returns a list. sapply does the same as lapply but tries to simplify the output to give a vector, matrix or array. In R storing objects in lists is very efficient, as you can subsequently operate on its elements using the same *apply methods. It will also be faster than running the for loops from your examples, where you dynamically grow a list (in R, you should always avoid doing that).
|
0

Here's an approach using your function and a for loop:

# Random draws
n  <- 1000
r  <- rnorm(n, mean = 100,  sd = 10)
c1 <- rnorm(n, mean = 10,  sd = 1)
c2 <- rnorm(n, mean = 20,  sd = 2)
c3 <- rnorm(n, mean = 30,  sd = 3)
X  <- data.frame(r, c1, c2, c3)

result <- list()

fun <- function(x){
  r  <- x[[1]]
  c  <- c(x[[2]], x[[3]], x[[4]])

  for(i in 1:3){
    result[i] <- r - c[i]
  }
  return(result)
}

# Create a list to store results 
profits <- rep(rep(list(1:3)),nrow(X))

# Loop throuhg each row of dataframe and store in profits.
for(i in 1:nrow(X)){

  profits_temp <- 
    fun(list(X[i,"r"],X[i,"c1"],X[i,"c2"],X[i,"c3"]))

  for(j in 1:3)
    profits[[i]][[j]] <- profits_temp[[j]]

  }

# Eye results
profits[[1]]
#> [1] 93.23594 81.25731 70.27699

profits[[2]]
#> [1] 80.50516 69.27517 63.36439

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.