R Markdown

This is an R Markdown document. Markdown is a simple formatting syntax for authoring HTML, PDF, and MS Word documents. For more details on using R Markdown see http://rmarkdown.rstudio.com.

When you click the Knit button a document will be generated that includes both content as well as the output of any embedded R code chunks within the document. You can embed an R code chunk like this:

summary(cars)
##      speed           dist       
##  Min.   : 4.0   Min.   :  2.00  
##  1st Qu.:12.0   1st Qu.: 26.00  
##  Median :15.0   Median : 36.00  
##  Mean   :15.4   Mean   : 42.98  
##  3rd Qu.:19.0   3rd Qu.: 56.00  
##  Max.   :25.0   Max.   :120.00

Including Plots

You can also embed plots, for example:

Note that the echo = FALSE parameter was added to the code chunk to prevent printing of the R code that generated the plot.

Setting up the environment:

# List of packages
packages <- c("tidyverse", "infer", "fst", "modelsummary", "effects", "survey", "performance", "flextable", "broom", "scales", "ggeffects", "marginaleffects", "interactions", "see") # add any you need here

# Install packages if they aren't installed already
new_packages <- packages[!(packages %in% installed.packages()[,"Package"])]
if(length(new_packages)) install.packages(new_packages)

# Load the packages
lapply(packages, library, character.only = TRUE)
## ── 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.4.4     ✔ tibble    3.2.1
## ✔ lubridate 1.9.3     ✔ tidyr     1.3.0
## ✔ purrr     1.0.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
## Warning: package 'infer' was built under R version 4.3.3
## Warning: package 'effects' was built under R version 4.3.3
## Loading required package: carData
## Warning: package 'carData' was built under R version 4.3.3
## lattice theme set by effectsTheme()
## See ?effectsTheme for details.
## Warning: package 'survey' was built under R version 4.3.3
## Loading required package: grid
## Loading required package: Matrix
## 
## Attaching package: 'Matrix'
## 
## The following objects are masked from 'package:tidyr':
## 
##     expand, pack, unpack
## 
## Loading required package: survival
## 
## Attaching package: 'survey'
## 
## The following object is masked from 'package:graphics':
## 
##     dotchart
## Warning: package 'performance' was built under R version 4.3.3
## 
## Attaching package: 'flextable'
## 
## The following object is masked from 'package:purrr':
## 
##     compose
## 
## 
## Attaching package: 'scales'
## 
## The following object is masked from 'package:purrr':
## 
##     discard
## 
## The following object is masked from 'package:readr':
## 
##     col_factor
## Warning: package 'ggeffects' was built under R version 4.3.3
## Warning: package 'marginaleffects' was built under R version 4.3.3
## Warning: package 'interactions' was built under R version 4.3.3
## 
## Attaching package: 'interactions'
## 
## The following object is masked from 'package:ggeffects':
## 
##     johnson_neyman
## Warning: package 'see' was built under R version 4.3.3
## [[1]]
##  [1] "lubridate" "forcats"   "stringr"   "dplyr"     "purrr"     "readr"    
##  [7] "tidyr"     "tibble"    "ggplot2"   "tidyverse" "stats"     "graphics" 
## [13] "grDevices" "utils"     "datasets"  "methods"   "base"     
## 
## [[2]]
##  [1] "infer"     "lubridate" "forcats"   "stringr"   "dplyr"     "purrr"    
##  [7] "readr"     "tidyr"     "tibble"    "ggplot2"   "tidyverse" "stats"    
## [13] "graphics"  "grDevices" "utils"     "datasets"  "methods"   "base"     
## 
## [[3]]
##  [1] "fst"       "infer"     "lubridate" "forcats"   "stringr"   "dplyr"    
##  [7] "purrr"     "readr"     "tidyr"     "tibble"    "ggplot2"   "tidyverse"
## [13] "stats"     "graphics"  "grDevices" "utils"     "datasets"  "methods"  
## [19] "base"     
## 
## [[4]]
##  [1] "modelsummary" "fst"          "infer"        "lubridate"    "forcats"     
##  [6] "stringr"      "dplyr"        "purrr"        "readr"        "tidyr"       
## [11] "tibble"       "ggplot2"      "tidyverse"    "stats"        "graphics"    
## [16] "grDevices"    "utils"        "datasets"     "methods"      "base"        
## 
## [[5]]
##  [1] "effects"      "carData"      "modelsummary" "fst"          "infer"       
##  [6] "lubridate"    "forcats"      "stringr"      "dplyr"        "purrr"       
## [11] "readr"        "tidyr"        "tibble"       "ggplot2"      "tidyverse"   
## [16] "stats"        "graphics"     "grDevices"    "utils"        "datasets"    
## [21] "methods"      "base"        
## 
## [[6]]
##  [1] "survey"       "survival"     "Matrix"       "grid"         "effects"     
##  [6] "carData"      "modelsummary" "fst"          "infer"        "lubridate"   
## [11] "forcats"      "stringr"      "dplyr"        "purrr"        "readr"       
## [16] "tidyr"        "tibble"       "ggplot2"      "tidyverse"    "stats"       
## [21] "graphics"     "grDevices"    "utils"        "datasets"     "methods"     
## [26] "base"        
## 
## [[7]]
##  [1] "performance"  "survey"       "survival"     "Matrix"       "grid"        
##  [6] "effects"      "carData"      "modelsummary" "fst"          "infer"       
## [11] "lubridate"    "forcats"      "stringr"      "dplyr"        "purrr"       
## [16] "readr"        "tidyr"        "tibble"       "ggplot2"      "tidyverse"   
## [21] "stats"        "graphics"     "grDevices"    "utils"        "datasets"    
## [26] "methods"      "base"        
## 
## [[8]]
##  [1] "flextable"    "performance"  "survey"       "survival"     "Matrix"      
##  [6] "grid"         "effects"      "carData"      "modelsummary" "fst"         
## [11] "infer"        "lubridate"    "forcats"      "stringr"      "dplyr"       
## [16] "purrr"        "readr"        "tidyr"        "tibble"       "ggplot2"     
## [21] "tidyverse"    "stats"        "graphics"     "grDevices"    "utils"       
## [26] "datasets"     "methods"      "base"        
## 
## [[9]]
##  [1] "broom"        "flextable"    "performance"  "survey"       "survival"    
##  [6] "Matrix"       "grid"         "effects"      "carData"      "modelsummary"
## [11] "fst"          "infer"        "lubridate"    "forcats"      "stringr"     
## [16] "dplyr"        "purrr"        "readr"        "tidyr"        "tibble"      
## [21] "ggplot2"      "tidyverse"    "stats"        "graphics"     "grDevices"   
## [26] "utils"        "datasets"     "methods"      "base"        
## 
## [[10]]
##  [1] "scales"       "broom"        "flextable"    "performance"  "survey"      
##  [6] "survival"     "Matrix"       "grid"         "effects"      "carData"     
## [11] "modelsummary" "fst"          "infer"        "lubridate"    "forcats"     
## [16] "stringr"      "dplyr"        "purrr"        "readr"        "tidyr"       
## [21] "tibble"       "ggplot2"      "tidyverse"    "stats"        "graphics"    
## [26] "grDevices"    "utils"        "datasets"     "methods"      "base"        
## 
## [[11]]
##  [1] "ggeffects"    "scales"       "broom"        "flextable"    "performance" 
##  [6] "survey"       "survival"     "Matrix"       "grid"         "effects"     
## [11] "carData"      "modelsummary" "fst"          "infer"        "lubridate"   
## [16] "forcats"      "stringr"      "dplyr"        "purrr"        "readr"       
## [21] "tidyr"        "tibble"       "ggplot2"      "tidyverse"    "stats"       
## [26] "graphics"     "grDevices"    "utils"        "datasets"     "methods"     
## [31] "base"        
## 
## [[12]]
##  [1] "marginaleffects" "ggeffects"       "scales"          "broom"          
##  [5] "flextable"       "performance"     "survey"          "survival"       
##  [9] "Matrix"          "grid"            "effects"         "carData"        
## [13] "modelsummary"    "fst"             "infer"           "lubridate"      
## [17] "forcats"         "stringr"         "dplyr"           "purrr"          
## [21] "readr"           "tidyr"           "tibble"          "ggplot2"        
## [25] "tidyverse"       "stats"           "graphics"        "grDevices"      
## [29] "utils"           "datasets"        "methods"         "base"           
## 
## [[13]]
##  [1] "interactions"    "marginaleffects" "ggeffects"       "scales"         
##  [5] "broom"           "flextable"       "performance"     "survey"         
##  [9] "survival"        "Matrix"          "grid"            "effects"        
## [13] "carData"         "modelsummary"    "fst"             "infer"          
## [17] "lubridate"       "forcats"         "stringr"         "dplyr"          
## [21] "purrr"           "readr"           "tidyr"           "tibble"         
## [25] "ggplot2"         "tidyverse"       "stats"           "graphics"       
## [29] "grDevices"       "utils"           "datasets"        "methods"        
## [33] "base"           
## 
## [[14]]
##  [1] "see"             "interactions"    "marginaleffects" "ggeffects"      
##  [5] "scales"          "broom"           "flextable"       "performance"    
##  [9] "survey"          "survival"        "Matrix"          "grid"           
## [13] "effects"         "carData"         "modelsummary"    "fst"            
## [17] "infer"           "lubridate"       "forcats"         "stringr"        
## [21] "dplyr"           "purrr"           "readr"           "tidyr"          
## [25] "tibble"          "ggplot2"         "tidyverse"       "stats"          
## [29] "graphics"        "grDevices"       "utils"           "datasets"       
## [33] "methods"         "base"

Loading data for Italy:

Italy_data <- read.fst("italy_data.fst")

code our main variable of interest:

Utilizing the variable pbldmn ()

Italy_data$weight <- Italy_data$dweight * Italy_data$pweight
survey_design <- svydesign(ids = ~1, data = Italy_data, weights = ~weight)
Italy_data <- Italy_data %>%
  mutate(protest = case_when(
    pbldmn == 1 ~ 1,    # Yes to 1
    pbldmn == 2 ~ 0,    # No to 0
    TRUE ~ NA_real_    # Everything else to NA
  ))
Italy_data <- Italy_data %>% filter(!is.na(protest))

Coding predictors:

Italy_data <- Italy_data %>%
  mutate(
    age = ifelse(agea == 999, NA, agea),
    adulthood = ifelse(age >= 18 & age <= 30, "Early", ifelse(age >= 31, "Later", NA)),
    cohort = ifelse(yrbrn < 1930 | yrbrn > 2000, NA, yrbrn),
    gen = case_when(
      yrbrn %in% 1900:1945 ~ "Interwar",
      yrbrn %in% 1946:1964 ~ "Baby Boomers",
      yrbrn %in% 1965:1979 ~ "Gen X",
      yrbrn %in% 1980:1996 ~ "Millennials",
      TRUE ~ as.character(cohort)
    ),
    gen = factor(gen, levels = c("Interwar", "Baby Boomers", "Gen X", "Millennials"))
  ) %>%
  # Recoding political ID and education
  mutate(
    polID = case_when(
      lrscale %in% 0:3 ~ "Left",                    
      lrscale %in% 7:10 ~ "Right",                     
      lrscale %in% 4:6 ~ "Moderate",                  
      lrscale %in% c(77, 88, 99) ~ NA_character_      
    ),
    educ.ba = case_when(
      essround < 5 & edulvla == 5 ~ "BA or more",
      essround >= 5 & edulvlb > 600 ~ "BA or more",
      TRUE ~ "No BA"
    ),
    edulvla = ifelse(edulvla %in% c(77, 88, 99), NA_integer_, edulvla),
    edulvlb = ifelse(edulvlb %in% c(5555, 7777, 8888), NA_integer_, edulvlb),
    educ.ba = factor(educ.ba, levels = c("No BA", "BA or more"))
  )

Categorical descriptive table

Italy_datadf <- Italy_data %>%
  mutate(protest = case_when(
    protest == 1 ~ "Yes",    # 1 to 'Yes'
    protest == 0 ~ "No",     # 0 to 'No'
    TRUE ~ NA_character_     # Keep NA values as is, but ensure character type
  ))

Italy_datadf <- Italy_datadf %>%
  # Relabeling variables for clarity
  mutate(
    Protest = protest,      
    Ideology = polID,      
    Generation = gen,
  )
table1 <- datasummary_skim(Italy_datadf  %>% dplyr::select(Protest, Ideology, Generation), type = "categorical",
                           title = "Table 1. Descriptive statistics for main variables", output = "flextable")

table1
Table 1. Descriptive statistics for main variables

N

%

Protest

No

6715

90.1

Yes

741

9.9

Ideology

Left

1391

18.7

Moderate

2527

33.9

Right

1483

19.9

NA

2055

27.6

Generation

Interwar

1200

16.1

Baby Boomers

2343

31.4

Gen X

1927

25.8

Millennials

1475

19.8

print(table1, preview ="pdf")
## a flextable object.
## col_keys: ` `, `  `, `N`, `%` 
## header has 1 row(s) 
## body has 10 row(s) 
## original dataset sample: 
##                        N    %
## 1  Protest       No 6715 90.1
## 2               Yes  741  9.9
## 3 Ideology     Left 1391 18.7
## 4          Moderate 2527 33.9
## 5             Right 1483 19.9

Numeric descriptive table

Italy_data <- Italy_data %>%
  mutate(
    Protest = protest,
    Ideology = case_when(
      lrscale > 10 ~ NA_real_,     # Set values above 10 to NA
      TRUE ~ lrscale               # Keep other values as is
    ),
    Generation = case_when(
      yrbrn > 2000 ~ NA_real_,       # Set values above 100 to NA
      yrbrn < 1920 ~ NA_real_,        # Set values below 15 to NA
      TRUE ~ yrbrn                  # Keep other values as is
    )
  )
df <- Italy_data
df <- df %>% filter(!is.na(Ideology))
df <- df %>% filter(!is.na(Generation))

table1b <- datasummary_skim(df  %>% dplyr::select(Protest, Ideology, Generation), title = "Table 2. Descriptive statistics for main variables", output = "flextable")
## Warning: The histogram argument is only supported for (a) output types "default",
##   "html", "kableExtra", or "gt"; (b) writing to file paths with extensions
##   ".html", ".jpg", or ".png"; and (c) Rmarkdown, knitr or Quarto documents
##   compiled to PDF (via kableExtra)  or HTML (via kableExtra or gt). Use
##   `histogram=FALSE` to silence this warning.
table1b
Table 2. Descriptive statistics for main variables

Unique (#)

Missing (%)

Mean

SD

Min

Median

Max

Protest

2

0

0.1

0.3

0.0

0.0

1.0

Ideology

11

0

5.1

2.5

0.0

5.0

10.0

Generation

81

0

1965.1

18.3

1920.0

1965.0

2000.0

Hypothesis testing with infer:

step 1:

test_stat <- Italy_datadf %>%
  specify(explanatory = Generation, # change variable name for explanatory variable
          response = Protest) %>% # change variable name for outcome of interest
  hypothesize(null = "independence") %>%
  calculate(stat = "Chisq") 
## Warning: Removed 511 rows containing missing values.
print(test_stat$stat)
## X-squared 
##  49.58601
null_dist <- Italy_datadf %>%
  specify(response = Protest, explanatory = Ideology) %>%
  hypothesize(null = "independence") %>%
  generate(reps = 1000, type = "permute") %>%
  calculate(stat = "Chisq")
## Warning: Removed 2055 rows containing missing values.
null_dist
## Response: Protest (factor)
## Explanatory: Ideology (factor)
## Null Hypothesis: independence
## # A tibble: 1,000 × 2
##    replicate  stat
##        <int> <dbl>
##  1         1 3.88 
##  2         2 7.08 
##  3         3 5.32 
##  4         4 0.136
##  5         5 0.468
##  6         6 0.814
##  7         7 3.83 
##  8         8 1.54 
##  9         9 0.431
## 10        10 0.617
## # ℹ 990 more rows
p_val <- null_dist %>% # 
  get_pvalue(obs_stat = test_stat, direction = "greater")
## Warning: Please be cautious in reporting a p-value of 0. This result is an approximation
## based on the number of `reps` chosen in the `generate()` step.
## ℹ See `get_p_value()` (`?infer::get_p_value()`) for more information.
p_val
## # A tibble: 1 × 1
##   p_value
##     <dbl>
## 1       0
conf_int <- null_dist%>%
  get_confidence_interval(level = 0.95, type = "percentile")


figure2 <- null_dist %>%
  visualize(data, bins = 10, method = "simulation", dens_color = "black") +
  shade_p_value(obs_stat = test_stat, direction = "greater") +
  shade_confidence_interval(endpoints = conf_int)

print(figure2)
## Warning in min(diff(unique_loc)): no non-missing arguments to min; returning
## Inf

ggsave("output_figure.pdf", plot = figure2, device = "pdf")
## Saving 7 x 5 in image
## Warning in min(diff(unique_loc)): no non-missing arguments to min; returning
## Inf
print(figure2, preview = "pdf")
## Warning in min(diff(unique_loc)): no non-missing arguments to min; returning
## Inf

# Assuming 'test_stat' is your observed test statistic value
test_stat <- 49.58601

conf_int <- null_dist %>%
  get_confidence_interval(level = 0.95, type = "percentile")

# Visualize the null distribution with manual x-axis limits
figure1 <- null_dist %>%
  visualize(bins = 15, method = "simulation", dens_color = "black") +
  geom_vline(aes(xintercept = test_stat), color = "red", linetype = "dashed", size = 1) +
  shade_p_value(obs_stat = test_stat, direction = "greater") +
  shade_confidence_interval(endpoints = conf_int) +
  coord_cartesian(xlim = c(0, max(null_dist$stat) * 1.1)) + # Set manual x-axis limits
  labs(title = "Figure 1. Simulation-Based Null Distribution",
       x = "Chi-Square",
       y = "Count")
## 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.
print(figure1)
## Warning in min(diff(unique_loc)): no non-missing arguments to min; returning
## Inf

print(figure1, preview= "PDF")
## Warning in min(diff(unique_loc)): no non-missing arguments to min; returning
## Inf

Regression modelling:

Italy_datadf <- Italy_datadf %>%
  mutate(
    Protest = case_when(
      Protest == "Yes" ~ 1,
      Protest == "No" ~ 0,
      TRUE ~ NA_integer_ 
    )
  )
model1 <- glm(Protest ~ Generation, family = binomial, data = Italy_datadf, weights = round(weight))  # single predictor 
model2 <- glm(Protest ~ Generation + Ideology, family = binomial, data = Italy_datadf, weights = round(weight)) # two predictors 
model3 <- glm(Protest ~ Generation + Ideology + Generation*Ideology, family = binomial, data = Italy_datadf, weights = round(weight)) # both predictors + interaction

Model Summary:

modelsummary(
  list(model1, model2, model3),
  fmt = 1,
  estimate  = c( "{estimate} ({std.error}){stars}",
                "{estimate} ({std.error}){stars}",
                "{estimate} ({std.error}){stars}"),
  statistic = NULL)

(1)

(2)

(3)

(Intercept)

-2.9 (0.1)***

-2.0 (0.1)***

-2.0 (0.1)***

GenerationBaby Boomers

0.8 (0.1)***

0.7 (0.1)***

0.7 (0.1)***

GenerationGen X

0.8 (0.1)***

0.7 (0.1)***

0.7 (0.1)***

GenerationMillennials

1.1 (0.1)***

1.1 (0.1)***

1.1 (0.1)***

IdeologyModerate

-0.9 (0.1)***

-1.0 (0.2)***

IdeologyRight

-0.9 (0.1)***

-0.9 (0.2)***

GenerationBaby Boomers × IdeologyModerate

0.1 (0.2)

GenerationGen X × IdeologyModerate

0.1 (0.2)

GenerationMillennials × IdeologyModerate

0.0 (0.2)

GenerationBaby Boomers × IdeologyRight

0.0 (0.2)

GenerationGen X × IdeologyRight

0.0 (0.2)

GenerationMillennials × IdeologyRight

-0.1 (0.2)

Num.Obs.

6945

5104

5104

AIC

12779.9

10545.5

10555.3

BIC

12807.3

10584.7

10633.8

Log.Lik.

-6385.936

-5266.741

-5265.666

RMSE

0.29

0.32

0.32

Improving display:

models <- list()
models[['SLR']]<- glm(Protest ~ Generation, family = binomial, data = Italy_datadf, weights = round(weight))  # single predictor  
models[['MLR']]<- glm(Protest ~ Generation + Ideology, family = binomial, data = Italy_datadf, weights = round(weight)) # two predictors 
models[['Interaction']]<- glm(Protest ~ Generation + Ideology + Generation*Ideology, family = binomial, data = Italy_datadf, weights = round(weight)) # both predictors + interaction

pdf("output_table.pdf", width = 10, height = 6)
modelsummary(models,
  fmt = 1,
  estimate  = c( "{estimate} ({std.error}){stars}",
                "{estimate} ({std.error}){stars}",
                "{estimate} ({std.error}){stars}"),
  statistic = NULL,
  exponentiate = FALSE, ## NOTE: this is to exponentiate directly, otherwise leave out or set to FALSE
  coef_rename = c("GenerationBaby Boomers" = "Baby Boomers", "GenerationGen X" = "Gen X", "GenerationMillennials" = "Millennials", "IdeologyModerate" = "Moderate", "IdeologyRight" = "Right"),
  title = 'Table 3. Regression models predicting protest')
Table 3. Regression models predicting protest

SLR

MLR

Interaction

(Intercept)

-2.9 (0.1)***

-2.0 (0.1)***

-2.0 (0.1)***

Baby Boomers

0.8 (0.1)***

0.7 (0.1)***

0.7 (0.1)***

Gen X

0.8 (0.1)***

0.7 (0.1)***

0.7 (0.1)***

Millennials

1.1 (0.1)***

1.1 (0.1)***

1.1 (0.1)***

Moderate

-0.9 (0.1)***

-1.0 (0.2)***

Right

-0.9 (0.1)***

-0.9 (0.2)***

Baby Boomers:Moderate

0.1 (0.2)

Gen X:Moderate

0.1 (0.2)

Millennials:Moderate

0.0 (0.2)

Baby Boomers:Right

0.0 (0.2)

Gen X:Right

0.0 (0.2)

Millennials:Right

-0.1 (0.2)

Num.Obs.

6945

5104

5104

AIC

12779.9

10545.5

10555.3

BIC

12807.3

10584.7

10633.8

Log.Lik.

-6385.936

-5266.741

-5265.666

RMSE

0.29

0.32

0.32

pdf("output_table.pdf")
print('Table 3. Regression models predicting protest')
## [1] "Table 3. Regression models predicting protest"
dev.off()
## png 
##   2

Interaction plot:

model3<- glm(Protest ~ Generation + Ideology + Generation*Ideology, family = binomial, data = Italy_datadf, weights = round(weight))
cat_plot(model3, pred = Generation, modx = Ideology , geom= "line", jnplot = TRUE) 

title = ('Figure 2. Catplot')

ggsave("catplot.pdf", plot = last_plot(), device = "pdf")
## Saving 7 x 5 in image
check_model(model1)