This is a “pro” worksheet, meaning that it assumes familiarity with
the material covered in sections up to and including Going Further
With R. For this worksheet, you’ll need to use the
rminr-data project; details of how to access this are
provided on the preprocessing worksheet.
Once you’ve opened that project, create a file anova5.R
in which to enter your R commands.
Two-factor within-subjects
In More on Bayesian ANOVA we looked at the
simplest possible way of doing this kind of analysis within R. There is
a reasonable case to be made that the approach we took there is too
simple. In particular, it assumes that the true size of the effect is
the same in every participant. In other words, it assumes the absence of
individual differences. As discussed by van den Berg et al. (2022), this
is not only unlikely to be true, where there is more than one
within-subjects factor, it can also lead to substantively different
conclusions to a traditional (NHST) ANOVA. This led them to propose an
alternative analysis that is more similar in concept to the traditional
analysis. In what follows we demonstrate how to do this analysis for the
same data set as used in Exercise 2 of More on
Bayesian ANOVA.
Whether emulating the models of traditional ANOVA is in itself the
best thing to do is debatable, as traditional ANOVA is potentially
problematic in a number of respects other than the use of NHST. However,
this is a discussion that we’ll leave for another time - if you’re
curious, read Rouder
et al. (2022).
Load and preprocess data
The following commands are part of the answer to Exercise 2 of More on Bayesian ANOVA and are not discussed
further here:
# Load packages
library(tidyverse)
library(BayesFactor)
# Load data, select Test phase, select appropriate columns, drop NAs
raw <- read_csv('case-studies/chris-mitchell/priming-faces.csv') %>%
filter(Running == "Test") %>%
select(Subject, Congruency, Load, RT) %>%
drop_na()
# Create subject-level summary
priming <- raw %>%
mutate(Subject = factor(Subject), Congruency = factor(Congruency),
Load = factor(Load)) %>%
group_by(Subject, Congruency, Load) %>% summarise(RT = mean(RT))
Calculate some initial Bayes factors
Enter this code into your script and run it; it may take a minute or
so to produce a result:
# Calculate Bayes Factors
BFo <- generalTestBF(
formula = RT ~ Congruency*Load + Subject + Subject:Congruency + Subject:Load,
data = data.frame(priming),
whichRandom = "Subject",
neverExclude = c("Subject", "Subject:Congruency", "Subj:Load"),
whichModels = "all"
)
# Display Bayes Factors
BFo
Bayes factor analysis
--------------
[1] Congruency + Subject + Congruency:Subject + Load:Subject : 1.883333e+30 ±1.15%
[2] Load + Subject + Congruency:Subject + Load:Subject : 7.871082e+33 ±0.79%
[3] Congruency:Load + Subject + Congruency:Subject + Load:Subject : 2.085742e+30 ±6.18%
[4] Congruency + Load + Subject + Congruency:Subject + Load:Subject : 2.734706e+33 ±1.83%
[5] Congruency + Congruency:Load + Subject + Congruency:Subject + Load:Subject : 6.585271e+29 ±1.56%
[6] Load + Congruency:Load + Subject + Congruency:Subject + Load:Subject : 2.940871e+33 ±2.25%
[7] Congruency + Load + Congruency:Load + Subject + Congruency:Subject + Load:Subject : 1.240869e+33 ±20.75%
[8] Subject + Congruency:Subject + Load:Subject : 5.655589e+30 ±0.38%
Against denominator:
Intercept only
---
Bayes factor type: BFlinearModel, JZS
Explanation of command
Much of the above code is the same as previous worksheets and is not
re-described here. The new components are:
generalTestBF
- Where you previously used the
anovaBF
command, we now use this more powerful command,
which we need in order to do this more complex analysis.
formula = RT ~ Congruency*Load + Subject + Subject:Congruency + Subject:Load
- This specifies what you wish to calculate. It has the same form as the
simpler calculation you did in Exercise 2, but adds two new terms:
Subject:Congruency
and Subject:Load
. The
:
means “interaction”. So, the two new terms we’re adding
here are an interaction of the Subject factor with the Congruency
condition, and an interaction of the Subject factor with the Load
condition. These interactions allow for the possibility that (i) the
true size of the effect of congruency differs between individuals, and
(ii) the true size of the effect of Load differs between
individuals.
neverExclude = c("Subject", "Subject:Congruency", "Subj:Load"), whichModels = "all"
- There is nothing conceptually deep about these two lines of code, they
just tell the generalTestBF
command to generate the set of
Bayes Factors we need in the next step. When using this code with your
own data, all you need to do is ensure that the
neverExclude
terms include the Subject factor, and the
interaction of that factor with each of your two within-subject factors
(in this case Congruency and Load)
Explanation of output
Although longer, the table is much the same as the one we saw in
Exercise 2. Each row is a Bayes Factor, and in each case that Bayes
Factor is calculated against a null model (Intercept only
-
the model that RT is unaffected by any of the factors, including the
participants). So, for example,
[8] Subject + Congruency:Subject + Load:Subject
has a large
BF in favour of the alternative model that RT is affected by individual
differences in overall RT and the size of the Congruency and Load
effects.
Calculate further Bayes Factors
In this (and many other) experiments, our focus is on the overall
effect of our experimental manipulation (Congruency, Load) on RT, rather
than on individual differences. We can get Bayes Factors for these
effects by using the subject model (model 8 above), rather than
the intercept, as the null model.
We do this in much the same way as we calculated the Bayes Factor for
the interaction in Exercise 2, i.e. by the /
operator. What
we want to do is apply that operator for each of the models, which we
can do with the following command:
# Recalculate Bayes Factors, taking subject model as denominator model
BF <- BFo[-length(BFo)] / BFo[length(BFo)]
# Display results
BF
Bayes factor analysis
--------------
[1] Congruency + Subject + Congruency:Subject + Load:Subject : 0.3330038 ±1.21%
[2] Load + Subject + Congruency:Subject + Load:Subject : 1391.735 ±0.87%
[3] Congruency:Load + Subject + Congruency:Subject + Load:Subject : 0.368793 ±6.19%
[4] Congruency + Load + Subject + Congruency:Subject + Load:Subject : 483.5404 ±1.87%
[5] Congruency + Congruency:Load + Subject + Congruency:Subject + Load:Subject : 0.1164383 ±1.61%
[6] Load + Congruency:Load + Subject + Congruency:Subject + Load:Subject : 519.9937 ±2.29%
[7] Congruency + Load + Congruency:Load + Subject + Congruency:Subject + Load:Subject : 219.4058 ±20.76%
Against denominator:
RT ~ Subject + Congruency:Subject + Load:Subject
---
Bayes factor type: BFlinearModel, JZS
Explanation of output
The effect of this command is to give us a Bayes Factor for each of
the other models, relative to the subject model. It achieves
this in the following way:
Explanation of command
length(BFo)
- This gives the number of rows in the table
of results (8 in this case)
BFo[length(BFo)]
- This returns the last row of the
table
BFo[-length(BFo)]
- The -
operator in this
case means “remove”, so this returns all the rows apart from the last
one.
BFo[-length(BFo)] / BFo[length(BFo)]
- Putting the two
previous commands together, this gives us a BF for each of the models
(except the last one), compared to the last one.
Pruning the Bayes Factors
That’s quite a lot of Bayes Factors! Some of these, we’re not going
to use. Specifically, there are Bayes Factors in that list for models
where interactions between the manipulated factors (Congruency, Load)
are assumed without the presence of corresponding main effects of those
manipulated factors. For example, model 3 is
Congruency:Load + Subject + Congruency:Subject + Load:Subject
,
which includes an interaction between Congurency and Load, but not a
main effect of either Congruency or Load. These kinds of models are
ill-defined, which is sometimes considered to be a bad thing.
So, we won’t consider them here. A relatively accessible discussion of
the problems of ill-defined models is provided by Rouder
et al. (2022)
We remove the models that are ill-defined on the manipulated factors
by identifying them manually, and then removing them from the list. In
this case, the ill-defined models are 3, 5 and 6; and we remove them
using the -
operator.
# Remove ill-defined models
BFp <- BF[-c(3, 5, 6)]
# Display remaining set
BFp
Bayes factor analysis
--------------
[1] Congruency + Subject + Congruency:Subject + Load:Subject : 0.3330038 ±1.21%
[2] Load + Subject + Congruency:Subject + Load:Subject : 1391.735 ±0.87%
[3] Congruency + Load + Subject + Congruency:Subject + Load:Subject : 483.5404 ±1.87%
[4] Congruency + Load + Congruency:Load + Subject + Congruency:Subject + Load:Subject : 219.4058 ±20.76%
Against denominator:
RT ~ Subject + Congruency:Subject + Load:Subject
---
Bayes factor type: BFlinearModel, JZS
Main effects
At this point, we have an table of Bayes Factors of the same form as
Exercise 2 of the previous worksheet. Row 1 is the main effect of
Conrguency. Row 2 is the main effect of Load. The Bayes Factors are not
quite the same as in Exercise 2 - we shouldn’t expect them to be, as
this is a different test. But the conclusions are the same. There is
moderate evidence for the null in the case of Congruency, and strong
evidence for an effect of Load.
Interaction
Finally, as in Exercise 2, we assess evidence for the interaction by
comparing two models - the model that contains just the two main effects
(model 3), and the model that also contains the interaction term (model
4)
# Calculate Bayes Factor for interaction
BFp[4] / BFp[3]
Bayes factor analysis
--------------
[1] Congruency + Load + Congruency:Load + Subject + Congruency:Subject + Load:Subject : 0.4537487 ±20.84%
Against denominator:
RT ~ Congruency + Load + Subject + Congruency:Subject + Load:Subject
---
Bayes factor type: BFlinearModel, JZS
Again, the Bayes Factor is not the same as before, because this is a
different test, but the conclusion is similar - there is moderate
evidence for the absence of an interaction.
Robustness of analysis
You might feel a bit uneasy that the results of two analyses - both
called Bayesian ANOVAs - are not exactly the same. You should not.
Analyzing data always involves making assumptions about those data and
the underlying psychological processes that generated them. For example,
here, the assumptions you make about the presence of individual
differences can matter for your conclusions about the presence of group
effects. Making reasonable assumptions is part of being a scientist, and
your conclusions can be affected by the assumptions that you make.
What we saw in this case was that Exercise 2, and the current
analysis, led to largely the same substantive conclusions, despite
starting from different assumptions. When this happens, it increases our
confidence in the conclusions we have drawn - we say the analysis is
robust across a range of assumptions. If your substantive conclusions
depend on your assumptions, and those assumptions are not well founded,
then your data may not be good enough to answer your questions.
This material is distributed under a Creative Commons licence.
CC-BY-SA 4.0.