I. Language Detection
library(stringr)
#########################################################
#initial data ------------------------------------------>
str.data = read.delim("~/Desktop/Winter_2014/Linguist275/HW/Final/final.txt",
header = F, sep = "\n", quote = "\"",
dec = ".", fill = TRUE, comment.char = "")
#initial string
str="#aababababaabaababaababaababa#"
#initial data ------------------------------------------>
#########################################################
#########################################################
#helper functions -------------------------------------->
#raw character counts for unconditional probabilites
raw.count = function(str) {
str=unlist(sapply(str, FUN=function(x){strsplit(x, split="")}))
a.count = 0
b.count = 0
hash.count = 0
#skipping the initial #???
for (n in 1:length(str)) {
if(str[n] == 'a') { a.count = a.count + 1 }
else if (str[n] == 'b') { b.count = b.count + 1 }
else { hash.count = hash.count + 1 }
}
return(c(a=a.count, b=b.count, hash=hash.count))
}
raw.count(str)
## a b hash
## 17 11 2
mless.mle = function(str) {
counts = raw.count(str)
return(counts / sum(counts))
}
#populate a 1st order markov chain transition matrix
#with 3 states
populate.transition = function(str) {
str=unlist(sapply(str, FUN=function(x){strsplit(x, split="")}))
#matrix to hold conditional counts
transition = matrix(
c(0, 0, 0, 0, 0, 0, 0, 0, 0),
nrow=3, ncol=3)
colnames(transition) = c("a", "b", "#")
rownames(transition) = c("a", "b", "#")
#populate matrix with counts
n = 1
next.state = current.state = ""
while (next.state != "#") {
current.state = str[n]
next.state = str[n + 1]
transition[current.state, next.state] =
transition[current.state, next.state] + 1
n = n + 1
}
return(transition)
}
#Calculates relative frequencies across rows
matrix.probs = function(m) {
#Some strings may contain 0 of "a" or "b"
if(sum(m[1,]) != 0) { m[1,] = m[1,] / sum(m[1,]) }
if(sum(m[2,]) != 0) { m[2,] = m[2,] / sum(m[2,]) }
if(sum(m[3,]) != 0) { m[3,] = m[3,] / sum(m[3,]) }
return(m)
}
#Max likelihood estimate markov model
markov.mle = function(str) {
#raw counts
counts = raw.count(str)
#relative probabilites
rel.probs = counts / sum(counts)
#relative probabilities (unconditional) to vars
p.a = rel.probs[1]
p.b = rel.probs[2]
p.hash = rel.probs[3]
m = matrix.probs(populate.transition(str))
#conditional probabilities
p.a.m =
p.a * m["a","a"] + p.b * m["b","a"] + p.hash * m["#","a"]
p.b.m =
p.a * m["a","b"] + p.b * m["b","b"] + p.hash * m["#","b"]
p.hash.m =
p.a * m["a","#"] + p.b * m["b","#"] + p.hash * m["#", "#"]
return(c(a = p.a.m, b = p.b.m, hash = p.hash.m))
}
#Global for later use
mkov.transition = matrix.probs(populate.transition(str))
#helper functions -------------------------------------->
#########################################################
a)
MLE estimates:
#########################################################
#model predictions-------------------------------------->
#MLE memoryless
mless.mle = mless.mle(str)
mless.mle
## a b hash
## 0.56666667 0.36666667 0.06666667
#sanity check
sum(mless.mle)
## [1] 1
#1st order Markov chain
mkov.mle = markov.mle(str)
mkov.mle
## a.a b.a hash.a
## 0.60000000 0.36666667 0.03333333
#sanity check
sum(mkov.mle)
## [1] 1
#model predictions-------------------------------------->
#########################################################
MLE estimates using rejection sampling:
#Calculating param estimates using sampling techniques for
#m1 - memoryless and m2 - markov
#m1 (memoryless params estimation using sampling method)
rj.estimates = function(str, n.sims=1000) {
#actual frequencies observed
counts = raw.count(str)
size = nchar(str)
a.cnt = counts[1]
b.cnt = counts[2]
hash.cnt = counts[3]
samples = c()
while (length(samples) < (n.sims * 3)) {
sim.params = runif(3, 0, 1)
sim.params = sim.params / sum(sim.params)
a.samp.cnt = rbinom(n=1, size=size, sim.params[1])
b.samp.cnt = rbinom(n=1, size=size, sim.params[2])
hash.samp.cnt = size - (a.samp.cnt + b.samp.cnt)
if (a.samp.cnt == a.cnt && b.samp.cnt == b.cnt &&
hash.samp.cnt == hash.cnt) {
samples = rbind(samples, sim.params)
}
}
return(c(p.a = mean(samples[,1]),
p.b = mean(samples[,2]), p.hash = mean(samples[,3])))
}
#m2 (markov params estimation using sampling method)
#I'm essentially generating lots of possible strings given the
#parameter estimates from the original string and then looking at the MLE
#for the params
mkov.sampling2 = function(str, i=1000) {
mkov.transition = matrix.probs(populate.transition(str))
results = c()
p.hash = 1
for(n in seq(from=1, to=i)) {
#if current state is p.hash
if(p.hash) {
flip=runif(1,0,1)
if (flip <= mkov.transition[3, 1]) {
p.a = 1
p.b = 0
p.hash = 0
} else if (flip <= 1 - mkov.transition[3,3]) {
p.a = 0
p.b = 1
p.hash = 0
} else {
p.a = 0
p.b = 0
p.hash = 1
}
}
#if current state is p.a
else if (p.a) {
flip = runif(1, 0, 1)
if(flip <= mkov.transition[1,1]) {
p.a = 1
p.b = 0
p.hash = 0
}
else if (flip <= 1 - mkov.transition[1,3]) {
p.a = 0
p.b = 1
p.hash = 0
}
else {
p.a = 0
p.b = 0
p.hash = 1
}
}
#else current state is p.b
else {
flip = runif(1,0,1)
if (flip <= mkov.transition[2,1]) {
p.a = 1
p.b = 0
p.hash = 0
} else if (flip <= 1 - mkov.transition[2,3]) {
p.a = 0
p.b = 1
p.hash = 0
} else {
p.a = 0
p.b = 0
p.hash = 1
}
}
results = cbind(results, c(p.a, p.b, p.hash))
}
return(c(p.a=mean(results[1,]),
p.b = mean(results[2,]), p.hash = mean(results[3,])))
}
#Param estimates using sampling:
mmless.rject=rj.estimates(str)
mmless.rject
## p.a p.b p.hash
## 0.5051154 0.3476126 0.1472719
mkov.samp.est = mkov.sampling2(str)
mkov.samp.est
## p.a p.b p.hash
## 0.580 0.382 0.038
b) Entropy Rates
#Entropy for indiiv
entropy.rate = function(params) {
(params[1] * log2(1 / params[1]) +
params[2] * log2(1 / params[2]) +
params[3] * log2(1 / params[3])) / 3
}
#for some reason need to place here again
mless.mle = function(str) {
counts = raw.count(str)
return(counts / sum(counts))
}
#Entropy rates for our four param estimation methods
mmless.mle = mless.mle(str)
mkov.mle = markov.mle(str)
entropy.rate(mmless.mle)
## a
## 0.4185122
entropy.rate(mkov.mle)
## a.a
## 0.3788258
entropy.rate(mmless.rject)
## p.a
## 0.4781977
entropy.rate(mkov.samp.est)
## p.a
## 0.3884793
c) Classification of strings
#Two methods for estimating posteriors
#1 using dbinom
param.likelihoods1 = function(str, params) {
size = nchar(str)
counts = raw.count(str)
return(dbinom(counts[1], size, params[1]) *
dbinom(counts[2], size, params[2]) *
dbinom(counts[3], size, params[3]))
}
#2 using sampling
param.likelihoods2 = function(str, params, n.sims=1000) {
size = nchar(str)
counts = raw.count(str)
p.a = params[1]
p.b = params[2]
p.hash = params[3]
turns = 0
success = 0
while(success < n.sims) {
a.samp.cnt = rbinom(n=1, size=size, p.a)
b.samp.cnt = rbinom(n=1, size=size, p.b)
hash.samp.cnt = rbinom(n=1, size=size, p.hash)
if (a.samp.cnt == counts[1] &&
b.samp.cnt == counts[2] &&
hash.samp.cnt == counts[3]) {
success = success + 1
}
turns = turns + 1
}
return (success / turns)
}
bayes.factor = function(str, m1, m2) {
return(param.likelihoods1(str, m1) / param.likelihoods1(str, m2))
}
#mless.mle
#mkov.mle
#rj.estimates
#mkov.sampling2
#predictions
###:::----------CAUTION SLOW RUN-TIME-------------:::###
results = c()
for (i in 1:nrow(str.data)) {
str = as.character(str.data[i,1])
length = nchar(str)
mless.est = rj.estimates(str)
markov.est = mkov.sampling2(str)
b.factor = bayes.factor(str, mless.est, markov.est)
m.preference = ifelse(b.factor > 1, "m1", "m2")
results = rbind(results, c(str, length, b.factor, m.preference))
}
colnames(results) = c("char.string", "string.len",
"b.factor", "model.preference")
###:::----------CAUTION SLOW RUN-TIME-------------:::###
results
## char.string
## [1,] "#abbababababbabaabaabbaababbbaa#"
## [2,] "#abaababaaaaaabbbaabaaaa#"
## [3,] "#aaaaba#"
## [4,] "#babababab#"
## [5,] "#abaabababababbaba#"
## [6,] "#abaaabaababaaaaaaaaaaababbaaabaabbaabababaabababbbabaabaaababaaaaaaaaab#"
## [7,] "#aaaaba#"
## [8,] "#bababaababaabababaabababbabababbaababababbabaaabaabababababababaababababab#"
## [9,] "#abababbababa#"
## [10,] "#aababaaababa#"
## [11,] "#aaaa#"
## [12,] "#b#"
## [13,] "#bbab#"
## [14,] "#abbababa#"
## [15,] "#aaabaaaabaaba#"
## [16,] "#aabbabaabaabaababbababbababa#"
## [17,] "#aabba#"
## [18,] "#abbabaabababbbbababb#"
## [19,] "#aaaababaabaababaaaaababaaabaababababbabbababab#"
## [20,] "#aaaaaaabbaaba#"
## [21,] "#babaabaaabbbababbabbabababbabaaabbaaabbabaabababbababbba#"
## [22,] "#babababababa#"
## [23,] "#aaaa#"
## [24,] "#abaabaaababbbabaaaababbababbaabbabbabababbabaaaabababbbaa#"
## [25,] "#aababaaabaaaaababaaaaaabbaabaaaabbbababaabbbbbaaaaaabbabaaaabaabbabaaaaaaabbaaaaaaabaabaaaba#"
## [26,] "#abbbbaaaabbbaabaaaaabaabbabaaaabababaabaaaaaaaaabababaaaaaa#"
## [27,] "#ababaaaabbbaba#"
## [28,] "#aaaba#"
## [29,] "#bbbabaaaaaaabaaaaababa#"
## [30,] "#baabbababa#"
## [31,] "#aabbabaabbbaaaabbbbbaaab#"
## [32,] "#abaaaaaaababba#"
## [33,] "#abaababaababababa#"
## [34,] "#baaaaab#"
## [35,] "#ab#"
## [36,] "#baaaababababababaaabababababaababaaaabb#"
## [37,] "#aaaababaabaaaabbabbababaaaababbabbabababababb#"
## [38,] "#b#"
## [39,] "#aa#"
## [40,] "#ababaaba#"
## [41,] "#aaaaa#"
## [42,] "#abababababababbababbaaba#"
## [43,] "#aaababaabbaababababbababbabababababbabaaabababaaabaabaabababba#"
## [44,] "#abababaaabaabaabaaabaaaababababbababaabaabababbaababaabababaaababbaababbaabaa#"
## [45,] "#ababababaabbaabbabaabaabababaabababaababbbababaaaaaaababababbbabababbabab#"
## [46,] "#abababbabaababa#"
## [47,] "#a#"
## [48,] "#bbaaabbaaaabaababaaabaaaabbbbabbbaaabbaabaaabbaaaaaaaaaa#"
## [49,] "#baaaaaaabbbaabaaabaabaaababaabaab#"
## [50,] "#abaababababababababaaaababababababaaaaabaabababaaababaaabaababa#"
## string.len b.factor model.preference
## [1,] "32" "0.640487943009293" "m2"
## [2,] "25" "0.563350207916577" "m2"
## [3,] "8" "1.16389686193702" "m1"
## [4,] "11" "1.0314935830525" "m1"
## [5,] "19" "1.03971540114355" "m1"
## [6,] "73" "0.226854688987635" "m2"
## [7,] "8" "1.0083920526819" "m1"
## [8,] "76" "0.163311055045511" "m2"
## [9,] "14" "0.966647964765882" "m2"
## [10,] "14" "0.921021345520204" "m2"
## [11,] "6" "0.676372422060823" "m2"
## [12,] "3" "0.432281794612057" "m2"
## [13,] "6" "1.32549126663473" "m1"
## [14,] "10" "1.38164606434033" "m1"
## [15,] "15" "0.855773927119177" "m2"
## [16,] "30" "0.53376484433247" "m2"
## [17,] "7" "1.48035557227229" "m1"
## [18,] "22" "0.544700423621767" "m2"
## [19,] "48" "0.321960013512217" "m2"
## [20,] "15" "0.750402439933334" "m2"
## [21,] "58" "0.228680081256228" "m2"
## [22,] "14" "0.799924657942208" "m2"
## [23,] "6" "0.614026457387201" "m2"
## [24,] "59" "0.144864830732371" "m2"
## [25,] "94" "0.0821955685184869" "m2"
## [26,] "61" "0.274647007531366" "m2"
## [27,] "16" "0.872571429599374" "m2"
## [28,] "7" "1.22159632011216" "m1"
## [29,] "24" "0.461233510605669" "m2"
## [30,] "12" "1.24359148862928" "m1"
## [31,] "26" "0.488110158929704" "m2"
## [32,] "16" "0.772168553799172" "m2"
## [33,] "19" "0.814200982751354" "m2"
## [34,] "9" "0.995601203642725" "m2"
## [35,] "4" "1.08291964891957" "m1"
## [36,] "41" "0.5795914464702" "m2"
## [37,] "47" "0.29086365826729" "m2"
## [38,] "3" "0.415291225527665" "m2"
## [39,] "4" "0.612979279699205" "m2"
## [40,] "10" "1.0563843620882" "m1"
## [41,] "7" "0.592757774797543" "m2"
## [42,] "26" "0.560948597825319" "m2"
## [43,] "64" "0.183300070240158" "m2"
## [44,] "79" "0.116270263084536" "m2"
## [45,] "75" "0.210765851130452" "m2"
## [46,] "17" "0.910805285669443" "m2"
## [47,] "3" "0.416319338909822" "m2"
## [48,] "58" "0.415890797468166" "m2"
## [49,] "35" "0.345703707115387" "m2"
## [50,] "65" "0.168602497664647" "m2"
##################################################
#plotting categories by length------------------->
plot(results[,"string.len"],
results[,"b.factor"], pch=16, col="blue",
main = "Categorizing strings",
ylab="Posterior odds", xlab="character length")
abline(h=1, col="red", lty=2)
#plotting categories by length------------------->
##################################################
m1.strings = results[which(results[,"model.preference"]=="m1"),"char.string"]
m2.strings = results[which(results[,"model.preference"]=="m2"),"char.string"]
aggregate.strings = function(data) {
string = ""
for(i in 1:length(data)) {
string = paste(string, data[i])
}
return(string)
}
m1.strings = aggregate.strings(m1.strings)
m2.strings = aggregate.strings(m2.strings)
#Edit out spaces
m1.strings=str_replace_all(m1.strings, "[^[:alnum:]#]", "")
m1.strings=str_replace_all(m1.strings, "##", "#")
m2.strings=str_replace_all(m2.strings, "[^[:alnum:]#]", "")
m2.strings=str_replace_all(m2.strings, "##", "#")
#Memoryless estimates
rj.estimates(m1.strings)
## p.a p.b p.hash
## 0.4806862 0.3685673 0.1507465
#Markov string estimates
mkov.sampling2(m2.strings)
## p.a p.b p.hash
## 0.490 0.477 0.033
matrix.probs(populate.transition(m2.strings))
## a b #
## a 0.2666667 0.6666667 0.06666667
## b 0.6666667 0.3333333 0.00000000
## # 1.0000000 0.0000000 0.00000000
d)
I’ve aggregated and combined the strings by the posterior and odds ratio. I’m combining the strings into one string and removing duplicate “##”. If these stochastic processes were actually generating these strings I assume the “#” would represent a logical break between two (so that the end “#” for one string would be the the beginning “#” for the next). With this new single string for each model I simply run my markov parameters estimates “mkov.sampling2()” and for the memoryless process “rj.estimates()”. I’ve displayed the transition matrix from my input. I’m a little confused about your distinction between the bayesian posterior odds and the ratio of likelihoods under the two models. Since the two models are equal a priori (uniform prior odds will cancel out) these two metrics should be identical…
II. Engaging the stats-for-psycholinguistics literature: Jaeger (2008)
a)
The fundamental issue with ANOVA is that it compares means of different experimental conditions and based on the sample variance within and between conditions, gives us information about whether to reject a null hypothesis. This is fine for continuous DV’s, however it is problematic for analysis of mean values for categorical variable in which we are likely looking at proportions of success or failures, etc. This is the crux of what Jaeger recommends against – using ANOVA over proportions. I thought Jaeger characterized one element of this problem nicely – ANOVAs for CDA have interpretability issues because they attribute probability mass to events that can never occur, “thereby underestimating the probability mass over events that can occur” (p.3). Furthermore, ANOVA makes assumptions about homogeneity of variance – a crucial problem when working with binomially distributed outcomes because the variance is only equal when p.1 and p.2 are equidistant from .5. This problem is exacerbated as the probabilities get more extreme (closer to 0 and 1). The arcsin-sqrt transformation has traditionally been considered as a method for dealing with some of these problems, however Jaeger shows that this approximation is also inadequate. Logistic regression, by contrast, is simply a linear model with a transformation (logit link function) of proportions into “space that is not bounded by 0 and 1” (log-odds space) (p.4). Additionally, this transformation accounts for the fact that with binomially distributed data, changes in proportions at the extremes should correspond with larger changes in the predictors than at .5. In this way, ordinary logit models better fit the nature of categorical outcomes. Jaeger continues with a host of other reasons why using ordinary logistic regressions is preferable to ANOVA. Among these are:
->Compatibility with multinomial distributions – in part this helps avoid the problems of data exclusion by being able to model the probability of errors.
->They are also more interpretable by providing information about the size and direction of an effect (coefficients in part)
->They are adaptable to imbalanced data
b)
The primary drawback with ordinary logit models is that they cannot account for random effects, which was likely a viable reason to go with ANOVA (despite its deficiencies) in the past. However, Jaeger introduces mixed logit models as a way around this. Without a logit link function, simply applying ANOVA to proportions is like running a linear probability model, which will have many of the same problems as ANOVA. The arcsine transformation is meant to approximate values as transformed by a logit link function (initially this was desirable because they were less costly, computationally), however this is no longer necessary.
c)
The ANOVA over the proportions of correct answers is inaccurate because there appears to be an effect of NP type on the proportion of correct answers for object RC’s compared to subject. However, this is in part because several of the subjects were at ceiling in their accuracy in Subject RC (Pronoun) conditions. ANOVA, even under the arcsine transformation, is unreliable at extremes around 0 or 1. The effect of NP on object RC accuracy appears larger because accuracy for subject RC was already so high. The ANOVA is unable to account for this as it simply considers the size of the difference. Of course, the difference is diminished when the outcome is naturally bounded (proportions) and subjects perform at ceiling. This is basically Jaeger’s first characterization of why ANOVAs are problematic with categorical data – they can assign probability mass to events which can’t exist.
d)
library(lme4)
## Loading required package: Matrix
## Loading required package: Rcpp
d = data.frame(read.delim("~/Desktop/Winter_2014/Linguist275/HW/Final/inbal.tab",
header = T, sep = "\t", quote = "\"",
dec = ".", fill = TRUE, comment.char = ""))
d = subset(d, modality == 1) # comprehension study only
d = subset(d, Correct != "#NULL!" & !is.na(Extraction) & !is.na(NPType))
#convert to factors and relabel
names(d)
## [1] "modality" "presentation" "instance" "sentence"
## [5] "itemby4" "itemby2" "child" "age"
## [9] "Extraction" "NPType" "animacymatch" "objectbias"
## [13] "headanimacy" "Response" "Correct" "CorrectRP"
## [17] "Reversal" "Agent" "OnlyObject" "OnlyObjectRP"
## [21] "Resumptive"
d$NPType = factor(d$NPType)
levels(d$NPType) = c("lexical", "pronoun")
d$RCType = factor(d$Extraction)
levels(d$RCType) = c("object", "subject")
#for ordinary logit and mixed-effects models
d$Correct = factor(d$Correct)
levels(d$Correct) = c("incorrect", "correct")
d$Condition = paste(d$NPType, d$RCType)
#Verifying Percentage of correct answers
d$Correct = as.numeric(d$Correct)
d.Subject.Pronoun = d[which(d[,"NPType"] == "pronoun" &
d[,"RCType"] == "subject"),]
d.Object.Pronoun = d[which(d[,"NPType"] == "pronoun" &
d[,"RCType"] == "object"),]
d.Subject.Lexical = d[which(d[,"NPType"] == "lexical" &
d[,"RCType"] == "subject"),]
d.Object.Lexical = d[which(d[,"NPType"] == "lexical" &
d[,"RCType"] == "object"),]
length(which(d.Subject.Pronoun$Correct == 2)) /
length(d.Subject.Pronoun$Correct)
## [1] 0.8433735
length(which(d.Object.Pronoun$Correct == 2)) /
length(d.Object.Pronoun$Correct)
## [1] 0.9565217
length(which(d.Subject.Lexical$Correct == 2)) /
length(d.Subject.Lexical$Correct)
## [1] 0.6890244
length(which(d.Object.Lexical$Correct == 2)) /
length(d.Object.Lexical$Correct)
## [1] 0.8956044
#non-transformed aov() -------------->
#F1 analysis - Subject analysis
summary(aov(as.numeric(Correct) ~ NPType * RCType +
Error(as.factor(child)), data = d))
##
## Error: as.factor(child)
## Df Sum Sq Mean Sq F value Pr(>F)
## NPType 1 0.559 0.5587 2.011 0.1716
## RCType 1 0.010 0.0103 0.037 0.8491
## NPType:RCType 1 1.430 1.4304 5.148 0.0345 *
## Residuals 20 5.558 0.2779
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Error: Within
## Df Sum Sq Mean Sq F value Pr(>F)
## NPType 1 1.97 1.967 17.755 2.86e-05 ***
## RCType 1 4.42 4.423 39.918 4.83e-10 ***
## NPType:RCType 1 0.39 0.395 3.561 0.0596 .
## Residuals 669 74.12 0.111
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#same analysis using lmer
anova(lmer(Correct ~ 1 + RCType * NPType + (1 | child), data=d))
## Analysis of Variance Table
## Df Sum Sq Mean Sq F value
## RCType 1 4.4190 4.4190 39.8877
## NPType 1 1.9540 1.9540 17.6374
## RCType:NPType 1 0.3889 0.3889 3.5108
#F2 analysis - Item analysis
summary(aov(as.numeric(Correct) ~ NPType * RCType +
Error(as.factor(itemby4)), data = d))
##
## Error: as.factor(itemby4)
## Df Sum Sq Mean Sq F value Pr(>F)
## NPType 1 0.0160 0.01597 0.053 0.829
## RCType 1 0.0248 0.02478 0.082 0.788
## NPType:RCType 1 0.0469 0.04695 0.156 0.713
## Residuals 4 1.2049 0.30122
##
## Error: Within
## Df Sum Sq Mean Sq F value Pr(>F)
## NPType 1 1.91 1.914 16.340 5.89e-05 ***
## RCType 1 4.60 4.598 39.242 6.61e-10 ***
## NPType:RCType 1 0.40 0.402 3.428 0.0645 .
## Residuals 685 80.25 0.117
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
anova(lmer(Correct ~ 1 + RCType * NPType + (1 | itemby4), data=d))
## Analysis of Variance Table
## Df Sum Sq Mean Sq F value
## RCType 1 4.4909 4.4909 38.3404
## NPType 1 1.9253 1.9253 16.4376
## RCType:NPType 1 0.3887 0.3887 3.3182
#F2 analysis - Combined analysis
summary(aov(as.numeric(Correct) ~ NPType * RCType +
Error(as.factor(child) / as.factor(itemby4)), data = d))
##
## Error: as.factor(child)
## Df Sum Sq Mean Sq F value Pr(>F)
## NPType 1 0.559 0.5587 2.011 0.1716
## RCType 1 0.010 0.0103 0.037 0.8491
## NPType:RCType 1 1.430 1.4304 5.148 0.0345 *
## Residuals 20 5.558 0.2779
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Error: as.factor(child):as.factor(itemby4)
## Df Sum Sq Mean Sq F value Pr(>F)
## NPType 1 0.000 0.00000 0.000 0.997
## RCType 1 0.022 0.02224 0.200 0.655
## NPType:RCType 1 0.276 0.27626 2.484 0.117
## Residuals 165 18.354 0.11124
##
## Error: Within
## Df Sum Sq Mean Sq F value Pr(>F)
## NPType 1 2.03 2.027 18.323 2.23e-05 ***
## RCType 1 4.45 4.452 40.237 5.03e-10 ***
## NPType:RCType 1 0.34 0.345 3.118 0.078 .
## Residuals 501 55.43 0.111
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#arc-sin transformed aov() - Subject analysis only (as referenced in Jaeger (2006))
d$a.sin.transform = asin(sqrt(as.numeric(d$Correct) / 100))
summary(aov(a.sin.transform ~ NPType * RCType + Error(child), data = d))
##
## Error: child
## Df Sum Sq Mean Sq
## NPType 1 0.000539 0.000539
##
## Error: Within
## Df Sum Sq Mean Sq F value Pr(>F)
## NPType 1 0.00336 0.003364 16.390 5.74e-05 ***
## RCType 1 0.00766 0.007659 37.320 1.67e-09 ***
## NPType:RCType 1 0.00066 0.000662 3.224 0.073 .
## Residuals 691 0.14182 0.000205
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
The results of my ANOVA show that the interaction is significant, but not nearly to the degree observed in Jaeger (2006)… I’m finding significance at .1 suggesting a fairly high likelihood of Type 1 error. My F-value for RCType is inflated compared to Jaeger’s which would indicate that this anova is actually doing a better job assessing the size of the effect from this variable, perhaps pulling from the interaction.
#Ordinary logit with interaction
#recode as factor
d$Correct = factor(d$Correct)
levels(d$Correct) = c("incorrect", "correct")
m.ordinary.logit = glm(Correct ~ RCType * NPType,
family=binomial, data=d)
summary(m.ordinary.logit)
##
## Call:
## glm(formula = Correct ~ RCType * NPType, family = binomial, data = d)
##
## Deviance Residuals:
## Min 1Q Median 3Q Max
## -2.5042 0.2982 0.4696 0.5837 0.8631
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 2.14931 0.24242 8.866 < 2e-16 ***
## RCTypesubject -1.35375 0.29534 -4.584 4.57e-06 ***
## NPTypepronoun 0.94173 0.43521 2.164 0.0305 *
## RCTypesubject:NPTypepronoun -0.05375 0.51329 -0.105 0.9166
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 587.02 on 695 degrees of freedom
## Residual deviance: 535.04 on 692 degrees of freedom
## AIC: 543.04
##
## Number of Fisher Scoring iterations: 5
#mixed logistic regression
library(lme4)
mr.1 = lmer(Correct ~ 1 + RCType * NPType +
(1 + RCType * NPType | child), data=d)
mr.2 = lmer(Correct ~ 1 + RCType * NPType +
(1 + as.numeric(RCType) | child), data=d)
mr.3 = lmer(Correct ~ 1 + RCType * NPType +
(1 + as.numeric(RCType) | child) + (1 | itemby4), data=d)
summary(mr.1)
## Linear mixed model fit by REML ['lmerMod']
## Formula: Correct ~ 1 + RCType * NPType + (1 + RCType * NPType | child)
## Data: d
##
## REML criterion at convergence: 480.7
##
## Scaled residuals:
## Min 1Q Median 3Q Max
## -2.93175 0.05601 0.21768 0.46547 1.71506
##
## Random effects:
## Groups Name Variance Std.Dev. Corr
## child (Intercept) 0.007437 0.08624
## RCTypesubject 0.007736 0.08796 0.25
## NPTypepronoun 0.002063 0.04542 -1.00 -0.22
## RCTypesubject:NPTypepronoun 0.002151 0.04637 0.72 -0.48 -0.75
## Residual 0.108673 0.32966
## Number of obs: 696, groups: child, 24
##
## Fixed effects:
## Estimate Std. Error t value
## (Intercept) 1.89525 0.03014 62.88
## RCTypesubject -0.20803 0.03979 -5.23
## NPTypepronoun 0.06114 0.03570 1.71
## RCTypesubject:NPTypepronoun 0.09516 0.05095 1.87
##
## Correlation of Fixed Effects:
## (Intr) RCTyps NPTypp
## RCTypesbjct -0.432
## NPTypepronn -0.707 0.396
## RCTypsb:NPT 0.468 -0.662 -0.690
summary(mr.2)
## Linear mixed model fit by REML ['lmerMod']
## Formula: Correct ~ 1 + RCType * NPType + (1 + as.numeric(RCType) | child)
## Data: d
##
## REML criterion at convergence: 482.3
##
## Scaled residuals:
## Min 1Q Median 3Q Max
## -2.90881 0.08094 0.25874 0.47210 1.68366
##
## Random effects:
## Groups Name Variance Std.Dev. Corr
## child (Intercept) 0.0001652 0.01285
## as.numeric(RCType) 0.0046405 0.06812 -1.00
## Residual 0.1096192 0.33109
## Number of obs: 696, groups: child, 24
##
## Fixed effects:
## Estimate Std. Error t value
## (Intercept) 1.89535 0.02702 70.15
## RCTypesubject -0.20795 0.03827 -5.43
## NPTypepronoun 0.06098 0.03461 1.76
## RCTypesubject:NPTypepronoun 0.09512 0.05027 1.89
##
## Correlation of Fixed Effects:
## (Intr) RCTyps NPTypp
## RCTypesbjct -0.431
## NPTypepronn -0.644 0.455
## RCTypsb:NPT 0.443 -0.661 -0.689
summary(mr.3)
## Linear mixed model fit by REML ['lmerMod']
## Formula:
## Correct ~ 1 + RCType * NPType + (1 + as.numeric(RCType) | child) +
## (1 | itemby4)
## Data: d
##
## REML criterion at convergence: 480.3
##
## Scaled residuals:
## Min 1Q Median 3Q Max
## -2.95055 0.03911 0.25042 0.48734 1.72317
##
## Random effects:
## Groups Name Variance Std.Dev. Corr
## child (Intercept) 0.0001845 0.01358
## as.numeric(RCType) 0.0047023 0.06857 -1.00
## itemby4 (Intercept) 0.0011587 0.03404
## Residual 0.1085686 0.32950
## Number of obs: 696, groups: child, 24; itemby4, 8
##
## Fixed effects:
## Estimate Std. Error t value
## (Intercept) 1.89561 0.02946 64.34
## RCTypesubject -0.21099 0.03832 -5.51
## NPTypepronoun 0.06104 0.03445 1.77
## RCTypesubject:NPTypepronoun 0.09694 0.05033 1.93
##
## Correlation of Fixed Effects:
## (Intr) RCTyps NPTypp
## RCTypesbjct -0.390
## NPTypepronn -0.588 0.453
## RCTypsb:NPT 0.403 -0.661 -0.685
Like Jaeger, my ordinary logistic and mixed logistic models are correctly identifying significant contributions from the RCType and NPType predictors, but not an interaction. As with my ANOVAs, however, my numbers are different than Jaeger’s. At this point I’m not sure what I’m doing wrong. I know Jaeger used a different regression library and functions (lrm from Design library) and I wonder if some of these differences come from the implementations of the functions.