Power Analysis
set.seed(123)
N=40
n.trials = 20
n.sim = 10*1000
ps <- future_replicate(n.sim, {
# subjects are assumed to be ~ U(0, 0.5) in a-only speech-in-noise perception
probs = runif(N, min = 0.0, max=0.50)
a_VoiceLeading = rbinom(N, size=n.trials, prob=probs)
a_MouthLeading = rbinom(N, size=n.trials, prob=probs)
# From Rennig et al., the AV benefit is ~ 30%
av_VoiceLeading_probs = probs + 0.2
av_MouthLeading_probs = probs + 0.3
av_VoiceLeading = rbinom(N, size=n.trials, prob=av_VoiceLeading_probs)
av_MouthLeading = rbinom(N, size=n.trials, prob=av_MouthLeading_probs)
# stack all the correct trials together
.correct <- c(a_VoiceLeading,a_MouthLeading, av_VoiceLeading, av_MouthLeading)
# times=4 because there are 4 total conditions
.data <- data.frame(id = rep(1:N, times=4),
lead = rep(c('VoiceLead', 'MouthLead', 'VoiceLead', 'MouthLead'), each=N),
condition=rep(c('A', 'AV'), each=2*N),
correct=.correct,
incorrect=n.trials-.correct)
# run the glmm, get the coef summary, extract the p-value for the interaction term
coef(summary(
glmer(cbind(correct, incorrect) ~ lead*condition + (1|id),
data=.data, family=binomial)
))['leadVoiceLead:conditionAV','Pr(>|z|)']
})
# proportion of simulations with p < 0.05
mean(ps < 0.05)
## [1] 0.7962
Analyze behavioral data
Find subjects that met the minimum accuracy cutoff
# Read in data
subjs <- read.csv('spin_behavior_KarasEtAl.csv')
# Get accuracy in clear AV (the easiest condition) to use as a cutoff
subjs %>% subset((.)$noise == 'N' & (.)$type == 'AV') %>%
do_aggregate(is_correct ~ id, mean) ->
by_subj_corr
accuracy_cutoff = 0.75
keepers <- by_subj_corr %>% subset((.)$is_correct >= accuracy_cutoff, select='id')
good_subjs <- subjs %>% subset((.)$id %in% keepers$id)
Subjects passing 0.75 accuracy criterion: 40 of 46
Accuracy measures for clear and noisy words
# Get the overall accuracy for clear words from the accurate bunch
good_subjs %>% do_aggregate(is_correct ~ noise, mean)
## noise is_correct
## 1 N 0.94875
## 2 Y 0.15375
# first make response_type a factor so that tabulating w/n a subject includes zero tally options
good_subjs$response_type %<>% factor(levels=c('Correct', 'Compatible', 'Incompatible'))
# For the noisy words, we expand the definition of correct to include first-viseme-compatible responses
# counts are needed for the GLMM, note that we are NOT collapsing across word
by_subject_noisy_counts <- good_subjs %>% subset((.)$noise == 'Y') %>%
do_aggregate(response_type ~ type + lead + id + word, table)
by_subject_noisy_counts$correct_or_compatible <- rowSums(by_subject_noisy_counts$response_type[,c('Correct', 'Compatible')])
by_subject_noisy_counts$incompatible <- by_subject_noisy_counts$response_type[,'Incompatible']
# Here we get the proportions for later graphs and descriptive stats
aggregate_noisy_proportions <- good_subjs %>% subset((.)$noise == 'Y') %>%
do_aggregate(response_type ~ type + lead + id, function(x) 100*table(x)/length(x))
aggregate_noisy_proportions$correct_or_compatible <- rowSums(aggregate_noisy_proportions$response_type[,c('Correct', 'Compatible')])
aggregate_noisy_proportions %<>% do_aggregate(correct_or_compatible ~ type + lead, m_se)
aggregate_noisy_proportions
## type lead correct_or_compatible.m correct_or_compatible.se
## 1 A MouthLead 32.375000 5.188707
## 2 AV MouthLead 59.750000 5.485850
## 3 A VoiceLead 77.375000 3.544531
## 4 AV VoiceLead 81.375000 3.476051
Generalized linear mixed model on noisy words
# GLMM on correct+compatible vs. incompatible
# Note: baseline is A:MouthLead b/c of alphabetical ordering of factor levels
glmer.mod <- glmer(cbind(correct_or_compatible, incompatible) ~ type * lead + (1|id) + (1|word),
family=binomial,
data=by_subject_noisy_counts)
print(summary(glmer.mod))
## Generalized linear mixed model fit by maximum likelihood (Laplace
## Approximation) [glmerMod]
## Family: binomial ( logit )
## Formula: cbind(correct_or_compatible, incompatible) ~ type * lead + (1 |
## id) + (1 | word)
## Data: by_subject_noisy_counts
##
## AIC BIC logLik deviance df.resid
## 2190.1 2212.7 -1089.1 2178.1 314
##
## Scaled residuals:
## Min 1Q Median 3Q Max
## -11.688 -1.580 0.331 1.433 7.227
##
## Random effects:
## Groups Name Variance Std.Dev.
## id (Intercept) 2.139 1.462
## word (Intercept) 1.550 1.245
## Number of obs: 320, groups: id, 40; word, 4
##
## Fixed effects:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -0.8838 0.9150 -0.966 0.334
## typeAV 1.5230 0.1244 12.243 < 2e-16 ***
## leadVoiceLead 3.1564 1.2561 2.513 0.012 *
## typeAV:leadVoiceLead -1.1477 0.1963 -5.845 5.06e-09 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Correlation of Fixed Effects:
## (Intr) typeAV ldVcLd
## typeAV -0.072
## leadVoiceLd -0.682 0.059
## typAV:ldVcL 0.045 -0.628 -0.079
# put coefficients back into odds-ratio space
exp(coef(summary(glmer.mod))[,1])
## (Intercept) typeAV leadVoiceLead
## 0.4132184 4.5858732 23.4853072
## typeAV:leadVoiceLead
## 0.3173716
Follow-up glmms on each word type
by_subject_noisy_counts %>% split((.)$lead) %>% lapply(function(.data) {
cat('\n-------- Output for', as.character(.data$lead[1]), '--------\n\n')
.mod <- glmer(cbind(correct_or_compatible, incompatible) ~ type + (1|id),
family=binomial,
data=.data)
print(summary(.mod))
round(exp(coef(summary(.mod))[,1]), 1)
})
##
## -------- Output for MouthLead --------
##
## Generalized linear mixed model fit by maximum likelihood (Laplace
## Approximation) [glmerMod]
## Family: binomial ( logit )
## Formula: cbind(correct_or_compatible, incompatible) ~ type + (1 | id)
## Data: .data
##
## AIC BIC logLik deviance df.resid
## 1169.4 1178.6 -581.7 1163.4 157
##
## Scaled residuals:
## Min 1Q Median 3Q Max
## -5.2677 -1.3077 -0.3268 1.5136 8.1167
##
## Random effects:
## Groups Name Variance Std.Dev.
## id (Intercept) 5.081 2.254
## Number of obs: 160, groups: id, 40
##
## Fixed effects:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -1.0997 0.3744 -2.937 0.00331 **
## typeAV 1.8120 0.1409 12.856 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Correlation of Fixed Effects:
## (Intr)
## typeAV -0.201
##
## -------- Output for VoiceLead --------
##
## Generalized linear mixed model fit by maximum likelihood (Laplace
## Approximation) [glmerMod]
## Family: binomial ( logit )
## Formula: cbind(correct_or_compatible, incompatible) ~ type + (1 | id)
## Data: .data
##
## AIC BIC logLik deviance df.resid
## 991.1 1000.3 -492.6 985.1 157
##
## Scaled residuals:
## Min 1Q Median 3Q Max
## -5.7094 -1.0023 0.4157 0.8704 3.3272
##
## Random effects:
## Groups Name Variance Std.Dev.
## id (Intercept) 3.47 1.863
## Number of obs: 160, groups: id, 40
##
## Fixed effects:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 2.0241 0.3275 6.180 6.39e-10 ***
## typeAV 0.3267 0.1412 2.314 0.0207 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Correlation of Fixed Effects:
## (Intr)
## typeAV -0.193
## $MouthLead
## (Intercept) typeAV
## 0.3 6.1
##
## $VoiceLead
## (Intercept) typeAV
## 7.6 1.4
Make some graphs
par(mfrow=1:2)
aggregate_noisy_proportions %>% split((.)$lead) %>% lapply(function(.lead) {
xp <- barplot(.lead$correct_or_compatible[,'m'], ylim=c(0,100),
main=.lead$lead[1], ylab='Percent Correct', names.arg = c('A', 'AV'),
col=adjustcolor(.colors, 0.6), las=1, axes=F, border=NA)
draw.axis(2, at=0:2*50)
ebars(xp, .lead$correct_or_compatible, col=.colors, code=0, lwd=2)
})

## $MouthLead
## NULL
##
## $VoiceLead
## NULL