Loading and preparing data for analysis

data = read.table("XP2CursorTransferFunction_logs.csv", header=TRUE, sep=",")

data = data %>% arrange(participantId, func, block, series, trialId)

# filtering first trial of each set, because we don't know where in the scene, the cursor come from.
# then mutate some data to adapt to our stats scripts
data = data %>%
    filter(trialId > 1) %>%
    mutate(distance = ifelse(longDist == "True", "long", "short")) %>%
    mutate(position = ifelse(target > 4, "far", "close")) %>%
    arrange(participantId, func, block, distance, position)

# aggregating by set of trials, and computing error rate:
dataErrorMeans = data %>%
    group_by(participantId, func, block, distance, position) %>%
    summarize(errorRate = computeErrorRate(success))

# same aggregation as above, but we compute the mean time:
dataSuccessAggr = data %>%
    filter(success == "True") %>%
    group_by(participantId, func, block, distance, position) %>%
    summarize(meanFinalTimer = mean(finalTimer))

# aggregated data, with mean time and error rate in the same data table:
dataAggr = left_join(dataSuccessAggr, dataErrorMeans, by = c("participantId", "func", "block", "distance", "position"))

# useful if we exclude block 1 in some analysis:
dataAggrBlock23 = dataAggr %>% filter(block > 1)

techniques = unique(data$func)
blocks = unique(data$block)
distances = unique(data$distance)
positions = unique(data$position)

1 Selection time

1.1 Effect of blocks on selection time

1.1.1 Check the normality of data

data.long = melt(dataAggr, id = c("meanFinalTimer", "participantId", "func", "block", "distance", "position"))

data.long$func = factor(data.long$func)
data.long$block = factor(data.long$block)
data.long$distance = factor(data.long$distance)
data.long$position = factor(data.long$position)

m <- aov(meanFinalTimer ~ func*block*distance*position, data=data.long)
pander(normalCheck(m))

Shapiro-Wilk normality test

data: res W = 0.92556, p-value = 1.259e-11

Shapiro-Wilk normality test: res
Test statistic P value
0.9256 1.259e-11 * * *

Selection time does not follow a normal distribution.

1.1.2 Determine boxcox transform lambda to make residuals normal

boxcox(meanFinalTimer ~ func*block*distance*position, data=data.long, plotit=T)

A lambda of around -0.4 is effective.

1.1.3 Transform data

datatr = data.long %>%
    mutate(meanFinalTimer = meanFinalTimer^(-0.4))
m <- aov(meanFinalTimer ~ func*block*distance*position, data=datatr)
pander(normalCheck(m))

Shapiro-Wilk normality test

data: res W = 0.99237, p-value = 0.09587

Shapiro-Wilk normality test: res
Test statistic P value
0.9924 0.09587

1.1.4 Repeated measures ANOVA on transformed data

anova = ezANOVA(datatr, dv=.(meanFinalTimer), wid=.(participantId), within=.(block), detailed=TRUE)

kable(anova_apa(anova, sph_corr ="gg", es = "ges", print=FALSE))
effect text
(Intercept) F(1, 8) = 2254.40, p < .001, getasq > .99
block F(2, 16) = 13.46, p < .001, getasq = .07

1.1.5 Repeated measures ANOVA on non-transformed data

anova = ezANOVA(data.long, dv=.(meanFinalTimer), wid=.(participantId), within=.(block), detailed=TRUE)

kable(anova_apa(anova, sph_corr ="gg", es = "ges", print=FALSE))
effect text
(Intercept) F(1, 8) = 306.11, p < .001, getasq = .97
block F(2, 16) = 10.77, p = .001, getasq = .08

There is no effect of learning so we continue the analysis using all blocks.

1.2 Analysis with all blocks

1.2.1 Repeated measures ANOVA Mean time on transformed data

anova = ezANOVA(datatr, dv=.(meanFinalTimer), wid=.(participantId), within=.(func, distance, position), detailed=TRUE)

kable(anova_apa(anova, sph_corr ="gg", es = "ges", print=FALSE))
effect text
(Intercept) F(1, 8) = 2254.40, p < .001, getasq = .99
func F(2, 16) = 3.77, p = .046, getasq = .07
distance F(1, 8) = 719.52, p < .001, getasq = .60
position F(1, 8) = 223.23, p < .001, getasq = .55
func:distance F(1.13, 9.06) = 0.78, p = .415, getasq < .01
func:position F(2, 16) = 25.10, p < .001, getasq = .16
distance:position F(1, 8) = 52.35, p < .001, getasq = .06
func:distance:position F(2, 16) = 7.40, p = .005, getasq = .03

1.2.2 Repeated measures ANOVA Mean time on non-transformed data

dataSuccess.long = melt(dataAggr, id = c("meanFinalTimer", "participantId", "func", "block", "distance", "position"))

dataSuccess.long$func = factor(dataSuccess.long$func)
dataSuccess.long$block = factor(dataSuccess.long$block)
data.long$distance = factor(data.long$distance)
data.long$position = factor(data.long$position)

anova = ezANOVA(dataSuccess.long, dv=.(meanFinalTimer), wid=.(participantId), within=.(func, distance, position), detailed=TRUE)

kable(anova_apa(anova, sph_corr ="gg", es = "ges", print=FALSE))
effect text
(Intercept) F(1, 8) = 306.11, p < .001, getasq = .96
func F(2, 16) = 5.22, p = .018, getasq = .10
distance F(1, 8) = 295.70, p < .001, getasq = .57
position F(1, 8) = 284.01, p < .001, getasq = .55
func:distance F(1.14, 9.09) = 3.00, p = .115, getasq = .02
func:position F(2, 16) = 35.21, p < .001, getasq = .18
distance:position F(1, 8) = 154.45, p < .001, getasq = .24
func:distance:position F(2, 16) = 4.49, p = .028, getasq = .02

1.2.3 Mean time per fonction

func meanFinalTimer
1_Prop 2.791502
2_Lerp 2.421525
3_Lerp_BetterProp 2.386873

1.2.3.1 Post-hoc analysis with Bonferroni correction

attach(datatr)
pw <- pairwise.t.test(meanFinalTimer, interaction(func), p.adj = "bonferroni")
detach(datatr)
kable(pw$p.value)
1_Prop 2_Lerp
2_Lerp 0.8353236 NA
3_Lerp_BetterProp 0.0542808 0.5937127

1.2.4 Mean time per fonction * distance

func distance meanFinalTimer
1_Prop long 3.518208
2_Lerp long 2.956052
3_Lerp_BetterProp long 2.977169
1_Prop short 2.064797
2_Lerp short 1.886999
3_Lerp_BetterProp short 1.796578

1.2.4.1 Post-hoc analysis with Bonferroni correction

attach(datatr)
pw <- pairwise.t.test(meanFinalTimer, interaction(func,distance), p.adj = "bonferroni")
detach(datatr)
kable(pw$p.value)
1_Prop.long 2_Lerp.long 3_Lerp_BetterProp.long 1_Prop.short 2_Lerp.short
2_Lerp.long 1.0000000 NA NA NA NA
3_Lerp_BetterProp.long 0.5498107 1 NA NA NA
1_Prop.short 0.0000000 0 8e-07 NA NA
2_Lerp.short 0.0000000 0 0e+00 1.0000000 NA
3_Lerp_BetterProp.short 0.0000000 0 0e+00 0.6671455 1

1.2.5 Mean time per fonction * position

func position meanFinalTimer
1_Prop close 1.863821
2_Lerp close 2.079784
3_Lerp_BetterProp close 1.895889
1_Prop far 3.719183
2_Lerp far 2.763267
3_Lerp_BetterProp far 2.877857

1.2.5.1 Post-hoc analysis with Bonferroni correction

attach(datatr)
pw <- pairwise.t.test(meanFinalTimer, interaction(func,position), p.adj = "bonferroni")
detach(datatr)
kable(pw$p.value)
1_Prop.close 2_Lerp.close 3_Lerp_BetterProp.close 1_Prop.far 2_Lerp.far
2_Lerp.close 0.4879705 NA NA NA NA
3_Lerp_BetterProp.close 1.0000000 0.5499846 NA NA NA
1_Prop.far 0.0000000 0.0000000 0e+00 NA NA
2_Lerp.far 0.0000001 0.0023068 1e-07 0.0011946 NA
3_Lerp_BetterProp.far 0.0000002 0.0033946 2e-07 0.0007942 1

2 Error rate

2.0.1 Check the normality of data

data.long = melt(dataAggr, id = c("errorRate", "participantId", "func", "block", "distance", "position"))

data.long$func = factor(data.long$func)
data.long$block = factor(data.long$block)
data.long$distance = factor(data.long$distance)
data.long$position = factor(data.long$position)

m <- aov(errorRate ~ func*block*distance*position, data=data.long)
pander(normalCheck(m))

Shapiro-Wilk normality test

data: res W = 0.90167, p-value = 1.168e-13

Shapiro-Wilk normality test: res
Test statistic P value
0.9017 1.168e-13 * * *

Error rate does not follow a normal distribution.

2.1 Effect of blocks on error rate

2.1.1 Running ANOVA on ART

m = art(errorRate ~ block + (1|participantId), data=data.long)
kable(anova(m)) 
Term F Df Df.res Pr(>F)
block block 0.6502996 2 313 0.5225931

2.1.2 Repeated measures ANOVA on non-transformed data

anova = ezANOVA(data.long, dv=.(errorRate), wid=.(participantId), within=.(block), detailed=TRUE)

kable(anova_apa(anova, sph_corr ="gg", es = "ges", print=FALSE))
effect text
(Intercept) F(1, 8) = 51.24, p < .001, getasq = .80
block F(2, 16) = 0.27, p = .766, getasq = .01

2.2 Analysis with all blocks

2.2.1 Running ANOVA on ART

m = art(errorRate ~ func*distance*position + (1|participantId), data=data.long)
kable(anova(m)) 
## Warning in optwrap(optimizer, devfun, getStart(start, rho$lower, rho$pp), :
## convergence code 3 from bobyqa: bobyqa -- a trust region step failed to
## reduce q
Term F Df Df.res Pr(>F)
func func 8.132635 2 304 0.0003625
distance distance 3.133194 1 304 0.0777156
position position 29.650015 1 304 0.0000001
func:distance func:distance 1.518695 2 304 0.2206543
func:position func:position 12.884696 2 304 0.0000043
distance:position distance:position 9.802322 1 304 0.0019124
func:distance:position func:distance:position 4.927234 2 304 0.0078357

2.2.2 Repeated measures ANOVA Error rate on non-transformed data

data.long = melt(dataAggr, id = c("errorRate", "participantId", "func", "block", "distance", "position"))

data.long$func = factor(data.long$func)
data.long$block = factor(data.long$block)
data.long$distance = factor(data.long$distance)
data.long$position = factor(data.long$position)

anova = ezANOVA(data.long, dv=.(errorRate), wid=.(participantId), within=.(func, distance, position), detailed=TRUE)

kable(anova_apa(anova, sph_corr ="gg", es = "ges", print=FALSE))
effect text
(Intercept) F(1, 8) = 51.24, p < .001, getasq = .48
func F(2, 16) = 4.55, p = .027, getasq = .08
distance F(1, 8) = 0.16, p = .702, getasq < .01
position F(1, 8) = 23.61, p = .001, getasq = .18
func:distance F(2, 16) = 1.25, p = .312, getasq = .02
func:position F(2, 16) = 7.17, p = .006, getasq = .09
distance:position F(1, 8) = 2.17, p = .179, getasq = .02
func:distance:position F(1.19, 9.52) = 0.46, p = .547, getasq = .01

2.2.3 Error rate per fonction

func errorRate
1_Prop 7.407407
2_Lerp 3.703704
3_Lerp_BetterProp 4.629630

2.2.3.1 Post-hoc analysis with Bonferroni correction

pw = lsmeans(artlm(m, "func"), pairwise ~ func, adjust = "bonferroni")
## Loading required namespace: lmerTest
## NOTE: Results may be misleading due to involvement in interactions
kable(summary(pw$contrasts))
contrast estimate SE df t.ratio p.value
1_Prop - 2_Lerp 48.29630 11.97627 19.0000000 4.032667 0.0021333
1_Prop - 3_Lerp_BetterProp 23.59259 11.97627 0.3877551 1.969945 1.0000000
2_Lerp - 3_Lerp_BetterProp -24.70370 11.97627 0.3877551 -2.062721 1.0000000
# attach(data.long)
# pw <- pairwise.t.test(errorRate, interaction(func), p.adj = "bonferroni")
# detach(data.long)
# kable(pw$p.value)

2.2.4 Error rate per fonction * distance

func distance errorRate
1_Prop long 6.481482
2_Lerp long 4.398148
3_Lerp_BetterProp long 5.555556
1_Prop short 8.333333
2_Lerp short 3.009259
3_Lerp_BetterProp short 3.703704

2.2.4.1 Post-hoc analysis with Bonferroni correction

datafilt.long = data.long %>% filter(distance == "short")
m = art(errorRate ~ func*position + (1|participantId), data=datafilt.long)
pw = lsmeans(artlm(m, "func"), pairwise ~ func, adjust = "bonferroni")
## NOTE: Results may be misleading due to involvement in interactions
kable(summary(pw$contrasts))
contrast estimate SE df t.ratio p.value
1_Prop - 2_Lerp 23.074074 8.116009 37.000000 2.8430320 0.0216999
1_Prop - 3_Lerp_BetterProp 16.981482 8.116009 0.755102 2.0923438 1.0000000
2_Lerp - 3_Lerp_BetterProp -6.092593 8.116009 0.755102 -0.7506882 1.0000000
datafilt.long = data.long %>% filter(distance == "long")
m = art(errorRate ~ func*position + (1|participantId), data=datafilt.long)
pw = lsmeans(artlm(m, "func"), pairwise ~ func, adjust = "bonferroni")
## NOTE: Results may be misleading due to involvement in interactions
kable(summary(pw$contrasts))
contrast estimate SE df t.ratio p.value
1_Prop - 2_Lerp 18.379630 8.408903 37.000000 2.1857346 0.1057184
1_Prop - 3_Lerp_BetterProp 3.814815 8.408903 0.755102 0.4536638 1.0000000
2_Lerp - 3_Lerp_BetterProp -14.564815 8.408903 0.755102 -1.7320708 1.0000000
attach(data.long)
pw <- pairwise.t.test(errorRate, interaction(func,distance), p.adj = "bonferroni")
detach(data.long)
kable(pw$p.value)
1_Prop.long 2_Lerp.long 3_Lerp_BetterProp.long 1_Prop.short 2_Lerp.short
2_Lerp.long 1.0000000 NA NA NA NA
3_Lerp_BetterProp.long 1.0000000 1.0000000 NA NA NA
1_Prop.short 1.0000000 0.3652646 1 NA NA
2_Lerp.short 0.7014988 1.0000000 1 0.0359324 NA
3_Lerp_BetterProp.short 1.0000000 1.0000000 1 0.1225898 1

2.2.5 Error rate per fonction * position

func position errorRate
1_Prop close 3.009259
2_Lerp close 3.472222
3_Lerp_BetterProp close 1.620370
1_Prop far 11.805556
2_Lerp far 3.935185
3_Lerp_BetterProp far 7.638889

2.2.5.1 Post-hoc analysis with Bonferroni correction

datafilt.long = data.long %>% filter(position == "close")
m = art(errorRate ~ func*distance + (1|participantId), data=datafilt.long)
pw = lsmeans(artlm(m, "func"), pairwise ~ func, adjust = "bonferroni")
## NOTE: Results may be misleading due to involvement in interactions
kable(summary(pw$contrasts))
contrast estimate SE df t.ratio p.value
1_Prop - 2_Lerp 3.296296 7.51097 37.000000 0.4388643 1
1_Prop - 3_Lerp_BetterProp 13.148148 7.51097 0.755102 1.7505260 1
2_Lerp - 3_Lerp_BetterProp 9.851852 7.51097 0.755102 1.3116618 1
datafilt.long = data.long %>% filter(position == "far")
m = art(errorRate ~ func*distance + (1|participantId), data=datafilt.long)
pw = lsmeans(artlm(m, "func"), pairwise ~ func, adjust = "bonferroni")
## NOTE: Results may be misleading due to involvement in interactions
kable(summary(pw$contrasts))
contrast estimate SE df t.ratio p.value
1_Prop - 2_Lerp 26.296296 8.201125 37.000000 3.2064253 0.0083108
1_Prop - 3_Lerp_BetterProp 6.314815 8.201125 0.755102 0.7699937 1.0000000
2_Lerp - 3_Lerp_BetterProp -19.981482 8.201125 0.755102 -2.4364316 0.9235550
attach(data.long)
pw <- pairwise.t.test(errorRate, interaction(func,position), p.adj = "bonferroni")
detach(data.long)
kable(pw$p.value)
1_Prop.close 2_Lerp.close 3_Lerp_BetterProp.close 1_Prop.far 2_Lerp.far
2_Lerp.close 1.0000000 NA NA NA NA
3_Lerp_BetterProp.close 1.0000000 1.0000000 NA NA NA
1_Prop.far 0.0000024 0.0000099 0.0000000 NA NA
2_Lerp.far 1.0000000 1.0000000 1.0000000 0.0000377 NA
3_Lerp_BetterProp.far 0.0765744 0.1744182 0.0043312 0.1744182 0.3713011

3 Preferences

techniques count
1_Prop 0
2_Lerp 5
3_Lerp_BetterProp 4