Twenty-eight postlingually-deafened cochlear implant (CI) users and 26 normal hearing (NH) adults perform a spoken-word recognition task. On each trial, 4 pictures appear on a monitor. The subject hears the name of one of them (e.g., wizard as the target) and clicks on the referent. The competitors, in this case, are: lizard (rhyme), whistle (cohort), baggage (unrelated).
Source: Farris-Trimble, A., McMurray, B., Cigrand, N., & Tomblin, J.B. (2014). The process of spoken word recognition in the face of signal degradation: Cochlear implant users and normal-hearing listeners. Journal of Experimental Psychology: Human Perception and Performance, 40(1), 308-327.
Data: ci{bdots}
# set options
options(digits = 4, show.signif.stars = FALSE)
# load packages for later use
pacman::p_load(bdots, tidyverse, nlme)
# input data
data(ci)
head(ci)
## protocol Subject Time Fixations LookType
## 1 NH 36 0 0 Cohort
## 2 NH 36 4 0 Cohort
## 3 NH 36 8 0 Cohort
## 4 NH 36 12 0 Cohort
## 5 NH 36 16 0 Cohort
## 6 NH 36 20 0 Cohort
str(ci)
## 'data.frame': 108216 obs. of 5 variables:
## $ protocol : Factor w/ 2 levels "CI","NH": 2 2 2 2 2 2 2 2 2 2 ...
## $ Subject : int 36 36 36 36 36 36 36 36 36 36 ...
## $ Time : int 0 4 8 12 16 20 24 28 32 36 ...
## $ Fixations: num 0 0 0 0 0 0 0 0 0 0 ...
## $ LookType : Factor w/ 4 levels "Cohort","Rhyme",..: 1 1 1 1 1 1 1 1 1 1 ...
# set plot themes to black-n-white
ot <- theme_set(theme_bw())
#ci$Subject <- as.factor(ci$Subject)
ggplot(ci, aes(Time, Fixations)) +
geom_point(size=rel(.1), pch = 1, alpha = .2) +
facet_grid(protocol ~ LookType)
不同的Looktype。比較CI與NH兩組的Fixations,pattern都差不多。 比較四種不同LookType,CI組的Rhyme和Target的Fixations變異比NH大。
# select target looking time course data
dta_t <- subset(ci, LookType == "Target") #只擷取LookType其中一個level作為subset
# order data by subject , time
dta_t <- dta_t[order(dta_t$Subject, dta_t$Time, decreasing = F), ]
# have a look
head(dta_t)
## protocol Subject Time Fixations LookType
## 39079 CI 2 0 0 Target
## 39080 CI 2 4 0 Target
## 39081 CI 2 8 0 Target
## 39082 CI 2 12 0 Target
## 39083 CI 2 16 0 Target
## 39084 CI 2 20 0 Target
# convert to groupedData object for nlme processing
dtag <- groupedData(Fixations ~ Time | Subject, outer = ~ protocol,data = as.data.frame(dta_t),
labels = list(x = "Time", y = "Fixations"),
units = list(x = "(ms)", y = "(proportion)") )
# lattice plot by subject
plot(dtag, pch = 20, aspect = .89)
# plot by subject given group = protocol
p <- plot(dtag, outer = TRUE, cex = .1, layout = c(2,1), aspect = .89)
update(p, legend = NULL)
分開畫每一個人的curve來看,大部分的人都滿相似的,有部分如68,64,24的curve不太相同。
把所有人的curve畫一起,並分CI與NH來看,兩組曲線圖變異程度不同,CI組個體間的curve圖看起來比較鬆散,NH比較緊密
##
# define the 3-parameter curve fitting function
fx <- function(x, P, S, C) {
P / (1 + exp(S*(C - x)/P))
}
#
# fit nls separately for all subjects
#
m0 <- nlsList(Fixations ~ fx(Time, P, S, C),
start = c(P = 0.87, S = 0.004, C = 757.8),
data = dtag)
# results of parameter estimates
coef(m0)
## P S C
## 24 0.5571 0.004108 708.9
## 81 0.7501 0.004582 804.5
## 6 0.7480 0.004294 801.2
## 90 0.7795 0.004376 780.4
## 2 0.7770 0.003588 811.9
## 41 0.8007 0.006199 728.2
## 84 0.8190 0.004794 792.4
## 30 0.8259 0.005005 819.4
## 16 0.8409 0.004715 804.8
## 10 0.8438 0.004999 788.8
## 23 0.8913 0.005303 924.2
## 75 0.8929 0.005409 805.3
## 79 0.9074 0.006026 698.8
## 48 0.8881 0.004349 792.9
## 76 0.8908 0.005846 761.5
## 4 0.9082 0.006417 736.7
## 74 0.9020 0.004945 762.7
## 72 0.8887 0.007259 822.3
## 17 0.9086 0.005659 788.8
## 29 0.9401 0.006854 777.9
## 19 0.9224 0.007384 709.1
## 82 0.9483 0.007750 734.1
## 83 0.9332 0.009380 761.1
## 94 0.9353 0.008019 735.2
## 22 0.9474 0.005805 814.3
## 86 0.9674 0.007940 690.5
## 99 0.9681 0.007187 817.4
## 3 0.9716 0.007568 677.7
## 68 0.6759 0.004333 745.9
## 64 0.8652 0.007813 761.6
## 55 0.8759 0.007716 711.8
## 98 0.8861 0.005607 744.8
## 100 0.8969 0.007230 795.8
## 51 0.9269 0.006000 705.6
## 105 0.9254 0.007172 685.0
## 38 0.9375 0.008724 696.7
## 40 0.9477 0.007433 721.8
## 65 0.9450 0.009253 671.1
## 42 0.9622 0.008466 622.0
## 71 0.9539 0.008714 658.7
## 63 0.9633 0.007947 671.7
## 95 0.9625 0.007309 725.4
## 66 0.9669 0.008559 777.7
## 106 0.9630 0.005560 743.0
## 56 0.9674 0.007671 681.7
## 60 0.9673 0.008303 680.0
## 85 0.9713 0.007033 690.3
## 102 0.9636 0.008226 722.6
## 36 0.9795 0.007957 698.8
## 61 0.9684 0.007889 683.7
## 73 0.9773 0.008403 673.0
## 47 0.9812 0.010171 704.2
## 53 0.9811 0.009324 722.4
## 58 0.9768 0.007508 682.5
# covert fitting separately by subject to subjects as random effects
m1 <- nlme(m0)
## Warning in (function (model, data = sys.frame(sys.parent()), fixed, random, :
## Iteration 1, LME step: nlminb() did not converge (code = 1). PORT message: false
## convergence (8)
## Warning in (function (model, data = sys.frame(sys.parent()), fixed, random, :
## Iteration 2, LME step: nlminb() did not converge (code = 1). PORT message: false
## convergence (8)
出現錯誤訊息: Iteration 1, LME step: nlminb() did not converge (code = 1). PORT message: false convergence (8)Iteration 2, LME step: nlminb() did not converge (code = 1). PORT message: false convergence (8)
# extract mean parameters values to use as initial values
prm0 <- round(unlist(lapply(m1$coef$fixed, mean)), digits=3)
prm0
## P S C
## 0.903 0.007 741.271
#
# update to let parameters dependent on protocol
# and simplify the covariance structure to a simple separate
# variances for each one of the parameters
#
m2 <- update(m1,
fixed = list(P + S + C ~ protocol), #let parameters dependent on protocol
random = pdDiag(P + S + C ~ 1),
groups = ~ Subject,
start = list(fixed = c(P = c(prm0[1], 0),
S = c(prm0[2], 0),
C = c(prm0[3], 0))))
summary(m2)
## Nonlinear mixed-effects model fit by maximum likelihood
## Model: Fixations ~ fx(Time, P, S, C)
## Data: dtag
## AIC BIC logLik
## -142249 -142167 71134
##
## Random effects:
## Formula: list(P ~ 1, S ~ 1, C ~ 1)
## Level: Subject
## Structure: Diagonal
## P.(Intercept) S.(Intercept) C.(Intercept) Residual
## StdDev: 0.07686 0.001344 45.51 0.01707
##
## Fixed effects: list(P + S + C ~ protocol)
## Value Std.Error DF t-value p-value
## P.(Intercept) 0.9 0.015 26995 59.87 0.0000
## P.protocolNH 0.1 0.021 26995 3.26 0.0011
## S.(Intercept) 0.0 0.000 26995 23.30 0.0000
## S.protocolNH 0.0 0.000 26995 4.87 0.0000
## C.(Intercept) 773.2 8.606 26995 89.85 0.0000
## C.protocolNH -66.4 12.401 26995 -5.35 0.0000
## Correlation:
## P.(In) P.prNH S.(In) S.prNH C.(In)
## P.protocolNH -0.694
## S.(Intercept) 0.000 0.000
## S.protocolNH 0.000 0.000 -0.694
## C.(Intercept) 0.000 0.000 0.000 0.000
## C.protocolNH 0.000 0.000 0.000 0.000 -0.694
##
## Standardized Within-Group Residuals:
## Min Q1 Med Q3 Max
## -4.2733 -0.7499 -0.1167 0.3908 4.9355
##
## Number of Observations: 27054
## Number of Groups: 54
#
# update model 2 to include auto-regressive dependence over time
# results have convergence issues - takes a few minutes
# set lmeControl options - change optimizer, etc.
#
m3 <- update(m2, corr = corAR1(0.95, form = ~ 1 | Subject),
control = lmeControl(opt = 'optim',
niterEM = 500,
maxIter = 1e6,
msMaxIter = 1e3))
## Warning in nlm(f = function(nlmePars) -logLik(nlmeSt, nlmePars), p =
## c(coef(nlmeSt)), : NA/Inf replaced by maximum positive value
## Warning in nlm(f = function(nlmePars) -logLik(nlmeSt, nlmePars), p =
## c(coef(nlmeSt)), : NA/Inf replaced by maximum positive value
## Warning in nlm(f = function(nlmePars) -logLik(nlmeSt, nlmePars), p =
## c(coef(nlmeSt)), : NA/Inf replaced by maximum positive value
## Warning in nlm(f = function(nlmePars) -logLik(nlmeSt, nlmePars), p =
## c(coef(nlmeSt)), : NA/Inf replaced by maximum positive value
## Warning in nlm(f = function(nlmePars) -logLik(nlmeSt, nlmePars), p =
## c(coef(nlmeSt)), : NA/Inf replaced by maximum positive value
## Warning in (function (model, data = sys.frame(sys.parent()), fixed, random, :
## Iteration 1, LME step: nlm() did not converge (code = 4). Do increase
## 'msMaxIter'!
## Warning in (function (model, data = sys.frame(sys.parent()), fixed, random, :
## Iteration 2, LME step: nlm() did not converge (code = 4). Do increase
## 'msMaxIter'!
## Warning in (function (model, data = sys.frame(sys.parent()), fixed, random, :
## Iteration 4, LME step: nlm() did not converge (code = 3).
## Warning in (function (model, data = sys.frame(sys.parent()), fixed, random, :
## Iteration 5, LME step: nlm() did not converge (code = 3).
NA/Inf replaced by maximum positive valueNA/Inf replaced by maximum positive valueNA/Inf replaced by maximum positive valueNA/Inf replaced by maximum positive valueNA/Inf replaced by maximum positive valueIteration 1, LME step: nlm() did not converge (code = 4). Do increase ‘msMaxIter’!Iteration 2, LME step: nlm() did not converge (code = 4). Do increase ‘msMaxIter’!Iteration 4, LME step: nlm() did not converge (code = 3). Iteration 5, LME step: nlm() did not converge (code = 3).
# show summary
summary(m3)
## Nonlinear mixed-effects model fit by maximum likelihood
## Model: Fixations ~ fx(Time, P, S, C)
## Data: dtag
## AIC BIC logLik
## -244799 -244709 122411
##
## Random effects:
## Formula: list(P ~ 1, S ~ 1, C ~ 1)
## Level: Subject
## Structure: Diagonal
## P.(Intercept) S.(Intercept) C.(Intercept) Residual
## StdDev: 0.07106 0.001399 46.43 0.02976
##
## Correlation Structure: AR(1)
## Formula: ~1 | Subject
## Parameter estimate(s):
## Phi
## 0.9962
## Fixed effects: list(P + S + C ~ protocol)
## Value Std.Error DF t-value p-value
## P.(Intercept) 0.9 0.014 26995 61.37 0.0000
## P.protocolNH 0.1 0.021 26995 3.17 0.0015
## S.(Intercept) 0.0 0.000 26995 21.23 0.0000
## S.protocolNH 0.0 0.000 26995 4.49 0.0000
## C.(Intercept) 754.6 9.312 26995 81.04 0.0000
## C.protocolNH -69.0 13.205 26995 -5.23 0.0000
## Correlation:
## P.(In) P.prNH S.(In) S.prNH C.(In)
## P.protocolNH -0.696
## S.(Intercept) 0.007 -0.005
## S.protocolNH -0.005 0.008 -0.692
## C.(Intercept) 0.012 -0.008 0.001 0.000
## C.protocolNH -0.008 0.008 -0.001 0.001 -0.705
##
## Standardized Within-Group Residuals:
## Min Q1 Med Q3 Max
## -3.30299 -1.01280 -0.47437 -0.06979 1.58923
##
## Number of Observations: 27054
## Number of Groups: 54
#
# check the auto-correlation function of residuals
#
plot(ACF(m3, resType = "n"), alpha = 0.05)
# residual plot to identify outlier
plot(m3, id = 0.05, pch = '.', adj = -0.1, cex = .4)
# residual plot by protocol
plot(m3, resid(., type = "p") ~ fitted(.) | protocol, abline = 0, pch = '.')
# residual plot by subject
plot(m3, resid(., type = "p") ~ fitted(.) | Subject, abline = 0, pch = '.')
# residual plot by subject
plot(m3, resid(., type = "p") ~ fitted(.) | Subject*protocol,
abline = 0, pch = '.')
# plot fitted lines over observations by subject
plot(augPred(m3, level = 0:1))
# fortify data with fitted values
dta_t$yhat <- fitted(m3)
# Normal group
ggplot(subset(dta_t, protocol == "NH"), aes(Time, Fixations)) +
geom_point(size = rel(.5), pch = 1, col = "skyblue") +
geom_line(aes(y = yhat, group = Subject)) +
coord_cartesian(ylim = c(0, 1)) +
facet_wrap(~ Subject, ncol = 7) +
labs(x = "Time (ms)", y = "Proportion of fixations") +
theme(legend.position = "NONE")
# implant group
ggplot(subset(dta_t, protocol == "CI"), aes(Time, Fixations)) +
geom_point(size = rel(.5), pch = 1, col = "pink") +
geom_line(aes(y = yhat, group = Subject)) +
coord_cartesian(ylim = c(0, 1)) +
facet_wrap(~ Subject, ncol = 7) +
labs(x = "Time (ms)", y = "Proportion of fixations") +
theme(legend.position = "NONE")