Exercises - Monte Carlo Simulations for Statistical Power
Basics of R for Data Science
Performing a Monte Carlo simulation for power analysis is much more than just computing power. It means clarifying the entire research scenario in depth, thinking about all possible sources of variability, defining the parameters that govern the phenomenon of interest. It requires explicitly stating the research hypothesis, distinguishing between hypothesis testing and research questions that will be limited to exploratory analyses. Additionally, it help produce a comprehensive set of preplanned analysis that you may use for a preregistration, because the process involves writing the actual code for the analysis.
Computing the Power of a T-Test via Monte Carlo Simulation
Computing power of a t-test for a particular Standardized Mean Difference does not necessarily require a simulation. For example, you could use pwr.t.test()
function from the pwr
package, for an analytical computation. However, let’s use this scenario as a starting point for practice.
The goal of the first exercise is to run a simulation with a large number of iterations (hint: at least a few thousand) for a single value of Standardized Mean Difference (named
d
) and a single sample size (namedN
)- use a
for
loop to iterate through the simulation for simplicity (though there are other options); - at each iteration, you need to generate two sets of random numbers from a random normal distribution (use the
rnorm()
function; remember than their mean difference must bed
); - at each iteration, you need to run a t-test (use the
t.test()
function); - at each iteration, you need to store some information, which at minimum is the p-value from the t-test, that you must extract from the t-test (note that you can use the
$
operator for extracting information from an object); - after all iterations, you must compute power as the mean number of iterations where p-value was below a critical threshold \(\alpha\) (generally set at \(0.05\)).
- use a
Verify that the result from the previous exercise is sufficiently close, within an acceptable margin of approximation, to the analytical result obtained using the
pwr.t.test()
from thepwr
package.Extend the above exercise by repeating the simulation across a range of
N
values (e.g., \(25, 50, 75, 100, 150, 200, 250, 300, 500\)), and a at least three values ofd
(e.g., \(0.10, 0.20, 0.30\)).- you can use the
expand.grid()
function to generate all combinations ofN
andd
automatically; - once you have these combinations of
N
andd
, you can iterate over them using a nested loop structure: onefor
loop to go through all combinations ofN
andd
, and an inner loop running the iterations for computing power at each step (note that you may also explore other approaches involving defining custom functions and other ways of iterating, which could even be more efficient than nested loops).
- you can use the
Plot the results of the previous exercise with different lines for different values of
d
, withN
on the horizontal axis, andpower
on the vertical axis.
Computing the “Type M Error” for a Correlation
When testing hypotheses without sufficient power, statistically significant results tend to be associated with overestimated effect sizes (because only sufficiently large effects reach significance, leading to an average overestimation of the true effect). When power is very low, this overestimation can be notably large. (Among other articles, Type M Error is described by Gelman & Carlin, 2014)
- Your primary task is to run a Monte Carlo simulation for power analysis, similar to the above t-test exercise, but this time for a correlation. However, in addition to storing the p-value for computing power, also store the observed effect size at each iteration. Finally, calculate how much the estimated effect size exceeds the true effect size (as a percentage), when \(p < 0.05\). Choose a particular combination of
N
andr
(e.g., \(N = 100\) and \(r = 0.15\) results in low power and a clear overestimation).- to simulate correlated variables, you can use the
mvrnorm()
function from theMASS
package; - to test the statistical significance of a correlation and calculate its effect size, you can use the
cor.test()
function; - in addition to computing the Type M Error, try to approximate the smallest significant effect size given your \(N\).
- to simulate correlated variables, you can use the
- Optional: you can repeat this entire exercise for a t-test (with Cohen’s d as the effect size).
Advanced - Computing the Power of a Linear Regression via Monte Carlo Simulation
Once you’ve completed the previous two exercises, you are ready to move to the next phase: using simulation to calculate power for real statistical models.
Model setup: Simulate data for a simple linear regression model:
\(y = b0 + b1*x + residual\)
where \(residual \sim Normal(0, \sigma)\)
Parameters:
Choose a specific sample size \(N\) for your simulation;
Assume \(\sigma = 1\) for simplicity, so you can focus only on \(b1\) as the effect size (practically, a standardized regression coefficient);
note: intercept \(b0\) is irrelevant for power estimation.
Run \(10,000\) iterations and see the power for the particular combination of \(N\) and standardized \(b1\).
Note: extracting p-values for predictors from a fitted lm
object might not be so easy. You will need to access the model’s summary. The p-values are stored in a coefficients table within the summary, and the extraction may look like this:
summary(fit)$coefficients["predictorName", "Pr(>|t|)"]
… Make it your habit to inspect fitted objects and their summaries with the str()
function to locate and extract different pieces of information!
Advanced - Computing the Power of a Mixed-Effects Binomial Regression via Monte Carlo Simulation
In psychological studies, power depends not only on the sample size \(N\), but also on the number of trials or items \(k\) administered to each subject. More trials means more precision and higher power, up to a certain point.
Our goal now is to simulate a scenario where a particular predictor x
(e.g., age, years of education, amount of training, or global intelligence) affects an underlying ability y
which is measured using a series of k
binomial-response trials for a sample of N
subject.
Mixed-effects binomial regression is a type of Generalized Linear-Mixed effects models, which can be implemented using functions like
glmer()
from thelme4
package, orglmmTMB()
from theglmmTMB
package. Remember to setfamily=binomial
.The effect size can be conceptualized as the true correlation
r
between predictorx
and the underlying abilityy
.While
y
is an underlying (latent) ability that you will simulate, it is not directly observed in the data you analyze. Instead, you will analyze only the binomial responses from trials that reflecty
.Remember that
x
cannot be the only factor affecting the binomial responses. Individual variability must also be considered. In generating data, remember that random intercept of the subject must also be a cause of the responses. Random interceptsrInt
for subjects should be included in the data generation process.Suggestions for simulating the binomial responses:
- generate
y
as a standard normal distribution (correlated with or caused byx
); - use the
pnorm()
function to converty
(or better,y + rInt
) into probabilities of success for each trial; - generate binomial responses using the
rbinom()
function.
- generate
Here is an example of a possible model specification:
glmer(resp ~ x + (1|idSubj), family=binomial(link=probit))
Remember that the simulated and analyzed dataframe must be in long format, with each row representing an individual response. This means each subject and all their information (
idSubj
,x
) must be repeated across multiple rows (userep()
for this purpose).Finally, see how statistical power varies with the number of trials \(k\) given the same sample size \(N\).