library(dplyr, warn.conflicts=FALSE)
library(purrr)
library(readr)
library(tidyr)
read_exp_data <- function(filename) {
x <- read_csv(filename) |>
mutate(
condition = factor(
condition,
levels = c("n", "cl", "c"),
labels = c("noise", "classical", "child")
),
participant = as.factor(participant)
)
df_all_responses <- x |>
filter(!is.na(trials.thisTrialN)) |>
select(
participant,
condition,
trial = trials.thisTrialN,
cue,
cue_id = trials.thisIndex,
response_order,
response,
response_start = response.start,
response_stop = response.stop
)
df_first_responses <- x |>
filter(
!is.na(trials.thisTrialN),
response_order == 1
) |>
select(
participant,
condition,
trial = trials.thisTrialN,
cue,
cue_id = trials.thisIndex,
response_order,
response,
rt = key_resp_cue.rt,
response_start = response.start,
response_stop = response.stop
)
return(list(first_response = df_first_responses, all_responses = df_all_responses))
} Melodies and Meanings Analysis
Introduction
Cox & Haebig (2023) used a child-oriented word association task (“imagine your responses will be given to a 3-year-old) alongside a standard (i.e., peer-oriented) word association task to estimate the semantic similarity structure among concepts on the MacAuthur-Bates Communicative Development Inventory (CDI). They were interested in modeling early vocabulary growth, and hypothesized that asking adults to adopt a child-oriented context when performing the task would lead to different word associations that would better reflect the semantic associations in a child’s environment. Relative to the peer-oriented associations, child-oriented associations supported more accurate predictions of vocabulary growth. The relationships between cues, which informed the vocabulary growth models, were different between the two sets of association norms, and these relationships are dictated by the associations generated in each condition. These associations were significantly different between tasks: child-oriented associations to the same cue words were more frequently-produced and contextual diverse words (SUBTLEX norms) consisting of fewer letters and phonemes and acquired at earlier ages (AoA; Kuperman norms).
This work is provocative and raises an important question: how are the adult participants generating word associations that are more child-appropriate when told to respond as if engaging with a 3-year-old? Recent connectionist computational cognitive models of controlled semantic cognition suggest that context-appropriate semantic retrieval is accomplished by integrating a concept representation with a context representation, which transforms or warps the concept representation to highlight relevant aspects and suppress irrelevant aspects (Giallanza et al, 2024; Hoffman, Lambon Ralph, & McClelland, 2018; Jackson, Rogers, & Lambon Ralph, 2021). If a context representation can be maintained in an active state while other conceptual knowledge is activated, this knowledge will be warped by the active context without much cognitive effort. Thus, contemporary models of controlled semantic cognition predict that, if participants have learned a relevant context representation from prior interactions with young children, the semantic knowledge that comes to mind while that context representation is active will naturally be more child-appropriate.
A plausible alternative hypothesis that does not adopt the connectionist framework is that participants in the child-oriented condition are explicitly searching their semantic memory for words that they judge to be child appropriate. The first association to come to mind is dictated by their context-independent semantic knowledge, and if this is not deemed to satisfy the task instructions, it is rejected and another associate of the cue is considered. This would continue until either a satisfying association has been found or some time threshold has been crossed.
Prior experiments with contextualized word association tasks have been explicit about the context induction. Participants are told that ideal word associations will be child-appropriate, or short words, or creative, or peer-appropriate, and so may form explicit rules or criteria that would support the iterative memory search. However, some task instructions are more likely to be related to a coherent semantic context the participant has prior experience with. Namely, while participants might behave, speak, and think differently when interacting with a child, rarely do people need to moderate their semantic knowledge based on how many letters are in the words that refer to those concepts. Therefore, a short word context might elicit different behavior than a child-oriented context.
West, Haebig, & Cox (2024) ran this study with a subset of 60 cue words from the CDI and obtained a striking result: participants in the child-oriented condition produced shorter word associations than particpants who were explicitly instructed to produce short words. Additionally, relative to all five psycholinguistic metrics, the child condition differed from the peer and short-word conditions and peer and short-word conditions did not differ from each other. These results are difficult to reconcile if both tasks involve iterative memory search, but would be a natural outcome of participants in the child-oriented condition activating a previously-learned context that warps their concept representations to facilitate contextualized priming effects.
While striking, West, Haebig, & Cox (2024) relied on explicit task instructions to communicate the context. A stronger test of the warping hypothesis within this paradigm would be to activate a context without manipulating the standard word association task instruction, to respond with the first word that comes to mind. In the current study, we attempt to induce contexts with music without disclosing the intended effect of the music to the participants or providing them with any task instructions that would support an explicit strategy. If, for example, listening to children’s music activates a child-oriented context, we expect that word association responses will be more frequent, contextually diverse words with fewer letters and lower AoA.
Methods
Overview of methods should go here …
Process data
Dependencies
Main
Some manual data cleaning was done outside of R. Some participants have None in their response time field, and these should be cleared (or replaced with NA) to have empty cells for missing data. Here, we just load from each CSV file, creating a dataframe of just first responses for RT analysis and a dataframe of all responses for psycholinguisitic analysis of responses.
if (
!file.exists("data-quarto/first-response.rds") ||
!file.exists("data-quarto/all-responses.rds")
) {
# Create list of all CSV files in data directory ----
file_list <- list.files("data", pattern = "*.csv", full.names = TRUE, recursive = TRUE)
# Read each file and parse into first response and all responses data frames ----
d <- map(file_list, read_exp_data)
d_first_response <- map(d, ~ .x$first_response) |> list_rbind()
d_all_responses <- map(d, ~ .x$all_responses) |> list_rbind()
saveRDS(d_first_response, "data-quarto/first-response.rds")
saveRDS(d_all_responses, "data-quarto/all-responses.rds")
}Response Time Analysis
If participants caught on to the context manipulations and assumed that we expected them to do something other than simply report the first word that comes to mind (as they were instructed), they may have adopted some specific rule. We assume that this is impossible in the white noise condition of the experiment, but it might happen in the classical and children’s music conditions. One way to confirm that this is not happening is to look at the response times across conditions. Explicit rules that motivate memory search should result in longer response times on average than simply reporting the first word that comes to mind.
Using a generalized linear mixed effects model, we regressed the response time of each trial on the three-level condition factor (classical, child, noise). The factor was dummy coded with noise as the baseline condition. We modeled the fixed effects of the two contrasts (classical - noise, child - noise) and the random effect of condition within each cue.
Dependencies
library(dplyr)
library(purrr)
library(readr)
library(tidyr)
library(ggplot2)
library(lme4, warn.conflicts=FALSE)Warning: package 'lme4' was built under R version 4.5.2
Loading required package: Matrix
Attaching package: 'Matrix'
The following objects are masked from 'package:tidyr':
expand, pack, unpack
library(lmerTest, warn.conflicts=FALSE)Warning: package 'lmerTest' was built under R version 4.5.2
d_first_response <- readRDS("data/first-response.rds")Descriptive Plots
A summary of response times by condition. Error bars are standard error for a 1-sample t-test against zero, ignoring within-cue dependency across conditions.
d_first_response |>
drop_na() |>
group_by(condition, participant) |>
summarize(rt_mean_pp = mean(rt)) |>
group_by(condition) |>
mutate(
rt_mean_pp = rt_mean_pp * 1000
) |>
summarize(
rt_mean = mean(rt_mean_pp),
rt_sd = sd(rt_mean_pp),
n = n(),
n_pp = n_distinct(participant),
rt_se = rt_sd / sqrt(n_pp)
) |>
mutate(
condition = factor(condition, c("noise", "child", "classical"))
) |>
ggplot(aes(x = condition, y = rt_mean)) +
geom_bar(stat = "identity", position = position_dodge()) +
geom_errorbar(aes(
ymin = rt_mean - rt_se,
ymax = rt_mean + rt_se
)) +
ylab("Response Time (ms)") +
theme_classic(base_size = 16) +
theme(axis.title.x = element_blank())`summarise()` has grouped output by 'condition'. You can override using the
`.groups` argument.
Plot means and confidence intervals for within-cue differences by condition. This might be done better with model predictions + bootstrapped confidence intervals, but it serves here as a reasonable approximation.
d_first_response |>
drop_na() |>
mutate(rt = rt * 1000) |>
group_by(condition, cue) |>
summarize(rt_mean_cue = mean(rt)) |>
pivot_wider(id_cols = cue, names_from = condition, values_from = rt_mean_cue) |>
mutate(
cl_n = classical - noise,
ch_n = child - noise,
ch_cl = child - classical
) |>
summarize(
across(c(cl_n, ch_n, ch_cl), list(m=mean, s=sd, se= ~sd(.x)/sqrt(length(.x))))
) |>
pivot_longer(
cols = everything(),
names_sep = "_",
names_to = c("c1", "c2", "stat")
) |>
unite(contrast, c1, c2) |>
pivot_wider(id_cols = contrast, names_from = stat) |>
mutate(
ci = se * qt(.025, 59, lower.tail = FALSE),
contrast = factor(contrast, c("cl_n", "ch_n", "ch_cl"), c("classic - noise", "child - noise", "child - classic"))
) |>
ggplot(aes(x = contrast, y = m)) +
geom_pointrange(aes(ymin = m-ci, ymax=m+ci)) +
ylab("Response Time (ms)") +
theme_classic(base_size = 16) +
theme(
axis.title.x = element_blank()
)`summarise()` has grouped output by 'condition'. You can override using the
`.groups` argument.
Boxplot of response times by condition.
d_first_response |>
filter(rt > .250) |>
group_by(condition, participant) |>
summarize(rt = mean(rt)) |>
ggplot(aes(x = condition, y = rt)) +
geom_boxplot() +
theme_classic(base_size = 16)`summarise()` has grouped output by 'condition'. You can override using the
`.groups` argument.
Plot response time by participant within condition.
d_first_response |>
filter(rt > .250) |>
group_by(participant) |>
mutate(rt_z = (rt - mean(rt)) / sd(rt)) |>
filter(rt_z < 2.5) |>
ggplot(aes(x = participant, y = rt)) +
geom_boxplot() +
facet_wrap(vars(condition)) +
theme_classic(base_size = 16) +
theme(
axis.text.x = element_blank(),
axis.ticks.x = element_blank()
)Plot response time by cue within condition.
d_first_response |>
filter(rt > .250) |>
group_by(cue) |>
mutate(rt_z = (rt - mean(rt)) / sd(rt)) |>
filter(rt_z < 2.5) |>
ggplot(aes(x = cue, y = rt)) +
geom_boxplot() +
facet_wrap(vars(condition)) +
theme_classic(base_size = 16) +
theme(
axis.text.x = element_blank(),
axis.ticks.x = element_blank()
)Statistical models
Test for effect of condition on response time in a generalized linear mixed effect model. The inverse Gaussian link function addresses heteroskedasticity of residuals without transforming the data.
glm_filtered <- d_first_response |>
filter(rt > .250) |>
group_by(participant) |>
mutate(rt_z = (rt - mean(rt)) / sd(rt)) |>
filter(rt_z < 2.5) |>
ungroup() |>
glmer(
rt ~ condition + (condition | cue),
data = _,
family = inverse.gaussian("identity")
)Warning in checkConv(attr(opt, "derivs"), opt$par, ctrl = control$checkConv, :
Model failed to converge with max|grad| = 0.00807804 (tol = 0.002, component 1)
summary(glm_filtered)Generalized linear mixed model fit by maximum likelihood (Laplace
Approximation) [glmerMod]
Family: inverse.gaussian ( identity )
Formula: rt ~ condition + (condition | cue)
Data: ungroup(filter(mutate(group_by(filter(d_first_response, rt >
0.25), participant), rt_z = (rt - mean(rt))/sd(rt)), rt_z < 2.5))
AIC BIC logLik -2*log(L) df.resid
17670.4 17738.2 -8825.2 17650.4 6496
Scaled residuals:
Min 1Q Median 3Q Max
-1.2664 -0.6406 -0.3059 0.3156 9.5661
Random effects:
Groups Name Variance Std.Dev. Corr
cue (Intercept) 0.021503 0.14664
conditionclassical 0.007212 0.08492 0.47
conditionchild 0.007136 0.08447 0.21 -0.76
Residual 0.247949 0.49794
Number of obs: 6506, groups: cue, 60
Fixed effects:
Estimate Std. Error t value Pr(>|z|)
(Intercept) 1.89103 0.03749 50.446 < 2e-16 ***
conditionclassical 0.19820 0.04190 4.730 2.25e-06 ***
conditionchild 0.07413 0.03857 1.922 0.0546 .
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Correlation of Fixed Effects:
(Intr) cndtncl
cndtnclsscl -0.295
conditnchld -0.390 0.326
optimizer (Nelder_Mead) convergence code: 0 (OK)
Model failed to converge with max|grad| = 0.00807804 (tol = 0.002, component 1)
Visualize random effects
Plotting estimated condition contrasts by cue —-
model.frame(glm_filtered) |>
as_tibble() |>
bind_cols(
fit_c = predict(glm_filtered, re.form = ~ (condition | cue)),
fit_s = predict(glm_filtered, re.form = ~ (1 | cue)),
fit_m = predict(glm_filtered, re.form = NA),
resid = resid(glm_filtered)
) |>
pivot_wider(id_cols = cue, names_from = condition, values_from = fit_c, values_fn = mean) |>
mutate(
fit_c_clch = classical - child, fit_c_nch = noise - child, fit_c_ncl = noise - classical
) |>
pivot_longer(cols = starts_with("fit_c_"), names_to = "contrast", values_to = "diff") |>
ggplot(aes(x = contrast, y = diff, color = cue)) +
geom_point(position = position_jitter(width = .2)) +
theme_classic(base_size = 16)Scatter plots of random effect relationships
Cond_DF2 <- as_tibble(ranef(glm_filtered)) |>
transmute(unit = grp,
term = case_when(term == "(Intercept)" ~ "b0_hat",
term == "conditionclassical" ~ "cl_hat",
term == "conditionchild" ~ "ch_hat"),
value = condval) |>
pivot_wider(id_cols = "unit", names_from = "term", values_from = "value") %>%
mutate(
noise = b0_hat + fixef(glm_filtered)[1],
classical = cl_hat + fixef(glm_filtered)[2],
child = ch_hat + fixef(glm_filtered)[3]
)
ranef_cor_plots <- list(
noise_chlid = Cond_DF2 |>
ggplot(aes(x = noise, y = child)) +
geom_point(aes(col = unit), size = 3) +
geom_density2d(bins = 4, col = "grey", adjust = 3),
noise_classical = Cond_DF2 |>
ggplot(aes(x = noise, y = classical)) +
geom_point(aes(col = unit), size = 3) +
geom_density2d(bins = 4, col = "grey", adjust = 3),
child_classical = Cond_DF2 |>
ggplot(aes(x = child, y = classical)) +
geom_point(aes(col = unit), size = 3) +
geom_density2d(bins = 4, col = "grey", adjust = 3)
) |> map(~ .x + theme_classic(base_size = 16))
walk(ranef_cor_plots, print)