This code is to accompany the paper, along with the data, and is provided as-is, without any implication of warranty. Code by Andres Karjus (andreskarjus.github.io). Run all the code blocks to replicate the analyses and plot in the paper. Place the data file (csv) into the working directory, or define a full path below, and install tidyverse and lme4 if not present. lmerTest is optional, providing a more informative summary() function for the mixed models.

Packages and data

# Define the full path to the data file here
datapath="Experiment_dataset.csv"

# Load packages; install those if missing, using install.packages("name")
library(tidyverse) # 2.0.0; dplyr 1.1.0
library(lme4)      # 1.1.30
library(colorspace) # used for plot colors
library(lmerTest) # p-values estimated using Satterthwaite's degrees of freedom method
# Initial data operations; this is left here just for reference; not to be executed
# d = read_delim(datapath, delim = ";") %>% 
#   mutate(MOVEMENT2 = case_when(CAMERA_MOVEMENT=="Static" ~ "_Static", T~"All_moving")) %>% 
#   mutate(CAMERA_MOVEMENT = relevel(as.factor(CAMERA_MOVEMENT), "Static")) %>% 
#   mutate(MOOD = relevel(as.factor(MOOD), "Neutral")) %>% 
#   mutate(AROUSAL=6-AROUSAL, VALENCE=6-VALENCE) # homogenize scale direction
# write_csv(d, "Experiment_dataset.csv")

Load data

d = read_csv(datapath) %>%   # and adjust baselines for models and plots:
  mutate(CAMERA_MOVEMENT = relevel(as.factor(CAMERA_MOVEMENT), "Static")) %>% 
  mutate(MOOD = relevel(as.factor(MOOD), "Neutral"))

Statistical modelling

Mood (scene) is treated as a categorical control variable.

# valence and arousal
null=lmer(VALENCE~   (1|PARTICIPANT), data=d )
hyp1 = lmer(VALENCE~MOOD + (1|PARTICIPANT), data=d )
hyp2 = lmer(VALENCE~MOVEMENT2 * MOOD + (1|PARTICIPANT), data=d )
anova(null, hyp1, hyp2) %>% print
## refitting model(s) with ML (instead of REML)
## Data: d
## Models:
## null: VALENCE ~ (1 | PARTICIPANT)
## hyp1: VALENCE ~ MOOD + (1 | PARTICIPANT)
## hyp2: VALENCE ~ MOVEMENT2 * MOOD + (1 | PARTICIPANT)
##      npar    AIC    BIC  logLik deviance   Chisq Df Pr(>Chisq)    
## null    3 393.75 402.38 -193.88   387.75                          
## hyp1    5 364.34 378.72 -177.17   354.34 33.4103  2   5.56e-08 ***
## hyp2    8 369.50 392.50 -176.75   353.50  0.8413  3     0.8396    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# summary(hyp2)

null=lmer(AROUSAL~   (1|PARTICIPANT), data=d )
hyp1 = lmer(AROUSAL~MOOD +  (1|PARTICIPANT), data=d )
hyp2 = lmer(AROUSAL~MOOD * MOVEMENT2 +  (1|PARTICIPANT), data=d )
anova(null,hyp1, hyp2) %>% print
## refitting model(s) with ML (instead of REML)
## Data: d
## Models:
## null: AROUSAL ~ (1 | PARTICIPANT)
## hyp1: AROUSAL ~ MOOD + (1 | PARTICIPANT)
## hyp2: AROUSAL ~ MOOD * MOVEMENT2 + (1 | PARTICIPANT)
##      npar    AIC    BIC  logLik deviance   Chisq Df Pr(>Chisq)    
## null    3 420.83 429.46 -207.42   414.83                          
## hyp1    5 408.11 422.49 -199.06   398.11 16.7195  2  0.0002341 ***
## hyp2    8 409.55 432.55 -196.77   393.55  4.5639  3  0.2066576    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# summary(hyp2)

# Q1
null=lmer(Q1~   (1|PARTICIPANT), data=d )
hyp1 = lmer(Q1~MOOD +  (1|PARTICIPANT), data=d )
hyp2 = lmer(Q1~MOOD*MOVEMENT2 +  (1|PARTICIPANT), data=d )
anova(null,hyp1, hyp2) %>% print
## refitting model(s) with ML (instead of REML)
## Data: d
## Models:
## null: Q1 ~ (1 | PARTICIPANT)
## hyp1: Q1 ~ MOOD + (1 | PARTICIPANT)
## hyp2: Q1 ~ MOOD * MOVEMENT2 + (1 | PARTICIPANT)
##      npar    AIC    BIC  logLik deviance   Chisq Df Pr(>Chisq)   
## null    3 387.52 396.17 -190.76   381.52                         
## hyp1    5 388.97 403.39 -189.49   378.97  2.5494  2   0.279516   
## hyp2    8 382.55 405.62 -183.28   366.55 12.4175  3   0.006082 **
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# summary(hyp2)

# Q2
null=  lmer(Q2~   (1|PARTICIPANT), data=d )
hyp1 = lmer(Q2~MOOD +  (1|PARTICIPANT), data=d )
hyp2 = lmer(Q2~MOOD * MOVEMENT2 +  (1|PARTICIPANT), data=d )
anova(null,hyp1, hyp2) %>% print
## refitting model(s) with ML (instead of REML)
## Data: d
## Models:
## null: Q2 ~ (1 | PARTICIPANT)
## hyp1: Q2 ~ MOOD + (1 | PARTICIPANT)
## hyp2: Q2 ~ MOOD * MOVEMENT2 + (1 | PARTICIPANT)
##      npar    AIC    BIC  logLik deviance   Chisq Df Pr(>Chisq)    
## null    3 403.64 412.29 -198.82   397.64                          
## hyp1    5 406.83 421.24 -198.41   396.83  0.8163  2     0.6649    
## hyp2    8 385.64 408.71 -184.82   369.64 27.1826  3  5.391e-06 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# summary(hyp2)


# Q3 doesn't have static; no dif between cameras
q3d = d %>% filter(MOVEMENT2=="All_moving") %>% 
  mutate(CAMERA_MOVEMENT = relevel(as.factor(CAMERA_MOVEMENT), "Dolly"))
null=  lmer(Q3~   (1|PARTICIPANT), data=q3d )
hyp1 = lmer(Q3~MOOD +  (1|PARTICIPANT), data=q3d )
hyp2 = lmer(Q3~MOOD*CAMERA_MOVEMENT +  (1|PARTICIPANT), data=q3d )
anova(null,hyp1, hyp2) %>% print
## refitting model(s) with ML (instead of REML)
## Data: q3d
## Models:
## null: Q3 ~ (1 | PARTICIPANT)
## hyp1: Q3 ~ MOOD + (1 | PARTICIPANT)
## hyp2: Q3 ~ MOOD * CAMERA_MOVEMENT + (1 | PARTICIPANT)
##      npar    AIC    BIC  logLik deviance  Chisq Df Pr(>Chisq)
## null    3 269.24 277.02 -131.62   263.24                     
## hyp1    5 272.61 285.58 -131.30   262.61 0.6316  2     0.7292
## hyp2   11 281.59 310.14 -129.80   259.59 3.0150  6     0.8070
# summary(hyp2)

Individual explorative tests

glm(VALENCE~  CAMERA_MOVEMENT, data=d %>% filter(MOOD=="Positive") %>% mutate(CAMERA_MOVEMENT=relevel(as.factor(CAMERA_MOVEMENT), "Dolly" )) ) %>% summary()
glm(AROUSAL~  CAMERA_MOVEMENT, data=d %>% filter(MOOD=="Positive") %>% mutate(CAMERA_MOVEMENT=relevel(as.factor(CAMERA_MOVEMENT), "Static" )) ) %>% summary()
glm(AROUSAL~  CAMERA_MOVEMENT, data=d %>% filter(MOOD=="Neutral") %>% mutate(CAMERA_MOVEMENT=relevel(as.factor(CAMERA_MOVEMENT), "Static" )) ) %>% summary()
glm(AROUSAL~  CAMERA_MOVEMENT, data=d %>% filter(MOOD=="Negative") %>% mutate(CAMERA_MOVEMENT=relevel(as.factor(CAMERA_MOVEMENT), "Static" )) ) %>% summary()
glm(Q1~  CAMERA_MOVEMENT, data=d %>% filter(MOOD=="Neutral") %>% mutate(CAMERA_MOVEMENT=relevel(as.factor(CAMERA_MOVEMENT), "Static" )) ) %>% summary()
glm(Q1~  CAMERA_MOVEMENT, data=d %>% filter(MOOD=="Positive") %>% mutate(CAMERA_MOVEMENT=relevel(as.factor(CAMERA_MOVEMENT), "Static" )) ) %>% summary()
glm(Q1~  CAMERA_MOVEMENT, data=d %>% filter(MOOD=="Negative") %>% mutate(CAMERA_MOVEMENT=relevel(as.factor(CAMERA_MOVEMENT), "Static" )) ) %>% summary()
glm(Q2~  CAMERA_MOVEMENT, data=d %>% filter(MOOD=="Neutral") %>% mutate(CAMERA_MOVEMENT=relevel(as.factor(CAMERA_MOVEMENT), "Static" )) ) %>% summary()
glm(Q2~  CAMERA_MOVEMENT, data=d %>% filter(MOOD=="Positive") %>% mutate(CAMERA_MOVEMENT=relevel(as.factor(CAMERA_MOVEMENT), "Static" )) ) %>% summary()
glm(Q3~  CAMERA_MOVEMENT, data=d %>% filter(MOOD=="Negative", CAMERA_MOVEMENT!="Static") %>% mutate(CAMERA_MOVEMENT=relevel(as.factor(CAMERA_MOVEMENT), "Steadicam" )) ) %>% summary()

Plots

# reorganize data
dl = d %>% select(-MOVEMENT2) %>% 
  pivot_longer(
    cols = c(VALENCE, AROUSAL, Q1, Q2, Q3),
    names_to = "QUESTION",
    values_to = "value"
  ) %>% group_by(MOOD, CAMERA_MOVEMENT, QUESTION) %>% 
  summarise(value=mean(value, na.rm=T)) %>% 
  ungroup() %>% 
  mutate(value=round(value,2)) %>% 
  group_by(QUESTION) %>% 
  mutate(valuenorm = scale(value)) %>% 
  ungroup() %>% 
  mutate(MOOD=case_match(MOOD, "Neutral"~"Neutral\nambig.", "Positive"~"Positive\nerotic", "Negative"~"Negative\nhorror")) %>% 
  mutate(QUESTION=case_match(QUESTION,"Q1"~ "Q1 (involvement)", "Q2"~"Q2 (own eyes)", "Q3"~"Q3 (move w/ cam)", "AROUSAL"~"Arousal", "VALENCE"~"Valence")) %>% 
    mutate(CAMERA_MOVEMENT=case_match(CAMERA_MOVEMENT, "Steadicam"~"Steadi\ncam", "Handheld"~"Hand\nheld", .default = CAMERA_MOVEMENT)) %>% 
  mutate(CAMERA_MOVEMENT=fct_relevel(CAMERA_MOVEMENT, "Static")) %>% 
  mutate(QUESTION=fct_relevel(as.factor(QUESTION), "Valence", "Arousal")) 



ggplot(dl, aes(MOOD, CAMERA_MOVEMENT, fill=valuenorm))+
  geom_tile()+
  geom_text(aes(label=value, color=valuenorm), size=4.1)+
  theme_bw(base_size = 14)+
  facet_wrap(~QUESTION, nrow=1)+
  scale_fill_gradientn(colors=c("white", "#8cb8fa" ) %>% lighten(0.1), na.value = "white")+
  scale_color_gradientn(colors=c("white", "#8cb8fa") %>% darken(0.7), na.value = "white")+
  coord_cartesian(expand=0)+
  theme(legend.position = "none",
        axis.ticks = element_blank(),
        axis.title = element_blank(),
        plot.margin = margin(0,0,0,0),
        axis.text.x=element_text(angle=90, hjust=1, vjust=0.4, lineheight=0.8),
        axis.text.y=element_text(lineheight=0.7),
        strip.text = element_text(hjust=0, margin = margin(2, 0,2,0.5))
        )