Power Analysis and Simulation Tutorial
contributed by Lisa Schwetlick and Daniel Backhaus
This tutorial demonstrates how to conduct power analyses and data simulation using Julia and the MixedModelsSim package.
Power analysis is an important tool for planning an experimental design. Here we show how to:
- Use existing data as a basis for power calculations by simulating new data.
- Adapt parameters in a given linear mixed model to analyze power without changing the existing data set.
- Create a (simple) balanced fully crossed dataset from scratch and analyze power.
- Recreate a more complex dataset from scratch and analyze power for specific model parameter but various sample sizes.
Setup
Load the packages we'll be using in Julia
First, here are the packages needed in this example.
using MixedModels        # run mixed models
using MixedModelsSim     # simulation utilities
using DataFrames, Tables # work with dataframes
using StableRNGs         # random number generator
using Statistics         # basic statistical functions
using DataFrameMacros    # dplyr-like operations
using CairoMakie         # plotting package
CairoMakie.activate!(type="svg") # use vector graphics
using MixedModelsMakie   # some extra plotting function for MixedModels
using ProgressMeter      # show progress in loopsDefine number of iterations
Here we define how many model simulations we want to do. A large number will give more reliable results, but will take longer to compute. It is useful to set it to a low number for testing, and increase it for your final analysis.
# for real power analysis, set this much higher
nsims = 100100Use existing data to simulate new data
Build a linear mixed model from existing data
For the first example we are going to simulate data bootstrapped from an existing data set, namely Experiment 2 from
Kronmüller, E., & Barr, D. J. (2007). Perspective-free pragmatics: Broken precedents and the recovery-from-preemption hypothesis. Journal of Memory and Language, 56(3), 436-455.
This was an experiment about how in a conversation the change of a speaker or the change of precedents (which are patterns of word usage to describe an object, e.g. one can refer to the same object "white shoes", "runners", "sneakers") affects the understanding.
In experiment, objects were presented on a screen while participants listened to instructions to move the objects around. Participants' eye movements were tracked. The dependent variable is response time, defined as the latency between the onset of the test description and the moment at which the target was selected. The independent variables are speaker (old vs. new), precedents (maintain vs. break) and cognitive load (yes vs. no; from a secondary memory task).
We first load the data and define some characteristics like the contrasts and the underlying model. This dataset is one of the example datasets provided by MixedModels.jl.
Load existing data:
kb07 = MixedModels.dataset(:kb07);Arrow.Table with 1789 rows, 7 columns, and schema:
 :subj      String
 :item      String
 :spkr      String
 :prec      String
 :load      String
 :rt_trunc  Int16
 :rt_raw    Int16Set contrasts:
contrasts = Dict(:spkr => HelmertCoding(),
                 # set the reference level such that all the coefs
                 # have the same sign, which makes the plotting nicer
                 :prec => HelmertCoding(base="maintain"),
                 :load => HelmertCoding(),
                 # pseudo-contrast for grouping variables
                 :item => Grouping(),
                 :subj => Grouping());Dict{Symbol, StatsModels.AbstractContrasts} with 5 entries:
  :item => Grouping()
  :spkr => HelmertCoding(nothing, nothing)
  :load => HelmertCoding(nothing, nothing)
  :prec => HelmertCoding("maintain", nothing)
  :subj => Grouping()The chosen linear mixed model (LMM) for this dataset is defined by the following model formula:
kb07_f = @formula(rt_trunc ~ 1 + spkr + prec + load + (1|subj) + (1 + prec|item));FormulaTerm
Response:
  rt_trunc(unknown)
Predictors:
  1
  spkr(unknown)
  prec(unknown)
  load(unknown)
  (subj)->1 | subj
  (prec,item)->(1 + prec) | itemFit the model:
kb07_m = fit(MixedModel, kb07_f, kb07; contrasts=contrasts)Linear mixed model fit by maximum likelihood
 rt_trunc ~ 1 + spkr + prec + load + (1 | subj) + (1 + prec | item)
    logLik   -2 logLik      AIC         AICc        BIC     
 -14331.9251  28663.8501  28681.8501  28681.9513  28731.2548
Variance components:
            Column    Variance  Std.Dev.  Corr.
item     (Intercept)  133015.244 364.713
         prec: break   63766.936 252.521 +0.70
subj     (Intercept)   88819.436 298.026
Residual              462443.388 680.032
 Number of obs: 1789; levels of grouping factors: 32, 56
  Fixed-effects parameters:
───────────────────────────────────────────────────
                 Coef.  Std. Error      z  Pr(>|z|)
───────────────────────────────────────────────────
(Intercept)  2181.85       77.4681  28.16    <1e-99
spkr: old      67.879      16.0785   4.22    <1e-04
prec: break   333.791      47.4472   7.03    <1e-11
load: yes      78.5904     16.0785   4.89    <1e-05
───────────────────────────────────────────────────Simulate from existing data with same model parameters
We will first look at the power of the dataset with the same parameters as in the original data set. This means that each dataset will have the exact number of observations as the original data. Here, we use the model kb07_m we fitted above to our dataset kb07.
You can use the parametricbootstrap() function to run nsims iterations of data sampled using the parameters from kb07_m.
The parametric bootstrap is actually a simulation procedure. Each bootstrap iteration
- simulates new data based on an existing model
- then fits a model to that data to obtain new estimates.
Set up a random seed to make the simulation reproducible. You can use your favourite number.
To use multithreading, you need to set the number of worker threads you want to use. In VS Code, open the settings (gear icon in the lower left corner) and search for "thread". Set julia.NumThreads to the number of threads you want to use (at least 1 less than the total number of processor cores available, so that you can continue watching YouTube while the simulation runs).
Set random seed for reproducibility:
rng = StableRNG(42);StableRNGs.LehmerRNG(state=0x00000000000000000000000000000055)Run nsims iterations:
kb07_sim = parametricbootstrap(rng, nsims, kb07_m; use_threads = false);MixedModels.MixedModelBootstrap{Float64}(NamedTuple{(:objective, :σ, :β, :se, :θ), Tuple{Float64, Float64, NamedTuple{(Symbol("(Intercept)"), Symbol("spkr: old"), Symbol("prec: break"), Symbol("load: yes")), NTuple{4, Float64}}, StaticArrays.SVector{4, Float64}, StaticArrays.SVector{4, Float64}}}[(objective = 28621.66122973884, σ = 672.4991153396045, β = ((Intercept) = 2284.5565628301756, spkr: old = 29.05915716613222, prec: break = 379.9847248998523, load: yes = 53.20071447985788), se = [62.63937449444163, 15.90044008505303, 48.4279032536216, 15.90042085806977], θ = [0.3903524942759862, 0.08111837592991708, 0.3761295710931823, 0.4334569423573605]), (objective = 28700.426430585034, σ = 688.5739177545875, β = ((Intercept) = 2123.422323016969, spkr: old = 64.85037492855119, prec: break = 289.89569382378454, load: yes = 83.45915360780549), se = [86.34670115387901, 16.280453113958643, 56.522383921787934, 16.280432729208798], θ = [0.6432203748625043, 0.3570597484491862, 0.26502807224609065, 0.35392408426631944]), (objective = 28663.146079508846, σ = 682.397626054181, β = ((Intercept) = 2107.3120182875873, spkr: old = 51.54052897678158, prec: break = 304.43149607718806, load: yes = 92.35230681819063), se = [72.00268337148206, 16.134396763551504, 43.702894220240395, 16.13437407630944], θ = [0.4420781090821898, 0.28343197634941764, 0.1817315411865715, 0.500149433098055]), (objective = 28779.158761173534, σ = 707.0282009675263, β = ((Intercept) = 2057.3400317494547, spkr: old = 55.15524917103106, prec: break = 279.5744999175273, load: yes = 80.64505149883762), se = [71.03472400381301, 16.716787795480176, 44.69341495035265, 16.71676670205502], θ = [0.4791118713480484, 0.19683296237975073, 0.26690157286131116, 0.36366921860549306]), (objective = 28675.52582528763, σ = 689.3923894974258, β = ((Intercept) = 2197.172122031604, spkr: old = 86.71558458395147, prec: break = 359.4131348875641, load: yes = 90.42962382351936), se = [68.79969724872757, 16.299780903096032, 39.98754817469052, 16.299759657248853], θ = [0.4833996839282064, 0.20147941340268288, 0.2217663056429221, 0.3427802615675902]), (objective = 28657.22954337883, σ = 680.3764269012571, β = ((Intercept) = 2216.280151867676, spkr: old = 72.6018159986637, prec: break = 295.38534739893834, load: yes = 78.8484255853359), se = [63.47320088786341, 16.086663441739667, 45.804676064876816, 16.08664357955179], θ = [0.35073972078917715, 0.20376994238178106, 0.29261466757333326, 0.49070900578830107]), (objective = 28613.20367623644, σ = 670.9590331228859, β = ((Intercept) = 2188.6874269394148, spkr: old = 120.40236893132285, prec: break = 390.90568555195875, load: yes = 65.38287159092833), se = [77.14439766412617, 15.863991001537599, 48.26436137996117, 15.863969864575505], θ = [0.5487821797594216, 0.28654463650793094, 0.2560947678011314, 0.42656330141780213]), (objective = 28612.53827091664, σ = 668.9735179965959, β = ((Intercept) = 2208.5708805559634, spkr: old = 46.07531709460607, prec: break = 379.53461035451636, load: yes = 83.80643585502673), se = [77.58373555994747, 15.81702974537941, 47.5634580081015, 15.817007500764001], θ = [0.4884442870557914, 0.3184820250618456, 0.20601767986943084, 0.5517115083613405]), (objective = 28666.090916404868, σ = 682.3514744172359, β = ((Intercept) = 2048.3359403563204, spkr: old = 101.3596165155684, prec: break = 264.15803930679505, load: yes = 66.83240556428207), se = [79.86379819654846, 16.133338929577047, 45.084882587024865, 16.13331683939055], θ = [0.5722596278905615, 0.25926952777850243, 0.23364547806181468, 0.4034073765570476]), (objective = 28656.104010729017, σ = 675.3423052693641, β = ((Intercept) = 2186.1183745031653, spkr: old = 76.67121239990821, prec: break = 398.8565855664798, load: yes = 104.53314074571416), se = [82.19802298162648, 15.967653816106564, 58.52329593568008, 15.967633319222308], θ = [0.5837563155927353, 0.34871824477630864, 0.31750574116283925, 0.4493697873181847])  …  (objective = 28659.658527792566, σ = 682.9654622397829, β = ((Intercept) = 2222.243725091555, spkr: old = 73.02209223031697, prec: break = 396.6107828309655, load: yes = 59.78652154036543), se = [71.74797142721341, 16.147852763363794, 43.89380175901485, 16.14783164695024], θ = [0.5006152306470721, 0.23026685572999728, 0.2475202077398464, 0.3848995266348859]), (objective = 28728.441657102503, σ = 691.6685077742866, β = ((Intercept) = 2115.0960001438025, spkr: old = 83.38422408185265, prec: break = 289.1578150410527, load: yes = 62.14836282811545), se = [76.95709520785539, 16.353663624523588, 53.06631703618115, 16.353643079910835], θ = [0.5311794297112482, 0.24992197290192836, 0.3286509860128854, 0.4100955496100045]), (objective = 28645.884142509432, σ = 673.7036042542402, β = ((Intercept) = 2249.6002132297826, spkr: old = 76.18822579834854, prec: break = 374.5745887091143, load: yes = 78.11102616712819), se = [82.32429166322378, 15.928899669882037, 46.4258248178296, 15.92887698554432], θ = [0.5483727688478892, 0.27358578734945943, 0.24335711077179856, 0.5278634605731732]), (objective = 28576.273049531286, σ = 662.553372352801, β = ((Intercept) = 2031.312908806899, spkr: old = 48.41916044535698, prec: break = 227.56709343498022, load: yes = 72.31695979338163), se = [77.13695608143144, 15.66526725936014, 41.970895884350504, 15.66524523304666], θ = [0.5334005030163075, 0.21541048676268648, 0.25322154284856163, 0.4794193352279356]), (objective = 28534.675383406702, σ = 653.3871079581936, β = ((Intercept) = 2158.5668839912596, spkr: old = 74.84688749127197, prec: break = 365.13366345186, load: yes = 85.13975713163073), se = [73.37544695627122, 15.448560340575813, 50.872492120241844, 15.448540603206729], θ = [0.5192630539097025, 0.23954453944505633, 0.34455401583862855, 0.45063109656316575]), (objective = 28674.94345486157, σ = 679.9974790103457, β = ((Intercept) = 2133.6109915498782, spkr: old = 65.02465678549977, prec: break = 380.8536875682997, load: yes = 88.10858125417431), se = [83.92959598916728, 16.077699476467252, 46.25977917415873, 16.077676218860905], θ = [0.5642515456931602, 0.2817081960999918, 0.22549350912520819, 0.5144290450488861]), (objective = 28753.603903059902, σ = 696.6355513026296, β = ((Intercept) = 2123.5050085159914, spkr: old = 80.38728201197178, prec: break = 281.5455213842189, load: yes = 59.437761098963676), se = [73.99969494734891, 16.47109539603513, 48.96909630371763, 16.471073688195123], θ = [0.45051854814129905, 0.25584041829931603, 0.27345082346172933, 0.49536374816054085]), (objective = 28738.062312273316, σ = 693.5001149741282, β = ((Intercept) = 2202.0914588154897, spkr: old = 83.60021137695578, prec: break = 356.44291336777775, load: yes = 106.50397921851932), se = [76.27533004105213, 16.396976668252112, 42.2621435724981, 16.39695411636129], θ = [0.4888921799439929, 0.1545691500509428, 0.2775944987738257, 0.47733134737808613]), (objective = 28623.208208112348, σ = 675.0618508576679, β = ((Intercept) = 2053.3096758573215, spkr: old = 38.37279438596176, prec: break = 264.69231225968474, load: yes = 127.72088258400603), se = [83.930587598558, 15.96095645034335, 52.74478692713863, 15.960935366657566], θ = [0.6318393341042916, 0.36104987196352023, 0.21704387235069153, 0.3683832975374932]), (objective = 28669.412866477785, σ = 682.8361803691766, β = ((Intercept) = 2148.834678333924, spkr: old = 66.2357757417777, prec: break = 353.28465310687204, load: yes = 71.42965863053816), se = [74.21250009120587, 16.144820965972283, 44.94498998730651, 16.144800329416853], θ = [0.5259039821979027, 0.18296105066670537, 0.29542084680014497, 0.3823044302503038])], LinearAlgebra.LowerTriangular{Float64, Matrix{Float64}}[[0.5363168233715857 0.0; 0.25981993107379 0.2653016002105174], [0.4382528181348316]], [[1, 2, 4], [1]], [0.0, -Inf, 0.0, 0.0], (item = ("(Intercept)", "prec: break"), subj = ("(Intercept)",)))Try: Run the code above with or without use_threads = true. Did performance get better or worse? Check out the help information parametricbootstrap to see why!
The returned value kb07_sim contains the results of the bootstrapping procedure, which we can convert to a dataframe
df = DataFrame(kb07_sim.allpars);
first(df, 12)12 rows × 5 columns
| iter | type | group | names | value | |
|---|---|---|---|---|---|
| Int64 | String | String? | String? | Float64 | |
| 1 | 1 | β | missing | (Intercept) | 2284.56 | 
| 2 | 1 | β | missing | spkr: old | 29.0592 | 
| 3 | 1 | β | missing | prec: break | 379.985 | 
| 4 | 1 | β | missing | load: yes | 53.2007 | 
| 5 | 1 | σ | item | (Intercept) | 262.512 | 
| 6 | 1 | σ | item | prec: break | 258.762 | 
| 7 | 1 | ρ | item | (Intercept), prec: break | 0.210819 | 
| 8 | 1 | σ | subj | (Intercept) | 291.499 | 
| 9 | 1 | σ | residual | missing | 672.499 | 
| 10 | 2 | β | missing | (Intercept) | 2123.42 | 
| 11 | 2 | β | missing | spkr: old | 64.8504 | 
| 12 | 2 | β | missing | prec: break | 289.896 | 
The dataframe df has 4500 rows: 9 parameters, each from 500 iterations.
nrow(df)900We can now plot some bootstrapped parameters:
fig = Figure()
σres = @subset(df, :type == "σ" && :group == "residual")
ax = Axis(fig[1,1:2]; xlabel = "residual standard deviation", ylabel = "Density")
density!(ax, σres.value)
βInt = @subset(df, :type == "β" && :names == "(Intercept)")
ax = Axis(fig[1,3]; xlabel = "fixed effect for intercept")
density!(ax, βInt.value)
βSpeaker = @subset(df, :type == "β" && :names == "spkr: old")
ax = Axis(fig[2,1]; xlabel = "fixed effect for spkr: old", ylabel = "Density")
density!(ax, βSpeaker.value)
βPrecedents = @subset(df, :type == "β" && :names == "prec: break")
ax = Axis(fig[2,2]; xlabel = "fixed effect for prec: break")
density!(ax, βPrecedents.value)
βLoad = @subset(df, :type == "β" && :names == "load: yes")
ax = Axis(fig[2,3]; xlabel = "fixed effect for load: yes")
density!(ax, βLoad.value)
Label(fig[0,:]; text = "Parametric bootstrap replicates by parameter", textsize=25)
figFor the fixed effects, we can do this more succinctly via the ridgeplot functionality in MixedModelsMakie, even optionally omitting the intercept (which we often don't care about).
ridgeplot(kb07_sim; show_intercept=false)Next, we extract the p-values of the fixed-effects parameters into a dataframe
kb07_sim_df = DataFrame(kb07_sim.coefpvalues);
first(kb07_sim_df, 8)8 rows × 6 columns
| iter | coefname | β | se | z | p | |
|---|---|---|---|---|---|---|
| Int64 | Symbol | Float64 | Float64 | Float64 | Float64 | |
| 1 | 1 | (Intercept) | 2284.56 | 62.6394 | 36.4716 | 3.13097e-291 | 
| 2 | 1 | spkr: old | 29.0592 | 15.9004 | 1.82757 | 0.0676142 | 
| 3 | 1 | prec: break | 379.985 | 48.4279 | 7.8464 | 4.28148e-15 | 
| 4 | 1 | load: yes | 53.2007 | 15.9004 | 3.34587 | 0.000820253 | 
| 5 | 2 | (Intercept) | 2123.42 | 86.3467 | 24.5918 | 1.5453e-133 | 
| 6 | 2 | spkr: old | 64.8504 | 16.2805 | 3.98333 | 6.7957e-5 | 
| 7 | 2 | prec: break | 289.896 | 56.5224 | 5.12887 | 2.91494e-7 | 
| 8 | 2 | load: yes | 83.4592 | 16.2804 | 5.12635 | 2.95417e-7 | 
Now that we have a bootstrapped data, we can start our power calculation.
Power calculation
The function power_table() from MixedModelsSim takes the output of parametricbootstrap() and calculates the proportion of simulations where the p-value is less than alpha for each coefficient. You can set the alpha argument to change the default value of 0.05 (justify your alpha).
ptbl = power_table(kb07_sim, 0.05)4-element Vector{NamedTuple{(:coefname, :power), Tuple{String, Float64}}}:
 (coefname = "prec: break", power = 1.0)
 (coefname = "(Intercept)", power = 1.0)
 (coefname = "spkr: old", power = 0.99)
 (coefname = "load: yes", power = 1.0)An estimated power of 1 means that in every iteration the specific parameter we are looking at was below our alpha. An estimated power of 0.5 means that in half of our iterations the specific parameter we are looking at was below our alpha. An estimated power of 0 means that for none of our iterations the specific parameter we are looking at was below our alpha.
You can also do it manually:
prec_p = kb07_sim_df[kb07_sim_df.coefname .== Symbol("prec: break"),:p]
mean(prec_p .< 0.05)1.0For a nicer display, you can use pretty_table:
pretty_table(ptbl)┌─────────────┬─────────┐
│    coefname │   power │
│      String │ Float64 │
├─────────────┼─────────┤
│ prec: break │     1.0 │
│ (Intercept) │     1.0 │
│   spkr: old │    0.99 │
│   load: yes │     1.0 │
└─────────────┴─────────┘The simulation so far should not be interpreted as the power of the original K&B experiment. Observed power calculations are generally problematic. Instead, the example so far should serve to show how power can be computed using a simulation procedure.
In the next section, we show how to use previously observed data – such as pilot data – as the basis for a simulation study with a different effect size.
Adapt parameters in a given linear mixed model to analyze power without generating additional data
Let's say we want to check our power to detect effects of spkr, prec, and load that are only half the size as in our pilot data. We can set a new vector of beta values (fixed effects) with the β argument to parametricbootstrap().
Specify β:
new_beta = kb07_m.β
new_beta[2:4] = kb07_m.β[2:4] / 23-element Vector{Float64}:
  33.939510821966415
 166.8953106066071
  39.295220532876364Run simulations:
kb07_sim_half = parametricbootstrap(StableRNG(42), nsims, kb07_m; β = new_beta, use_threads = false);MixedModels.MixedModelBootstrap{Float64}(NamedTuple{(:objective, :σ, :β, :se, :θ), Tuple{Float64, Float64, NamedTuple{(Symbol("(Intercept)"), Symbol("spkr: old"), Symbol("prec: break"), Symbol("load: yes")), NTuple{4, Float64}}, StaticArrays.SVector{4, Float64}, StaticArrays.SVector{4, Float64}}}[(objective = 28621.66122973882, σ = 672.4991153396998, β = ((Intercept) = 2284.5565628301943, spkr: old = -4.880353655839888, prec: break = 213.08941429324926, load: yes = 13.905493946973557), se = [62.63937450177407, 15.900440085055273, 48.42790324687082, 15.90042085807201], θ = [0.3903524943566226, 0.08111837606714789, 0.37612957100202415, 0.4334569423613265]), (objective = 28700.42643058503, σ = 688.5739178928687, β = ((Intercept) = 2123.4223230164134, spkr: old = 30.910864106051303, prec: break = 123.00038321771699, load: yes = 44.163933075499024), se = [86.34670090780197, 16.280453117226845, 56.52238364907122, 16.28043273247696], θ = [0.6432203725352988, 0.3570597472943777, 0.26502806971308085, 0.3539240840774174]), (objective = 28663.146079508853, σ = 682.3976260353294, β = ((Intercept) = 2107.3120182875796, spkr: old = 17.601018154877988, prec: break = 137.53618547051138, load: yes = 53.057086285303775), se = [72.00268328643759, 16.134396763106313, 43.70289408382902, 16.13437407586425], θ = [0.4420781071901476, 0.28343197468742676, 0.18173154154432442, 0.5001494345867834]), (objective = 28779.15876117352, σ = 707.0282009566688, β = ((Intercept) = 2057.340031749851, spkr: old = 21.215738349459738, prec: break = 112.67918931053929, load: yes = 41.34983096561435), se = [71.03472398264013, 16.716787795223773, 44.69341490974508, 16.716766701798615], θ = [0.4791118711225782, 0.19683296124924504, 0.2669015732670978, 0.36366921868587504]), (objective = 28675.52582528766, σ = 689.3923894388587, β = ((Intercept) = 2197.172122032728, spkr: old = 52.77607376327011, prec: break = 192.51782427966424, load: yes = 51.13440328950001), se = [68.79969756241971, 16.29978090171133, 39.987547955657085, 16.299759655864023], θ = [0.48339968628764085, 0.2014794127432824, 0.22176630362401079, 0.34278026330168787]), (objective = 28657.229543378842, σ = 680.3764268873775, β = ((Intercept) = 2216.280151867675, spkr: old = 38.66230517670057, prec: break = 128.490036792331, load: yes = 39.553205052460314), se = [63.47320091935412, 16.08666344141158, 45.80467608131773, 16.086643579223697], θ = [0.35073972119122154, 0.20376994258578154, 0.2926146676192893, 0.4907090057984363]), (objective = 28613.203676236426, σ = 670.959033093031, β = ((Intercept) = 2188.6874269391374, spkr: old = 86.46285810915118, prec: break = 224.01037494557565, load: yes = 26.08765105837224), se = [77.14439771703886, 15.863991000832199, 48.26436075268532, 15.863969863869979], θ = [0.548782178908351, 0.2865446309081849, 0.2560947656923019, 0.42656330460147984]), (objective = 28612.53827091663, σ = 668.973518031094, β = ((Intercept) = 2208.5708805559857, spkr: old = 12.13580627261508, prec: break = 212.6392997479401, load: yes = 44.51121532212603), se = [77.58373546005897, 15.8170297461946, 47.56345797445287, 15.817007501579196], θ = [0.4884442857101702, 0.3184820249667847, 0.20601767942041413, 0.5517115086180431]), (objective = 28666.090916404894, σ = 682.3514744086093, β = ((Intercept) = 2048.335940356172, spkr: old = 67.42010569358986, prec: break = 97.26272870017921, load: yes = 27.53718503150303), se = [79.8637982121194, 16.13333892937325, 45.08488259985265, 16.13331683918676], θ = [0.5722596281292326, 0.25926952761771394, 0.233645478417921, 0.40340737635936824]), (objective = 28656.10401072901, σ = 675.3423050629441, β = ((Intercept) = 2186.118374503309, spkr: old = 42.7317015781917, prec: break = 231.96127495961915, load: yes = 65.2379202126872), se = [82.1980236615007, 15.967653811226967, 58.523296302418125, 15.967633314342699], θ = [0.5837563221586701, 0.3487182480134617, 0.317505742581594, 0.4493697882253582])  …  (objective = 28659.658526616506, σ = 682.9633591515573, β = ((Intercept) = 2222.2437055284145, spkr: old = 39.08255796708661, prec: break = 229.71549566563183, load: yes = 20.491320570659216), se = [71.74762110140712, 16.147803065994946, 43.8958287217414, 16.14778195020493], θ = [0.5006058850592012, 0.23024957476787203, 0.24756258477936116, 0.3849179018350522]), (objective = 28728.44165710253, σ = 691.668507794978, β = ((Intercept) = 2115.0960001437315, spkr: old = 49.44471325974656, prec: break = 122.26250443458173, load: yes = 22.853142295279685), se = [76.95709505032222, 16.35366362501272, 53.066317125589336, 16.35364308039999], θ = [0.5311794282352981, 0.24992197343302547, 0.3286509865575056, 0.41009554944456234]), (objective = 28645.88414250942, σ = 673.7036047945654, β = ((Intercept) = 2249.600213227903, spkr: old = 42.24871497841036, prec: break = 207.67927810046842, load: yes = 38.81580563609717), se = [82.3242883567362, 15.928899682657798, 46.42582355486184, 15.928876998320256], θ = [0.5483727311909375, 0.2735857690054127, 0.24335711390651263, 0.5278634641384086]), (objective = 28576.2730495337, σ = 662.5533674508848, β = ((Intercept) = 2031.3129087916473, spkr: old = 14.479649660669146, prec: break = 60.671782791092774, load: yes = 33.02173927576104), se = [77.13695121052045, 15.66526714350804, 41.97090180487328, 15.665245117196124], θ = [0.5334004007433438, 0.21541047996255525, 0.2532216239195116, 0.47941944609383536]), (objective = 28534.675383406702, σ = 653.3871096380038, β = ((Intercept) = 2158.566883987628, spkr: old = 40.90737667201204, prec: break = 198.23835284254693, load: yes = 45.84453660239054), se = [73.37544284172263, 15.44856038028505, 50.87249054859448, 15.448540642916223], θ = [0.5192630172926284, 0.23954452332475465, 0.34455400820491217, 0.4506310784916277]), (objective = 28674.943454861612, σ = 679.9974788346508, β = ((Intercept) = 2133.610991553883, spkr: old = 31.08514596308663, prec: break = 213.95837696213866, load: yes = 48.813360717298025), se = [83.92959645251013, 16.077699472312165, 46.25977798390025, 16.077676214705463], θ = [0.5642515435647696, 0.28170818845762124, 0.225493501944067, 0.5144290587179273]), (objective = 28753.603903059917, σ = 696.635551209764, β = ((Intercept) = 2123.5050085160588, spkr: old = 46.44777118880053, prec: break = 114.65021077880074, load: yes = 20.142540565979502), se = [73.999694987591, 16.471095393840315, 48.969095915257824, 16.47107368600024], θ = [0.4505185457143607, 0.25584041409614944, 0.2734508228843026, 0.49536375288692697]), (objective = 28738.06231227348, σ = 693.5001157222124, β = ((Intercept) = 2202.091458812693, spkr: old = 49.660700557610525, prec: break = 189.54760275856052, load: yes = 67.2087586885129), se = [76.27532784191467, 16.39697668594015, 42.26214579505656, 16.396954134050258], θ = [0.4888921789281661, 0.15456914428622387, 0.27759452403581575, 0.47733130675018576]), (objective = 28623.20820811236, σ = 675.0618508977703, β = ((Intercept) = 2053.3096758573315, spkr: old = 4.433283563923175, prec: break = 97.79700165314173, load: yes = 88.4256620511036), se = [83.93058754820157, 15.960956451291137, 52.74478689718531, 15.96093536760536], θ = [0.6318393337648364, 0.36104987178349857, 0.21704387208555168, 0.3683832970069386]), (objective = 28669.412866477793, σ = 682.8361803417732, β = ((Intercept) = 2148.8346783338484, spkr: old = 32.29626491973762, prec: break = 186.38934250033452, load: yes = 32.134438097731), se = [74.21250016243675, 16.144820965324495, 44.944989995733465, 16.14480032876906], θ = [0.5259039827480323, 0.18296105098619814, 0.2954208467090988, 0.38230443065610475])], LinearAlgebra.LowerTriangular{Float64, Matrix{Float64}}[[0.5363168233715857 0.0; 0.25981993107379 0.2653016002105174], [0.4382528181348316]], [[1, 2, 4], [1]], [0.0, -Inf, 0.0, 0.0], (item = ("(Intercept)", "prec: break"), subj = ("(Intercept)",)))Power calculation
power_table(kb07_sim_half)4-element Vector{NamedTuple{(:coefname, :power), Tuple{String, Float64}}}:
 (coefname = "prec: break", power = 0.93)
 (coefname = "(Intercept)", power = 1.0)
 (coefname = "spkr: old", power = 0.54)
 (coefname = "load: yes", power = 0.73)Create and analyze a (simple) balanced fully crossed dataset from scratch
In some situations, instead of using an existing dataset it may be useful to simulate the data from scratch. This could be the case when pilot data or data from a previous study are not available. Note that we still have to assume (or perhaps guess) a particular effect size, which can be derived from previous work (whether experimental or practical). In the worst case, we can start from the smallest effect size that we would find interesting or meaningful.
In order to simulate data from scratch, we have to:
- specify the effect sizes manually
- manually create an experimental design, according to which data can be simulated
If we simulate data from scratch, we can manipulate the arguments β, σ and θ (in addition to the number of subjects and items) Lets have a closer look at them, define their meaning and we will see where the corresponding values in the model output are.
Fixed Effects (βs)
β are our effect sizes.
If we look again on our LMM summary from the kb07-dataset kb07_m we see our four β under fixed-effects parameters in the Coef.-column.
kb07_mLinear mixed model fit by maximum likelihood
 rt_trunc ~ 1 + spkr + prec + load + (1 | subj) + (1 + prec | item)
    logLik   -2 logLik      AIC         AICc        BIC     
 -14331.9251  28663.8501  28681.8501  28681.9513  28731.2548
Variance components:
            Column    Variance  Std.Dev.  Corr.
item     (Intercept)  133015.244 364.713
         prec: break   63766.936 252.521 +0.70
subj     (Intercept)   88819.436 298.026
Residual              462443.388 680.032
 Number of obs: 1789; levels of grouping factors: 32, 56
  Fixed-effects parameters:
───────────────────────────────────────────────────
                 Coef.  Std. Error      z  Pr(>|z|)
───────────────────────────────────────────────────
(Intercept)  2181.85       77.4681  28.16    <1e-99
spkr: old      67.879      16.0785   4.22    <1e-04
prec: break   333.791      47.4472   7.03    <1e-11
load: yes      78.5904     16.0785   4.89    <1e-05
───────────────────────────────────────────────────kb07_m.β4-element Vector{Float64}:
 2181.8526392913914
   67.87902164393283
  333.7906212132142
   78.59044106575273(These can also be accessed with the appropriately named coef function.)
Residual Variance (σ)
σ is the residual-standard deviation listed under the variance components.
kb07_m.σ680.0319023667461Random Effects (θ)
The meaning of θ is a bit less intuitive. In a less complex model (one that only has intercepts for the random effects) or if we suppress the correlations in the formula with zerocorr() then θ describes the relationship between the standard deviation of the random effects and the residual standard deviation.
In our kb07_m example:
- The residualstandard deviation is680.032.
- The standard deviation of our first variance component item (Intercept)is364.713.
- Thus our first θis the relationship: variance component devided byresidualstandard deviation: $364.713 / 680.032 = 0.53631$
kb07_m.θ4-element Vector{Float64}:
 0.5363168233715857
 0.25981993107379
 0.2653016002105174
 0.4382528181348316We also can calculate the θ for variance component subj (Intercept). The residual standard deviation is 680.032. The standard deviation of our variance component subj (Intercept) is 298.026. Thus, the related θ is the relationship: variance component devided by residual standard deviation 298.026 /  680.032 =  0.438252
We can not calculate the θs for variance component item prec: break this way, because it includes the correlation of item prec: break and item (Intercept). But keep in mind that the relation of  item prec: break-variability (252.521) and the residual-variability (680.032) is $252.521  /  680.032 =  0.3713369$.
The θ vector is the flattened version of the lower Cholesky factor variance-covariance matrix. The Cholesky factor is in some sense a "matrix square root" (so like storing standard deviations instead of variances) and is a lower triangular matrix. The on-diagonal elements are just the standard deviations (the σ's). If all off-diagonal elements are zero, we can use our calculation above. The off-diagonal elements are covariances and correspond to the correlations (the ρ's). If they are unequal to zero, as it is in our kb07-dataset, one way to get the two missing θ-values is to take the values directly from the model we have already fitted.
See the two inner values:
kb07_m.θ4-element Vector{Float64}:
 0.5363168233715857
 0.25981993107379
 0.2653016002105174
 0.4382528181348316Another way is to make use of the create_re() function. Here you have to define the relation of all random effects variabilities to the variability of the residuals, as shown above, and the correlation-matrices.
Let's start by defining the correlation matrix for the item-part.
The diagonal is always 1.0 because everything is perfectly correlated with itself. The elements below the diagonal follow the same form as the Corr. entries in the output of VarCorr(). In our example the correlation of item prec: break and item (Intercept) is -0.7. The elements above the diagonal are just a mirror image.
re_item_corr = [1.0 -0.7; -0.7 1.0]2×2 Matrix{Float64}:
  1.0  -0.7
 -0.7   1.0Now we put together all relations of standard deviations and the correlation-matrix for the item-group. This calculates the covariance factorization which is the theta matrix.
re_item = create_re(0.536, 0.371; corrmat = re_item_corr)2×2 LinearAlgebra.LowerTriangular{Float64, Matrix{Float64}}:
  0.536    ⋅ 
 -0.2597  0.264947
Don't be too specific with your values in create_re(). Generally we don't these values very precisely because estimating them precisely requires large amounts of data. Additionally, if there are numerical problems (rounding errors), you will get the error-message: PosDefException: matrix is not Hermitian; Cholesky factorization failed.
Although we advise against using these values, you can extract the exact values like so (just as a pedagogical demonstration):
corr_exact = VarCorr(kb07_m).σρ.item.ρ[1]
σ_residuals_exact = kb07_m.σ
σ_1_exact = VarCorr(kb07_m).σρ.item.σ[1] / σ_residuals_exact
σ_2_exact = VarCorr(kb07_m).σρ.item.σ[2] / σ_residuals_exact
re_item_corr = [1.0 corr_exact; corr_exact 1.0]
re_item = create_re(σ_1_exact, σ_2_exact; corrmat = re_item_corr)2×2 LinearAlgebra.LowerTriangular{Float64, Matrix{Float64}}:
 0.536317   ⋅ 
 0.25982   0.265302Let's continue with the subj-part.
Since there the by-subject random effects have only one entry (the intercept), there are no correlations to specify and we can omit the corrmat argument.
Now we put together all relations of standard deviations and the correlation-matrix for the subj-group:
This calculates the covariance factorization which is the theta matrix.
re_subj = create_re(0.438)1×1 LinearAlgebra.LowerTriangular{Float64, LinearAlgebra.Diagonal{Float64, Vector{Float64}}}:
 0.438If you want the exact value you can use
σ_residuals_exact = kb07_m.σ
σ_3_exact = VarCorr(kb07_m).σρ.subj.σ[1] / σ_residuals_exact
re_subj = create_re(σ_3_exact)1×1 LinearAlgebra.LowerTriangular{Float64, LinearAlgebra.Diagonal{Float64, Vector{Float64}}}:
 0.4382528181348316As mentioned above θ is the compact form of these covariance matrices:
createθ(kb07_m; item=re_item, subj=re_subj)4-element Vector{Float64}:
 0.5363168233715857
 0.25981993107379
 0.2653016002105175
 0.4382528181348316The function createθ is putting the random effects into the correct order and then putting them into the compact form. Even for the same formula, the order may vary between datasets (and hence models fit to those datasets) because of a particular computational trick used in MixedModels.jl. Because of this trick, we need to specify the model along with the random effects so that the correct order can be determined.
We can install these parameter in the parametricbootstrap()-function or in the model like this:
# need the fully qualified name here because Makie also defines update!
MixedModelsSim.update!(kb07_m, item=re_item, subj=re_subj)Linear mixed model fit by maximum likelihood
 rt_trunc ~ 1 + spkr + prec + load + (1 | subj) + (1 + prec | item)
    logLik   -2 logLik      AIC         AICc        BIC     
 -14331.9251  28663.8501  28681.8501  28681.9513  28731.2548
Variance components:
            Column    Variance  Std.Dev.  Corr.
item     (Intercept)  133015.244 364.713
         prec: break   63766.936 252.521 +0.70
subj     (Intercept)   88819.436 298.026
Residual              462443.388 680.032
 Number of obs: 1789; levels of grouping factors: 32, 56
  Fixed-effects parameters:
───────────────────────────────────────────────────
                 Coef.  Std. Error      z  Pr(>|z|)
───────────────────────────────────────────────────
(Intercept)  2181.85       77.4681  28.16    <1e-99
spkr: old      67.879      16.0785   4.22    <1e-04
prec: break   333.791      47.4472   7.03    <1e-11
load: yes      78.5904     16.0785   4.89    <1e-05
───────────────────────────────────────────────────A simple example from scratch
Having this knowledge about the parameters we can now simulate data from scratch
The simdat_crossed() function from MixedModelsSim lets you set up a data frame with a specified experimental design. For now, it only makes fully balanced crossed designs, but you can generate an unbalanced design by simulating data for the largest cell and deleting extra rows.
First, we will set an easy design where subj_n subjects per age group (Old or Young) respond to item_n items in each of two conditions (A or B).
Your factors need to be specified separately for between-subject, between-item, and within-subject/item factors using Dict with the name of each factor as the keys and vectors with the names of the levels as values.
We start with the between subject factors:
subj_btwn = Dict(:age => ["O", "Y"])Dict{Symbol, Vector{String}} with 1 entry:
  :age => ["O", "Y"]There are no between-item factors in this design so you can omit it or set it to nothing. Note that if you have factors which are between subject and between item, you need to put them in both dicts.
item_btwn = nothingNext, we put within-subject/item factors in a dict:
both_win = Dict(:condition => ["A", "B"])Dict{Symbol, Vector{String}} with 1 entry:
  :condition => ["A", "B"]Define subject and item number:
subj_n = 10
item_n = 3030Simulate data:
dat = simdat_crossed(subj_n,
                     item_n,
                     subj_btwn = subj_btwn,
                     item_btwn = item_btwn,
                     both_win = both_win);600-element Vector{NamedTuple{(:subj, :age, :item, :condition, :dv), Tuple{String, String, String, String, Float64}}}:
 (subj = "S01", age = "O", item = "I01", condition = "A", dv = -0.27351016685419677)
 (subj = "S02", age = "Y", item = "I01", condition = "A", dv = -0.07913743936095087)
 (subj = "S03", age = "O", item = "I01", condition = "A", dv = -0.7513138549983934)
 (subj = "S04", age = "Y", item = "I01", condition = "A", dv = -1.1804069563746886)
 (subj = "S05", age = "O", item = "I01", condition = "A", dv = -0.8351526580165848)
 (subj = "S06", age = "Y", item = "I01", condition = "A", dv = 1.1746014506716254)
 (subj = "S07", age = "O", item = "I01", condition = "A", dv = 0.6641105459394487)
 (subj = "S08", age = "Y", item = "I01", condition = "A", dv = -1.1164115875234257)
 (subj = "S09", age = "O", item = "I01", condition = "A", dv = -0.4430688714573556)
 (subj = "S10", age = "Y", item = "I01", condition = "A", dv = -1.377314801035193)
 ⋮
 (subj = "S02", age = "Y", item = "I30", condition = "B", dv = 0.23497178269968144)
 (subj = "S03", age = "O", item = "I30", condition = "B", dv = 1.717021072744995)
 (subj = "S04", age = "Y", item = "I30", condition = "B", dv = 0.6468295385824457)
 (subj = "S05", age = "O", item = "I30", condition = "B", dv = -0.9239441241432992)
 (subj = "S06", age = "Y", item = "I30", condition = "B", dv = -1.1649161302026236)
 (subj = "S07", age = "O", item = "I30", condition = "B", dv = 0.379780137236555)
 (subj = "S08", age = "Y", item = "I30", condition = "B", dv = -0.05457075645244976)
 (subj = "S09", age = "O", item = "I30", condition = "B", dv = 1.425586499173869)
 (subj = "S10", age = "Y", item = "I30", condition = "B", dv = -0.894542775194046)Have a look:
first(DataFrame(dat),8)8 rows × 5 columns
| subj | age | item | condition | dv | |
|---|---|---|---|---|---|
| String | String | String | String | Float64 | |
| 1 | S01 | O | I01 | A | -0.27351 | 
| 2 | S02 | Y | I01 | A | -0.0791374 | 
| 3 | S03 | O | I01 | A | -0.751314 | 
| 4 | S04 | Y | I01 | A | -1.18041 | 
| 5 | S05 | O | I01 | A | -0.835153 | 
| 6 | S06 | Y | I01 | A | 1.1746 | 
| 7 | S07 | O | I01 | A | 0.664111 | 
| 8 | S08 | Y | I01 | A | -1.11641 | 
The values we see in the column dv is just random noise (drawn from the standard normal distribution)
Set contrasts:
contrasts = Dict(:age => HelmertCoding(),
                 :condition => HelmertCoding(),
                 :subj => Grouping(),
                 :item => Grouping());Dict{Symbol, StatsModels.AbstractContrasts} with 4 entries:
  :item      => Grouping()
  :age       => HelmertCoding(nothing, nothing)
  :condition => HelmertCoding(nothing, nothing)
  :subj      => Grouping()Define formula:
f1 = @formula(dv ~ 1 + age * condition + (1|item) + (1|subj));FormulaTerm
Response:
  dv(unknown)
Predictors:
  1
  age(unknown)
  condition(unknown)
  (item)->1 | item
  (subj)->1 | subj
  age(unknown) & condition(unknown)Note that we did not include condition as random slopes for item and subject. This is mainly to keep the example simple and to keep the parameter θ easier to understand (see Section 3 above for the explanation of θ).
Fit the model:
m1 = fit(MixedModel, f1, dat; contrasts=contrasts)Linear mixed model fit by maximum likelihood
 dv ~ 1 + age + condition + age & condition + (1 | item) + (1 | subj)
   logLik   -2 logLik     AIC       AICc        BIC    
  -852.1105  1704.2209  1718.2209  1718.4101  1748.9994
Variance components:
            Column   VarianceStd.Dev.
item     (Intercept)  0.00000 0.00000
subj     (Intercept)  0.00000 0.00000
Residual              1.00249 1.00125
 Number of obs: 600; levels of grouping factors: 30, 10
  Fixed-effects parameters:
───────────────────────────────────────────────────────────────
                             Coef.  Std. Error      z  Pr(>|z|)
───────────────────────────────────────────────────────────────
(Intercept)             0.0258618    0.0408757   0.63    0.5269
age: Y                 -0.0311722    0.0408757  -0.76    0.4457
condition: B            0.00492309   0.0408757   0.12    0.9041
age: Y & condition: B  -0.0204655    0.0408757  -0.50    0.6166
───────────────────────────────────────────────────────────────Because the dv is just random noise from N(0,1), there will be basically no subject or item random variance, residual variance will be near 1.0, and the estimates for all effects should be small. Don't worry, we'll specify fixed and random effects directly in parametricbootstrap().
Set random seed for reproducibility:
rng = StableRNG(42);StableRNGs.LehmerRNG(state=0x00000000000000000000000000000055)Specify β, σ, and θ, we just made up this parameter:
new_beta = [0., 0.25, 0.25, 0.]
new_sigma = 2.0
new_theta = [1.0, 1.0]2-element Vector{Float64}:
 1.0
 1.0Run nsims iterations:
sim1 = parametricbootstrap(rng, nsims, m1;
                           β = new_beta,
                           σ = new_sigma,
                           θ = new_theta,
                           use_threads = false);MixedModels.MixedModelBootstrap{Float64}(NamedTuple{(:objective, :σ, :β, :se, :θ), Tuple{Float64, Float64, NamedTuple{(Symbol("(Intercept)"), Symbol("age: Y"), Symbol("condition: B"), Symbol("age: Y & condition: B")), NTuple{4, Float64}}, StaticArrays.SVector{4, Float64}, StaticArrays.SVector{2, Float64}}}[(objective = 2692.3610314326556, σ = 2.047878422128479, β = ((Intercept) = -1.2514691972112222, age: Y = 1.0092232885828654, condition: B = 0.2234187328428462, age: Y & condition: B = 0.0694707523830102), se = [0.7207930400834414, 0.6118632768701686, 0.08360428649117849, 0.08360428649117849], θ = [1.0190343374706794, 0.93596092540392]), (objective = 2637.6876454916783, σ = 1.9664599289442164, β = ((Intercept) = 0.5933304076892756, age: Y = 0.16250401624005908, condition: B = 0.061846206409283754, age: Y & condition: B = -0.12242391752570912), se = [0.7344968192944272, 0.6637529490367065, 0.0802803904257166, 0.0802803904257166], θ = [0.8760165519241518, 1.0595496559575368]), (objective = 2584.1440148821357, σ = 1.8805353428190845, β = ((Intercept) = 0.12032043743901646, age: Y = 0.161266724204998, condition: B = 0.2753148935121752, age: Y & condition: B = 0.06880234926264962), se = [0.5120307889801435, 0.3559679542947235, 0.07677253388627658, 0.07677253388627658], θ = [1.0719842907954271, 0.5845025504712554]), (objective = 2674.0451340881077, σ = 2.0068020142808707, β = ((Intercept) = -0.08942325214775138, age: Y = 1.2445124382240416, condition: B = 0.2756056156168278, age: Y & condition: B = 0.0019319947128912914), se = [0.8064188859914504, 0.7041897587539765, 0.08192734916296024, 0.08192734916296024], θ = [1.0725545966917651, 1.1021123747923551]), (objective = 2687.746337155375, σ = 2.0633485113253083, β = ((Intercept) = -0.6692467458281341, age: Y = 0.5105132739560954, condition: B = 0.20596770138857237, age: Y & condition: B = -0.025674164168205425), se = [0.5568391658345431, 0.44337910649084006, 0.08423585023797138, 0.08423585023797138], θ = [0.8942458505540337, 0.6671443589171886]), (objective = 2676.4559583634505, σ = 2.0373920161719585, β = ((Intercept) = 0.4394355388817507, age: Y = 0.17931804380053945, condition: B = 0.3439411667123486, age: Y & condition: B = 0.11467757327199375), se = [0.7436384903738149, 0.6778629978192658, 0.08317618076069253, 0.08317618076069253], θ = [0.8220377108840188, 1.0441744521972427]), (objective = 2662.551612757377, σ = 2.0073631998043373, β = ((Intercept) = -0.5461799640539531, age: Y = -0.02080028588099537, condition: B = 0.3215822875574084, age: Y & condition: B = -0.06355472394332845), se = [0.5695549460868241, 0.4253146250876276, 0.08195025946601907, 0.08195025946601907], θ = [1.0336204263846103, 0.6574595717799832]), (objective = 2636.7044040219607, σ = 1.954776490952805, β = ((Intercept) = -0.3166835135942168, age: Y = 0.4757914674418209, condition: B = 0.3230515858298644, age: Y & condition: B = 0.0719370653921513), se = [0.7909222263252593, 0.7141321535883423, 0.07980341606704316, 0.07980341606704316], θ = [0.9525595938540089, 1.148028622355752]), (objective = 2690.427621457432, σ = 2.0452657137897403, β = ((Intercept) = 0.5834695426913978, age: Y = -0.08265650422637691, condition: B = 0.22652487157529685, age: Y & condition: B = -0.08648642349416008), se = [0.713293207574403, 0.6040754002681835, 0.08349762311990142, 0.08349762311990142], θ = [1.0158071632782435, 0.925022909509678]), (objective = 2708.5482577153466, σ = 2.0835992734160165, β = ((Intercept) = 0.3589849794878687, age: Y = -0.15549678679448692, condition: B = 0.23911665978067334, age: Y & condition: B = 0.0006507184175729701), se = [0.693026485033347, 0.5897517518595667, 0.08506258413838359, 0.08506258413838359], θ = [0.9567958048784746, 0.8857067298317605])  …  (objective = 2637.953771647584, σ = 1.984259775848997, β = ((Intercept) = 0.5624601448509522, age: Y = 2.035284607902835, condition: B = 0.2738520910204433, age: Y & condition: B = -0.02377150987765371), se = [0.6520863506893775, 0.592085747404145, 0.08100706613265611, 0.08100706613265611], θ = [0.754188899686933, 0.9347227887101589]), (objective = 2658.694677765882, σ = 2.007900160339613, β = ((Intercept) = 0.5605369038496435, age: Y = 0.9185091186664872, condition: B = 0.20189088196302707, age: Y & condition: B = -0.021413324927476038), se = [0.5626614691240543, 0.4430653784513061, 0.08197218078807635, 0.08197218078807635], θ = [0.946056397030952, 0.6857451194632282]), (objective = 2676.0209006214204, σ = 2.008318596574926, β = ((Intercept) = 0.9187827821595882, age: Y = -0.7412780384350943, condition: B = 0.15986383394434422, age: Y & condition: B = -0.01256851270662999), se = [0.6741813238064768, 0.5019648848725771, 0.08198926337584982, 0.08198926337584982], θ = [1.227427859514288, 0.779774097873545]), (objective = 2636.285217088517, σ = 1.9815379266037738, β = ((Intercept) = 0.5180933838122017, age: Y = -0.023801640955858842, condition: B = 0.2935427363321596, age: Y & condition: B = 0.08734688663859327), se = [0.5669093212616615, 0.4853577179865684, 0.08089594710252984, 0.08089594710252984], θ = [0.8097248606906711, 0.7637335399162329]), (objective = 2661.361754846196, σ = 1.9970235459614263, β = ((Intercept) = -0.1292362637564912, age: Y = 0.34126300340694227, condition: B = 0.29045825647582413, age: Y & condition: B = 0.09646631934185011), se = [0.6172962168062865, 0.4730812440924852, 0.0815281448654834, 0.0815281448654834], θ = [1.0876046376400201, 0.7379140104661702]), (objective = 2737.1516171783737, σ = 2.1149977572086556, β = ((Intercept) = 0.2759725559383277, age: Y = 0.12611603784255773, condition: B = 0.30420567292028167, age: Y & condition: B = 0.03363570717209677), se = [0.7647988593890811, 0.6254988613162034, 0.08634442187153381, 0.08634442187153381], θ = [1.1396727828559727, 0.9262727278475748]), (objective = 2662.0588866146113, σ = 1.9838399912132256, β = ((Intercept) = -0.10945975366426042, age: Y = 0.8786858809755252, condition: B = 0.15786028589462547, age: Y & condition: B = 0.09809830059469311), se = [0.7862677398732353, 0.6733672728202433, 0.08098992849666445, 0.08098992849666445], θ = [1.1207893410970118, 1.0655678283102006]), (objective = 2654.167217165057, σ = 2.0101301800600737, β = ((Intercept) = -0.4893983891634397, age: Y = 0.3751152402364922, condition: B = 0.17421380067393283, age: Y & condition: B = -0.0175052059057118), se = [0.5558292085606646, 0.46294204165604835, 0.08206322096193423, 0.08206322096193423], θ = [0.838209622276473, 0.71675309297399]), (objective = 2746.499890360901, σ = 2.120182358303396, β = ((Intercept) = 0.2009778529545533, age: Y = -0.4535819594043236, condition: B = 0.3388435174600142, age: Y & condition: B = -0.02264787332360129), se = [0.8168065429729632, 0.6562382572383776, 0.0865560823249003, 0.0865560823249003], θ = [1.25639232271128, 0.9702360153460529]), (objective = 2660.5285117476787, σ = 2.000112356860582, β = ((Intercept) = 0.3251552146833946, age: Y = 1.0042210728972152, condition: B = 0.18809918940694154, age: Y & condition: B = 0.011991756750343068), se = [0.892502737614661, 0.8370180819216047, 0.08165424504239806, 0.08165424504239806], θ = [0.8483119390312998, 1.3170553256179196])], LinearAlgebra.LowerTriangular{Float64, Matrix{Float64}}[[0.0], [0.0]], [[1], [1]], [0.0, 0.0], (item = ("(Intercept)",), subj = ("(Intercept)",)))Power calculation
ptbl= power_table(sim1)4-element Vector{NamedTuple{(:coefname, :power), Tuple{String, Float64}}}:
 (coefname = "age: Y", power = 0.23)
 (coefname = "condition: B", power = 0.86)
 (coefname = "(Intercept)", power = 0.08)
 (coefname = "age: Y & condition: B", power = 0.04)For nicely displaying it, you can use pretty_table:
pretty_table(ptbl)┌───────────────────────┬─────────┐
│              coefname │   power │
│                String │ Float64 │
├───────────────────────┼─────────┤
│                age: Y │    0.23 │
│          condition: B │    0.86 │
│           (Intercept) │    0.08 │
│ age: Y & condition: B │    0.04 │
└───────────────────────┴─────────┘Compute power curves for a more complex dataset
Recreate the kb07-dataset from scratch
For full control over all parameters in our kb07 data set we will recreate the design using the method shown above.
Define subject and item number:
subj_n = 56
item_n = 3232Define factors in a dict:
subj_btwn = nothing
item_btwn = nothing
both_win = Dict(:spkr => ["old", "new"],
                :prec => ["maintain", "break"],
                :load => ["yes", "no"]);Dict{Symbol, Vector{String}} with 3 entries:
  :spkr => ["old", "new"]
  :load => ["yes", "no"]
  :prec => ["maintain", "break"]Try: Play with simdat_crossed.
Simulate data:
fake_kb07 = simdat_crossed(subj_n, item_n,
                           subj_btwn = subj_btwn,
                           item_btwn = item_btwn,
                           both_win = both_win);14336-element Vector{NamedTuple{(:subj, :item, :spkr, :load, :prec, :dv), Tuple{String, String, String, String, String, Float64}}}:
 (subj = "S01", item = "I01", spkr = "old", load = "yes", prec = "maintain", dv = 0.18629512809763302)
 (subj = "S02", item = "I01", spkr = "old", load = "yes", prec = "maintain", dv = -1.3089267555058464)
 (subj = "S03", item = "I01", spkr = "old", load = "yes", prec = "maintain", dv = 1.1129793764541815)
 (subj = "S04", item = "I01", spkr = "old", load = "yes", prec = "maintain", dv = -0.5030535276523742)
 (subj = "S05", item = "I01", spkr = "old", load = "yes", prec = "maintain", dv = 0.30782631362865365)
 (subj = "S06", item = "I01", spkr = "old", load = "yes", prec = "maintain", dv = 0.987155131812637)
 (subj = "S07", item = "I01", spkr = "old", load = "yes", prec = "maintain", dv = 0.3314430526857845)
 (subj = "S08", item = "I01", spkr = "old", load = "yes", prec = "maintain", dv = -1.065897058245874)
 (subj = "S09", item = "I01", spkr = "old", load = "yes", prec = "maintain", dv = -1.1515024069068265)
 (subj = "S10", item = "I01", spkr = "old", load = "yes", prec = "maintain", dv = 0.38145568288881293)
 ⋮
 (subj = "S48", item = "I32", spkr = "new", load = "no", prec = "break", dv = 1.149678771992115)
 (subj = "S49", item = "I32", spkr = "new", load = "no", prec = "break", dv = 0.9909926328894012)
 (subj = "S50", item = "I32", spkr = "new", load = "no", prec = "break", dv = -1.52072006466391)
 (subj = "S51", item = "I32", spkr = "new", load = "no", prec = "break", dv = 1.2833334923738688)
 (subj = "S52", item = "I32", spkr = "new", load = "no", prec = "break", dv = -0.7050898619329027)
 (subj = "S53", item = "I32", spkr = "new", load = "no", prec = "break", dv = -0.03137788897934821)
 (subj = "S54", item = "I32", spkr = "new", load = "no", prec = "break", dv = 0.6608460798710237)
 (subj = "S55", item = "I32", spkr = "new", load = "no", prec = "break", dv = -1.8628659521372648)
 (subj = "S56", item = "I32", spkr = "new", load = "no", prec = "break", dv = 0.6302346667943238)Make a dataframe:
fake_kb07_df = DataFrame(fake_kb07);Have a look:
first(fake_kb07_df,8)8 rows × 6 columns
| subj | item | spkr | load | prec | dv | |
|---|---|---|---|---|---|---|
| String | String | String | String | String | Float64 | |
| 1 | S01 | I01 | old | yes | maintain | 0.186295 | 
| 2 | S02 | I01 | old | yes | maintain | -1.30893 | 
| 3 | S03 | I01 | old | yes | maintain | 1.11298 | 
| 4 | S04 | I01 | old | yes | maintain | -0.503054 | 
| 5 | S05 | I01 | old | yes | maintain | 0.307826 | 
| 6 | S06 | I01 | old | yes | maintain | 0.987155 | 
| 7 | S07 | I01 | old | yes | maintain | 0.331443 | 
| 8 | S08 | I01 | old | yes | maintain | -1.0659 | 
The function simdat_crossed generates a balanced fully crossed design. Unfortunately, our original design is not balanced fully crossed. Every subject saw an image only once, thus in one of eight possible conditions. To simulate that we only keep one of every eight lines.
We sort the dataframe to enable easier selection
sort!(fake_kb07_df, [:subj, :item, :load, :prec, :spkr])In order to select only the relevant rows of the data set we define an index which represents a random choice of one of every eight rows. First we generate a vector idx which represents which row to keep in each set of 8.
len = div(length(fake_kb07), 8) # integer division
idx = rand(rng, 1:8 , len)
show(idx)[8, 7, 7, 6, 1, 8, 7, 3, 1, 6, 4, 6, 2, 3, 5, 8, 3, 1, 2, 7, 2, 2, 3, 2, 5, 7, 3, 6, 1, 6, 2, 6, 6, 2, 6, 3, 3, 7, 6, 3, 1, 1, 6, 2, 4, 6, 3, 4, 8, 3, 5, 5, 5, 6, 4, 6, 8, 6, 8, 6, 6, 7, 3, 1, 2, 4, 4, 2, 7, 2, 3, 4, 2, 7, 5, 8, 3, 6, 4, 3, 6, 1, 5, 8, 4, 7, 5, 1, 8, 4, 7, 4, 2, 4, 6, 7, 2, 3, 2, 6, 8, 1, 4, 6, 8, 8, 5, 7, 2, 2, 2, 6, 8, 8, 2, 8, 5, 1, 3, 1, 3, 7, 3, 1, 7, 6, 5, 1, 1, 4, 3, 3, 8, 2, 7, 1, 6, 6, 3, 5, 7, 3, 2, 3, 6, 5, 2, 8, 5, 1, 4, 4, 2, 8, 6, 1, 7, 8, 7, 2, 4, 8, 5, 8, 4, 7, 7, 2, 4, 8, 7, 1, 2, 5, 4, 4, 5, 3, 7, 6, 4, 6, 8, 7, 7, 5, 6, 1, 1, 6, 2, 3, 1, 4, 4, 7, 1, 2, 1, 6, 7, 2, 5, 3, 2, 4, 6, 4, 4, 1, 5, 4, 2, 4, 3, 7, 4, 7, 4, 1, 3, 2, 4, 1, 6, 4, 1, 3, 1, 8, 8, 5, 4, 4, 3, 3, 6, 6, 5, 5, 7, 8, 7, 8, 1, 2, 4, 3, 3, 5, 6, 7, 6, 8, 1, 6, 2, 1, 3, 7, 1, 6, 1, 5, 7, 3, 8, 5, 1, 2, 6, 3, 1, 8, 4, 6, 7, 7, 3, 3, 5, 4, 7, 2, 7, 8, 2, 3, 3, 8, 7, 8, 6, 3, 8, 5, 5, 2, 5, 3, 1, 2, 6, 2, 4, 8, 8, 8, 1, 1, 4, 1, 2, 6, 1, 7, 3, 3, 5, 1, 6, 3, 2, 5, 5, 5, 4, 3, 6, 1, 2, 7, 1, 5, 2, 2, 5, 1, 3, 8, 4, 3, 1, 5, 1, 7, 5, 2, 5, 5, 5, 2, 6, 5, 5, 8, 2, 4, 5, 7, 8, 3, 7, 2, 3, 5, 1, 6, 3, 8, 4, 8, 8, 2, 6, 6, 7, 2, 7, 7, 3, 2, 8, 1, 5, 4, 4, 3, 4, 1, 5, 2, 6, 2, 7, 8, 7, 3, 3, 4, 3, 4, 2, 3, 8, 5, 8, 1, 2, 3, 1, 2, 3, 6, 2, 5, 1, 2, 4, 8, 6, 6, 3, 1, 5, 6, 2, 1, 3, 4, 7, 4, 6, 4, 5, 4, 3, 6, 6, 3, 4, 7, 2, 8, 8, 5, 4, 1, 5, 6, 4, 7, 5, 1, 8, 2, 2, 7, 4, 3, 7, 5, 2, 7, 1, 3, 7, 1, 2, 6, 1, 1, 2, 5, 4, 5, 8, 3, 3, 3, 6, 4, 6, 5, 4, 4, 8, 1, 5, 8, 4, 6, 1, 4, 4, 7, 3, 8, 4, 8, 6, 2, 3, 4, 2, 7, 2, 4, 3, 7, 6, 2, 7, 5, 2, 7, 4, 3, 3, 2, 8, 3, 2, 3, 2, 7, 5, 8, 6, 4, 3, 1, 8, 7, 8, 3, 7, 6, 2, 5, 4, 6, 6, 1, 8, 8, 4, 3, 6, 2, 6, 6, 4, 3, 6, 1, 5, 4, 2, 2, 1, 3, 5, 6, 5, 8, 7, 2, 6, 6, 7, 1, 5, 3, 7, 3, 8, 8, 1, 7, 4, 8, 3, 6, 6, 1, 4, 3, 7, 8, 4, 3, 5, 2, 3, 8, 7, 6, 6, 6, 4, 7, 3, 7, 2, 1, 7, 2, 4, 7, 1, 1, 2, 8, 1, 6, 4, 6, 6, 2, 5, 3, 5, 8, 6, 6, 3, 6, 7, 7, 5, 8, 3, 6, 6, 8, 8, 6, 1, 6, 8, 7, 7, 7, 6, 6, 3, 5, 4, 7, 2, 2, 6, 2, 1, 6, 8, 1, 6, 7, 7, 7, 7, 1, 1, 1, 4, 4, 3, 1, 2, 8, 7, 3, 3, 6, 5, 2, 7, 3, 6, 4, 2, 6, 5, 4, 8, 5, 3, 2, 6, 7, 8, 6, 5, 8, 1, 1, 7, 4, 1, 4, 4, 1, 6, 1, 2, 1, 2, 8, 2, 7, 5, 4, 5, 2, 6, 7, 7, 2, 7, 6, 3, 6, 6, 6, 4, 5, 1, 7, 1, 7, 3, 4, 5, 6, 3, 1, 5, 3, 2, 3, 8, 7, 3, 3, 3, 4, 4, 8, 2, 1, 6, 2, 4, 5, 4, 6, 2, 1, 7, 8, 1, 5, 4, 6, 1, 3, 7, 8, 8, 5, 8, 7, 1, 1, 7, 2, 7, 8, 8, 4, 1, 4, 8, 3, 5, 2, 3, 4, 1, 6, 6, 2, 2, 5, 5, 2, 2, 8, 2, 3, 7, 1, 5, 7, 6, 3, 1, 6, 6, 6, 3, 7, 5, 7, 7, 7, 4, 8, 1, 1, 6, 2, 8, 4, 6, 2, 3, 6, 8, 4, 7, 7, 3, 2, 3, 3, 7, 3, 7, 4, 7, 5, 5, 1, 6, 4, 6, 3, 6, 1, 7, 3, 7, 5, 7, 6, 4, 4, 6, 7, 2, 2, 4, 5, 6, 7, 8, 4, 5, 3, 5, 3, 8, 2, 6, 6, 7, 3, 8, 8, 6, 6, 6, 4, 8, 2, 5, 4, 4, 3, 4, 4, 7, 6, 6, 5, 7, 4, 8, 4, 4, 6, 8, 5, 7, 6, 3, 2, 3, 7, 1, 1, 3, 3, 3, 4, 7, 8, 7, 2, 5, 1, 6, 7, 3, 1, 5, 2, 4, 5, 5, 1, 5, 7, 4, 4, 7, 5, 6, 8, 6, 3, 5, 3, 8, 3, 4, 8, 5, 6, 2, 2, 2, 6, 2, 7, 2, 4, 7, 8, 6, 7, 8, 3, 4, 6, 3, 2, 2, 6, 2, 4, 6, 7, 2, 8, 7, 2, 8, 5, 3, 6, 3, 7, 5, 4, 3, 1, 6, 6, 7, 6, 3, 5, 3, 2, 6, 4, 4, 1, 7, 7, 5, 5, 5, 7, 4, 6, 1, 2, 7, 7, 8, 4, 4, 1, 1, 5, 3, 5, 3, 7, 8, 5, 6, 7, 2, 3, 1, 5, 4, 1, 4, 7, 2, 6, 8, 1, 3, 7, 3, 1, 8, 7, 7, 4, 7, 4, 1, 7, 5, 7, 1, 1, 2, 4, 4, 1, 8, 3, 5, 2, 7, 5, 2, 5, 6, 8, 5, 5, 7, 1, 3, 8, 1, 1, 2, 4, 7, 6, 4, 8, 5, 4, 5, 6, 3, 3, 7, 8, 8, 6, 6, 2, 4, 7, 5, 6, 8, 2, 2, 4, 7, 2, 7, 6, 8, 7, 2, 8, 4, 2, 6, 6, 4, 3, 5, 7, 8, 1, 5, 7, 5, 6, 7, 4, 1, 3, 4, 7, 3, 7, 1, 6, 6, 2, 4, 1, 1, 5, 5, 5, 4, 6, 8, 6, 2, 2, 6, 3, 5, 7, 6, 4, 6, 4, 8, 8, 7, 1, 4, 2, 6, 5, 2, 6, 7, 5, 7, 5, 3, 2, 1, 6, 6, 1, 6, 5, 6, 6, 8, 5, 3, 3, 4, 2, 3, 6, 2, 7, 1, 6, 2, 3, 3, 8, 8, 7, 5, 4, 8, 5, 5, 7, 7, 4, 8, 5, 2, 5, 7, 3, 8, 5, 7, 1, 5, 3, 5, 7, 6, 6, 6, 1, 2, 7, 3, 6, 2, 2, 3, 5, 3, 8, 8, 6, 4, 6, 6, 5, 3, 1, 8, 8, 6, 1, 6, 7, 1, 8, 6, 2, 7, 6, 8, 4, 2, 4, 4, 8, 1, 2, 3, 7, 5, 1, 3, 4, 7, 8, 6, 7, 4, 6, 5, 3, 6, 6, 1, 2, 7, 4, 2, 6, 6, 5, 5, 3, 3, 7, 3, 6, 8, 4, 7, 5, 3, 5, 8, 6, 3, 3, 3, 1, 7, 6, 2, 2, 4, 8, 3, 5, 8, 8, 3, 7, 4, 4, 7, 8, 4, 5, 5, 2, 7, 8, 7, 4, 6, 5, 7, 6, 7, 7, 2, 2, 7, 7, 5, 2, 6, 8, 4, 8, 6, 1, 8, 1, 7, 8, 2, 7, 7, 5, 6, 7, 4, 2, 3, 6, 4, 7, 8, 8, 6, 1, 5, 6, 5, 5, 8, 2, 6, 5, 6, 1, 4, 1, 8, 6, 7, 2, 5, 3, 2, 3, 6, 6, 2, 4, 7, 8, 7, 1, 3, 7, 8, 2, 2, 3, 4, 4, 6, 1, 1, 6, 3, 7, 4, 6, 8, 2, 4, 6, 3, 2, 2, 8, 1, 6, 7, 3, 1, 5, 3, 7, 6, 7, 2, 7, 4, 3, 8, 4, 6, 1, 4, 1, 2, 6, 1, 7, 5, 4, 6, 7, 3, 7, 3, 5, 7, 2, 7, 4, 7, 1, 8, 1, 5, 1, 3, 3, 8, 6, 1, 7, 8, 3, 4, 4, 3, 3, 2, 7, 5, 3, 6, 1, 1, 6, 2, 1, 5, 7, 2, 7, 2, 5, 1, 6, 4, 8, 4, 2, 8, 2, 1, 4, 8, 8, 3, 3, 2, 3, 3, 5, 1, 3, 7, 4, 2, 7, 5, 8, 3, 5, 2, 4, 6, 5, 5, 5, 1, 2, 4, 7, 1, 4, 2, 6, 8, 5, 4, 4, 4, 1, 1, 5, 6, 3, 7, 2, 2, 4, 4, 6, 4, 2, 2, 4, 8, 4, 2, 8, 4, 2, 4, 4, 4, 5, 2, 1, 1, 7, 6, 8, 7, 6, 3, 1, 2, 1, 2, 2, 5, 3, 5, 4, 6, 4, 6, 2, 3, 4, 8, 7, 8, 6, 1, 8, 1, 4, 1, 4, 5, 2, 3, 2, 2, 6, 7, 4, 8, 7, 8, 5, 7, 7, 8, 2, 2, 4, 5, 2, 8, 8, 5, 8, 7, 7, 1, 2, 6, 4, 6, 2, 6, 1, 2, 6, 2, 7, 1, 3, 2, 4, 2, 5, 7, 4, 5, 6, 6, 7, 4, 6, 8, 5, 2, 3, 8, 8, 7, 8, 2, 4, 7, 5, 1, 7, 7, 1, 5, 8, 8, 4, 5, 4, 6, 4, 8, 7, 2, 3, 5, 6, 5, 2, 6, 7, 2, 2, 4, 8, 6, 5, 7, 3, 8, 1, 2, 6, 3, 7, 3, 2, 2, 3, 2, 7, 8, 5, 8, 4, 4, 7, 6, 5, 6, 7, 5, 5, 4, 3, 4, 3, 5, 7, 8, 3, 5, 3, 3, 5, 5, 4, 4, 1, 6, 5, 1, 6, 3, 2, 5, 2, 8, 3, 5, 1, 5, 2, 6, 2, 3, 3, 8, 5, 6, 7, 3, 2, 2, 4, 1, 7, 8, 6, 8, 2, 5, 8, 3, 8, 2, 8, 8, 1, 2, 1, 1, 7, 2, 2, 3, 8, 4, 7, 6, 6, 5, 8, 6, 5, 5, 7, 6, 7, 2, 1]Then we create an array A, of the same length that is populated multiples of the number 8. Added together A and idx give the indexes of one row from each set of 8s.
A = repeat([8], inner=len-1)
push!(A, 0)
A = cumsum(A)
idx = idx .+ A
show(idx)[16, 23, 31, 38, 41, 56, 63, 67, 73, 86, 92, 102, 106, 115, 125, 136, 139, 145, 154, 167, 170, 178, 187, 194, 205, 215, 219, 230, 233, 246, 250, 262, 270, 274, 286, 291, 299, 311, 318, 323, 329, 337, 350, 354, 364, 374, 379, 388, 400, 403, 413, 421, 429, 438, 444, 454, 464, 470, 480, 486, 494, 503, 507, 513, 522, 532, 540, 546, 559, 562, 571, 580, 586, 599, 605, 616, 619, 630, 636, 643, 654, 657, 669, 680, 684, 695, 701, 705, 720, 724, 735, 740, 746, 756, 766, 775, 778, 787, 794, 806, 816, 817, 828, 838, 848, 856, 861, 871, 874, 882, 890, 902, 912, 920, 922, 936, 941, 945, 955, 961, 971, 983, 987, 993, 1007, 1014, 1021, 1025, 1033, 1044, 1051, 1059, 1072, 1074, 1087, 1089, 1102, 1110, 1115, 1125, 1135, 1139, 1146, 1155, 1166, 1173, 1178, 1192, 1197, 1201, 1212, 1220, 1226, 1240, 1246, 1249, 1263, 1272, 1279, 1282, 1292, 1304, 1309, 1320, 1324, 1335, 1343, 1346, 1356, 1368, 1375, 1377, 1386, 1397, 1404, 1412, 1421, 1427, 1439, 1446, 1452, 1462, 1472, 1479, 1487, 1493, 1502, 1505, 1513, 1526, 1530, 1539, 1545, 1556, 1564, 1575, 1577, 1586, 1593, 1606, 1615, 1618, 1629, 1635, 1642, 1652, 1662, 1668, 1676, 1681, 1693, 1700, 1706, 1716, 1723, 1735, 1740, 1751, 1756, 1761, 1771, 1778, 1788, 1793, 1806, 1812, 1817, 1827, 1833, 1848, 1856, 1861, 1868, 1876, 1883, 1891, 1902, 1910, 1917, 1925, 1935, 1944, 1951, 1960, 1961, 1970, 1980, 1987, 1995, 2005, 2014, 2023, 2030, 2040, 2041, 2054, 2058, 2065, 2075, 2087, 2089, 2102, 2105, 2117, 2127, 2131, 2144, 2149, 2153, 2162, 2174, 2179, 2185, 2200, 2204, 2214, 2223, 2231, 2235, 2243, 2253, 2260, 2271, 2274, 2287, 2296, 2298, 2307, 2315, 2328, 2335, 2344, 2350, 2355, 2368, 2373, 2381, 2386, 2397, 2403, 2409, 2418, 2430, 2434, 2444, 2456, 2464, 2472, 2473, 2481, 2492, 2497, 2506, 2518, 2521, 2535, 2539, 2547, 2557, 2561, 2574, 2579, 2586, 2597, 2605, 2613, 2620, 2627, 2638, 2641, 2650, 2663, 2665, 2677, 2682, 2690, 2701, 2705, 2715, 2728, 2732, 2739, 2745, 2757, 2761, 2775, 2781, 2786, 2797, 2805, 2813, 2818, 2830, 2837, 2845, 2856, 2858, 2868, 2877, 2887, 2896, 2899, 2911, 2914, 2923, 2933, 2937, 2950, 2955, 2968, 2972, 2984, 2992, 2994, 3006, 3014, 3023, 3026, 3039, 3047, 3051, 3058, 3072, 3073, 3085, 3092, 3100, 3107, 3116, 3121, 3133, 3138, 3150, 3154, 3167, 3176, 3183, 3187, 3195, 3204, 3211, 3220, 3226, 3235, 3248, 3253, 3264, 3265, 3274, 3283, 3289, 3298, 3307, 3318, 3322, 3333, 3337, 3346, 3356, 3368, 3374, 3382, 3387, 3393, 3405, 3414, 3418, 3425, 3435, 3444, 3455, 3460, 3470, 3476, 3485, 3492, 3499, 3510, 3518, 3523, 3532, 3543, 3546, 3560, 3568, 3573, 3580, 3585, 3597, 3606, 3612, 3623, 3629, 3633, 3648, 3650, 3658, 3671, 3676, 3683, 3695, 3701, 3706, 3719, 3721, 3731, 3743, 3745, 3754, 3766, 3769, 3777, 3786, 3797, 3804, 3813, 3824, 3827, 3835, 3843, 3854, 3860, 3870, 3877, 3884, 3892, 3904, 3905, 3917, 3928, 3932, 3942, 3945, 3956, 3964, 3975, 3979, 3992, 3996, 4008, 4014, 4018, 4027, 4036, 4042, 4055, 4058, 4068, 4075, 4087, 4094, 4098, 4111, 4117, 4122, 4135, 4140, 4147, 4155, 4162, 4176, 4179, 4186, 4195, 4202, 4215, 4221, 4232, 4238, 4244, 4251, 4257, 4272, 4279, 4288, 4291, 4303, 4310, 4314, 4325, 4332, 4342, 4350, 4353, 4368, 4376, 4380, 4387, 4398, 4402, 4414, 4422, 4428, 4435, 4446, 4449, 4461, 4468, 4474, 4482, 4489, 4499, 4509, 4518, 4525, 4536, 4543, 4546, 4558, 4566, 4575, 4577, 4589, 4595, 4607, 4611, 4624, 4632, 4633, 4647, 4652, 4664, 4667, 4678, 4686, 4689, 4700, 4707, 4719, 4728, 4732, 4739, 4749, 4754, 4763, 4776, 4783, 4790, 4798, 4806, 4812, 4823, 4827, 4839, 4842, 4849, 4863, 4866, 4876, 4887, 4889, 4897, 4906, 4920, 4921, 4934, 4940, 4950, 4958, 4962, 4973, 4979, 4989, 5000, 5006, 5014, 5019, 5030, 5039, 5047, 5053, 5064, 5067, 5078, 5086, 5096, 5104, 5110, 5113, 5126, 5136, 5143, 5151, 5159, 5166, 5174, 5179, 5189, 5196, 5207, 5210, 5218, 5230, 5234, 5241, 5254, 5264, 5265, 5278, 5287, 5295, 5303, 5311, 5313, 5321, 5329, 5340, 5348, 5355, 5361, 5370, 5384, 5391, 5395, 5403, 5414, 5421, 5426, 5439, 5443, 5454, 5460, 5466, 5478, 5485, 5492, 5504, 5509, 5515, 5522, 5534, 5543, 5552, 5558, 5565, 5576, 5577, 5585, 5599, 5604, 5609, 5620, 5628, 5633, 5646, 5649, 5658, 5665, 5674, 5688, 5690, 5703, 5709, 5716, 5725, 5730, 5742, 5751, 5759, 5762, 5775, 5782, 5787, 5798, 5806, 5814, 5820, 5829, 5833, 5847, 5849, 5863, 5867, 5876, 5885, 5894, 5899, 5905, 5917, 5923, 5930, 5939, 5952, 5959, 5963, 5971, 5979, 5988, 5996, 6008, 6010, 6017, 6030, 6034, 6044, 6053, 6060, 6070, 6074, 6081, 6095, 6104, 6105, 6117, 6124, 6134, 6137, 6147, 6159, 6168, 6176, 6181, 6192, 6199, 6201, 6209, 6223, 6226, 6239, 6248, 6256, 6260, 6265, 6276, 6288, 6291, 6301, 6306, 6315, 6324, 6329, 6342, 6350, 6354, 6362, 6373, 6381, 6386, 6394, 6408, 6410, 6419, 6431, 6433, 6445, 6455, 6462, 6467, 6473, 6486, 6494, 6502, 6507, 6519, 6525, 6535, 6543, 6551, 6556, 6568, 6569, 6577, 6590, 6594, 6608, 6612, 6622, 6626, 6635, 6646, 6656, 6660, 6671, 6679, 6683, 6690, 6699, 6707, 6719, 6723, 6735, 6740, 6751, 6757, 6765, 6769, 6782, 6788, 6798, 6803, 6814, 6817, 6831, 6835, 6847, 6853, 6863, 6870, 6876, 6884, 6894, 6903, 6906, 6914, 6924, 6933, 6942, 6951, 6960, 6964, 6973, 6979, 6989, 6995, 7008, 7010, 7022, 7030, 7039, 7043, 7056, 7064, 7070, 7078, 7086, 7092, 7104, 7106, 7117, 7124, 7132, 7139, 7148, 7156, 7167, 7174, 7182, 7189, 7199, 7204, 7216, 7220, 7228, 7238, 7248, 7253, 7263, 7270, 7275, 7282, 7291, 7303, 7305, 7313, 7323, 7331, 7339, 7348, 7359, 7368, 7375, 7378, 7389, 7393, 7406, 7415, 7419, 7425, 7437, 7442, 7452, 7461, 7469, 7473, 7485, 7495, 7500, 7508, 7519, 7525, 7534, 7544, 7550, 7555, 7565, 7571, 7584, 7587, 7596, 7608, 7613, 7622, 7626, 7634, 7642, 7654, 7658, 7671, 7674, 7684, 7695, 7704, 7710, 7719, 7728, 7731, 7740, 7750, 7755, 7762, 7770, 7782, 7786, 7796, 7806, 7815, 7818, 7832, 7839, 7842, 7856, 7861, 7867, 7878, 7883, 7895, 7901, 7908, 7915, 7921, 7934, 7942, 7951, 7958, 7963, 7973, 7979, 7986, 7998, 8004, 8012, 8017, 8031, 8039, 8045, 8053, 8061, 8071, 8076, 8086, 8089, 8098, 8111, 8119, 8128, 8132, 8140, 8145, 8153, 8165, 8171, 8181, 8187, 8199, 8208, 8213, 8222, 8231, 8234, 8243, 8249, 8261, 8268, 8273, 8284, 8295, 8298, 8310, 8320, 8321, 8331, 8343, 8347, 8353, 8368, 8375, 8383, 8388, 8399, 8404, 8409, 8423, 8429, 8439, 8441, 8449, 8458, 8468, 8476, 8481, 8496, 8499, 8509, 8514, 8527, 8533, 8538, 8549, 8558, 8568, 8573, 8581, 8591, 8593, 8603, 8616, 8617, 8625, 8634, 8644, 8655, 8662, 8668, 8680, 8685, 8692, 8701, 8710, 8715, 8723, 8735, 8744, 8752, 8758, 8766, 8770, 8780, 8791, 8797, 8806, 8816, 8818, 8826, 8836, 8847, 8850, 8863, 8870, 8880, 8887, 8890, 8904, 8908, 8914, 8926, 8934, 8940, 8947, 8957, 8967, 8976, 8977, 8989, 8999, 9005, 9014, 9023, 9028, 9033, 9043, 9052, 9063, 9067, 9079, 9081, 9094, 9102, 9106, 9116, 9121, 9129, 9141, 9149, 9157, 9164, 9174, 9184, 9190, 9194, 9202, 9214, 9219, 9229, 9239, 9246, 9252, 9262, 9268, 9280, 9288, 9295, 9297, 9308, 9314, 9326, 9333, 9338, 9350, 9359, 9365, 9375, 9381, 9387, 9394, 9401, 9414, 9422, 9425, 9438, 9445, 9454, 9462, 9472, 9477, 9483, 9491, 9500, 9506, 9515, 9526, 9530, 9543, 9545, 9558, 9562, 9571, 9579, 9592, 9600, 9607, 9613, 9620, 9632, 9637, 9645, 9655, 9663, 9668, 9680, 9685, 9690, 9701, 9711, 9715, 9728, 9733, 9743, 9745, 9757, 9763, 9773, 9783, 9790, 9798, 9806, 9809, 9818, 9831, 9835, 9846, 9850, 9858, 9867, 9877, 9883, 9896, 9904, 9910, 9916, 9926, 9934, 9941, 9947, 9953, 9968, 9976, 9982, 9985, 9998, 10007, 10009, 10024, 10030, 10034, 10047, 10054, 10064, 10068, 10074, 10084, 10092, 10104, 10105, 10114, 10123, 10135, 10141, 10145, 10155, 10164, 10175, 10184, 10190, 10199, 10204, 10214, 10221, 10227, 10238, 10246, 10249, 10258, 10271, 10276, 10282, 10294, 10302, 10309, 10317, 10323, 10331, 10343, 10347, 10358, 10368, 10372, 10383, 10389, 10395, 10405, 10416, 10422, 10427, 10435, 10443, 10449, 10463, 10470, 10474, 10482, 10492, 10504, 10507, 10517, 10528, 10536, 10539, 10551, 10556, 10564, 10575, 10584, 10588, 10597, 10605, 10610, 10623, 10632, 10639, 10644, 10654, 10661, 10671, 10678, 10687, 10695, 10698, 10706, 10719, 10727, 10733, 10738, 10750, 10760, 10764, 10776, 10782, 10785, 10800, 10801, 10815, 10824, 10826, 10839, 10847, 10853, 10862, 10871, 10876, 10882, 10891, 10902, 10908, 10919, 10928, 10936, 10942, 10945, 10957, 10966, 10973, 10981, 10992, 10994, 11006, 11013, 11022, 11025, 11036, 11041, 11056, 11062, 11071, 11074, 11085, 11091, 11098, 11107, 11118, 11126, 11130, 11140, 11151, 11160, 11167, 11169, 11179, 11191, 11200, 11202, 11210, 11219, 11228, 11236, 11246, 11249, 11257, 11270, 11275, 11287, 11292, 11302, 11312, 11314, 11324, 11334, 11339, 11346, 11354, 11368, 11369, 11382, 11391, 11395, 11401, 11413, 11419, 11431, 11438, 11447, 11450, 11463, 11468, 11475, 11488, 11492, 11502, 11505, 11516, 11521, 11530, 11542, 11545, 11559, 11565, 11572, 11582, 11591, 11595, 11607, 11611, 11621, 11631, 11634, 11647, 11652, 11663, 11665, 11680, 11681, 11693, 11697, 11707, 11715, 11728, 11734, 11737, 11751, 11760, 11763, 11772, 11780, 11787, 11795, 11802, 11815, 11821, 11827, 11838, 11841, 11849, 11862, 11866, 11873, 11885, 11895, 11898, 11911, 11914, 11925, 11929, 11942, 11948, 11960, 11964, 11970, 11984, 11986, 11993, 12004, 12016, 12024, 12027, 12035, 12042, 12051, 12059, 12069, 12073, 12083, 12095, 12100, 12106, 12119, 12125, 12136, 12139, 12149, 12154, 12164, 12174, 12181, 12189, 12197, 12201, 12210, 12220, 12231, 12233, 12244, 12250, 12262, 12272, 12277, 12284, 12292, 12300, 12305, 12313, 12325, 12334, 12339, 12351, 12354, 12362, 12372, 12380, 12390, 12396, 12402, 12410, 12420, 12432, 12436, 12442, 12456, 12460, 12466, 12476, 12484, 12492, 12501, 12506, 12513, 12521, 12535, 12542, 12552, 12559, 12566, 12571, 12577, 12586, 12593, 12602, 12610, 12621, 12627, 12637, 12644, 12654, 12660, 12670, 12674, 12683, 12692, 12704, 12711, 12720, 12726, 12729, 12744, 12745, 12756, 12761, 12772, 12781, 12786, 12795, 12802, 12810, 12822, 12831, 12836, 12848, 12855, 12864, 12869, 12879, 12887, 12896, 12898, 12906, 12916, 12925, 12930, 12944, 12952, 12957, 12968, 12975, 12983, 12985, 12994, 13006, 13012, 13022, 13026, 13038, 13041, 13050, 13062, 13066, 13079, 13081, 13091, 13098, 13108, 13114, 13125, 13135, 13140, 13149, 13158, 13166, 13175, 13180, 13190, 13200, 13205, 13210, 13219, 13232, 13240, 13247, 13256, 13258, 13268, 13279, 13285, 13289, 13303, 13311, 13313, 13325, 13336, 13344, 13348, 13357, 13364, 13374, 13380, 13392, 13399, 13402, 13411, 13421, 13430, 13437, 13442, 13454, 13463, 13466, 13474, 13484, 13496, 13502, 13509, 13519, 13523, 13536, 13537, 13546, 13558, 13563, 13575, 13579, 13586, 13594, 13603, 13610, 13623, 13632, 13637, 13648, 13652, 13660, 13671, 13678, 13685, 13694, 13703, 13709, 13717, 13724, 13731, 13740, 13747, 13757, 13767, 13776, 13779, 13789, 13795, 13803, 13813, 13821, 13828, 13836, 13841, 13854, 13861, 13865, 13878, 13883, 13890, 13901, 13906, 13920, 13923, 13933, 13937, 13949, 13954, 13966, 13970, 13979, 13987, 14000, 14005, 14014, 14023, 14027, 14034, 14042, 14052, 14057, 14071, 14080, 14086, 14096, 14098, 14109, 14120, 14123, 14136, 14138, 14152, 14160, 14161, 14170, 14177, 14185, 14199, 14202, 14210, 14219, 14232, 14236, 14247, 14254, 14262, 14269, 14280, 14286, 14293, 14301, 14311, 14318, 14327, 14330, 14329]Reduce the balanced fully crossed design to the original experimental design:
fake_kb07_df= fake_kb07_df[idx, :]
rename!(fake_kb07_df, :dv => :rt_trunc)Now we can use the simulated data in the same way as above.
Set contrasts:
contrasts = Dict(:spkr => HelmertCoding(),
                 :prec => HelmertCoding(),
                 :load => HelmertCoding(),
                 :item => Grouping(),
                 :subj => Grouping());Dict{Symbol, StatsModels.AbstractContrasts} with 5 entries:
  :item => Grouping()
  :spkr => HelmertCoding(nothing, nothing)
  :load => HelmertCoding(nothing, nothing)
  :prec => HelmertCoding(nothing, nothing)
  :subj => Grouping()Define formula, same as above:
kb07_f = @formula(rt_trunc ~ 1 + spkr + prec + load + (1|subj) + (1+prec|item));FormulaTerm
Response:
  rt_trunc(unknown)
Predictors:
  1
  spkr(unknown)
  prec(unknown)
  load(unknown)
  (subj)->1 | subj
  (prec,item)->(1 + prec) | itemFit the model:
fake_kb07_m = fit(MixedModel, kb07_f, fake_kb07_df; contrasts=contrasts)Linear mixed model fit by maximum likelihood
 rt_trunc ~ 1 + spkr + prec + load + (1 | subj) + (1 + prec | item)
   logLik   -2 logLik     AIC       AICc        BIC    
 -2582.4767  5164.9534  5182.9534  5183.0544  5232.3731
Variance components:
             Column      Variance  Std.Dev.   Corr.
item     (Intercept)     0.0066031 0.0812592
         prec: maintain  0.0002603 0.0161324 -1.00
subj     (Intercept)     0.0000000 0.0000000
Residual                 1.0394972 1.0195574
 Number of obs: 1792; levels of grouping factors: 32, 56
  Fixed-effects parameters:
────────────────────────────────────────────────────────
                      Coef.  Std. Error      z  Pr(>|z|)
────────────────────────────────────────────────────────
(Intercept)     -0.0204677    0.0280603  -0.73    0.4657
spkr: old       -3.63082e-5   0.0242115  -0.00    0.9988
prec: maintain  -0.0114596    0.024392   -0.47    0.6385
load: yes       -0.0142565    0.0241543  -0.59    0.5550
────────────────────────────────────────────────────────Set random seed for reproducibility:
rng = StableRNG(42);StableRNGs.LehmerRNG(state=0x00000000000000000000000000000055)Then, again, we specify β, σ, and θ. Here we use the values that we found in the model of the existing dataset:
#beta
new_beta = [2181.85, 67.879, -333.791, 78.5904] #manual
new_beta = kb07_m.β #grab from existing model
#sigma
new_sigma = 680.032 #manual
new_sigma = kb07_m.σ #grab from existing model
#theta
re_item_corr = [1.0 -0.7; -0.7 1.0]
re_item = create_re(0.536, 0.371; corrmat = re_item_corr)
re_subj = create_re(0.438)
new_theta = createθ(kb07_m; item=re_item, subj=re_subj)4-element Vector{Float64}:
  0.536
 -0.2597
  0.26494699469893973
  0.438Run nsims iterations:
fake_kb07_sim = parametricbootstrap(rng, nsims, fake_kb07_m,
                        β = new_beta,
                        σ = new_sigma,
                        θ = new_theta,
                        use_threads = false);MixedModels.MixedModelBootstrap{Float64}(NamedTuple{(:objective, :σ, :β, :se, :θ), Tuple{Float64, Float64, NamedTuple{(Symbol("(Intercept)"), Symbol("spkr: old"), Symbol("prec: maintain"), Symbol("load: yes")), NTuple{4, Float64}}, StaticArrays.SVector{4, Float64}, StaticArrays.SVector{4, Float64}}}[(objective = 28669.259186094198, σ = 671.5885157562202, β = ((Intercept) = 2116.4233659350675, spkr: old = 90.29598722692766, prec: maintain = 402.53317309804953, load: yes = 91.06728725080163), se = [85.97890860378737, 16.36949534092283, 52.00052394507954, 16.273661210698602], θ = [0.6339049268491873, -0.3441036796910489, 0.23400465372719198, 0.4275482430927668]), (objective = 28748.17755452174, σ = 692.1085846372117, β = ((Intercept) = 2128.0748840761507, spkr: old = 76.22813011637506, prec: maintain = 392.9390606534017, load: yes = 85.23151676971408), se = [82.05340184239644, 16.84602524776764, 49.42808539311057, 16.74587163797111], θ = [0.6194826793226844, -0.2627885238616967, 0.27479168805808596, 0.28925357363485]), (objective = 28713.689607804725, σ = 679.6001565572238, β = ((Intercept) = 2263.0989812961852, spkr: old = 72.00119770832265, prec: maintain = 300.5134587592506, load: yes = 91.43156161561612), se = [80.39183989557073, 16.570714186481904, 40.03489823618301, 16.47913820929552], θ = [0.5402665771294228, -0.19636765484516752, 0.2319137457418749, 0.4909121211319967]), (objective = 28790.93146183371, σ = 701.5602652561488, β = ((Intercept) = 2081.587051735704, spkr: old = 101.08271117881472, prec: maintain = 346.3725103396874, load: yes = 92.07032673906949), se = [65.12306869773991, 17.083870375782855, 45.09958662673437, 16.981638777991407], θ = [0.40609982082094287, -0.2589259445203882, 0.21576624325081947, 0.4026409930104535]), (objective = 28732.966854365135, σ = 689.3642188469038, β = ((Intercept) = 2191.715607939053, spkr: old = 83.61684273505894, prec: maintain = 314.76465072394205, load: yes = 73.44844219925342), se = [71.93577201985023, 16.78341363174965, 45.006633152301625, 16.686443979605755], θ = [0.49459505758971034, -0.27594724421322003, 0.2039526352943622, 0.38717686054970224]), (objective = 28702.004760986354, σ = 680.29129935911, β = ((Intercept) = 2236.505637074698, spkr: old = 80.93974966358974, prec: maintain = 305.3260519191703, load: yes = 78.0628071374858), se = [66.76292656852691, 16.584980529388954, 39.85147475048624, 16.488649316188905], θ = [0.3731382115805396, -0.18798498792637236, 0.23620923366666993, 0.513676963000073]), (objective = 28691.936077851096, σ = 676.7499957982127, β = ((Intercept) = 2187.4165827095794, spkr: old = 72.66509254982736, prec: maintain = 316.18403893243465, load: yes = 97.2112828070939), se = [78.59212443940265, 16.500294813313364, 52.02954474240744, 16.400923236791524], θ = [0.5679570429802484, -0.30256720166933043, 0.28092161804605326, 0.398589526328698]), (objective = 28676.247068835906, σ = 674.2037691824543, β = ((Intercept) = 2268.56457988348, spkr: old = 52.57223383133322, prec: maintain = 314.73056490521867, load: yes = 68.56523725534491), se = [70.73068360788167, 16.44263907892879, 51.70105031157452, 16.34185380401494], θ = [0.4909499185797137, -0.267742796453443, 0.31274870683496825, 0.4032951531442249]), (objective = 28725.862416929893, σ = 681.1355470243724, β = ((Intercept) = 2172.6039321863773, spkr: old = 51.969593815286345, prec: maintain = 273.3898633902248, load: yes = 102.54853312574605), se = [77.23798128161653, 16.619413412793392, 49.43804124839382, 16.519972089594734], θ = [0.5297347676964624, -0.22872971658986746, 0.312349861218784, 0.44397275686632354]), (objective = 28707.118567623747, σ = 679.2393291054317, β = ((Intercept) = 2180.211365716273, spkr: old = 59.533396260118714, prec: maintain = 321.3181651091462, load: yes = 74.37792471300412), se = [80.08885373622033, 16.556605419176513, 37.19883024595, 16.467883882307778], θ = [0.5427518854653216, -0.17150016200680412, 0.21876356947869724, 0.48086603736600086])  …  (objective = 28754.19312763013, σ = 688.3845552245672, β = ((Intercept) = 2213.5324730562143, spkr: old = 74.16762071004251, prec: maintain = 303.24507230778863, load: yes = 101.54275174856079), se = [78.59099066413518, 16.784563189847532, 49.75344911876351, 16.68502138688372], θ = [0.5382813651702011, -0.2869376244471737, 0.25718138647790134, 0.4370441161651395]), (objective = 28765.106784469623, σ = 693.6098893109744, β = ((Intercept) = 2227.907360784621, spkr: old = 78.65653616506137, prec: maintain = 248.57776193597743, load: yes = 93.5208192327574), se = [80.04953096666142, 16.891936004090258, 49.6443258277401, 16.79366948960645], θ = [0.5683356224584679, -0.3147925223406032, 0.21488100311465733, 0.385779262101789]), (objective = 28701.755167472395, σ = 681.6824101379696, β = ((Intercept) = 2143.963265646107, spkr: old = 76.38685336145892, prec: maintain = 318.9073418044376, load: yes = 52.26069956232986), se = [66.0454333173166, 16.616367943982226, 43.28393973355664, 16.51834612136182], θ = [0.43039896674070766, -0.19661516828362513, 0.2677366472817831, 0.411866381034523]), (objective = 28555.50493858117, σ = 652.4341717361215, β = ((Intercept) = 2098.809036392673, spkr: old = 56.261963909814355, prec: maintain = 380.22052419473454, load: yes = 72.13223384996594), se = [70.92737131834025, 15.905425398695211, 42.750818175605325, 15.812840977322743], θ = [0.502962010532743, -0.23528809746976065, 0.2516889697627381, 0.43276154268721717]), (objective = 28592.39930424976, σ = 661.1915067744728, β = ((Intercept) = 2236.6420583872655, spkr: old = 65.5753811811622, prec: maintain = 350.1347274657253, load: yes = 71.90494961799577), se = [67.26353250709771, 16.101567442336805, 38.12294302614731, 16.01077228081067], θ = [0.4126817796497823, -0.23540448758507126, 0.17973134347256597, 0.4997579977243629]), (objective = 28737.867792307185, σ = 686.3562333865018, β = ((Intercept) = 2165.5845328464457, spkr: old = 68.19907944050384, prec: maintain = 330.3213180234784, load: yes = 99.92516620926254), se = [79.73699718355105, 16.729903020826175, 50.01840328333006, 16.63068709758695], θ = [0.5718286188484873, -0.28370651157381, 0.2660412839448008, 0.38951090239224445]), (objective = 28826.598972463602, σ = 701.3668191045812, β = ((Intercept) = 2087.7688645897533, spkr: old = 73.81094865523943, prec: maintain = 354.39559659135676, load: yes = 91.9505378941776), se = [78.94784372699141, 17.10787032527432, 46.62882283376117, 17.00802731264435], θ = [0.5107714554592957, -0.22453127557418068, 0.2689337293324921, 0.47024606881039893]), (objective = 28761.512289196024, σ = 694.4292806263832, β = ((Intercept) = 2251.109619532989, spkr: old = 57.76798295145353, prec: maintain = 295.88613962699213, load: yes = 69.34637819024407), se = [84.59025027512753, 16.887552347927432, 55.819121192334066, 16.78959526838661], θ = [0.6126844811349216, -0.40477680201463095, 0.15583661040976818, 0.3772228908123967]), (objective = 28668.492995796652, σ = 671.199925337552, β = ((Intercept) = 2121.2788037715577, spkr: old = 32.47224072787255, prec: maintain = 368.95936532235055, load: yes = 41.63501182461447), se = [69.70038228921253, 16.376482776818385, 46.15609442309597, 16.278202837553547], θ = [0.4333844756724288, -0.21744096704523086, 0.2921450074506603, 0.4932756703783597]), (objective = 28684.776841222083, σ = 678.2946093351993, β = ((Intercept) = 2127.075198808972, spkr: old = 66.77914928808589, prec: maintain = 359.9951032872033, load: yes = 53.36276031826636), se = [69.82882055611474, 16.530601112841403, 50.040511934340905, 16.429853966250047], θ = [0.5002008940654212, -0.25287606559163683, 0.3025787362417235, 0.3518053212374168])], LinearAlgebra.LowerTriangular{Float64, Matrix{Float64}}[[0.07970047213483207 0.0; -0.01582297975679605 0.0], [0.0]], [[1, 2, 4], [1]], [0.0, -Inf, 0.0, 0.0], (item = ("(Intercept)", "prec: maintain"), subj = ("(Intercept)",)))Power calculation
power_table(fake_kb07_sim)4-element Vector{NamedTuple{(:coefname, :power), Tuple{String, Float64}}}:
 (coefname = "(Intercept)", power = 1.0)
 (coefname = "spkr: old", power = 0.99)
 (coefname = "prec: maintain", power = 1.0)
 (coefname = "load: yes", power = 1.0)Compare to the powertable from the existing data:
power_table(kb07_sim)4-element Vector{NamedTuple{(:coefname, :power), Tuple{String, Float64}}}:
 (coefname = "prec: break", power = 1.0)
 (coefname = "(Intercept)", power = 1.0)
 (coefname = "spkr: old", power = 0.99)
 (coefname = "load: yes", power = 1.0)We have successfully recreated the power simulation of an existing dataset from scratch. This has the advantage, that we now can iterate over different numbers of subjects and items.
Loop over subject and item sizes
When designing a study, you may be interested in trying various numbers of subjects and items to see how that affects the power of your study. To do this you can use a loop to run simulations over a range of values for any parameter.
We first define every fixed things outside the loop (same as above):
Define factors in a dict:
subj_btwn = nothing
item_btwn = nothing
both_win = Dict(:spkr => ["old", "new"],
                :prec => ["maintain", "break"],
                :load => ["yes", "no"]);Dict{Symbol, Vector{String}} with 3 entries:
  :spkr => ["old", "new"]
  :load => ["yes", "no"]
  :prec => ["maintain", "break"]Set contrasts:
contrasts = Dict(:spkr => HelmertCoding(),
                 :prec => HelmertCoding(base="maintain"),
                 :load => HelmertCoding());Dict{Symbol, StatsModels.HelmertCoding} with 3 entries:
  :spkr => HelmertCoding(nothing, nothing)
  :load => HelmertCoding(nothing, nothing)
  :prec => HelmertCoding("maintain", nothing)Set random seed for reproducibility:
rng = StableRNG(42);StableRNGs.LehmerRNG(state=0x00000000000000000000000000000055)Define formula:
kb07_f = @formula(rt_trunc ~ 1 + spkr + prec + load + (1 | subj) + (1 + prec | item));FormulaTerm
Response:
  rt_trunc(unknown)
Predictors:
  1
  spkr(unknown)
  prec(unknown)
  load(unknown)
  (subj)->1 | subj
  (prec,item)->(1 + prec) | itema Specify β, σ, and θ:
#beta
new_beta = [2181.85, 67.879, -333.791, 78.5904]
new_beta = kb07_m.β
#sigma
new_sigma = 680.032
new_sigma = kb07_m.σ
#theta
re_item_corr = [1.0 -0.7; -0.7 1.0]
re_item = create_re(0.536, 0.371; corrmat = re_item_corr)
re_subj = create_re(0.438)
# because the ordering in theta is dependent on the design,
# we will create it within the loop as we change the design parameters
# (e.g. n subjects and n items)1×1 LinearAlgebra.LowerTriangular{Float64, LinearAlgebra.Diagonal{Float64, Vector{Float64}}}:
 0.438Then we define the variables that out loop will iterate over
Define subject and item numbers as arrays:
sub_ns = [20, 30, 40];
item_ns = [16, 24, 32];3-element Vector{Int64}:
 16
 24
 32Make an empty dataframe:
d = DataFrame();Run the loop:
@showprogress for subj_n in sub_ns, item_n in item_ns
    # Make balanced fully crossed data:
    fake = simdat_crossed(subj_n, item_n;
                          subj_btwn = subj_btwn,
                          item_btwn = item_btwn,
                          both_win = both_win);
    fake_df = DataFrame(fake)
    # Reduce the balanced fully crossed design to the original experimental design:
    fake_df = sort(fake_df, [:subj, :item, :load, :prec, :spkr])
    local len = convert(Int64,(length(fake)/8))
    local idx = rand(rng, collect(1:8) , len)
    local A = repeat([8], inner=len-1)
    push!(A, 0)
    A = cumsum(A)
    idx = idx + A
    fake_df = fake_df[idx, :]
    rename!(fake_df, :dv => :rt_trunc)
    # create the model:
    fake_m = LinearMixedModel(kb07_f, fake_df, contrasts=contrasts);
    local new_theta = createθ(fake_m; item=re_item, subj=re_subj)
    # Run nsims iterations:
    fake_sim = parametricbootstrap(rng, nsims, fake_m,
                                   β = new_beta,
                                   σ = new_sigma,
                                   θ = new_theta,
                                   use_threads = false,
                                   hide_progress=true);
    # Power calculation
    local ptbl = power_table(fake_sim)
    ptdf = DataFrame(ptbl)
    ptdf[!, :item_n] .= item_n
    ptdf[!, :subj_n] .= subj_n
    append!(d, ptdf)
end
Progress:  67%|███████████████████████████▍             |  ETA: 0:00:03
Progress: 100%|█████████████████████████████████████████| Time: 0:00:08Our dataframe d now contains the power information for each combination of subjects and items.
first(d, 10)10 rows × 4 columns
| coefname | power | item_n | subj_n | |
|---|---|---|---|---|
| String | Float64 | Int64 | Int64 | |
| 1 | prec: break | 0.99 | 16 | 20 | 
| 2 | (Intercept) | 1.0 | 16 | 20 | 
| 3 | spkr: old | 0.43 | 16 | 20 | 
| 4 | load: yes | 0.49 | 16 | 20 | 
| 5 | prec: break | 1.0 | 24 | 20 | 
| 6 | (Intercept) | 1.0 | 24 | 20 | 
| 7 | spkr: old | 0.59 | 24 | 20 | 
| 8 | load: yes | 0.63 | 24 | 20 | 
| 9 | prec: break | 1.0 | 32 | 20 | 
| 10 | (Intercept) | 1.0 | 32 | 20 | 
Lastly we plot our results:
function subplot_power(f::Figure, dat, coefname)
    coefpow = @subset(dat, :coefname == coefname)
    ax = Axis(f;
              xlabel="n subjects",
              ylabel="n items",
              #aspect=AxisAspect(1),
              title="$(coefname)")
    heatmap!(ax, coefpow.subj_n, coefpow.item_n, coefpow.power; colorrange=[0,1])
    return ax
end
fig = Figure(; resolution=(1300,300))
for (idx, cn) in enumerate(sort(unique(d.coefname)))
    fig[1, idx] = subplot_power(fig, d, cn)
end
Colorbar(fig[1, end+1]; label="power", vertical=true, colorrange=[0,1], colormap=:viridis)
figAcknowledgements
The text here is based on a tutorial presented at a ZiF workshop by Lisa DeBruine (Feb. 2020) and presented again by Phillip Alday during the SMLP Summer School (Sep. 2020).
Updated and extended by Lisa Schwetlick & Daniel Backhaus, with the kind help of Phillip Alday, after changes to the package.