Loading Packages and Data into R

# List of packages
packages <- c("tidyverse", "infer", "fst") # 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.4
## ✔ 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
## [[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"
france_data <- read.fst("france_data.fst")
ess <- read_fst("All-ESS-Data.fst")

Task 1

Based on the recoding of lrscale for “left” and “right” and omitting “moderates” (see Tutorial 4), and educ.ba from this tutorial, do the long form coding for Chi-Square. Using the steps outlined in the tutorial, first generate the tables of expected proportions and frequencies. Determine and interpret the critical value for independence. Finally, determine and interpret both the Pearson’s chi-squared statistic and the p-value. Discuss main takeaways.

france_data <- ess %>%
  # Filter for France
  filter(cntry == "FR") %>%

  
  # Recoding lrscale and cleaning educ.ba (will use later)
  mutate(
    lrscale = case_when(
      lrscale %in% 0:3 ~ "Left",  # Left-wing (0 to 3)
      lrscale %in% 7:10 ~ "Right",   # Right-wing (7 to 10)
     TRUE ~ NA_character_ # Moderate (4, 5, 6) and special codes (77, 88, 99)
    ),
    

    # Recoding education with case_when for consistency
    educ.ba = case_when(
      essround < 5 & edulvla == 5 ~ "BA or more",
      essround >= 5 & edulvlb > 600 ~ "BA or more",
      TRUE ~ "No BA"
    )  ) %>%
  # Ensure all NAs are handled uniformly
  mutate(
    edulvla = ifelse(edulvla %in% c(77, 88), NA_integer_, edulvla),
    edulvlb = ifelse(edulvlb %in% c(5555, 7777, 8888), NA_integer_, edulvlb),
    educ.ba = as.character(educ.ba)  # Force educ.ba to be treated as character
  )
# using unique to identify
unique(france_data$lrscale)
## [1] "Left"  NA      "Right"
# does not return NA, but the following does
unique(france_data$lrscale)
## [1] "Left"  NA      "Right"
# here's how to make sure it's really cleaned
france_data <- france_data %>% filter(!is.na(lrscale))
france_data <- france_data %>% filter(!is.na(educ.ba))

# double-checking + quick distribution
unique(france_data$lrscale)
## [1] "Left"  "Right"
table(france_data$lrscale)
## 
##  Left Right 
##  4709  4302

Long form coding Chi- square

## Not necessary, but always good to rename when doing operations so you can backtrack to clean dataset
tab_dat <- france_data

# Create a crosstable and save it into the object mytab
mytab <- table(tab_dat$lrscale, tab_dat$educ.ba)

# Calculate row-wise sums of counts in table 
rsums <- rowSums(mytab)

# Calculate column-wise sums of counts in table
csums <- colSums(mytab)

# Get the total count of the table (N)
N <- sum(mytab)

# Generate table of expected proportions
ptab <- tcrossprod(rsums/N, csums/N)
cat("Table of Expected Proportions:\n")
## Table of Expected Proportions:
print(ptab)
##           [,1]      [,2]
## [1,] 0.1311243 0.3914592
## [2,] 0.1197912 0.3576253
# Table of expected frequencies
ftab <- N * ptab
cat("Table of Expected Frequencies:\n")
## Table of Expected Frequencies:
print(ftab)
##          [,1]     [,2]
## [1,] 1181.561 3527.439
## [2,] 1079.439 3222.561
# Critical Value for Independence:
alpha <- 0.05 # significance level
c_val <- qchisq(alpha, df = 1, lower.tail = FALSE)
cat("Critical Value:", round(c_val, 3), "\n")
## Critical Value: 3.841

Answer

the critical value of independence is 3.841 which shows the threshold of our test statistic

Calculating and interpreting Pearson’s chi-squared statistic

# Pearson's chi-squared statistic:
test_stat_lr <- sum((mytab - ftab)^2 / ftab) # calculating observed chi-square statistic (sum of) observing expected squared over expected
cat("Pearson's X^2:", round(test_stat_lr, 4), "\n")
## Pearson's X^2: 100.8551

Calculating the p-value

# p-value:
p_val <- pchisq(test_stat_lr, df = 1, lower.tail = F)
cat("p-value:", round(p_val, 4), "\n")
## p-value: 0
# Using chisq.test for validation:
cat("Chi-squared test result:\n")
## Chi-squared test result:
print(chisq.test(mytab, correct = F))
## 
##  Pearson's Chi-squared test
## 
## data:  mytab
## X-squared = 100.86, df = 1, p-value < 2.2e-16

Answer for Pearson’s chi-squared statistic

100.8551 shows how much our observed data deviates from what we would expect. while the p-value is 0 which the probaily of observing the data

main takeways for task 1

since the observed pearson is 100.8551 it is greater than the critical value of 3.841. as a high chi-squared value indicates that the observed data significantly differs from what would be expected if categories were independent. along with this since the p-value is small it indicates strong evidence against the null hypothesis

Task 2

Do Steps 1 to 3 using the infer package (as we did in the tutorial), for the variables sbbsntx (recoded to the corresponding 5 categories) as the response and domicil (recoded to urban and rural, setting 2 to urban instead of peri-urban) as the explanatory variable for the country of France.

See variable info here: https://ess.sikt.no/en/datafile/ffc43f48-e15a-4a1c-8813-47eda377c355/92?tab=1&elements=[%22874a86e2-a0f6-40b4-aef7-51c8eed98a7d/1%22])

Provide interpretations of the output (consider the variable info).

france_data <- ess %>%
  # Filter for France
  filter(cntry == "FR") %>%
  
  # Recoding to the corresponding 5 categories
  mutate(
    sbbsntx = case_when(
       sbbsntx == 1 ~ "Agree Strongly",
       sbbsntx == 2 ~ "Agree",
       sbbsntx == 3 ~ "Neither Agree or Disagree",
       sbbsntx == 4 ~ "Diagree",
       sbbsntx == 5 ~ "Strongly Disargree",
      sbbsntx %in% c(7, 8, 9) ~ NA_character_,  # Handle other specific cases where you want to set it as NA
      TRUE ~ as.character(sbbsntx)  # Keep other values as-is but ensure they are characters
    ),
     # Recoding to urban and rural
    domicil = case_when(
        domicil == 1 ~ "Urban",
        domicil == 2 ~ "Urban",
        domicil == 3 ~ "Rural",
        domicil == 4 ~ "Rural",
        domicil == 5 ~ "Rural",
         domicil %in% c(7, 8, 9) ~ NA_character_,  # Handle other specific cases where you want to set it as NA
      TRUE ~ as.character(domicil)  # Keep other values as-is but ensure they are characters
   )
  )
# using unique to identify
unique(france_data$sbbsntx)
## [1] NA                          "Agree Strongly"           
## [3] "Neither Agree or Disagree" "Agree"                    
## [5] "Strongly Disargree"        "Diagree"
# does not return NA, but the following does
unique(france_data$sbbsntx)
## [1] NA                          "Agree Strongly"           
## [3] "Neither Agree or Disagree" "Agree"                    
## [5] "Strongly Disargree"        "Diagree"
# here's how to make sure it's really cleaned
france_data <- france_data %>% filter(!is.na(sbbsntx))
france_data <- france_data %>% filter(!is.na(domicil))

# double-checking + quick distribution
unique(france_data$sbbsntx)
## [1] "Agree Strongly"            "Neither Agree or Disagree"
## [3] "Agree"                     "Strongly Disargree"       
## [5] "Diagree"
table(france_data$sbbsntx)
## 
##                     Agree            Agree Strongly                   Diagree 
##                      1538                       756                       640 
## Neither Agree or Disagree        Strongly Disargree 
##                       804                       301

Step 1

test_stat <- france_data %>%
  specify(explanatory = domicil, # change variable name for explanatory variable
          response = sbbsntx) %>% # change variable name for outcome of interest
  hypothesize(null = "independence") %>%
  calculate(stat = "Chisq") # replace in between quotation marks appropriate test statistic

print(test_stat$stat)
## X-squared 
##  19.97187

Step 2

null_distribution <- france_data %>%
  specify(explanatory = domicil,
          response = sbbsntx) %>%
  hypothesize(null = "independence") %>%
  generate(reps = 1000, type = "permute") %>% # only line we are adding, use reps = 1000 as standard but change if too computationally demanding
  calculate(stat = "Chisq")

Step 3

p_val <- null_distribution %>% # replace name here if you assigned something other than null_distribution above
  get_pvalue(obs_stat = test_stat, direction = "two-sided") # would only replace test_stat if assigned another name in Step 1
## 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

Interpretation for Task 2

since the p-value is low we reject the null hypothesis

Task 3

Now do Step 4 (i.e., the null distribution visualization with confidence intervals) for the same variables and country as Task 2, and interpret the output.

null_distribution %>%
  visualize() +
  shade_p_value(obs_stat = test_stat, direction = "two-sided")
## Warning: Chi-Square usually corresponds to right-tailed tests. Proceed with
## caution.

null_distribution
## Response: sbbsntx (factor)
## Explanatory: domicil (factor)
## Null Hypothesis: independence
## # A tibble: 1,000 × 2
##    replicate  stat
##        <int> <dbl>
##  1         1 3.88 
##  2         2 4.32 
##  3         3 6.39 
##  4         4 1.53 
##  5         5 2.90 
##  6         6 3.62 
##  7         7 0.717
##  8         8 1.57 
##  9         9 1.28 
## 10        10 1.05 
## # ℹ 990 more rows
conf_int <- null_distribution %>%
  get_confidence_interval(level = 0.95, type = "percentile")


null_distribution %>%
  visualize() +
  shade_p_value(obs_stat = test_stat, direction = "two-sided") +
  shade_confidence_interval(endpoints = conf_int)
## Warning: Chi-Square usually corresponds to right-tailed tests. Proceed with
## caution.

null_distribution
## Response: sbbsntx (factor)
## Explanatory: domicil (factor)
## Null Hypothesis: independence
## # A tibble: 1,000 × 2
##    replicate  stat
##        <int> <dbl>
##  1         1 3.88 
##  2         2 4.32 
##  3         3 6.39 
##  4         4 1.53 
##  5         5 2.90 
##  6         6 3.62 
##  7         7 0.717
##  8         8 1.57 
##  9         9 1.28 
## 10        10 1.05 
## # ℹ 990 more rows

Task 4

Conduct Steps 1 to 3 using the infer package (as we did in the tutorial), for the variables ccrdprs (leaving it on the 0-10 numeric scale) as the response (or outcome) variable and lrscale (recoded as left and right, omitting “moderates” as we did in Task 1) as the explanatory variable.

Variable info here: https://ess.sikt.no/en/datafile/ffc43f48-e15a-4a1c-8813-47eda377c355/92?tab=1&elements=[%2283b08cf2-508e-49a0-9fc8-c3cc281290ec/4%22]

Provide interpretations of the output (consider the variable info).

  france_data <- ess %>%
  # Filter for France
  filter(cntry == "FR") %>%
  
     # Recoding 'lrscale' 
    mutate(
    lrscale = case_when(
      lrscale %in% 0:3 ~ "Left",                    # Values 0 to 3 in 'lrscale' are categorized as "Left"
      lrscale %in% 7:10 ~ "Right",                  # Values 7 to 10 in 'lrscale' are categorized as "Right"
      lrscale %in% 4:6 ~ "Moderate",                # Values 4 to 6 in 'lrscale' are categorized as "Moderate"
      lrscale %in% c(77, 88, 99) ~ NA_character_    # Values 77, 88, 99 in 'lrscale' are set as NA
    ),

    ccrdprs = ifelse(ccrdprs %in% c(66, 77, 88, 99), NA, ccrdprs),   # Set values 66, 77, 88, 99 in 'ccrdprs' to NA
  )
france_data <- france_data %>% filter(!is.na(lrscale))
# using unique to identify
unique(france_data$lrscale)
## [1] "Left"     "Moderate" "Right"
# does not return NA, but the following does
unique(france_data$lrscale)
## [1] "Left"     "Moderate" "Right"
# here's how to make sure it's really cleaned
france_data <- france_data %>% filter(!is.na(lrscale))
france_data <- france_data %>% filter(!is.na(ccrdprs))

# double-checking + quick distribution
unique(france_data$lrscale)
## [1] "Left"     "Right"    "Moderate"
table(france_data$lrscale)
## 
##     Left Moderate    Right 
##      889     1754      982

Step 1

test_stat <- france_data %>%
  specify(explanatory = lrscale, # change variable name for explanatory variable
          response = ccrdprs) %>% # change variable name for outcome of interest
  hypothesize(null = "independence") %>%
  calculate(stat = "F") # replace in between quotation marks appropriate test statistic

print(test_stat$stat)
## [1] 15.41871

Step 2

null_distribution <- france_data %>%
  specify(explanatory = lrscale,
          response = ccrdprs) %>%
  hypothesize(null = "independence") %>%
  generate(reps = 1000, type = "permute") %>% # only line we are adding, use reps = 1000 as standard but change if too computationally demanding
  calculate(stat = "F")

Step 3

p_val <- null_distribution %>% # replace name here if you assigned something other than null_distribution above
  get_pvalue(obs_stat = test_stat, direction = "greater") # would only replace test_stat if assigned another name in Step 1
## 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

Interpretation for Task 4

since the p-value is low we reject the null hypothesis revaling a significant association

Task 5

Now do Step 4 (i.e., the null distribution visualization with confidence intervals) for the same variables and country as Task 4, and interpret the output.

null_distribution %>%
  visualize() +
  shade_p_value(obs_stat = test_stat, direction = "greater")
## Warning in min(diff(unique_loc)): no non-missing arguments to min; returning
## Inf

null_distribution
## Response: ccrdprs (numeric)
## Explanatory: lrscale (factor)
## Null Hypothesis: independence
## # A tibble: 1,000 × 2
##    replicate   stat
##        <int>  <dbl>
##  1         1 0.0301
##  2         2 1.39  
##  3         3 0.473 
##  4         4 0.104 
##  5         5 0.341 
##  6         6 2.17  
##  7         7 0.338 
##  8         8 0.0301
##  9         9 0.0489
## 10        10 0.400 
## # ℹ 990 more rows
conf_int <- null_distribution %>%
  get_confidence_interval(level = 0.95, type = "percentile")


null_distribution %>%
  visualize() +
  shade_p_value(obs_stat = test_stat, direction = "greater") +
  shade_confidence_interval(endpoints = conf_int)
## Warning in min(diff(unique_loc)): no non-missing arguments to min; returning
## Inf

null_distribution
## Response: ccrdprs (numeric)
## Explanatory: lrscale (factor)
## Null Hypothesis: independence
## # A tibble: 1,000 × 2
##    replicate   stat
##        <int>  <dbl>
##  1         1 0.0301
##  2         2 1.39  
##  3         3 0.473 
##  4         4 0.104 
##  5         5 0.341 
##  6         6 2.17  
##  7         7 0.338 
##  8         8 0.0301
##  9         9 0.0489
## 10        10 0.400 
## # ℹ 990 more rows

Interpretation

a left-skewed distribution is shown in the graph