Did you know: goats have horizontally
elongated pupils. In a survey of 214 land species, this shape
went with plant-eating prey animals, with few exceptions, while vertical
slits were more common in ambush predators.
Source: Banks et al. (2015), Science Advances
Today you run a complete GAMM analysis yourselves, in pairs, on a new data set.
___ appears.Rmd file of
these slides, on GitHub. Try first; look only when you are stuckA simulated pupillometry experiment on listening effort. 24 participants heard 12 sentences in quiet and 12 in background noise. Pupil size was recorded from sentence onset, baseline-corrected and averaged into 100 ms bins from 0 to 3,000 ms.
The research question: does noise change the pupil response, and from what point in time?
set.seed(2031)
participants <- tibble(participant = sprintf("P%02d", 1:24),
p_amp = rnorm(24, 0, 40), p_lat = rnorm(24, 0, 150), p_noise = rnorm(24, 0, 30))
trials <- crossing(participants, condition = c("quiet", "noise"), trial = 1:12) |>
mutate(trial_id = paste(participant, condition, trial, sep = "."),
t_amp = rnorm(n(), 0, 40), t0 = rnorm(n(), 0, 15))
pdata <- crossing(trials, time = seq(0, 3000, by = 100)) |>
arrange(trial_id, time) |>
mutate(peak = 180 + p_amp + if_else(condition == "noise", 90 + p_noise, 0) + t_amp - 4 * (trial - 6.5),
latency = 1200 + p_lat + if_else(condition == "noise", 250, 0),
pupil = t0 + peak * (time / latency)^3 * exp(3 * (1 - time / latency))) |>
group_by(trial_id) |>
mutate(pupil = round(pupil + as.numeric(arima.sim(list(ar = 0.8), n = n(), sd = 12)), 1)) |>
ungroup() |>
mutate(across(c(participant, condition, trial_id), factor),
condition = fct_relevel(condition, "quiet")) |>
select(participant, condition, trial, trial_id, time, pupil)Four trials per condition from participant P01. Single trials are noisy and differ in height: the random smooths and the AR1 model will have to deal with both.
pdata. How many rows are there, and how many
trajectories?Before fitting anything, write down what you expect the model to find.
17856 rows and 576 trajectories. In noise, the response tends to be larger and later, with a lot of variation between participants.
Did you know: when a goat lowers its head to graze, its
eyes rotate so that the pupils stay parallel to
the ground. Grazing animals can rotate each eye by 50 degrees or more,
about ten times the range of human eyes.
Source: Banks et al. (2015), Science Advances
pdata$condition_o <- as.ordered(pdata$condition)
contrasts(pdata$condition_o) <- "contr.treatment"
m_gam <- bam(pupil ~ ___ + s(time, k = 20) + s(time, by = ___, k = 20),
data = pdata, method = "ML")
summary(m_gam)k = 20 enough? Check with
k.check()resid_acf <- function(res, traj, max_lag = 8) {
d <- tibble(res, traj)
map(0:max_lag, \(l) d |>
mutate(res_lag = lag(res, l), .by = traj) |>
summarise(acf = cor(res, res_lag, use = "complete.obs")) |>
mutate(lag = l)) |>
list_rbind()
}The same function as in lecture 4. Compute the autocorrelation of the
residuals of m_gam within trajectories, and plot it against
the lag.
Every participant heard both conditions, so the comparison is within participants. Participants can differ in their curves and also in how noise changes their curves.
pdata$part_cond <- interaction(pdata$participant, pdata$condition)
m_rs <- bam(pupil ~ condition_o + s(time, k = 20) + s(time, by = condition_o, k = 20) +
s(time, ___, bs = "fs", m = 1, k = 5),
data = pdata, method = "ML")part_cond and not
participant? Look back at “The random slope equivalent” in
lecture 4part_cond have,
and how many coefficients do the random smooths add?rho <- itsadug::start_value_rho(m_rs)
m_ar <- bam(pupil ~ condition_o + s(time, k = 20) + s(time, by = condition_o, k = 20) +
s(time, part_cond, bs = "fs", m = 1, k = 5),
data = pdata, method = "ML", AR.start = ___, rho = rho)AR.start. Is the data frame sorted the way the AR1 model
needs?residuals(m_gam), residuals(m_rs) and
m_ar$std.rsdYour rho should be close to 0.92. With the AR1 model,
little autocorrelation should remain.
Did you know: goats can tell human facial expressions
apart. Shown photos of the same unfamiliar person looking happy and
angry, 20 goats preferred to approach and explore the happy faces, mainly when these were on the right.
Source: Nawroth et al. (2018), Royal Society Open Science
k.check(m_ar). Does any smooth
need a higher k?m_ar$std.rsd) against the fitted values, and make a QQ
plot. Lecture 4 has the codem_gam? Why?Fit a null model without any term for condition, and compare it with
m_ar using itsadug::compareML().
Before you run it, decide what the null model must keep from
m_ar, and what must go. If the two models differ in more
than the condition terms, the comparison does not test noise alone.
With {tidygam}, plot:
Use exclude_terms for the random smooths, as in lecture
4. Then make the figure ready for a paper, as in lecture 2: axis titles
with units, a caption that says what the ribbons are, colours that
survive greyscale.
Swap paragraphs with another pair. Could they refit your model from your description alone?
The simulation has one more effect built in: the pupil response gets smaller over the 12 trials, as participants tire or get used to the task.
s(trial, k = 5) to
m_ar. Does the model improve?ti(time, trial). Does fatigue
only shrink the curve, or does it change its shape too?These slides
mcanzi.de/ss27_advanced_statistics/
Code and all lectures
github.com/mcanzi/ss27_advanced_statistics