Efficiencies for nested for loop
r
Solution
I reorganized your code a bit and got rid of the inner loop.
- Sampling one long vector of random numbers (and then collapsing it into a matrix) is much faster than repeatedly sampling short vectors (`replicate`, as suggested in another answer, is nice for readability, but in this case you can do better by sampling random numbers in a block)
- `colSums` is faster than summing inside a `for` loop or using `apply`.
- it's just sugar (i.e. it isn't actually any more efficient), but you can use `mean(pvals<=alpha)` in place of `sum(pvals<=alpha)/length(alpha)`
- I defined a function to return the power for a specified set of parameters (including sample size), then used `sapply` to range over the vector of sizes (not faster than a `for` loop, but cleaner and maybe easier to generalize).
Code:
powfun <- function(ssize=100,
m=1000, ## samples per trial
popmean=120, ## pop mean
popvar=225, ## known/established pop variance
newvar=144, ## variance of new methodology
alpha=0.01,
sampchisq=FALSE) ## sample directly from chi-squared distrib?
{
if (!sampchisq) {
ymat <- matrix(rnorm(ssize*m,popmean,sd=sqrt(newvar)),ncol=m)
ts <- colSums((ymat-popmean)^2/popvar) ## test statistic
} else {
ts <- rchisq(m,df=ssize)*newvar/popvar
}
pvals <- pchisq(ts,df=ssize) ## pval
mean(pvals<=alpha) ## power
}
Do you really need the power for every integer value of sample size, or would a more widely spaced sample be OK (if you need exact values, interpolation would probably be pretty accurate)
ssizevec <- seq(10,250,by=5)
set.seed(101)
system.time(powvec <- sapply(ssizevec,powfun,m=5000)) ## 13 secs elapsed
This is reasonably fast and might get you up to `m=1e5` if you needed, but I'm not quite sure why you need results that are that precise -- the power curve is reasonably smooth with `m=5000` ...
If you're impatiently waiting for long simulations, you can also get a progress bar to print by replacing `sapply(ssizevec,powfun,m=5000)` with `library(plyr); aaply(ssizevec,.margins=1,powfun,.progress="text",m=5000)`
Finally, I think you can speed the whole up a lot by sampling chi-squared values directly, or by doing an analytical power calculation (!). I think that `rchisq(m,df=ssize)*newvar/popvar` is equivalent to the first two lines of the loop, and you might even be able to do a numerical computation on the chi-squared densities directly ...
system.time(powvec2 <- sapply(ssizevec,powfun,m=5000,sampchisq=TRUE))
## 0.24 seconds elapsed
(I just tried this out, sampling `m=1e5` at every value of sample size from 1 to 200 ... it takes 24 seconds ... but I still think it might be unnecessary.)
A picture:
par(bty="l",las=1)
plot(ssizevec,powvec,type="l",xlab="sample size",ylab="power",
xlim=c(0,250),ylim=c(0,1))
lines(ssizevec,powvec2,col="red")
Problem
I've created the following code that nests a for loop inside of a for loop in R. It is a simulation to calculate Power. I've read that R isn't great for doing for loops but I was wondering if there are any efficiencies I could apply to make this run a bit faster. I'm fairly new to R as well as programming of any sort. Right now the run times I'm seeing are: m=10 I get .17 sec m=100 I get 3.95 sec m=1000 I get 246.26 sec m=2000 I get 1003.55 sec I was hoping to set the number of times to sample, m, upwards of 100K but I'm afraid to even set this at 10K Here is the code: ``` m = 1000 # number of times we are going to take samples popmean=120 # set population mean at 120 popvar=225 # set known/established population variance at 225 newvar=144 # variance of new methodology alpha=.01 # set alpha teststatvect = matrix(nrow=m,ncol=1) # empty vector to populate with test statistics power = matrix(nrow=200,ncol=1) # empty vector to populate with power system.time( # not needed - using to gauge how long this takes for (n in 1:length(power)) # begin for loop for different sample sizes for(i in 1:m){ # begin for loop to take "m" samples y=rnorm(n,popmean,sqrt(newvar)) # sample of size n with mean 120 and var=144 ts=sum((y-popmean)^2/popvar) # calculate test statistic for each sample teststatvect[i]=ts # loop and populate the vector to hold test statistics vecpvals=pchisq(teststatvect,n) # calculate the pval of each statistic power[n]=length(which(vecpvals<=alpha))/length(vecpvals) # loop to populate power vector. Power is the proportion lessthan ot equal to alpha } } ) ```