IfADO ReStoWa Pilot Testing

Walking task only pilot

library(tidyverse)
## Warning: package 'tidyverse' was built under R version 4.2.3
## Warning: package 'tidyr' was built under R version 4.2.3
## Warning: package 'readr' was built under R version 4.2.3
## Warning: package 'dplyr' was built under R version 4.2.3
## Warning: package 'stringr' was built under R version 4.2.3
## Warning: package 'forcats' was built under R version 4.2.3
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ ggplot2   3.5.2     ✔ tibble    3.3.0
## ✔ lubridate 1.9.4     ✔ tidyr     1.3.1
## ✔ purrr     1.1.0     
## ── 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(dplyr)
library(readxl)
library(lme4)
## Loading required package: Matrix
## 
## Attaching package: 'Matrix'
## 
## The following objects are masked from 'package:tidyr':
## 
##     expand, pack, unpack
library(car)
## Loading required package: carData
## 
## Attaching package: 'car'
## 
## The following object is masked from 'package:dplyr':
## 
##     recode
## 
## The following object is masked from 'package:purrr':
## 
##     some
library(emmeans)
## Welcome to emmeans.
## Caution: You lose important information if you filter this package's results.
## See '? untidy'
library(effects)
## Warning: package 'effects' was built under R version 4.2.3
## lattice theme set by effectsTheme()
## See ?effectsTheme for details.
library(patchwork)
library(knitr)


# Accuracy metrics
walkingPilot <- read_excel("WalkingPilotConditions.xlsx", sheet = 2)
## New names:
## • `` -> `...4`
## • `` -> `...9`
## • `` -> `...10`
## • `` -> `...12`
## • `` -> `...13`
walkingPilot <- walkingPilot %>%
  mutate(condition = case_when(
    grepl("short", walkingPilot$`VP/Speed`, ignore.case = TRUE) ~ "short",
    grepl("normal", walkingPilot$`VP/Speed`, ignore.case = TRUE) ~ "normal",
    grepl("long", walkingPilot$`VP/Speed`, ignore.case = TRUE) ~ "long",
    TRUE ~ NA_character_
  ))

walkingPilot$condition <- factor(walkingPilot$condition, levels = c("short", "normal", "long"))

walkingPilot <- walkingPilot %>% select( "VP/Speed", '0.8', '0.9', '1', condition)
walkingPilot <- na.omit(walkingPilot)

# removing outliers (VPs: 5 (highest acc: 24%) + 7 highest acc: 43%)
walkingPilot <- walkingPilot %>% filter(!grepl("^5|^7", `VP/Speed`))

# long format transformation 
walkingPilot_long <- walkingPilot %>% 
  rename(
    ID = 1,
    Speed_0.8 = 2,
    Speed_0.9 = 3,
    Speed_1.0 = 4,
    Condition = 5
  ) %>% 
  pivot_longer(
    cols = starts_with("Speed_"),
         names_to = "Speed",
         values_to = "Accuracy") %>% 
    mutate(
      Speed = as.numeric(str_remove(Speed, "Speed_")),
      Condition = as.factor(Condition)
    )

plot_walking_pilot_conditions <- walkingPilot_long %>%
  group_by(Speed, Condition) %>%
  summarise(mean_acc = mean(Accuracy, na.rm = TRUE),
            se = sd(Accuracy, na.rm = TRUE)/sqrt(n())) %>%
  ggplot(aes(x = factor(Speed), y = mean_acc, fill = Condition)) +
  geom_col(position = position_dodge(0.8), width = 0.7) +
  geom_errorbar(aes(ymin = mean_acc - se, ymax = mean_acc + se), 
                width = 0.2, position = position_dodge(0.8)) +
  labs(x = "Treadmill Speed in (m/s)",
       y = "Mean Accuracy in (%)",
       fill = "Step Distance") +
  scale_color_discrete(labels = c(
    "short" = "Short",
    "normal" = "Normal",
    "long" = "Long"
  )) + 
    theme_minimal()
## `summarise()` has grouped output by 'Speed'. You can override using the
## `.groups` argument.
summary_table <- walkingPilot_long %>%
  group_by(Speed, Condition) %>%
  summarise(
    mean_acc = mean(Accuracy, na.rm = TRUE),
    sd = sd(Accuracy, na.rm = TRUE),
    n = sum(!is.na(Accuracy)),
    se = sd / sqrt(n),
    .groups = "drop"
  )

print(summary_table)
## # A tibble: 9 × 6
##   Speed Condition mean_acc    sd     n    se
##   <dbl> <fct>        <dbl> <dbl> <int> <dbl>
## 1   0.8 short         71.6  21.1     8  7.47
## 2   0.8 normal        75.4  10.6     8  3.74
## 3   0.8 long          69.1  19.9     8  7.04
## 4   0.9 short         58.7  19.8     8  6.99
## 5   0.9 normal        63.7  16.1     8  5.71
## 6   0.9 long          62.2  12.9     8  4.57
## 7   1   short         44.0  21.0     8  7.41
## 8   1   normal        45.3  18.0     8  6.36
## 9   1   long          57.9  14.9     8  5.26
# clearing up IDs to match across different conditions
walkingPilot_long <- walkingPilot_long %>% 
  mutate(ID = sub("_.*", "", ID))

str(walkingPilot_long)
## tibble [72 × 4] (S3: tbl_df/tbl/data.frame)
##  $ ID       : chr [1:72] "1" "1" "1" "1" ...
##  $ Condition: Factor w/ 3 levels "short","normal",..: 1 1 1 2 2 2 3 3 3 1 ...
##  $ Speed    : num [1:72] 0.8 0.9 1 0.8 0.9 1 0.8 0.9 1 0.8 ...
##  $ Accuracy : num [1:72] 91 85.6 48.8 87.6 85.1 ...
walkingPilot_long$Speed <- as.factor(walkingPilot_long$Speed)
walkingPilot_long$ID <- as.factor(walkingPilot_long$ID)

# linear mixed model
single_task_model <- lmer(Accuracy ~ Speed * Condition + (1|ID), data = walkingPilot_long, REML = FALSE)

Anova(single_task_model) # Speed significantly affected performance
## Analysis of Deviance Table (Type II Wald chisquare tests)
## 
## Response: Accuracy
##                  Chisq Df Pr(>Chisq)    
## Speed           52.252  2  4.504e-12 ***
## Condition        2.576  2     0.2758    
## Speed:Condition  7.421  4     0.1152    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#summary(single_task_model)
single_task_emmeans <- emmeans(single_task_model, pairwise ~ Speed * Condition)
print(single_task_emmeans)
## $emmeans
##  Speed Condition emmean   SE   df lower.CL upper.CL
##  0.8   short       71.6 6.19 24.2     58.8     84.3
##  0.9   short       58.7 6.19 24.2     45.9     71.5
##  1     short       44.0 6.19 24.2     31.2     56.7
##  0.8   normal      75.4 6.19 24.2     62.7     88.2
##  0.9   normal      63.7 6.19 24.2     51.0     76.5
##  1     normal      45.3 6.19 24.2     32.6     58.1
##  0.8   long        69.1 6.19 24.2     56.3     81.9
##  0.9   long        62.2 6.19 24.2     49.5     75.0
##  1     long        57.9 6.19 24.2     45.1     70.7
## 
## Degrees-of-freedom method: kenward-roger 
## Confidence level used: 0.95 
## 
## $contrasts
##  contrast                          estimate   SE   df t.ratio p.value
##  Speed0.8 short - Speed0.9 short     12.870 5.89 73.1   2.185  0.4262
##  Speed0.8 short - Speed1 short       27.608 5.89 73.1   4.687  0.0004
##  Speed0.8 short - Speed0.8 normal    -3.857 5.89 73.1  -0.655  0.9992
##  Speed0.8 short - Speed0.9 normal     7.835 5.89 73.1   1.330  0.9189
##  Speed0.8 short - Speed1 normal      26.242 5.89 73.1   4.455  0.0009
##  Speed0.8 short - Speed0.8 long       2.491 5.89 73.1   0.423  1.0000
##  Speed0.8 short - Speed0.9 long       9.328 5.89 73.1   1.584  0.8105
##  Speed0.8 short - Speed1 long        13.679 5.89 73.1   2.322  0.3428
##  Speed0.9 short - Speed1 short       14.738 5.89 73.1   2.502  0.2483
##  Speed0.9 short - Speed0.8 normal   -16.727 5.89 73.1  -2.840  0.1216
##  Speed0.9 short - Speed0.9 normal    -5.035 5.89 73.1  -0.855  0.9946
##  Speed0.9 short - Speed1 normal      13.373 5.89 73.1   2.270  0.3734
##  Speed0.9 short - Speed0.8 long     -10.379 5.89 73.1  -1.762  0.7063
##  Speed0.9 short - Speed0.9 long      -3.542 5.89 73.1  -0.601  0.9996
##  Speed0.9 short - Speed1 long         0.809 5.89 73.1   0.137  1.0000
##  Speed1 short - Speed0.8 normal     -31.465 5.89 73.1  -5.342  <.0001
##  Speed1 short - Speed0.9 normal     -19.773 5.89 73.1  -3.357  0.0322
##  Speed1 short - Speed1 normal        -1.365 5.89 73.1  -0.232  1.0000
##  Speed1 short - Speed0.8 long       -25.116 5.89 73.1  -4.264  0.0018
##  Speed1 short - Speed0.9 long       -18.280 5.89 73.1  -3.103  0.0638
##  Speed1 short - Speed1 long         -13.929 5.89 73.1  -2.365  0.3189
##  Speed0.8 normal - Speed0.9 normal   11.693 5.89 73.1   1.985  0.5585
##  Speed0.8 normal - Speed1 normal     30.100 5.89 73.1   5.110  0.0001
##  Speed0.8 normal - Speed0.8 long      6.349 5.89 73.1   1.078  0.9757
##  Speed0.8 normal - Speed0.9 long     13.185 5.89 73.1   2.238  0.3928
##  Speed0.8 normal - Speed1 long       17.536 5.89 73.1   2.977  0.0877
##  Speed0.9 normal - Speed1 normal     18.407 5.89 73.1   3.125  0.0604
##  Speed0.9 normal - Speed0.8 long     -5.344 5.89 73.1  -0.907  0.9919
##  Speed0.9 normal - Speed0.9 long      1.492 5.89 73.1   0.253  1.0000
##  Speed0.9 normal - Speed1 long        5.844 5.89 73.1   0.992  0.9855
##  Speed1 normal - Speed0.8 long      -23.751 5.89 73.1  -4.032  0.0040
##  Speed1 normal - Speed0.9 long      -16.915 5.89 73.1  -2.872  0.1129
##  Speed1 normal - Speed1 long        -12.564 5.89 73.1  -2.133  0.4597
##  Speed0.8 long - Speed0.9 long        6.836 5.89 73.1   1.161  0.9621
##  Speed0.8 long - Speed1 long         11.188 5.89 73.1   1.899  0.6164
##  Speed0.9 long - Speed1 long          4.351 5.89 73.1   0.739  0.9980
## 
## Degrees-of-freedom method: kenward-roger 
## P value adjustment: tukey method for comparing a family of 9 estimates
single_task_effects <- allEffects(single_task_model)
single_task_effects_model <- as.data.frame(single_task_effects[[1]])

single_task_plot <- ggplot(single_task_effects_model, aes(x = Speed, y = fit, color = Condition, linetype = Condition)) +
  geom_path(aes(group = Condition), size = 1) +
  geom_point(size = 3) +
  scale_color_manual(values = c("short" = "green", "normal" = "tomato", "long"  = "blue")) +
  scale_linetype_manual(values = c("short" = "dashed", "normal" = "solid", "long"   = "dashed")) +
  scale_x_discrete(labels = c("1" = "1.0")) +
  ylab("Mean Accuracy in (%)") +
  xlab("Treadmill Speeds in (m/s)")
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
plot(single_task_plot)

# Subjective ratings
NASA_pilot <- read_excel("ReStoWa_WalkingPilot_NASA-TLX.xlsx", n_max = 10)
## New names:
## • `` -> `...1`
NASA_pilot <- NASA_pilot %>% rename(VP = ...1)

# remove outliers to keep consistent with performance data
NASA_pilot <- NASA_pilot %>% filter(VP != "pilot_05", VP != "pilot_07")

# replace NAs with average score 10 to keep pilot_06
NASA_pilot <- NASA_pilot %>% 
  mutate(across(everything(), ~ replace_na(.x, 10)))

# summarise per walking speed

NASA_pilot <- NASA_pilot %>% 
  mutate(short_0.8 = rowMeans(select(., starts_with("short_0.8")), na.rm = TRUE),
         normal_0.8 = rowMeans(select(., starts_with("normal_0.8")), na.rm = TRUE),
         long_0.8 = rowMeans(select(., starts_with("long_0.8")), na.rm = TRUE), 
         short_0.9 = rowMeans(select(., starts_with("short_0.9")), na.rm = TRUE),
         normal_0.9 = rowMeans(select(., starts_with("normal_0.9")), na.rm = TRUE),
         long_0.9 = rowMeans(select(., starts_with("long_0.9")), na.rm = TRUE), 
         short_1.0 = rowMeans(select(., starts_with("short_1.0")), na.rm = TRUE),
         normal_1.0 = rowMeans(select(., starts_with("normal_1.0")), na.rm = TRUE),
         long_1.0 = rowMeans(select(., starts_with("long_1.0")), na.rm = TRUE)
  )

NASA_pilot <- NASA_pilot %>% 
  rename(ID = VP)

NASA_TLX_long <- NASA_pilot %>% 
  pivot_longer(cols = -ID,
               names_to = c("Condition", "Speed"),
               names_sep = "_",
               values_to = "nasa_score"
  )
## Warning: Expected 2 pieces. Additional pieces discarded in 63 rows [1, 2, 3, 4, 5, 6, 7,
## 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...].
str(NASA_TLX_long)
## tibble [600 × 4] (S3: tbl_df/tbl/data.frame)
##  $ ID        : chr [1:600] "pilot_01" "pilot_01" "pilot_01" "pilot_01" ...
##  $ Condition : chr [1:600] "short" "short" "short" "short" ...
##  $ Speed     : chr [1:600] "0.8" "0.8" "0.8" "0.8" ...
##  $ nasa_score: num [1:600] 13 12 15 17 11 4 12 14 13 13 ...
NASA_TLX_long <- NASA_TLX_long %>% 
  mutate(Condition = as.factor(Condition),
         Speed = as.factor(Speed))

NASA_TLX_long$Condition <- factor(NASA_TLX_long$Condition, levels = c("short", "normal", "long"))

str(NASA_TLX_long)
## tibble [600 × 4] (S3: tbl_df/tbl/data.frame)
##  $ ID        : chr [1:600] "pilot_01" "pilot_01" "pilot_01" "pilot_01" ...
##  $ Condition : Factor w/ 3 levels "short","normal",..: 1 1 1 1 1 1 1 1 1 1 ...
##  $ Speed     : Factor w/ 6 levels "0.8","0.9","1",..: 1 1 1 1 1 1 1 2 2 2 ...
##  $ nasa_score: num [1:600] 13 12 15 17 11 4 12 14 13 13 ...
# nasa model
nasa_model <- lmer(nasa_score ~ Speed * Condition + (1|ID), data = NASA_TLX_long, REML = FALSE)

Anova(nasa_model)
## Analysis of Deviance Table (Type II Wald chisquare tests)
## 
## Response: nasa_score
##                    Chisq Df Pr(>Chisq)    
## Speed            47.1746  2  5.704e-11 ***
## Condition       149.4709  2  < 2.2e-16 ***
## Speed:Condition   4.0005  4     0.4059    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
summary(nasa_model)
## Linear mixed model fit by maximum likelihood  ['lmerMod']
## Formula: nasa_score ~ Speed * Condition + (1 | ID)
##    Data: NASA_TLX_long
## 
##       AIC       BIC    logLik -2*log(L)  df.resid 
##    2974.7    3022.6   -1476.3    2952.7       565 
## 
## Scaled residuals: 
##     Min      1Q  Median      3Q     Max 
## -3.8411 -0.5943  0.0689  0.6264  3.2130 
## 
## Random effects:
##  Groups   Name        Variance Std.Dev.
##  ID       (Intercept) 5.331    2.309   
##  Residual             9.359    3.059   
## Number of obs: 576, groups:  ID, 8
## 
## Fixed effects:
##                          Estimate Std. Error t value
## (Intercept)               11.9821     0.9015  13.292
## Speed0.9                  -0.4107     0.5408  -0.759
## Speed1.0                   1.9286     0.5408   3.566
## Conditionnormal           -3.5357     0.5408  -6.538
## Conditionlong             -2.9286     0.5408  -5.415
## Speed0.9:Conditionnormal   0.7143     0.7648   0.934
## Speed1.0:Conditionnormal  -0.4643     0.7648  -0.607
## Speed0.9:Conditionlong    -0.3750     0.7648  -0.490
## Speed1.0:Conditionlong    -0.2500     0.7648  -0.327
## 
## Correlation of Fixed Effects:
##               (Intr) Spd0.9 Spd1.0 Cndtnn Cndtnl Spd0.9:Cndtnn Spd1.0:Cndtnn
## Speed0.9      -0.300                                                        
## Speed1.0      -0.300  0.500                                                 
## Conditnnrml   -0.300  0.500  0.500                                          
## Conditinlng   -0.300  0.500  0.500  0.500                                   
## Spd0.9:Cndtnn  0.212 -0.707 -0.354 -0.707 -0.354                            
## Spd1.0:Cndtnn  0.212 -0.354 -0.707 -0.707 -0.354  0.500                     
## Spd0.9:Cndtnl  0.212 -0.707 -0.354 -0.354 -0.707  0.500         0.250       
## Spd1.0:Cndtnl  0.212 -0.354 -0.707 -0.354 -0.707  0.250         0.500       
##               Spd0.9:Cndtnl
## Speed0.9                   
## Speed1.0                   
## Conditnnrml                
## Conditinlng                
## Spd0.9:Cndtnn              
## Spd1.0:Cndtnn              
## Spd0.9:Cndtnl              
## Spd1.0:Cndtnl  0.500
single_task_emmeans_nasa <- emmeans(nasa_model, pairwise ~ Speed * Condition)
print(single_task_emmeans_nasa) # significant short vs normal, short vs long
## $emmeans
##  Speed Condition emmean    SE   df lower.CL upper.CL
##  0.8   short      11.98 0.955 12.9     9.92     14.0
##  0.9   short      11.57 0.955 12.9     9.51     13.6
##  1.0   short      13.91 0.955 12.9    11.85     16.0
##  0.8   normal      8.45 0.955 12.9     6.38     10.5
##  0.9   normal      8.75 0.955 12.9     6.69     10.8
##  1.0   normal      9.91 0.955 12.9     7.85     12.0
##  0.8   long        9.05 0.955 12.9     6.99     11.1
##  0.9   long        8.27 0.955 12.9     6.20     10.3
##  1.0   long       10.73 0.955 12.9     8.67     12.8
## 
## Degrees-of-freedom method: kenward-roger 
## Confidence level used: 0.95 
## 
## $contrasts
##  contrast                          estimate    SE  df t.ratio p.value
##  Speed0.8 short - Speed0.9 short      0.411 0.545 576   0.754  0.9979
##  Speed0.8 short - Speed1.0 short     -1.929 0.545 576  -3.541  0.0127
##  Speed0.8 short - Speed0.8 normal     3.536 0.545 576   6.492  <.0001
##  Speed0.8 short - Speed0.9 normal     3.232 0.545 576   5.934  <.0001
##  Speed0.8 short - Speed1.0 normal     2.071 0.545 576   3.803  0.0050
##  Speed0.8 short - Speed0.8 long       2.929 0.545 576   5.377  <.0001
##  Speed0.8 short - Speed0.9 long       3.714 0.545 576   6.819  <.0001
##  Speed0.8 short - Speed1.0 long       1.250 0.545 576   2.295  0.3468
##  Speed0.9 short - Speed1.0 short     -2.339 0.545 576  -4.295  0.0007
##  Speed0.9 short - Speed0.8 normal     3.125 0.545 576   5.737  <.0001
##  Speed0.9 short - Speed0.9 normal     2.821 0.545 576   5.180  <.0001
##  Speed0.9 short - Speed1.0 normal     1.661 0.545 576   3.049  0.0603
##  Speed0.9 short - Speed0.8 long       2.518 0.545 576   4.623  0.0002
##  Speed0.9 short - Speed0.9 long       3.304 0.545 576   6.065  <.0001
##  Speed0.9 short - Speed1.0 long       0.839 0.545 576   1.541  0.8357
##  Speed1.0 short - Speed0.8 normal     5.464 0.545 576  10.032  <.0001
##  Speed1.0 short - Speed0.9 normal     5.161 0.545 576   9.475  <.0001
##  Speed1.0 short - Speed1.0 normal     4.000 0.545 576   7.344  <.0001
##  Speed1.0 short - Speed0.8 long       4.857 0.545 576   8.918  <.0001
##  Speed1.0 short - Speed0.9 long       5.643 0.545 576  10.360  <.0001
##  Speed1.0 short - Speed1.0 long       3.179 0.545 576   5.836  <.0001
##  Speed0.8 normal - Speed0.9 normal   -0.304 0.545 576  -0.557  0.9998
##  Speed0.8 normal - Speed1.0 normal   -1.464 0.545 576  -2.688  0.1542
##  Speed0.8 normal - Speed0.8 long     -0.607 0.545 576  -1.115  0.9720
##  Speed0.8 normal - Speed0.9 long      0.179 0.545 576   0.328  1.0000
##  Speed0.8 normal - Speed1.0 long     -2.286 0.545 576  -4.197  0.0010
##  Speed0.9 normal - Speed1.0 normal   -1.161 0.545 576  -2.131  0.4530
##  Speed0.9 normal - Speed0.8 long     -0.304 0.545 576  -0.557  0.9998
##  Speed0.9 normal - Speed0.9 long      0.482 0.545 576   0.885  0.9937
##  Speed0.9 normal - Speed1.0 long     -1.982 0.545 576  -3.639  0.0090
##  Speed1.0 normal - Speed0.8 long      0.857 0.545 576   1.574  0.8189
##  Speed1.0 normal - Speed0.9 long      1.643 0.545 576   3.016  0.0661
##  Speed1.0 normal - Speed1.0 long     -0.821 0.545 576  -1.508  0.8517
##  Speed0.8 long - Speed0.9 long        0.786 0.545 576   1.443  0.8808
##  Speed0.8 long - Speed1.0 long       -1.679 0.545 576  -3.082  0.0548
##  Speed0.9 long - Speed1.0 long       -2.464 0.545 576  -4.524  0.0003
## 
## Degrees-of-freedom method: kenward-roger 
## P value adjustment: tukey method for comparing a family of 9 estimates
nasa_effects <- allEffects(nasa_model)
nasa_effects_model <- as.data.frame(nasa_effects[[1]])

nasa_effects_plot <- ggplot(nasa_effects_model, aes(x = Condition, y = fit)) +
  geom_point(aes(color = Condition), size = 3) +
  geom_errorbar(aes(ymin = fit - se, ymax = fit + se, color = Condition, width = 0.5), size = 1) +
  scale_color_manual(values = c("short" = "green", "normal" = "tomato", "long"  = "blue")) +
  ylab("Mean NASA-TLX score (1–20)") +
  xlab("Step Distance for 0.8 m/s")

plot(nasa_effects_plot)

combined_plot <- single_task_plot + nasa_effects_plot + plot_layout(ncol = 2) + 
  plot_annotation(tag_levels = "A", tag_suffix = ")")

ggsave("../../ReStoWa/MasterThesis/Graphs/single_task_walking.jpg", combined_plot, width = 11, height = 4, dpi = 300)