The contrast sensitivity function (CSF) relates visual sensitivity
(i.e., the reciprocal of minimum contrast needed to detect a pattern) to
the spatial frequency of the visual target (i.e., a sine wave grating).
The CSF is an important clinical tool and also has been used to
characterize visual mechanisms that constrain vision in a variety of
contexts. An experiment was conducted to assess the change in the CSF in
1-3 month old human infants. Contrast sensitivity was measured for sine
wave gratins with spatial frequencies of 1, 2, 3, and 4 cycles per
degree.Twenty infants in three age groups (4, 8, and 12 weeks)
participated in the study and each infant was tested with all four
spatial frequencies. The data are stored in the long- and wide-format
data frames csf.long
and csf.wide
, and in the
matrix csf.mat
.
options(contrasts=c("contr.sum","contr.poly"))
load(url("http://pnb.mcmaster.ca/bennett/psy710/datasets/csf2022.rda") )
summary(csf.long)
## subjID age sf sensitivity
## s1 : 4 w12:80 sf1:60 Min. : 0.2558
## s2 : 4 w4 :80 sf2:60 1st Qu.: 2.9995
## s3 : 4 w8 :80 sf3:60 Median : 6.7183
## s4 : 4 sf4:60 Mean : 11.7792
## s5 : 4 3rd Qu.: 14.5027
## s6 : 4 Max. :112.1023
## (Other):216
sapply(csf.wide,class)
## subjID age sf1 sf2 sf3 sf4
## "factor" "factor" "numeric" "numeric" "numeric" "numeric"
age
and
sf
on sensitivity
. Where necessary, correct
the \(p\) values for deviations from
sphericity. Do all of the \(p\) have to
be adjusted? Why or why not?options(contrasts=c("contr.sum","contr.poly"))
csf.aov.00 <- aov(sensitivity~age*sf+Error(subjID/(sf)),data=csf.long)
summary(csf.aov.00) # assumes sphericity
##
## Error: subjID
## Df Sum Sq Mean Sq F value Pr(>F)
## age 2 6259 3129.4 7.355 0.00144
## Residuals 57 24251 425.5
##
## Error: subjID:sf
## Df Sum Sq Mean Sq F value Pr(>F)
## sf 3 9611 3204 33.125 < 2e-16
## age:sf 6 3843 640 6.622 2.59e-06
## Residuals 171 16538 97
# next anova corrects p values:
library(afex)
csf.aov_car.00 <- aov_car(sensitivity~age*sf+Error(subjID/(sf)),data=csf.long)
summary(csf.aov_car.00)
##
## Univariate Type III Repeated-Measures ANOVA Assuming Sphericity
##
## Sum Sq num Df Error SS den Df F value Pr(>F)
## (Intercept) 33300 1 24251 57 78.2700 2.747e-12
## age 6259 2 24251 57 7.3555 0.00144
## sf 9611 3 16538 171 33.1253 < 2.2e-16
## age:sf 3843 6 16538 171 6.6223 2.594e-06
##
##
## Mauchly Tests for Sphericity
##
## Test statistic p-value
## sf 0.17456 2.0783e-19
## age:sf 0.17456 2.0783e-19
##
##
## Greenhouse-Geisser and Huynh-Feldt Corrections
## for Departure from Sphericity
##
## GG eps Pr(>F[GG])
## sf 0.52055 6.306e-10
## age:sf 0.52055 0.0003606
##
## HF eps Pr(>F[HF])
## sf 0.5325793 4.199534e-10
## age:sf 0.5325793 3.180706e-04
# anova(csf.aov_car.00,correction="HF") # lists anova table with corrected p values
## Sphericity does not apply to age because it is a between-subject variable, but sphericity is relevant to all of the other effects becuase they are within-subject factors that have more than 1 degree of freedom. Note that the Mauchly Test is significant, so the variance-covariance matrix of the residuals deviates significantly from sphericity.
lmer
in the lmerTest
package to
evaluate the effects of age
, and sf
(and
interactions) with a mixed model. Treat subj
as a random
factor and all the other factors as fixed. Evaluate the fixed effects
with \(F\) tests that assume sphericity
and chi-square
tests that do not assume sphericity.library(lmerTest)
csf.lmer.01 <- lmer(sensitivity~age*sf + (1|subjID),data=csf.long)
# F tests:
print(anova(csf.lmer.01)) # assumes sphericity/independence
## Type III Analysis of Variance Table with Satterthwaite's method
## Sum Sq Mean Sq NumDF DenDF F value Pr(>F)
## age 1422.8 711.4 2 57 7.3555 0.00144
## sf 9611.0 3203.7 3 171 33.1253 < 2.2e-16
## age:sf 3842.8 640.5 6 171 6.6223 2.594e-06
# chi-square tests:
library(car)
print(Anova(csf.lmer.01,type="III")) # does not assume sphericity
## Analysis of Deviance Table (Type III Wald chisquare tests)
##
## Response: sensitivity
## Chisq Df Pr(>Chisq)
## (Intercept) 78.270 1 < 2.2e-16
## age 14.711 2 0.0006391
## sf 99.376 3 < 2.2e-16
## age:sf 39.734 6 5.138e-07
lme
in the nlme
package to evaluate
the effects of age
, and sf
(and interactions)
with a mixed model. Treat subj
as a random factor and all
the other factors as fixed. For this model, assume that the
variance-covariance matrix of the residuals is not compound
symmetric. In other words, allow the correlations between levels of the
within-subject factor to vary (i.e., not be a constant value). List the
ANOVA table for the fixed effects.# we use correlation=corSym... allows correlations to vary
library(nlme)
csf.lme.01 <- lme(fixed = sensitivity~age*sf,
data=csf.long,
random = ~1|subjID,
correlation=corSymm(form=~1|subjID))
print(anova(csf.lme.01)) # does not assume compound symmetry
## numDF denDF F-value p-value
## (Intercept) 1 171 69.69831 <.0001
## age 2 57 6.81418 0.0022
## sf 3 171 20.89030 <.0001
## age:sf 6 171 6.51185 <.0001
# the correlations are listed in the output of the summary command:
# summary(csf.lme.01)
# Correlation Structure: General
# Formula: ~1 | subjID
# Parameter estimate(s):
# Correlation:
# 1 2 3
# 2 -0.130
# 3 0.647 -0.524
# 4 0.795 -0.511 0.806
# with emmeans:
library(emmeans)
csf.emm <- emmeans(csf.aov_car.00,specs="age")
contrast(csf.emm,method="pairwise",adjust="tukey")
## contrast estimate SE df t.ratio p.value
## w12 - w4 12.38 3.26 57 3.795 0.0010
## w12 - w8 7.76 3.26 57 2.378 0.0534
## w4 - w8 -4.62 3.26 57 -1.417 0.3391
##
## Results are averaged over the levels of: sf
## P value adjustment: tukey method for comparing a family of 3 estimates
# with wide-format df
print(csf.wide[1,]) # check columns
## subjID age sf1 sf2 sf3 sf4
## 1 s1 w4 18.72468 13.24864 21.88602 8.299064
avg.sens <- rowMeans(csf.wide[,3:6]) # average sensitivity for each subject
ageGroup <- csf.wide$age
age.aov.01 <- aov(avg.sens~ageGroup)
TukeyHSD(age.aov.01)
## Tukey multiple comparisons of means
## 95% family-wise confidence level
##
## Fit: aov(formula = avg.sens ~ ageGroup)
##
## $ageGroup
## diff lwr upr p adj
## w4-w12 -12.377067 -20.225188 -4.52894532 0.0010338
## w8-w12 -7.756493 -15.604614 0.09162851 0.0533853
## w8-w4 4.620574 -3.227547 12.46869506 0.3391456
sort(unique(csf.long$sf)) # get the values on the sf variable
## [1] sf1 sf2 sf3 sf4
## Levels: sf1 sf2 sf3 sf4
quad.weights <- contr.poly(n=4,scores=c(1,2,3,4))[,2] # just need 2nd column
csf.mat[1,] # check column names of matrix
## sf1 sf2 sf3 sf4
## 18.724685 13.248642 21.886018 8.299064
quad.scores <- csf.mat %*% quad.weights
ageGroup <- csf.wide$age
quad.aov.01 <- aov(quad.scores~ageGroup)
summary(quad.aov.01,intercept=TRUE)
## Df Sum Sq Mean Sq F value Pr(>F)
## (Intercept) 1 5458 5458 38.29 7.07e-08
## ageGroup 2 3021 1510 10.59 0.000122
## Residuals 57 8126 143
# grand mean is signficantly different from zero
# and quadratic trend differs across groups
# Tukey test(below) indicates that quadratic trend in 12 week olds differs from
# quadratic trend in the other two groups (but 4 & 8 week olds do not differ
# from each other)
TukeyHSD(quad.aov.01)
## Tukey multiple comparisons of means
## 95% family-wise confidence level
##
## Fit: aov(formula = quad.scores ~ ageGroup)
##
## $ageGroup
## diff lwr upr p adj
## w4-w12 17.250708 8.164760 26.336655 0.0000786
## w8-w12 10.458769 1.372821 19.544716 0.0203371
## w8-w4 -6.791939 -15.877887 2.294008 0.1791234
# These results are reasonably consistent with a graphical depiction of the results...