Repeat sampling with replacement until exceeding threshold

Viewed 40

I have a dataset with 2 variables: an ID and an amount. I want to sample records (with replacement) until the sum of the amounts sampled exceeds the original sum of amounts.

I have sample code that works, but is there a better way? I want to eventually run through 100K iterations on a large dataset and my method seems clunky.

In the code below, I'm just running through 3 iterations.

set.seed(7777)

df <- data.frame(ID = seq(1,5),
                 AMT = sample(1:100, 5, replace = T))

threshold <- sum(df$AMT)

output <- NULL
for (i in 1:3) {
  repeat{
    sel <- df[sample(nrow(df), size = 1),]
    sel <- cbind(iter=i, sel)
    output <- rbind(output,
                    sel)
    check_sum <- subset(output, iter == i)
    if(sum(check_sum$AMT) > threshold) break
  }
}
1 Answers

You can use recursion (function that calls itself). Also, you don't need to store all sampling result (here we store only row numbers).

set.seed(7777)
df <- data.frame(ID = 1:5,AMT = sample(1:100, 5, TRUE))
threshold <- sum(df$AMT)
# Specify N not to call it multiple times
N <- nrow(df)

repeatUntilSum <- function(input = NULL) {
    # Sample one row number and join with input
    result <- c(sample(N, 1), input)
    # Check if still too low 
    if (sum(df$AMT[result]) <= threshold) {
        # Run function again
        repeatUntilSum(result)
    } else {
        # Return full sampled result
        return(df[result, ])
    }
}

To run sampling n times use lapply (returns list that can be easily joined using data.table::rbindlist).

data.table::rbindlist(lapply(1:3, repeatUntilSum), idcol = "iter")
Related