0

I have a function runBootstrap whose output result is a vector of variable length (depending on # of values for cat, which itself is a product of test). Apologies that this isn't "minimal".

require(dplyr)

test <- function(combo) {
  if(combo[1] == 4) {
    cat <- 4
  } else if((combo[1] == 3 & combo[2] == 2) | (combo[1] == 2 & combo[2]     == 2)) {
    cat <- 3
  } else if((combo[1] == 2 & combo[2] == 1) | (combo[1] == 1 & combo[2]     == 2)) {
    cat <- 2
  } else {
    cat <- 1
  }
}

arg1.freqs <- c(0.5, 0.2, 0.1, 0.1)
arg2.freqs <- c(0.8, 0.2)

runBootstrap <- function(arg1.freqs, arg2.freqs) {
  sim.df <- data.frame(x1 = 1:10000, y1 = NA)
  sim.df$x1 <- sample(1:4, 10000, replace = TRUE,   
                      prob = arg1.freqs)          
  sim.df$y1 <- sample(1:2, 10000, replace = TRUE,
                      prob = arg2.freqs)
  sim.df$cat <- NA
  for(i in 1:nrow(sim.df)) {
    combo <- c(sim.df[i, 1], sim.df[i, 2])
    sim.df$cat[i] <- test(combo)
  }
  sim.df <- sim.df %>%
    select(cat) %>%
    group_by(cat) %>%
    summarise(n = n()) %>%
    mutate(freq = n / sum(n))
  sim.df <- as.data.frame(sim.df)
  result <- c(sim.df[1, 3], sim.df[2, 3])
}

In this current version there are only two values for cat so result is a vector of length 2; in a future version I will adjust code so that length(result) will equal # values of cat.

When using the function in a for loop, I would like to use the vector values to create new columns in an already existing data.frame df1. The code I've tried thus far is as follows:

df1$result <- NA
for (i in 1:nrow(df1)) {
  df1$result[i] <- runBootstrap(arg1.freqs, arg2.freqs)
}

This clearly doesn't work unless the result vector is length = 1. But I don't know the length of the vector until the function runs (although once it runs it will be same length each iteration).

What I would like to achieve is the following:

Example 1: if length(result) == 2

  df1.col x1 x2
1       1  1  1
2       2  2  2
3       3  3  3
4       4  4  4
5       5  5  5
6       6  6  6

Example 2: if length(result) == 3

  df1.col x1 x2 x3
1       1  1  1  1
2       2  2  2  2
3       3  3  3  3
4       4  4  4  4
5       5  5  5  5
6       6  6  6  6

Thanks for any advice or direction.

  • edited for clarification
  • UPDATE - edited with solution

I got it to work as I wanted by creating a blank list, populating, then using rbind as follows:

appendResults <- function(df1, arg1, arg2) {
  my.list <- vector("list", nrow(df1))
  for (i in 1:nrow(df1)) {    
    arg1.freqs <- as.numeric(arg1[i, 3:6])
    arg2.freqs <- as.numeric(arg2[i, 3:4])
    my.list[[i]] <- runBootstrap(arg1.freqs, arg2.freqs) 
  }
  result.df <- do.call(rbind, my.list)
  df2 <- do.call(cbind, list(df1, result.df))
}
a_wise
  • 71
  • 5
  • Calling a function `function` will confuse readers and colour highlighting systems. Providing an example of a function that behaves like your `function` (returning different lengths as you try to describe) will help readers understand faster your problem and find a solution that works. See http://stackoverflow.com/questions/5963269/how-to-make-a-great-r-reproducible-example – zeehio Apr 06 '17 at 09:39
  • Thanks @zeehio, will update to clarify. – a_wise Apr 06 '17 at 09:48

1 Answers1

0

Check this one, not sure what the result looks like, but this creates empty columns, equal to the length of results, with NAs:

# fake data frame
df1 <- data.frame(x = c(1,2,3), y = c("a", "b", "c"))

# say result has length 3
res <- c(5,6,7)

# make columns with names x1, ..., x + length of res
# and assign NA values to those column
df1[ , paste("x", 1:length(res), sep = "")] <- NA
din
  • 692
  • 5
  • 12
  • Thanks @din. I think this will work. Just need to play around with location (and learn to write cleaner code!). – a_wise Apr 06 '17 at 13:05
  • @zeehio - Thanks again. I got it to work with some tinkering, edited above with result. – a_wise Apr 10 '17 at 06:22