1. Load Libraries & Data
library(readxl)
library(janitor)
##
## Attaching package: 'janitor'
## The following objects are masked from 'package:stats':
##
## chisq.test, fisher.test
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.2.1 ✔ readr 2.2.0
## ✔ forcats 1.0.1 ✔ stringr 1.6.0
## ✔ ggplot2 4.0.3 ✔ tibble 3.3.1
## ✔ lubridate 1.9.5 ✔ tidyr 1.3.2
## ✔ purrr 1.2.2
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(lme4)
## Loading required package: Matrix
##
## Attaching package: 'Matrix'
##
## The following objects are masked from 'package:tidyr':
##
## expand, pack, unpack
library(lmerTest)
##
## Attaching package: 'lmerTest'
##
## The following object is masked from 'package:lme4':
##
## lmer
##
## The following object is masked from 'package:stats':
##
## step
library(glmmTMB)
## Warning in check_dep_version(dep_pkg = "TMB"): package version mismatch:
## glmmTMB was built with TMB package version 1.9.19
## Current TMB package version is 1.9.21
## Please re-install glmmTMB from source or restore original 'TMB' package (see '?reinstalling' for more information)
library(performance)
library(emmeans)
## Welcome to emmeans.
## Caution: You lose important information if you filter this package's results.
## See '? untidy'
df <- read_excel("~/Downloads/touch_data.xlsx", skip = 1) %>%
clean_names()
names(df)
## [1] "file" "subject_id"
## [3] "sex" "age"
## [5] "condition" "block"
## [7] "trial_ordinal" "exp"
## [9] "socinf" "item"
## [11] "trial_onset" "trial_offset"
## [13] "trial_duration_ms" "n_touches_total"
## [15] "n_touches_top" "n_touches_bottom"
## [17] "n_touches_screen_holder" "add_touch"
## [19] "ratio_top" "ratio_bottom"
## [21] "ratio_screen_holder" "first_touch_part"
## [23] "first_touch_onset" "latency_first_touch"
## [25] "first_top_touch_onset" "latency_first_top"
## [27] "total_touch_duration" "top_touch_duration"
head(df)
## # A tibble: 6 × 28
## file subject_id sex age condition block trial_ordinal exp socinf item
## <chr> <dbl> <chr> <chr> <chr> <dbl> <dbl> <chr> <chr> <chr>
## 1 D3_01… 15 fema… 16m … neutral 2 1 s a kn
## 2 D3_01… 15 fema… 16m … neutral 2 2 s a sn
## 3 D3_01… 15 fema… 16m … neutral 2 3 s a ge
## 4 D3_01… 15 fema… 16m … neutral 2 4 s a sa
## 5 D3_01… 15 fema… 16m … neutral 2 5 s a oa
## 6 D3_01… 15 fema… 16m … neutral 2 6 s a ba
## # ℹ 18 more variables: trial_onset <dbl>, trial_offset <dbl>,
## # trial_duration_ms <dbl>, n_touches_total <dbl>, n_touches_top <dbl>,
## # n_touches_bottom <dbl>, n_touches_screen_holder <dbl>, add_touch <dbl>,
## # ratio_top <dbl>, ratio_bottom <dbl>, ratio_screen_holder <dbl>,
## # first_touch_part <chr>, first_touch_onset <dbl>, latency_first_touch <dbl>,
## # first_top_touch_onset <dbl>, latency_first_top <dbl>,
## # total_touch_duration <dbl>, top_touch_duration <dbl>
2. Clean & Recode Variables
df_clean <- df %>%
mutate(
subject_id = as.factor(subject_id),
sex = as.factor(sex),
condition = as.factor(condition),
block = as.factor(block),
item = as.factor(item),
socinf = as.factor(socinf),
exp = as.factor(exp),
object_type = case_when(
str_detect(file, "D2") ~ "2D",
str_detect(file, "D3") ~ "3D",
TRUE ~ NA_character_
),
object_type = as.factor(object_type),
age_months = as.numeric(str_extract(age, "\\d+(?=m)")),
age_days = as.numeric(str_extract(age, "\\d+(?=d)")),
age_days = ifelse(is.na(age_days), 0, age_days),
age_numeric = age_months + age_days / 30.44,
n_top_touch = as.numeric(n_touches_top),
top_touch_duration = as.numeric(top_touch_duration),
top_touch_latency = as.numeric(latency_first_top),
log_top_duration = log(top_touch_duration + 1),
log_top_latency = log(top_touch_latency + 1)
)
names(df_clean)
## [1] "file" "subject_id"
## [3] "sex" "age"
## [5] "condition" "block"
## [7] "trial_ordinal" "exp"
## [9] "socinf" "item"
## [11] "trial_onset" "trial_offset"
## [13] "trial_duration_ms" "n_touches_total"
## [15] "n_touches_top" "n_touches_bottom"
## [17] "n_touches_screen_holder" "add_touch"
## [19] "ratio_top" "ratio_bottom"
## [21] "ratio_screen_holder" "first_touch_part"
## [23] "first_touch_onset" "latency_first_touch"
## [25] "first_top_touch_onset" "latency_first_top"
## [27] "total_touch_duration" "top_touch_duration"
## [29] "object_type" "age_months"
## [31] "age_days" "age_numeric"
## [33] "n_top_touch" "top_touch_latency"
## [35] "log_top_duration" "log_top_latency"
summary(df_clean)
## file subject_id sex age
## Length:805 41 : 17 female:409 Length:805
## Class :character 59 : 17 male :396 Class :character
## Mode :character 63 : 17 Mode :character
## 2 : 16
## 4 : 16
## 5 : 16
## (Other):706
## condition block trial_ordinal exp socinf item
## negative:356 2:404 Min. :1.000 k:378 a:434 bc : 52
## neutral :434 3:401 1st Qu.:2.000 s:427 p:371 ge : 52
## NA's : 15 Median :4.000 sc : 52
## Mean :4.453 gp : 51
## 3rd Qu.:6.000 oa : 51
## Max. :9.000 ro : 51
## (Other):496
## trial_onset trial_offset trial_duration_ms n_touches_total
## Min. : 11968 Min. : 17750 Min. : 4184 Min. : 0.00
## 1st Qu.: 75718 1st Qu.: 93706 1st Qu.: 5986 1st Qu.: 1.00
## Median :150212 Median :163644 Median :11290 Median : 3.00
## Mean :155201 Mean :171668 Mean :16467 Mean : 4.19
## 3rd Qu.:220524 3rd Qu.:237662 3rd Qu.:30000 3rd Qu.: 6.00
## Max. :439314 Max. :459828 Max. :42580 Max. :28.00
##
## n_touches_top n_touches_bottom n_touches_screen_holder add_touch
## Min. :0.00 Min. : 0.0000 Min. : 0.000 Min. :1.000
## 1st Qu.:0.00 1st Qu.: 0.0000 1st Qu.: 0.000 1st Qu.:1.000
## Median :1.00 Median : 0.0000 Median : 0.000 Median :1.000
## Mean :1.27 Mean : 0.8211 Mean : 2.099 Mean :1.147
## 3rd Qu.:2.00 3rd Qu.: 1.0000 3rd Qu.: 4.000 3rd Qu.:1.000
## Max. :9.00 Max. :11.0000 Max. :21.000 Max. :2.000
##
## ratio_top ratio_bottom ratio_screen_holder first_touch_part
## Min. :0.0000 Min. :0.0000 Min. :0.0000 Length:805
## 1st Qu.:0.1111 1st Qu.:0.0000 1st Qu.:0.0000 Class :character
## Median :0.3750 Median :0.0000 Median :0.0000 Mode :character
## Mean :0.4557 Mean :0.2094 Mean :0.3348
## 3rd Qu.:1.0000 3rd Qu.:0.3875 3rd Qu.:0.7143
## Max. :1.0000 Max. :1.0000 Max. :1.0000
## NA's :118 NA's :118 NA's :118
## first_touch_onset latency_first_touch first_top_touch_onset latency_first_top
## Min. : 12660 Min. : 0 Min. : 12750 Min. : 0
## 1st Qu.: 80739 1st Qu.: 476 1st Qu.: 85340 1st Qu.: 680
## Median :152388 Median : 987 Median :152388 Median : 1972
## Mean :157582 Mean : 3328 Mean :157727 Mean : 4964
## 3rd Qu.:221081 3rd Qu.: 2822 3rd Qu.:222156 3rd Qu.: 6360
## Max. :440164 Max. :31180 Max. :440164 Max. :37580
## NA's :118 NA's :118 NA's :268 NA's :268
## total_touch_duration top_touch_duration object_type age_months
## Min. :-203320 Min. : 0 3D:805 Min. : 1.00
## 1st Qu.: 2076 1st Qu.: 0 1st Qu.:14.00
## Median : 6052 Median : 816 Median :15.00
## Mean : 8987 Mean : 2456 Mean :14.65
## 3rd Qu.: 13090 3rd Qu.: 3332 3rd Qu.:15.00
## Max. : 88774 Max. :30702 Max. :16.00
##
## age_days age_numeric n_top_touch top_touch_latency
## Min. : 0.00 Min. : 1.394 Min. :0.00 Min. : 0
## 1st Qu.:10.00 1st Qu.:14.821 1st Qu.:0.00 1st Qu.: 680
## Median :20.00 Median :15.624 Median :1.00 Median : 1972
## Mean :17.36 Mean :15.216 Mean :1.27 Mean : 4964
## 3rd Qu.:25.00 3rd Qu.:15.986 3rd Qu.:2.00 3rd Qu.: 6360
## Max. :30.00 Max. :16.460 Max. :9.00 Max. :37580
## NA's :268
## log_top_duration log_top_latency
## Min. : 0.000 Min. : 0.000
## 1st Qu.: 0.000 1st Qu.: 6.524
## Median : 6.706 Median : 7.587
## Mean : 5.022 Mean : 7.379
## 3rd Qu.: 8.112 3rd Qu.: 8.758
## Max. :10.332 Max. :10.534
## NA's :268
df_clean <- df_clean %>%
mutate(
object_type = case_when(
block == 2 ~ "2D",
block == 3 ~ "3D",
block == "2" ~ "2D",
block == "3" ~ "3D",
TRUE ~ NA_character_
),
object_type = as.factor(object_type)
)
table(df_clean$block, df_clean$object_type, useNA = "ifany")
##
## 2D 3D
## 2 404 0
## 3 0 401
3. Linear Mixed Models
3a. Touch Duration Model
duration_model <- lmer(
log_top_duration ~ object_type * condition + object_type * age_numeric +
sex + trial_ordinal +
(1 | subject_id) + (1 | item),
data = df_clean,
na.action = na.omit
)
3b. Touch Latency Model
latency_model <- lmer(
log_top_latency ~ object_type * condition + object_type * age_numeric +
sex + trial_ordinal +
(1 | subject_id) + (1 | item),
data = df_clean,
na.action = na.omit
)
3c. Touch Frequency Model (Negative Binomial)
frequency_model_nb <- glmmTMB(
n_top_touch ~ object_type * condition + object_type * age_numeric +
sex + trial_ordinal +
(1 | subject_id) + (1 | item),
data = df_clean,
family = nbinom2,
na.action = na.omit
)
3d. Full Model Summaries
# Duration
summary(duration_model)
## Linear mixed model fit by REML. t-tests use Satterthwaite's method [
## lmerModLmerTest]
## Formula:
## log_top_duration ~ object_type * condition + object_type * age_numeric +
## sex + trial_ordinal + (1 | subject_id) + (1 | item)
## Data: df_clean
##
## REML criterion at convergence: 4001.7
##
## Scaled residuals:
## Min 1Q Median 3Q Max
## -2.6363 -0.7025 0.1783 0.6669 2.4125
##
## Random effects:
## Groups Name Variance Std.Dev.
## subject_id (Intercept) 2.631 1.622
## item (Intercept) 1.038 1.019
## Residual 7.916 2.813
## Number of obs: 790, groups: subject_id, 51; item, 16
##
## Fixed effects:
## Estimate Std. Error df t value Pr(>|t|)
## (Intercept) 0.36008 2.04042 64.14977 0.176 0.86048
## object_type3D 6.75831 1.49436 720.90875 4.523 7.14e-06
## conditionneutral 0.10359 0.54247 62.69265 0.191 0.84917
## age_numeric 0.14758 0.13382 60.72320 1.103 0.27449
## sexmale 0.40998 0.50469 46.20402 0.812 0.42076
## trial_ordinal 0.13884 0.04381 721.67919 3.169 0.00159
## object_type3D:conditionneutral 1.15344 0.41039 728.55230 2.811 0.00508
## object_type3D:age_numeric -0.28659 0.09811 721.19630 -2.921 0.00360
##
## (Intercept)
## object_type3D ***
## conditionneutral
## age_numeric
## sexmale
## trial_ordinal **
## object_type3D:conditionneutral **
## object_type3D:age_numeric **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Correlation of Fixed Effects:
## (Intr) obj_3D cndtnn ag_nmr sexmal trl_rd ob_3D:
## objct_typ3D -0.366
## conditnntrl -0.022 0.011
## age_numeric -0.961 0.359 -0.126
## sexmale 0.030 -0.002 0.039 -0.158
## trial_ordnl -0.096 -0.002 -0.003 0.000 0.008
## objct_ty3D: 0.011 -0.024 -0.377 0.048 -0.014 -0.008
## objct_t3D:_ 0.358 -0.979 0.048 -0.366 0.006 0.004 -0.130
anova(duration_model)
## Type III Analysis of Variance Table with Satterthwaite's method
## Sum Sq Mean Sq NumDF DenDF F value Pr(>F)
## object_type 188.410 188.410 1 721.11 23.8020 1.315e-06 ***
## condition 14.515 14.515 1 46.30 1.8337 0.182263
## age_numeric 0.009 0.009 1 45.57 0.0012 0.972721
## sex 5.224 5.224 1 46.20 0.6599 0.420765
## trial_ordinal 79.492 79.492 1 721.68 10.0423 0.001594 **
## object_type:condition 62.530 62.530 1 728.55 7.8995 0.005078 **
## object_type:age_numeric 67.550 67.550 1 721.20 8.5336 0.003595 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# Latency
summary(latency_model)
## Linear mixed model fit by REML. t-tests use Satterthwaite's method [
## lmerModLmerTest]
## Formula:
## log_top_latency ~ object_type * condition + object_type * age_numeric +
## sex + trial_ordinal + (1 | subject_id) + (1 | item)
## Data: df_clean
##
## REML criterion at convergence: 2162.4
##
## Scaled residuals:
## Min 1Q Median 3Q Max
## -3.9425 -0.3877 0.0790 0.5679 2.2863
##
## Random effects:
## Groups Name Variance Std.Dev.
## subject_id (Intercept) 1.200 1.0955
## item (Intercept) 0.238 0.4879
## Residual 2.988 1.7285
## Number of obs: 523, groups: subject_id, 51; item, 16
##
## Fixed effects:
## Estimate Std. Error df t value
## (Intercept) 10.989016 2.075739 212.415314 5.294
## object_type3D -4.137927 1.837938 463.498309 -2.251
## conditionneutral -0.016047 0.406134 68.917991 -0.040
## age_numeric -0.171963 0.134580 196.380186 -1.278
## sexmale -0.211370 0.354255 39.985716 -0.597
## trial_ordinal -0.056990 0.033733 461.028586 -1.689
## object_type3D:conditionneutral 0.008664 0.329197 477.008433 0.026
## object_type3D:age_numeric 0.218743 0.119255 464.993404 1.834
## Pr(>|t|)
## (Intercept) 2.98e-07 ***
## object_type3D 0.0248 *
## conditionneutral 0.9686
## age_numeric 0.2028
## sexmale 0.5541
## trial_ordinal 0.0918 .
## object_type3D:conditionneutral 0.9790
## object_type3D:age_numeric 0.0673 .
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Correlation of Fixed Effects:
## (Intr) obj_3D cndtnn ag_nmr sexmal trl_rd ob_3D:
## objct_typ3D -0.763
## conditnntrl -0.042 0.038
## age_numeric -0.980 0.752 -0.072
## sexmale 0.022 -0.006 0.065 -0.116
## trial_ordnl -0.113 0.048 -0.001 0.036 0.018
## objct_ty3D: 0.033 -0.041 -0.490 0.024 -0.053 -0.008
## objct_t3D:_ 0.756 -0.991 0.010 -0.755 0.013 -0.045 -0.061
anova(latency_model)
## Type III Analysis of Variance Table with Satterthwaite's method
## Sum Sq Mean Sq NumDF DenDF F value Pr(>F)
## object_type 15.1025 15.1025 1 464.49 5.0551 0.02502 *
## condition 0.0032 0.0032 1 41.35 0.0011 0.97388
## age_numeric 1.2250 1.2250 1 63.66 0.4100 0.52425
## sex 1.0636 1.0636 1 39.99 0.3560 0.55410
## trial_ordinal 8.5270 8.5270 1 461.03 2.8542 0.09181 .
## object_type:condition 0.0021 0.0021 1 477.01 0.0007 0.97901
## object_type:age_numeric 10.0515 10.0515 1 464.99 3.3644 0.06726 .
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# Frequency
summary(frequency_model_nb)
## Family: nbinom2 ( log )
## Formula:
## n_top_touch ~ object_type * condition + object_type * age_numeric +
## sex + trial_ordinal + (1 | subject_id) + (1 | item)
## Data: df_clean
##
## AIC BIC logLik -2*log(L) df.resid
## 2304.5 2355.9 -1141.3 2282.5 779
##
## Random effects:
##
## Conditional model:
## Groups Name Variance Std.Dev.
## subject_id (Intercept) 0.23875 0.4886
## item (Intercept) 0.05074 0.2252
## Number of obs: 790, groups: subject_id, 51; item, 16
##
## Dispersion parameter for nbinom2 family (): 7.24
##
## Conditional model:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -1.799748 0.929133 -1.937 0.0527 .
## object_type3D 2.013127 0.841160 2.393 0.0167 *
## conditionneutral -0.008747 0.176957 -0.049 0.9606
## age_numeric 0.104467 0.060322 1.732 0.0833 .
## sexmale 0.084965 0.158249 0.537 0.5913
## trial_ordinal 0.021147 0.015508 1.364 0.1727
## object_type3D:conditionneutral 0.153916 0.143623 1.072 0.2839
## object_type3D:age_numeric -0.117476 0.054490 -2.156 0.0311 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
drop1(frequency_model_nb, test = "Chisq")
## Single term deletions
##
## Model:
## n_top_touch ~ object_type * condition + object_type * age_numeric +
## sex + trial_ordinal + (1 | subject_id) + (1 | item)
## Df AIC LRT Pr(>Chi)
## <none> 2304.5
## sex 1 2302.8 0.2874 0.59187
## trial_ordinal 1 2304.4 1.8632 0.17226
## object_type:condition 1 2303.7 1.1507 0.28341
## object_type:age_numeric 1 2308.4 5.9347 0.01485 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
4. Does 2D vs 3D Significantly Affect Top Touch Outcomes?
# ── Touch Duration ────────────────────────────────────────────────────────────
cat("=== TOUCH DURATION: Effect of 2D vs 3D ===\n")
## === TOUCH DURATION: Effect of 2D vs 3D ===
anova(duration_model) # F-test for object_type and object_type:condition
## Type III Analysis of Variance Table with Satterthwaite's method
## Sum Sq Mean Sq NumDF DenDF F value Pr(>F)
## object_type 188.410 188.410 1 721.11 23.8020 1.315e-06 ***
## condition 14.515 14.515 1 46.30 1.8337 0.182263
## age_numeric 0.009 0.009 1 45.57 0.0012 0.972721
## sex 5.224 5.224 1 46.20 0.6599 0.420765
## trial_ordinal 79.492 79.492 1 721.68 10.0423 0.001594 **
## object_type:condition 62.530 62.530 1 728.55 7.8995 0.005078 **
## object_type:age_numeric 67.550 67.550 1 721.20 8.5336 0.003595 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# Estimated marginal means for 2D vs 3D (collapsing over condition)
emm_dur <- emmeans(duration_model, ~ object_type)
## NOTE: Results may be misleading due to involvement in interactions
print(emm_dur)
## object_type emmean SE df lower.CL upper.CL
## 2D 3.48 0.371 41.4 2.73 4.23
## 3D 6.46 0.371 41.4 5.71 7.21
##
## Results are averaged over the levels of: condition, sex
## Degrees-of-freedom method: kenward-roger
## Confidence level used: 0.95
pairs(emm_dur) # pairwise contrast: 2D vs 3D
## contrast estimate SE df t.ratio p.value
## 2D - 3D -2.98 0.205 732 -14.492 <0.0001
##
## Results are averaged over the levels of: condition, sex
## Degrees-of-freedom method: kenward-roger
# ── Touch Latency ─────────────────────────────────────────────────────────────
cat("\n=== TOUCH LATENCY: Effect of 2D vs 3D ===\n")
##
## === TOUCH LATENCY: Effect of 2D vs 3D ===
anova(latency_model)
## Type III Analysis of Variance Table with Satterthwaite's method
## Sum Sq Mean Sq NumDF DenDF F value Pr(>F)
## object_type 15.1025 15.1025 1 464.49 5.0551 0.02502 *
## condition 0.0032 0.0032 1 41.35 0.0011 0.97388
## age_numeric 1.2250 1.2250 1 63.66 0.4100 0.52425
## sex 1.0636 1.0636 1 39.99 0.3560 0.55410
## trial_ordinal 8.5270 8.5270 1 461.03 2.8542 0.09181 .
## object_type:condition 0.0021 0.0021 1 477.01 0.0007 0.97901
## object_type:age_numeric 10.0515 10.0515 1 464.99 3.3644 0.06726 .
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
emm_lat <- emmeans(latency_model, ~ object_type)
## NOTE: Results may be misleading due to involvement in interactions
print(emm_lat)
## object_type emmean SE df lower.CL upper.CL
## 2D 7.99 0.237 62.3 7.52 8.47
## 3D 7.19 0.224 49.9 6.74 7.64
##
## Results are averaged over the levels of: condition, sex
## Degrees-of-freedom method: kenward-roger
## Confidence level used: 0.95
pairs(emm_lat)
## contrast estimate SE df t.ratio p.value
## 2D - 3D 0.796 0.166 483 4.805 <0.0001
##
## Results are averaged over the levels of: condition, sex
## Degrees-of-freedom method: kenward-roger
# ── Touch Frequency ───────────────────────────────────────────────────────────
cat("\n=== TOUCH FREQUENCY: Effect of 2D vs 3D ===\n")
##
## === TOUCH FREQUENCY: Effect of 2D vs 3D ===
drop1(frequency_model_nb, test = "Chisq") # likelihood-ratio test for each term
## Single term deletions
##
## Model:
## n_top_touch ~ object_type * condition + object_type * age_numeric +
## sex + trial_ordinal + (1 | subject_id) + (1 | item)
## Df AIC LRT Pr(>Chi)
## <none> 2304.5
## sex 1 2302.8 0.2874 0.59187
## trial_ordinal 1 2304.4 1.8632 0.17226
## object_type:condition 1 2303.7 1.1507 0.28341
## object_type:age_numeric 1 2308.4 5.9347 0.01485 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
emm_freq <- emmeans(frequency_model_nb, ~ object_type, type = "response")
## NOTE: Results may be misleading due to involvement in interactions
print(emm_freq)
## object_type response SE df asymp.LCL asymp.UCL
## 2D 0.924 0.0982 Inf 0.75 1.14
## 3D 1.252 0.1290 Inf 1.02 1.53
##
## Results are averaged over the levels of: condition, sex
## Confidence level used: 0.95
## Intervals are back-transformed from the log scale
pairs(emm_freq)
## contrast ratio SE df null z.ratio p.value
## 2D / 3D 0.738 0.0541 Inf 1 -4.146 <0.0001
##
## Results are averaged over the levels of: condition, sex
## Tests are performed on the log scale
5. Prepare Plot Dataset
df_plot <- df_clean %>%
mutate(
object_type = factor(object_type, levels = c("2D", "3D")),
condition = factor(condition, levels = c("negative", "neutral")),
socinf = factor(socinf)
)
# Object category mapping (4 categories based on item codes)
df_plot <- df_plot %>%
mutate(
object_category = case_when(
item %in% c("ro", "da", "ba", "ge") ~ "Plants",
item %in% c("gp", "bc", "sa", "oa") ~ "Novel Artifacts",
item %in% c("ra", "ha", "ke", "kn") ~ "Familiar Artifacts",
item %in% c("ur", "sc", "sn", "st") ~ "Natural Objects",
TRUE ~ NA_character_
),
object_category = factor(object_category,
levels = c("Plants",
"Novel Artifacts",
"Familiar Artifacts",
"Natural Objects"))
)
6. Graphs 1–3: Top Touch Outcomes by Trial Number (1–8)
Graph 1 — Touch Latency by Trial Number
g1_data <- df_plot %>%
group_by(trial_ordinal) %>%
summarise(
mean_val = mean(top_touch_latency, na.rm = TRUE),
sd_val = sd(top_touch_latency, na.rm = TRUE),
n = sum(!is.na(top_touch_latency)),
.groups = "drop"
) %>%
mutate(se = sd_val / sqrt(n))
ggplot(g1_data, aes(x = trial_ordinal, y = mean_val)) +
geom_line(linewidth = 0.8, colour = "#4472C4") +
geom_point(size = 3, colour = "#4472C4") +
geom_errorbar(
aes(ymin = mean_val - sd_val, ymax = mean_val + sd_val),
width = 0.2, colour = "#4472C4"
) +
scale_x_continuous(breaks = 1:8) +
labs(
title = "Graph 1: Top Touch Latency by Trial Number",
x = "Trial Number",
y = "Mean Latency to First Top Touch (ms) ± SD"
) +
theme_classic(base_size = 14)

Graph 2 — Touch Duration by Trial Number
g2_data <- df_plot %>%
group_by(trial_ordinal) %>%
summarise(
mean_val = mean(top_touch_duration, na.rm = TRUE),
sd_val = sd(top_touch_duration, na.rm = TRUE),
n = sum(!is.na(top_touch_duration)),
.groups = "drop"
) %>%
mutate(se = sd_val / sqrt(n))
ggplot(g2_data, aes(x = trial_ordinal, y = mean_val)) +
geom_line(linewidth = 0.8, colour = "#ED7D31") +
geom_point(size = 3, colour = "#ED7D31") +
geom_errorbar(
aes(ymin = mean_val - sd_val, ymax = mean_val + sd_val),
width = 0.2, colour = "#ED7D31"
) +
scale_x_continuous(breaks = 1:8) +
labs(
title = "Graph 2: Top Touch Duration by Trial Number",
x = "Trial Number",
y = "Mean Top Touch Duration (ms) ± SD"
) +
theme_classic(base_size = 14)

Graph 3 — Touch Frequency by Trial Number
g3_data <- df_plot %>%
group_by(trial_ordinal) %>%
summarise(
mean_val = mean(n_top_touch, na.rm = TRUE),
sd_val = sd(n_top_touch, na.rm = TRUE),
n = sum(!is.na(n_top_touch)),
.groups = "drop"
) %>%
mutate(se = sd_val / sqrt(n))
ggplot(g3_data, aes(x = trial_ordinal, y = mean_val)) +
geom_line(linewidth = 0.8, colour = "#70AD47") +
geom_point(size = 3, colour = "#70AD47") +
geom_errorbar(
aes(ymin = mean_val - sd_val, ymax = mean_val + sd_val),
width = 0.2, colour = "#70AD47"
) +
scale_x_continuous(breaks = 1:8) +
labs(
title = "Graph 3: Top Touch Frequency by Trial Number",
x = "Trial Number",
y = "Mean Number of Top Touches ± SD"
) +
theme_classic(base_size = 14)

7. Graphs 4–6: Top Touch Outcomes by 2D/3D × Social Info
Condition
Graph 4 — Touch Latency by 2D/3D and Social Info
g4_data <- df_plot %>%
group_by(condition, object_type) %>%
summarise(
mean_val = mean(top_touch_latency, na.rm = TRUE),
sd_val = sd(top_touch_latency, na.rm = TRUE),
n = sum(!is.na(top_touch_latency)),
.groups = "drop"
) %>%
mutate(se = sd_val / sqrt(n))
ggplot(g4_data, aes(x = condition, y = mean_val, fill = object_type)) +
geom_col(position = position_dodge(width = 0.8), width = 0.7) +
geom_errorbar(
aes(ymin = mean_val - sd_val, ymax = mean_val + sd_val),
position = position_dodge(width = 0.8),
width = 0.2
) +
scale_fill_manual(values = c("2D" = "#4472C4", "3D" = "#ED7D31")) +
labs(
title = "Graph 4: Top Touch Latency by Condition and Object Type",
x = "Condition",
y = "Mean Latency to First Top Touch (ms) ± SD",
fill = "Object Type"
) +
theme_classic(base_size = 14)

Graph 5 — Touch Duration by 2D/3D and Social Info
g5_data <- df_plot %>%
group_by(condition, object_type) %>%
summarise(
mean_val = mean(top_touch_duration, na.rm = TRUE),
sd_val = sd(top_touch_duration, na.rm = TRUE),
n = sum(!is.na(top_touch_duration)),
.groups = "drop"
) %>%
mutate(se = sd_val / sqrt(n))
ggplot(g5_data, aes(x = condition, y = mean_val, fill = object_type)) +
geom_col(position = position_dodge(width = 0.8), width = 0.7) +
geom_errorbar(
aes(ymin = mean_val - sd_val, ymax = mean_val + sd_val),
position = position_dodge(width = 0.8),
width = 0.2
) +
scale_fill_manual(values = c("2D" = "#4472C4", "3D" = "#ED7D31")) +
labs(
title = "Graph 5: Top Touch Duration by Condition and Object Type",
x = "Condition",
y = "Mean Top Touch Duration (ms) ± SD",
fill = "Object Type"
) +
theme_classic(base_size = 14)

Graph 6 — Touch Frequency by 2D/3D and Social Info
g6_data <- df_plot %>%
group_by(condition, object_type) %>%
summarise(
mean_val = mean(n_top_touch, na.rm = TRUE),
sd_val = sd(n_top_touch, na.rm = TRUE),
n = sum(!is.na(n_top_touch)),
.groups = "drop"
) %>%
mutate(se = sd_val / sqrt(n))
ggplot(g6_data, aes(x = condition, y = mean_val, fill = object_type)) +
geom_col(position = position_dodge(width = 0.8), width = 0.7) +
geom_errorbar(
aes(ymin = mean_val - sd_val, ymax = mean_val + sd_val),
position = position_dodge(width = 0.8),
width = 0.2
) +
scale_fill_manual(values = c("2D" = "#4472C4", "3D" = "#ED7D31")) +
labs(
title = "Graph 6: Top Touch Frequency by Condition and Object Type",
x = "Condition",
y = "Mean Number of Top Touches ± SD",
fill = "Object Type"
) +
theme_classic(base_size = 14)

8. Graphs 7–9: Top Touch Outcomes by Object Category × 2D/3D, Split
by Social Info
Graph 7 — Touch Latency by Object Category × 2D/3D (split by Social
Info)
g7_data <- df_plot %>%
group_by(condition, object_category, object_type) %>%
summarise(
mean_val = mean(top_touch_latency, na.rm = TRUE),
sd_val = sd(top_touch_latency, na.rm = TRUE),
n = sum(!is.na(top_touch_latency)),
.groups = "drop"
) %>%
mutate(se = sd_val / sqrt(n))
ggplot(g7_data, aes(x = object_category, y = mean_val, fill = object_type)) +
geom_col(position = position_dodge(width = 0.8), width = 0.7) +
geom_errorbar(
aes(ymin = mean_val - sd_val, ymax = mean_val + sd_val),
position = position_dodge(width = 0.8),
width = 0.2
) +
facet_wrap(~ condition, labeller = labeller(condition = c(
negative = "Condition: Negative",
neutral = "Condition: Neutral"
))) +
scale_fill_manual(values = c("2D" = "#4472C4", "3D" = "#ED7D31")) +
labs(
title = "Graph 7: Top Touch Latency by Object Category and Object Type",
x = "Object Category",
y = "Mean Latency to First Top Touch (ms) ± SD",
fill = "Object Type"
) +
theme_classic(base_size = 13) +
theme(axis.text.x = element_text(angle = 30, hjust = 1))

Graph 8 — Touch Duration by Object Category × 2D/3D (split by Social
Info)
g8_data <- df_plot %>%
group_by(condition, object_category, object_type) %>%
summarise(
mean_val = mean(top_touch_duration, na.rm = TRUE),
sd_val = sd(top_touch_duration, na.rm = TRUE),
n = sum(!is.na(top_touch_duration)),
.groups = "drop"
) %>%
mutate(se = sd_val / sqrt(n))
ggplot(g8_data, aes(x = object_category, y = mean_val, fill = object_type)) +
geom_col(position = position_dodge(width = 0.8), width = 0.7) +
geom_errorbar(
aes(ymin = mean_val - sd_val, ymax = mean_val + sd_val),
position = position_dodge(width = 0.8),
width = 0.2
) +
facet_wrap(~ condition, labeller = labeller(condition = c(
negative = "Condition: Negative",
neutral = "Condition: Neutral"
))) +
scale_fill_manual(values = c("2D" = "#4472C4", "3D" = "#ED7D31")) +
labs(
title = "Graph 8: Top Touch Duration by Object Category and Object Type",
x = "Object Category",
y = "Mean Top Touch Duration (ms) ± SD",
fill = "Object Type"
) +
theme_classic(base_size = 13) +
theme(axis.text.x = element_text(angle = 30, hjust = 1))

Graph 9 — Touch Frequency by Object Category × 2D/3D (split by
Social Info)
g9_data <- df_plot %>%
group_by(condition, object_category, object_type) %>%
summarise(
mean_val = mean(n_top_touch, na.rm = TRUE),
sd_val = sd(n_top_touch, na.rm = TRUE),
n = sum(!is.na(n_top_touch)),
.groups = "drop"
) %>%
mutate(se = sd_val / sqrt(n))
ggplot(g9_data, aes(x = object_category, y = mean_val, fill = object_type)) +
geom_col(position = position_dodge(width = 0.8), width = 0.7) +
geom_errorbar(
aes(ymin = mean_val - sd_val, ymax = mean_val + sd_val),
position = position_dodge(width = 0.8),
width = 0.2
) +
facet_wrap(~ condition, labeller = labeller(condition = c(
negative = "Condition: Negative",
neutral = "Condition: Neutral"
))) +
scale_fill_manual(values = c("2D" = "#4472C4", "3D" = "#ED7D31")) +
labs(
title = "Graph 9: Top Touch Frequency by Object Category and Object Type",
x = "Object Category",
y = "Mean Number of Top Touches ± SD",
fill = "Object Type"
) +
theme_classic(base_size = 13) +
theme(axis.text.x = element_text(angle = 30, hjust = 1))

9. Original Graphs: Outcomes by Condition × 2D/3D (from v1)
Touch Duration by Condition and Object Type
duration_bar <- df_plot %>%
group_by(condition, object_type) %>%
summarise(
mean_duration = mean(top_touch_duration, na.rm = TRUE),
se_duration = sd(top_touch_duration, na.rm = TRUE) / sqrt(sum(!is.na(top_touch_duration))),
.groups = "drop"
)
ggplot(duration_bar, aes(x = condition, y = mean_duration, fill = object_type)) +
geom_col(position = position_dodge(width = 0.8), width = 0.7) +
geom_errorbar(
aes(ymin = mean_duration - se_duration, ymax = mean_duration + se_duration),
position = position_dodge(width = 0.8),
width = 0.2
) +
scale_fill_manual(values = c("2D" = "#4472C4", "3D" = "#ED7D31")) +
labs(
title = "Top Touch Duration by Condition and Object Type",
x = "Condition",
y = "Mean Top Touch Duration (ms) ± SE",
fill = "Object Type"
) +
theme_classic(base_size = 14)

Touch Latency by Condition and Object Type
latency_bar <- df_plot %>%
group_by(condition, object_type) %>%
summarise(
mean_latency = mean(top_touch_latency, na.rm = TRUE),
se_latency = sd(top_touch_latency, na.rm = TRUE) / sqrt(sum(!is.na(top_touch_latency))),
.groups = "drop"
)
ggplot(latency_bar, aes(x = condition, y = mean_latency, fill = object_type)) +
geom_col(position = position_dodge(width = 0.8), width = 0.7) +
geom_errorbar(
aes(ymin = mean_latency - se_latency, ymax = mean_latency + se_latency),
position = position_dodge(width = 0.8),
width = 0.2
) +
scale_fill_manual(values = c("2D" = "#4472C4", "3D" = "#ED7D31")) +
labs(
title = "Top Touch Latency by Condition and Object Type",
x = "Condition",
y = "Mean Latency to First Top Touch (ms) ± SE",
fill = "Object Type"
) +
theme_classic(base_size = 14)

Touch Frequency by Condition and Object Type
frequency_bar <- df_plot %>%
group_by(condition, object_type) %>%
summarise(
mean_touch = mean(n_top_touch, na.rm = TRUE),
se_touch = sd(n_top_touch, na.rm = TRUE) / sqrt(sum(!is.na(n_top_touch))),
.groups = "drop"
)
ggplot(frequency_bar, aes(x = condition, y = mean_touch, fill = object_type)) +
geom_col(position = position_dodge(width = 0.8), width = 0.7) +
geom_errorbar(
aes(ymin = mean_touch - se_touch, ymax = mean_touch + se_touch),
position = position_dodge(width = 0.8),
width = 0.2
) +
scale_fill_manual(values = c("2D" = "#4472C4", "3D" = "#ED7D31")) +
labs(
title = "Top Touch Frequency by Condition and Object Type",
x = "Condition",
y = "Mean Number of Top Touches ± SE",
fill = "Object Type"
) +
theme_classic(base_size = 14)

10. Graphs 10–12: Top Touch Outcomes for Each of the 16 Objects
Graph 10 — Touch Latency by Object Item
g10_data <- df_plot %>%
group_by(item) %>%
summarise(
mean_val = mean(top_touch_latency, na.rm = TRUE),
sd_val = sd(top_touch_latency, na.rm = TRUE),
n = sum(!is.na(top_touch_latency)),
.groups = "drop"
) %>%
mutate(
se = sd_val / sqrt(n),
item = fct_reorder(item, mean_val)
)
ggplot(g10_data, aes(x = item, y = mean_val)) +
geom_col(fill = "#4472C4", width = 0.7) +
geom_errorbar(
aes(ymin = mean_val - sd_val, ymax = mean_val + sd_val),
width = 0.3
) +
labs(
title = "Graph 10: Top Touch Latency for Each of the 16 Objects",
x = "Object (Item)",
y = "Mean Latency to First Top Touch (ms) ± SD"
) +
theme_classic(base_size = 13) +
theme(axis.text.x = element_text(angle = 45, hjust = 1))

Graph 11 — Touch Duration by Object Item
g11_data <- df_plot %>%
group_by(item) %>%
summarise(
mean_val = mean(top_touch_duration, na.rm = TRUE),
sd_val = sd(top_touch_duration, na.rm = TRUE),
n = sum(!is.na(top_touch_duration)),
.groups = "drop"
) %>%
mutate(
se = sd_val / sqrt(n),
item = fct_reorder(item, mean_val)
)
ggplot(g11_data, aes(x = item, y = mean_val)) +
geom_col(fill = "#ED7D31", width = 0.7) +
geom_errorbar(
aes(ymin = mean_val - sd_val, ymax = mean_val + sd_val),
width = 0.3
) +
labs(
title = "Graph 11: Top Touch Duration for Each of the 16 Objects",
x = "Object (Item)",
y = "Mean Top Touch Duration (ms) ± SD"
) +
theme_classic(base_size = 13) +
theme(axis.text.x = element_text(angle = 45, hjust = 1))

Graph 12 — Touch Frequency by Object Item
g12_data <- df_plot %>%
group_by(item) %>%
summarise(
mean_val = mean(n_top_touch, na.rm = TRUE),
sd_val = sd(n_top_touch, na.rm = TRUE),
n = sum(!is.na(n_top_touch)),
.groups = "drop"
) %>%
mutate(
se = sd_val / sqrt(n),
item = fct_reorder(item, mean_val)
)
ggplot(g12_data, aes(x = item, y = mean_val)) +
geom_col(fill = "#70AD47", width = 0.7) +
geom_errorbar(
aes(ymin = mean_val - sd_val, ymax = mean_val + sd_val),
width = 0.3
) +
labs(
title = "Graph 12: Top Touch Frequency for Each of the 16 Objects",
x = "Object (Item)",
y = "Mean Number of Top Touches ± SD"
) +
theme_classic(base_size = 13) +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
