<jupyter_start><jupyter_text>Opinions and Gaze: Data Analysis (Step 3 of 3) This Jupyter notebook contains the data analysis for for "Seeing the other side: Conflict and controversy increase gaze coordination" (Paxton, Dale, & Richardson, *in preparation*). This notebook is the **last of three** notebooks for the "Opinions and Gaze" project. This must be run **after** the `oag-data_cleaning.ipynb` and `oag-data_processing.ipynb`files. To run this file from scratch, you will need:* `data/04-analysis_dataframes`: Directory of analysis- and    plotting-ready dataframes, produced by `oag-data_processing.ipynb`.    * `oag-plotting_df.csv`: Dataframe of real and baseline data.* `supplementary-code/`: Directory of additional functions and global    variables. **Note**: Due to data sensitivity (per the Institutional Review Board of the University of California, Merced), only researchers from ICPSR member institutions may accessstudy data through the approved link.  Variable key Linear lag (continuous): `ot1` Quadratic lag (continuous): `ot2` Opinion congruence (factor, contrast-coded):- Agreement: `agree = .5`- Disagreement: `agree = -.5` Topic class (factor, contrast-coded):* Mixed-view: `viewtype = .5`* Dominant-view: `viewtype = -.5` Data type (factor, contrast-coded):* Real data: `data = .5`* Baseline (shuffled) data: `data = -.5`  Table of contents * [Preliminaries](Preliminaries)    - [Import data and convert factors](Import-data-and-convert-factors)* [Descriptive statistics](Descriptive-statistics)    - [Derive listener segment statistics](Derive-listener-segment-statistics)    - [Derive listener demographic statistics](Derive-listener-demographic-statistics)* [Plotting](Plotting)* [Data analysis](Data-analysis)    - [Plot-level analysis](Plot-level-analysis)    - [Planned analyses](Planned-analyses)    - [Exploratory analyses](Exploratory-analyses)    - [Baseline comparisons](Baseline-comparisons) **Written by**: <NAME> (University of California, Berkeley) **Date last modified**: 11 April 2018 ***  Preliminaries<jupyter_code># clear the space
rm(list=ls())
# read in the needed files and functions
source('../supplementary-code/libraries_and_functions-oag.r')<jupyter_output>Loading required package: crqa
Loading required package: Matrix
Warning message:
“package ‘Matrix’ was built under R version 3.3.2”Loading required package: tseriesChaos
Loading required package: deSolve
Loading required package: fields
Warning message:
“package ‘fields’ was built under R version 3.3.2”Loading required package: spam
Loading required package: grid
Spam version 1.4-0 (2016-08-29) is loaded.
Type 'help( Spam)' or 'demo( spam)' for a short introduction 
and overview of this package.
Help for individual functions is also obtained by adding the
suffix '.spam' to the function name, e.g. 'help( chol.spam)'.

Attaching package: ‘spam’

The following objects are masked from ‘package:base’:

    backsolve, forwardsolve

Loading required package: maps
Loading required package: plot3D
Loading required package: pracma
Warning message:
“package ‘pracma’ was built under R version 3.3.2”
Attaching package: ‘pracma’

The following object is masked from ‘package:deSolve’:

    rk4

The f[...]<jupyter_text>Import data and convert factors<jupyter_code># read in plotting dataframe
plotting_file = file.path(analysis_data_path,
                          'oag-plotting_df.csv')
plotting_df = read.csv(plotting_file,
                       sep=",", header=TRUE)
# create a real-data analysis dataframe
analysis_real_df = plotting_df %>%

    # filter out baseline
    dplyr::filter(data==max(data)) %>%

    # reorder agreement for plotting
    mutate(agree = factor(agree, levels=c(.5, -.5))) %>%

    # factorize everything else
    mutate_at(funs(factor), 
              .vars = factor_variables[!factor_variables %in% c('agree',
                                                                'data')]) %>%

    # convert recurrence to normal distribution 
    mutate(r = Gaussianize(r,
                           type='s'))
# create a real-data plotting dataframe
plotting_real_df = plotting_df %>%

    # filter out baseline
    dplyr::filter(data==max(data)) %>%

    # reorder agreement for plotting
    mutate(agree = factor(agree, levels=c(.5, -.5))) %>%

    # factorize everything else
    mutate_at(funs(factor), 
              .vars = factor_variables[!factor_variables %in% c('agree',
                                                                'data')])
# create a baseline-data analysis dataframe
analysis_baseline_df = plotting_df %>%

    # filter out baseline
    dplyr::filter(data==min(data)) %>%

    # reorder agreement for plotting
    mutate(agree = factor(agree, levels=c(.5, -.5))) %>%

    # factorize everything else
    mutate_at(funs(factor), 
              .vars = factor_variables[!factor_variables %in% c('agree',
                                                                'data')]) %>%
    # convert recurrence to normal distribution 
    mutate(r = Gaussianize(r,
                           type='s')) 
# create a baseline-data plotting dataframe
plotting_baseline_df = plotting_df %>%

    # filter out baseline
    dplyr::filter(data==min(data)) %>%

    # reorder agreement for plotting
    mutate(agree = factor(agree, levels=c(.5, -.5))) %>%

    # factorize everything else
    mutate_at(funs(factor), 
              .vars = factor_variables[!factor_variables %in% c('agree',
                                                                'data')])
# factorize the comparison plotting dataframe
analysis_joint_df = plotting_df %>%

    # reorder agreement for plotting
    mutate(agree = factor(agree, levels=c(.5 ,-.5))) %>%

    # factorize everything else
    mutate_at(funs(factor), 
              .vars = factor_variables[!factor_variables %in% 'agree']) %>%

    # convert recurrence to normal distribution 
    mutate(r = Gaussianize(r,
                           type='s'))
# factorize the comparison plotting dataframe
plotting_df = plotting_df %>%
    
    # reorder agreement for plotting
    mutate(agree = factor(agree, levels=c(.5 ,-.5))) %>%

    # factorize everything else
    mutate_at(funs(factor), 
              .vars = factor_variables[!factor_variables %in% 'agree'])<jupyter_output><empty_output><jupyter_text>***  Descriptive statistics This section produces basic descriptive statistics about individual speakers' segments and about listeners'demographic data.  Derive listener segment statistics  Total number of unique listeners with any recorded data Listeners who successfully had any recorded data will have had a survey saved with their data. We count thosefiles to show how many listeners had any data recorded. How many unique **gaze files** do we have?<jupyter_code>gaze_file_names = list.files('../data/01-input/listener-gaze-raw',
                             recursive=FALSE)
gaze_participants = str_extract_all(gaze_file_names,
                                    '\\d{5}')
gaze_participants = lapply(gaze_participants, trimws)
length(unique(gaze_participants))<jupyter_output><empty_output><jupyter_text>How many unique **questionnaire files** do we have?<jupyter_code>questionnaire_file_names = list.files('../data/01-input/listener-responses-raw',
                                      recursive=FALSE,
                                     pattern='*.tsv')
questionnaire_participants = str_extract_all(questionnaire_file_names,
                                             '\\d{5}')
questionnaire_participants = lapply(questionnaire_participants, trimws)
length(unique(questionnaire_participants))<jupyter_output><empty_output><jupyter_text>How many **unique participants with gaze and/or questionnairefiles** do we have?<jupyter_code>questions_or_gaze_participants = c(unique(gaze_participants),
                                   unique(questionnaire_participants))
length(unique(questions_or_gaze_participants))<jupyter_output><empty_output><jupyter_text>Unfiltered listeners and missing data by segment Considering all listeners with any recorded data (i.e.,any listener with _at least some_ gaze data tracked during _at least one_ of the segments and with _some_included metadata) and all segments with at any recordeddata (i.e., no individual listeners' segments in whichno samples were recorded):1. how many listeners do we have overall,1. how many listeners do we have per segment, 1. and what proportion of data are missing per segment?<jupyter_code># load in the missing data table
missing_data_filename = file.path(processed_data_path,
                                  'listener-missing_data.csv')
missing_data = read.table(missing_data_filename,
                          sep=',',
                          header=TRUE)
unfiltered_listeners = missing_data %>% ungroup() %>%
    dplyr::filter(is.na(r_event))
unfiltered_stats = unfiltered_listeners %>% ungroup() %>%
    group_by(topic, side) %>%
    summarise(unique_listeners = n(),
              proportion_missing = round(mean(proportion),
                                        3))
unfiltered_stats<jupyter_output><empty_output><jupyter_text>**How many unique listeners were included in the unfiltered dataset**?<jupyter_code>length(unique(unfiltered_listeners$listener))<jupyter_output><empty_output><jupyter_text>On average, **how many listeners were included on each segment in the unfiltered dataset**?<jupyter_code>round(mean(unfiltered_stats$unique_listeners),2)<jupyter_output><empty_output><jupyter_text>On average, **what proportion of the gaze data was missing across the unfiltered dataset**?<jupyter_code>round(mean(unfiltered_stats$proportion_missing),3)<jupyter_output><empty_output><jupyter_text>Filtered listeners and missing data by segment Considering just the listeners that we consider to haveusable data for each segment (i.e., no more than 30% missing gaze data in the segment):1. how many listeners do we have overall,1. how many listeners do we have per segment, 1. and what proportion of data are missing per segment?<jupyter_code>filtered_listeners = missing_data %>% ungroup() %>%
    dplyr::filter(is.na(r_event) & proportion<=.3)
filtered_stats = filtered_listeners %>% ungroup() %>%
    group_by(topic, side) %>%
    summarise(unique_listeners = n(),
              proportion_missing = round(mean(proportion),
                                        3))
filtered_stats<jupyter_output><empty_output><jupyter_text>**How many unique listeners are in the filtered dataset**?<jupyter_code>length(unique(filtered_listeners$listener))<jupyter_output><empty_output><jupyter_text>On average, **how many listeners were included on each segment in the filtered dataset**?<jupyter_code>round(mean(filtered_stats$unique_listeners),2)<jupyter_output><empty_output><jupyter_text>On average, **what proportion of the gaze data was missing in the filtered dataset**?<jupyter_code>round(mean(filtered_stats$proportion_missing),3)<jupyter_output><empty_output><jupyter_text>Filtered segments and missing data by listener<jupyter_code>filtered_segments = filtered_listeners %>% ungroup() %>%
    group_by(listener) %>%
    summarise(segments = n(),
              proportion_missing = mean(proportion))<jupyter_output><empty_output><jupyter_text>On average, **how many segments does each listener contribute to the filtered dataset**?<jupyter_code>round(mean(filtered_segments$segments),2)<jupyter_output><empty_output><jupyter_text>On average, **what proportion of missing data does each listener have for segments included in the filtered dataset**?<jupyter_code>round(mean(filtered_segments$proportion_missing),3)<jupyter_output><empty_output><jupyter_text>Discarded listeners How many listeners were discarded because they were**missing required questionnaire or opinion** data?<jupyter_code># create dataframe for missing files
missing_metadata_participants = data.frame()
# load in missing opinion dataframe, if it exists
missing_opinion_filename = file.path(processed_data_path,
                                     'listener-missing_opinions.csv')
if (file.exists(missing_opinion_filename)){

    # add reason for missing
    missing_opinions = read.table(missing_opinion_filename,
                                  sep=",",
                                  header=TRUE) %>%
        mutate(reason = 'missing_opinions')
    
    # append to dataframe
    missing_metadata_participants = rbind.data.frame(missing_metadata_participants,
                                                     missing_opinions)
}
# load in missing questionnaire dataframe, if it exists
missing_questionnaire_filename = file.path(processed_data_path,
                                           'listener-missing_questionnaire.csv')
if (file.exists(missing_questionnaire_filename)){

    # add reason for missing
    missing_questionnaire = read.table(missing_questionnaire_filename,
                                       sep=",",
                                       header=TRUE)
    
    # append to dataframe
    missing_metadata_participants = rbind.data.frame(missing_metadata_participants,
                                                     missing_questionnaire)
}
length(unique(missing_metadata_participants$listener))<jupyter_output><empty_output><jupyter_text>How many listeners were completely discarded from the datasetdue to having **more than 30% missing data in all trials?**<jupyter_code>discarded_listeners = missing_data %>% ungroup() %>%
    dplyr::filter(is.na(r_event)) %>%
    dplyr::filter(!(listener %in% unique(filtered_segments$listener)))
length(unique(discarded_listeners$listener))<jupyter_output><empty_output><jupyter_text>How many listeners were discarded due to **other equipment error**?<jupyter_code>discarded_participants = c(unique(missing_metadata_participants$listener),
                           unique(discarded_listeners$listener))
recruited_participants = unique(questions_or_gaze_participants)
included_participants = unique(filtered_listeners$listener)
length(recruited_participants) - 
    length(discarded_participants) -
        length(included_participants)<jupyter_output><empty_output><jupyter_text>Derive listener demographic statistics **Note**: All numeric categories were derived alphabetically.<jupyter_code># read in dataset
demographics_data = plotting_real_df %>% ungroup() %>%

    # keep only the columns we need
    dplyr::select(one_of(crqa_questionnaire_columns),
           -topic_and_side,
           -agree) %>%

    # only one line per participant
    distinct()<jupyter_output>Warning message:
“Unknown variables: `topic`, `side`”<jupyter_text>What is the self-reported **gender distribution**? *Note: Participants were asked about gender but were provided with sex categories.*<jupyter_code>gender_data = demographics_data %>% ungroup() %>%
    group_by(gender) %>%
    summarise(gender_counts = n(),
              gender_proportion = round((n()/nrow(demographics_data)),
                                        3))
# female = lower, male = higher
gender_data<jupyter_output><empty_output><jupyter_text>What is the self-reported **mean age**?<jupyter_code>age = demographics_data %>%
    dplyr::select(age) %>%
    mutate(age = gsub("[^0-9.]", "", age)) %>%
    dplyr::filter(age != '') %>%
    mutate(age = as.numeric(age)) %>%
    .$age
round(mean(age),2)<jupyter_output><empty_output><jupyter_text>What is the self-reported **native language** distribution?<jupyter_code>native_lang_data = demographics_data %>% ungroup() %>%

    # convert all to lowercase
    mutate(native_lang = tolower(native_lang)) %>%
    mutate(native_lang = trimws(native_lang)) %>%
    
    # get counts
    group_by(native_lang) %>%
    summarise(native_lang_counts = n(),
              native_lang_proportion = round(n()/nrow(demographics_data),
                                            3)) %>%
    ungroup() %>%

    # arrange in descending order
    dplyr::arrange(native_lang)
# English = lowest, Spanish = middle, other = highest
native_lang_data<jupyter_output><empty_output><jupyter_text>Derive listener opinion statistics<jupyter_code># read in dataset
opinion_data = plotting_real_df %>% ungroup() %>%

    # keep only the columns we need
    dplyr::select(one_of(crqa_questionnaire_columns),
           topic_and_side,
          -gender,
          -native_lang,
          -age) %>%

    # get one line per topic and side
    distinct()<jupyter_output>Warning message:
“Unknown variables: `topic`, `side`”<jupyter_text>What is the **distribution of self-reported agreement** of listeners with the speaker, regardless of topic and side?<jupyter_code>agree_bias = opinion_data %>% ungroup() %>%
    group_by(agree) %>%
    summarise(agree_counts = n(),
              agree_proportions = round(n()/nrow(opinion_data),
                                       3))
# agree = .5, disagree = -.5
agree_bias<jupyter_output><empty_output><jupyter_text>***  Plotting  Distribution of opinion congruence by topic class<jupyter_code># prepare for histogram
agreement_plots = plotting_df %>% ungroup() %>%

    # get just one line per listener per topic
    dplyr::select(speaker, listener, topic_and_side, agree, viewtype) %>%
    group_by(speaker, listener, topic_and_side, agree, viewtype) %>%
    distinct() %>%
    ungroup() %>%

    # group for counts
    group_by(agree, viewtype) %>%
    summarise(counts = n()) %>%
    ungroup() %>%

    # convert counts to proportion
    mutate(proportion = counts/sum(counts))
# create the plot
agreement_distribution = ggplot(data=agreement_plots,
                                aes(x=as.factor(agree),
                                    y=proportion,
                                    fill=as.factor(viewtype)),
                               labeller = agree_labeller) +
    geom_bar(stat="identity",
             position='dodge')+
    scale_fill_manual(name="Opinion congruence",
                      breaks=c(.5,-.5),
                      labels=c("Agree", "Disagree"),
                      values=c("#b2182b", "#67a9cf")) +
    scale_x_discrete(labels=c("Mixed-view","Dominant-view")) +
    theme(legend.position='bottom') +
    xlab("Topic class") +
    ylab("Proportion") +
    ggtitle("Listener agreement by topic class\nand opinion congruence")
# save a high-resolution version
ggsave(plot = agreement_distribution,
       height = 4,
       width = 4,
       filename = '../figures/gca-agreement_viewtype_distribution.jpg')
# save a smaller version of the plot
ggsave(plot = agreement_distribution,
       height = 4,
       width = 4,
       dpi=100,
       filename = '../figures/gca-agreement_viewtype_distribution-inline.jpg')<jupyter_output><empty_output><jupyter_text>![](../figures/gca-agreement_viewtype_distribution-inline.jpg) The plot above breaks down listeners' self-reportedagreement (blue) or disagreement (red) with each segment, alongwith whether that segment was part of a mixed- (right)or dominant-view (left) topic.  Individual segment plot for all listeners What do all listeners' individual segments look like?<jupyter_code># plot all individual listeners
all_listener_plot = ggplot(plotting_real_df, 
                           aes(x=t, 
                               y=r, 
                               group=topic_and_side, 
                               color=as.factor(agree))) + 
    facet_wrap(~listener) +
    geom_path() +
    scale_color_manual(name="Opinion congruence",
                      labels=c("Agree", "Disagree"),
                      values=c("#67a9cf", "#b2182b")) +
    xlab("Lag (in 10Hz samples)") +
    ylab("Recurrence (rec)") +
    ggtitle("Gaze coordination between listeners and speakers
by lag and opinion congruence") +
    theme(strip.text.x = element_blank(),
          strip.background = element_rect(colour="white", 
                                          fill="white"),
      legend.position='bottom')
# save a high-resolution version
ggsave(plot = all_listener_plot,
       height = 7,
       width = 7,
       filename = '../figures/gca-individual_trials.jpg')
# save an inline display version
ggsave(plot = all_listener_plot,
       height = 7,
       width = 7,
       dpi=100,
       filename = '../figures/gca-individual_trials-inline.jpg')<jupyter_output><empty_output><jupyter_text>![](../figures/gca-individual_trials-inline.jpg) Each panel above presents all usable data from a singlelistener. Each line is a single diagonal recurrence profilebetween the listener and the speaker for a single audiosegment. Lines are color-coded according to whether the listener rated having agreed with (blue) or disagreed with(red) the speaker after hearing their segment.  Gaze coordination by opinion congruence and lag How does speaker-listener gaze coordination look when considering listeners' disagreement versus agreement?<jupyter_code># plot recurrence by agreement only
r_by_agreement = ggplot(data=plotting_real_df,
                        aes(x=t, 
                            y=r, 
                            color=agree,
                            group=agree)) + 
    # plot mean DRP curves
    geom_smooth(method="loess",
                se=TRUE) +

    # add in lines for raw means
    geom_line(data = plotting_real_df %>%
                      group_by(t, agree) %>%
                      summarise(r = mean(r)),
              aes(x = t,
                  y = r,
                  color = agree,
                  group = agree)) +

    # set color by agreement
    scale_color_manual(name="Opinion congruence",
                       breaks=c("1","0"),
                       labels=c("Agree","Disagree"),
                       values=c("#67a9cf","#b2182b")) + 

    # set labels
    xlab("Lag (in 10Hz samples)") +
    ylab("Recurrence (rec)") +
    ggtitle("Gaze coordination by lag
and opinion congruence") +
    theme(strip.background = element_rect(colour="white", 
                                          fill="white"),
          legend.position='bottom') 
# save a high-resolution version
ggsave(plot = r_by_agreement,
       height = 5,
       width = 4,
       filename = '../figures/gca-main_interaction_plot.jpg')
# save an inline display version
ggsave(plot = r_by_agreement,
       height = 5,
       width = 4,
       dpi=100,
       filename = '../figures/gca-main_interaction_plot-inline.jpg')<jupyter_output><empty_output><jupyter_text>![](../figures/gca-main_interaction_plot-inline.jpg) The figure above shows the aggregated diagonal recurrence profile (DRP) for the planned analysis,predicting recurrence (*rec*) with opinioncongruence (listener agreement [blue] versus disagreement[red] with the speaker in each segment) and lag.  Gaze coordination by opinion congruence, topic class, and lag How does gaze coordination look when considering opinion congruence (agreement versus disagreement) and topicclass (dominant- versus mixed-view topics)?<jupyter_code># plot recurrence by agreement and viewtype
r_by_agreement_and_viewtype = ggplot(data=plotting_real_df %>%
                                         mutate(agree = ifelse(agree==-.5,
                                                               "Disagree",
                                                               "Agree")), 
                                     aes(x=t, 
                                         y=r, 
                                         color=viewtype,
                                         group=viewtype)) + 

    # add in lines for raw means
    geom_line(data = plotting_real_df %>%
                      mutate(agree = ifelse(agree==-.5,
                                            "Disagree",
                                            "Agree")) %>%
                      group_by(t, agree, viewtype) %>%
                      summarise(r = mean(r)),
              aes(x = t,
                  y = r,
                  color = viewtype,
                  group = viewtype)) +

    # separate by agreement
    facet_wrap(~ agree) +

    # set color by dominant- versus mixed-view
    scale_color_manual(name="Topic class", 
                       breaks=c(-.5, .5),
                       labels=c("Dominant-view", "Mixed-view"),
                       values=c("#d95f02","#7570b3")) + 

    # set labels
    xlab("Lag (in 10Hz samples)") +
    ylab("Recurrence (rec)") +
    ggtitle("Gaze coordination by lag,
opinion congruence, and topic class") +
    theme(strip.background = element_rect(colour="white", 
                                          fill="white"),
          legend.position='bottom') +

    # plot mean DRP curves
    geom_smooth(method="loess",
                se=TRUE)
# save a high-resolution version
ggsave(plot = r_by_agreement_and_viewtype,
       height = 5,
       width = 4,
       filename = '../figures/gca-exploratory_interaction_plot.jpg')
# save an inline display version
ggsave(plot = r_by_agreement_and_viewtype,
       height = 5,
       width = 4,
       dpi=100,
       filename = '../figures/gca-exploratory_interaction_plot-inline.jpg')<jupyter_output><empty_output><jupyter_text>![](../figures/gca-exploratory_interaction_plot-inline.jpg) The figure above shows the aggregated diagonal recurrence profile (DRP) for the exploratory analysis,predicting recurrence (*rec*) with opinioncongruence (listener agreement [left panel] versus disagreement [right panel] with the speaker in each segment),topic class (dominant-view [orange] or mixed-view [blue]), and lag. ***  Data analysis  Plot-level analysis<jupyter_code># create subsets for plot-level metric analysis
one_liner = plotting_real_df %>%
    dplyr::filter(t==0) %>%

    # shift lag to account for window
    mutate(maxlag = maxlag-win_size-1)

# separate for agreement and disagreement
one_liner_agree = one_liner %>%
    dplyr::filter(agree==.5)
one_liner_disagree = one_liner %>%
    dplyr::filter(agree==-.5)<jupyter_output><empty_output><jupyter_text>Maximum recurrence Is maximum recurrence different from 0? Yes.<jupyter_code>t.test(one_liner$maxrec)<jupyter_output><empty_output><jupyter_text>Are the maximum recurrence values for bothagreement and disagreement different from 0?Yes for each.<jupyter_code>t.test(one_liner_agree$maxrec)
t.test(one_liner_disagree$maxrec)<jupyter_output><empty_output><jupyter_text>Does maximum recurrence differ betweenagreement and disagreement? No.<jupyter_code>lm.maxrec.agree = lmer(maxrec ~ agree + 
                       (1 + agree | listener) + 
                       (1 + agree | topic_and_side), 
                       data = one_liner)
pander_lme(lm.maxrec.agree)<jupyter_output>|      &nbsp;       |  Estimate  |  Std..Error  |  t.value  |   p    |  sig  |
|:-----------------:|:----------:|:------------:|:---------:|:------:|:-----:|
|  **(Intercept)**  |  0.08872   |   0.01339    |   6.623   | 0.0001 |  ***  |
|   **agree-0.5**   | -0.001505  |   0.005324   |  -0.2828  |  0.78  |       |<jupyter_text>What is the mean maximum recurrence for agreementand for disagreement?<jupyter_code>print(paste0('Mean maximum recurrence for agreement: ', 
             round(mean(one_liner_agree$maxrec), 3)))
print(paste0('Mean maximum recurrence for disagreement: ',
             round(mean(one_liner_disagree$maxrec), 3)))<jupyter_output>[1] "Mean maximum recurrence for disagreement: 0.103"<jupyter_text>Maximum lag Is maxlag different from 0 overall? No.<jupyter_code>t.test(one_liner$maxlag)<jupyter_output><empty_output><jupyter_text>Is maximum lag for each agreement group different from 0? No for each.<jupyter_code>t.test(one_liner_agree$maxlag)
t.test(one_liner_disagree$maxlag)<jupyter_output><empty_output><jupyter_text>Does maximum lag differ by agreement or disagreement?No.<jupyter_code>lm.maxlag.agree = lmer(maxlag ~ agree + 
                       (1 + agree | listener) + 
                       (1 | topic_and_side), 
                       data = one_liner)
pander_lme(lm.maxlag.agree)<jupyter_output>|      &nbsp;       |  Estimate  |  Std..Error  |  t.value  |  p   |  sig  |
|:-----------------:|:----------:|:------------:|:---------:|:----:|:-----:|
|  **(Intercept)**  |  0.06094   |    1.426     |  0.04273  | 0.97 |       |
|   **agree-0.5**   |   0.7486   |    2.394     |  0.3127   | 0.75 |       |<jupyter_text>What is the mean maximum lag for agreement andfor disagreement?<jupyter_code>print(paste0('Mean maximum lag for agreement: ',
             round(mean(one_liner_agree$maxlag), 2),
             ' samples (',
             round(mean(one_liner_agree$maxlag)/10, 2),
             ' sec)'))
print(paste0('Mean maximum lag for disagreement: ',
             round(mean(one_liner_disagree$maxlag), 2),
             ' samples (',
             round(mean(one_liner_disagree$maxlag)/10, 2),
             ' sec)'))<jupyter_output>[1] "Mean maximum lag for disagreement: 1.02 samples (0.1 sec)"<jupyter_text>Planned analyses Here, we'll test the relations among recurrence (`r`), linear lag (`ot1`), quadratic lag (`ot2`), and opinion congruence(`agree`) that we had anticipated testing at the outset of the study.  Two-way interaction model First, we'll model the data using all main terms and upto two-way interaction terms.<jupyter_code>planned_model_twowayint = lmer(r ~ ot1 + ot2 + agree +
                               ot1:agree + ot2:agree + ot1:ot2 +
                               (1 + ot1 + ot2 + agree | listener) + 
                               (1 + ot1 + ot2 + agree | topic_and_side), 
                               data=analysis_real_df)
pander_lme(planned_model_twowayint)<jupyter_output>|       &nbsp;        |  Estimate  |  Std..Error  |  t.value  |   p    |  sig  |
|:-------------------:|:----------:|:------------:|:---------:|:------:|:-----:|
|   **(Intercept)**   |  0.06542   |   0.01323    |   4.946   | 0.0001 |  ***  |
|       **ot1**       | -0.004661  |   0.00855    |  -0.5452  |  0.59  |       |
|       **ot2**       | -0.001987  |   0.006938   |  -0.2864  |  0.78  |       |
|    **agree-0.5**    |  -0.01107  |   0.004569   |  -2.422   | 0.015  |   *   |
|  **ot1:agree-0.5**  |  0.004621  |   0.002023   |   2.284   | 0.022  |   *   |
|  **ot2:agree-0.5**  |  0.005464  |   0.001999   |   2.733   | 0.006  |  **   |
|     **ot1:ot2**     | 0.0009637  |   0.007659   |  0.1258   |  0.9   |       |<jupyter_text>Full interaction model Next, let's include the main terms and all possible interaction terms.<jupyter_code>planned_model_allint = lmer(r ~ ot1 * ot2 * agree +
                            (1 + ot1 + ot2 + agree | listener) + 
                            (1 + ot1 + ot2 + agree | topic_and_side), 
                            data=analysis_real_df)
pander_lme(planned_model_allint)<jupyter_output>|         &nbsp;          |  Estimate  |  Std..Error  |  t.value  |   p    |  sig  |
|:-----------------------:|:----------:|:------------:|:---------:|:------:|:-----:|
|     **(Intercept)**     |  0.07311   |   0.01386    |   5.276   | 0.0001 |  ***  |
|         **ot1**         |  -0.01449  |   0.01005    |  -1.442   | 0.149  |       |
|         **ot2**         | -0.009901  |   0.008139   |  -1.217   | 0.224  |       |
|      **agree-0.5**      |  -0.03397  |   0.01314    |  -2.586   |  0.01  |   *   |
|       **ot1:ot2**       |  0.01109   |   0.009396   |   1.18    | 0.238  |       |
|    **ot1:agree-0.5**    |  0.03391   |   0.01588    |   2.135   | 0.033  |   *   |
|    **ot2:agree-0.5**    |  0.02904   |   0.01284    |   2.262   | 0.024  |   *   |
|  **ot1:ot2:agree-0.5**  |  -0.03016  |   0.01622    |   -1.86   | 0.063  |   .   |<jupyter_text>Model comparison Which model better accounts for the data?<jupyter_code>anova(planned_model_twowayint,
      planned_model_allint)<jupyter_output>refitting model(s) with ML (instead of REML)<jupyter_text>The model with all possible interaction terms trends towardbut does not significantly perform better than the modelwith only two-way interaction terms.  Exploratory analyses Next, we'll look into the relations among recurrence (`r`), linear lag (`ot1`), quadratic lag (`ot2`), opinion congruence(`agree`), and topic class (`viewtype`) -- a new variable that we had not expected to explore at the outset of the study.  Two-way interaction model First, we'll model the data using all main terms and upto two-way interaction terms.<jupyter_code>exploratory_model_twowayint = lmer(r ~ ot1 + ot2 + agree + viewtype +
                                   ot1:agree + ot2:agree +
                                   ot1:viewtype + ot2:viewtype +
                                   agree:viewtype + ot1:ot2 +
                                   (1 + ot1 + ot2 + agree + viewtype | listener) + 
                                   (1 + ot1 + ot2 + agree + viewtype | topic_and_side), 
                                   data=analysis_real_df)
pander_lme(exploratory_model_twowayint)<jupyter_output>|           &nbsp;            |  Estimate  |  Std..Error  |  t.value  |   p   |  sig  |
|:---------------------------:|:----------:|:------------:|:---------:|:-----:|:-----:|
|       **(Intercept)**       |  0.04945   |   0.02003    |   2.469   | 0.014 |   *   |
|           **ot1**           | -0.002962  |   0.008594   |  -0.3447  | 0.73  |       |
|           **ot2**           |  0.003951  |   0.007004   |   0.564   | 0.57  |       |
|        **agree-0.5**        |  -0.01357  |   0.006366   |  -2.131   | 0.033 |   *   |
|       **viewtype0.5**       |   0.0302   |   0.02898    |   1.042   |  0.3  |       |
|      **ot1:agree-0.5**      |  0.005014  |   0.001901   |   2.637   | 0.008 |  **   |
|      **ot2:agree-0.5**      |  0.005575  |   0.001867   |   2.987   | 0.003 |  **   |
|     **ot1:viewtype0.5**     |  -0.00354  |   0.004668   |  -0.7585  | 0.45  |       |
|     **ot2:viewtype0.5**     |  -0.01081  |   0.004956   |  -2.181   | 0.029 |   *   |
|  **agree-0.5:viewtype0.5**  [...]<jupyter_text>Full interaction model Next, let's include the main terms and all possible interaction terms.<jupyter_code>exploratory_model_allint = lmer(r ~ ot1 * ot2 * agree * viewtype +
                                (1 + ot1 + ot2 + agree + viewtype | listener) + 
                                (1 + ot1 + ot2 + agree + viewtype | topic_and_side), 
                                data=analysis_real_df)
pander_lme(exploratory_model_allint)<jupyter_output>|               &nbsp;                |  Estimate  |  Std..Error  |  t.value  |   p    |  sig  |
|:-----------------------------------:|:----------:|:------------:|:---------:|:------:|:-----:|
|           **(Intercept)**           |  0.03278   |   0.02234    |   1.467   | 0.142  |       |
|               **ot1**               |  0.01771   |   0.01321    |   1.341   |  0.18  |       |
|               **ot2**               |   0.0199   |    0.0107    |   1.861   | 0.063  |   .   |
|            **agree-0.5**            | -0.009714  |   0.02094    |  -0.4638  |  0.64  |       |
|           **viewtype0.5**           |  0.07826   |   0.03396    |   2.305   | 0.021  |   *   |
|             **ot1:ot2**             |  -0.01869  |   0.01257    |  -1.487   | 0.137  |       |
|          **ot1:agree-0.5**          |  0.002696  |   0.02545    |  0.1059   |  0.92  |       |
|          **ot2:agree-0.5**          |  0.00655   |   0.02056    |  0.3186   |  0.75  |       |
|         **ot1:viewtype0.5*[...]<jupyter_text>Model comparison Which model better accounts for the data?<jupyter_code>anova(exploratory_model_twowayint,
      exploratory_model_allint)<jupyter_output>refitting model(s) with ML (instead of REML)<jupyter_text>Here, the full model accounts for the datasignificantly better than the model that includes only limited interaction terms.  Post-hoc analyses Given the possible differences by opinion congruence and topic class, let's dig a bit more deeply into the exploratorymodel. What's actually driving the significant effects thatwe see?<jupyter_code># separate dataframes by type
topic_dfs = split(analysis_real_df,
                  analysis_real_df$viewtype)
dominant_df = topic_dfs$`-0.5`
mixed_df = topic_dfs$`0.5`<jupyter_output><empty_output><jupyter_text>**Dominant-view model** (`viewtype = -.5`)<jupyter_code>exploratory_model_dominant = lmer(r ~ ot1 * ot2 * agree +
                                (1 + ot1 + ot2 + agree | listener) + 
                                (1 + ot1 + ot2 + agree | topic_and_side), 
                                data=dominant_df)
pander_lme(exploratory_model_dominant)<jupyter_output>|         &nbsp;          |  Estimate  |  Std..Error  |  t.value  |   p   |  sig  |
|:-----------------------:|:----------:|:------------:|:---------:|:-----:|:-----:|
|     **(Intercept)**     |  0.03087   |   0.01832    |   1.685   | 0.092 |   .   |
|         **ot1**         |  0.01989   |   0.01306    |   1.523   | 0.128 |       |
|         **ot2**         |  0.01926   |   0.01176    |   1.638   | 0.101 |       |
|      **agree-0.5**      |  -0.00482  |   0.02018    |  -0.2388  | 0.81  |       |
|       **ot1:ot2**       |  -0.01869  |   0.01219    |  -1.533   | 0.125 |       |
|    **ot1:agree-0.5**    | 0.0004748  |   0.02471    |  0.01922  | 0.98  |       |
|    **ot2:agree-0.5**    |  0.01001   |   0.01998    |  0.5006   | 0.62  |       |
|  **ot1:ot2:agree-0.5**  | -0.004327  |   0.02521    |  -0.1717  | 0.86  |       |<jupyter_text>We find no significant effects for dominant-view topics based onlistener agreement or disagreement. **Mixed-view model** (`viewtype = .5`)<jupyter_code>exploratory_model_mixed = lmer(r ~ ot1 * ot2 * agree +
                                (1 + ot1 + ot2 + agree | listener) + 
                                (1 + ot1 + ot2 + agree | topic_and_side), 
                                data=mixed_df)
pander_lme(exploratory_model_mixed)<jupyter_output>|         &nbsp;          |  Estimate  |  Std..Error  |  t.value  |   p    |  sig  |
|:-----------------------:|:----------:|:------------:|:---------:|:------:|:-----:|
|     **(Intercept)**     |   0.1128   |   0.01859    |   6.068   | 0.0001 |  ***  |
|         **ot1**         |  -0.04638  |   0.01318    |  -3.519   | 0.0004 |  ***  |
|         **ot2**         |  -0.03933  |   0.009552   |  -4.118   | 0.0001 |  ***  |
|      **agree-0.5**      |  -0.06297  |   0.01538    |  -4.094   | 0.0001 |  ***  |
|       **ot1:ot2**       |  0.04025   |   0.01173    |   3.431   | 0.001  |  **   |
|    **ot1:agree-0.5**    |  0.06565   |   0.01791    |   3.667   | 0.0002 |  ***  |
|    **ot2:agree-0.5**    |  0.05119   |   0.01446    |   3.54    | 0.0004 |  ***  |
|  **ot1:ot2:agree-0.5**  |  -0.05764  |   0.01827    |  -3.154   | 0.002  |  **   |<jupyter_text>The mixed-view topics appear to be doing the majority of the heavylifting with these data: All effects here are significant.  Baseline comparisons Let's add a term to account for real (`data=.5`) versus baseline data (`data=-.5`) to the planned and exploratory models reportedabove. In each, we'll again compare models that include only up to two-way interactions with those that include all possible interaction terms to see which better account for the data.  Planned analysis  Two-way interaction model<jupyter_code>planned_model_baseline_twowayint = lmer(r ~ ot1 + ot2 + agree + data +
                                        ot1:agree + ot2:agree + 
                                        ot1:data + ot2:data + 
                                        agree:data + ot1:ot2 +
                                        (1 + agree | listener) + 
                                        (1 | topic_and_side), 
                                        data=analysis_joint_df)
pander_lme(planned_model_baseline_twowayint)<jupyter_output>|         &nbsp;          |  Estimate  |  Std..Error  |  t.value  |   p    |  sig  |
|:-----------------------:|:----------:|:------------:|:---------:|:------:|:-----:|
|     **(Intercept)**     |  0.06424   |   0.01197    |   5.367   | 0.0001 |  ***  |
|         **ot1**         | -0.003331  |   0.001919   |  -1.736   | 0.083  |   .   |
|         **ot2**         | -0.002774  |   0.001554   |  -1.785   | 0.074  |   .   |
|      **agree-0.5**      | -0.0003169 |   0.001872   |  -0.1693  |  0.87  |       |
|       **data0.5**       | 0.0003562  |  0.0008942   |  0.3983   |  0.69  |       |
|    **ot1:agree-0.5**    | -0.0008192 |  0.0004651   |  -1.761   | 0.078  |   .   |
|    **ot2:agree-0.5**    |  0.001456  |  0.0004651   |   3.131   | 0.002  |  **   |
|     **ot1:data0.5**     | -0.000742  |  0.0007639   |  -0.9713  |  0.33  |       |
|     **ot2:data0.5**     | -0.0003869 |  0.0007639   |  -0.5065  |  0.61  |       |
|  **agree-0.5:data0.5**  | -0.0004536 |  0.0002071   |   -2.19[...]<jupyter_text>Full interaction model<jupyter_code>planned_model_baseline_allint = lmer(r ~ ot1 * ot2 * agree * data +
                                     (1 + agree | listener) + 
                                     (1 | topic_and_side), 
                                     data=analysis_joint_df)
pander_lme(planned_model_baseline_allint)<jupyter_output>|             &nbsp;              |  Estimate  |  Std..Error  |  t.value  |   p    |  sig  |
|:-------------------------------:|:----------:|:------------:|:---------:|:------:|:-----:|
|         **(Intercept)**         |  0.06473   |   0.01203    |   5.381   | 0.0001 |  ***  |
|             **ot1**             | -0.004067  |   0.00246    |  -1.654   | 0.098  |   .   |
|             **ot2**             | -0.003422  |   0.001987   |  -1.722   | 0.085  |   .   |
|          **agree-0.5**          | -0.001308  |   0.003795   |  -0.3447  |  0.73  |       |
|           **data0.5**           |  0.008107  |   0.006429   |   1.261   | 0.207  |       |
|           **ot1:ot2**           |  0.003625  |   0.002515   |   1.441   |  0.15  |       |
|        **ot1:agree-0.5**        | 0.0007918  |   0.004245   |  0.1865   |  0.85  |       |
|        **ot2:agree-0.5**        |  0.002921  |   0.00343    |  0.8516   |  0.39  |       |
|         **ot1:data0.5**         | -0.009389  |   0.008157   |  -1.[...]<jupyter_text>Model comparison<jupyter_code>anova(planned_model_baseline_twowayint,
      planned_model_baseline_allint)<jupyter_output>refitting model(s) with ML (instead of REML)<jupyter_text>We're significantly better able to maximize log-likelihood of the model by including all possible interaction terms, rather than just including two-way interactions.  Exploratory analysis  Two-way interaction model<jupyter_code>exploratory_model_baseline_twowayint = lmer(r ~ ot1 + ot2 + agree + viewtype + data +
                                            ot1:agree + ot2:agree + 
                                            ot1:viewtype + ot2:viewtype +
                                            ot1:ot2 + agree:viewtype +
                                            ot1:data + ot2:data + 
                                            viewtype:data + agree:data +
                                            (1 + data | listener) + 
                                            (1 + data | topic_and_side), 
                                            data=analysis_joint_df)
pander_lme(exploratory_model_baseline_twowayint)<jupyter_output>|           &nbsp;            |   Estimate   |  Std..Error  |  t.value  |   p    |  sig  |
|:---------------------------:|:------------:|:------------:|:---------:|:------:|:-----:|
|       **(Intercept)**       |   0.05171    |   0.01845    |   2.802   | 0.005  |  **   |
|           **ot1**           |  -0.001227   |   0.002058   |  -0.5965  |  0.55  |       |
|           **ot2**           |  -0.002773   |   0.001672   |  -1.658   | 0.097  |   .   |
|        **agree-0.5**        |   0.002929   |  0.0005975   |   4.903   | 0.0001 |  ***  |
|       **viewtype0.5**       |   0.02221    |   0.02462    |  0.9023   |  0.37  |       |
|         **data0.5**         |  0.0005654   |   0.001481   |  0.3817   |  0.7   |       |
|      **ot1:agree-0.5**      | -0.000003684 |  0.0005041   | -0.007308 |  0.99  |       |
|      **ot2:agree-0.5**      |   0.001457   |  0.0005041   |   2.89    | 0.004  |  **   |
|     **ot1:viewtype0.5**     |  -0.004165   |  0.0004809   |   -8.66   | 0.0001 |  *** [...]<jupyter_text>Full interaction model<jupyter_code>exploratory_model_baseline_allint = lmer(r ~ ot1 * ot2 * agree * viewtype * data +
                                         (1 + data | listener) + 
                                         (1 + data | topic_and_side), 
                                         data=analysis_joint_df)
pander_lme(exploratory_model_baseline_allint)<jupyter_output>|                   &nbsp;                    |  Estimate   |  Std..Error  |  t.value  |   p    |  sig  |
|:-------------------------------------------:|:-----------:|:------------:|:---------:|:------:|:-----:|
|               **(Intercept)**               |   0.05214   |   0.01861    |   2.802   | 0.005  |  **   |
|                   **ot1**                   |  -0.000602  |   0.003722   |  -0.1617  |  0.87  |       |
|                   **ot2**                   |  -0.003444  |   0.003007   |  -1.145   |  0.25  |       |
|                **agree-0.5**                | -0.0004907  |   0.006066   | -0.08089  |  0.94  |       |
|               **viewtype0.5**               |   0.02233   |   0.02495    |  0.8951   |  0.37  |       |
|                 **data0.5**                 |  -0.02251   |   0.009794   |  -2.299   | 0.022  |   *   |
|                 **ot1:ot2**                 |  0.002148   |   0.003807   |  0.5643   |  0.57  |       |
|              **ot1:agree-0.5**            [...]<jupyter_text>Model comparison<jupyter_code>anova(exploratory_model_baseline_twowayint,
      exploratory_model_baseline_allint)<jupyter_output>refitting model(s) with ML (instead of REML)
{'fraction_non_alphanumeric': 0.13767132285326444, 'fraction_numerical': 0.05031308840916057, 'mean_word_length': 2.083232077764277, 'pattern_counts': {'":': 0, '<': 194, '<?xml version=': 0, '>': 272, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 107, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '53', 'dir': 'jupyter-structured-clean-dedup', 'id': '501174', 'license': 'MIT', 'n_tokens_mistral': 15917, 'n_tokens_neox': 13974, 'n_words': 3068, 'path': 'analyses/oag-data_analysis.ipynb', 'repo_name': 'a-paxton/opinions-and-gaze-coordination', 'size': '45677'}