france_data <- read_fst("france_data.fst")
ess <- read_fst("All-ESS-Data.fst")
dim(france_data)
## [1] 19038 2665
dim(ess)
## [1] 490555 2665
head(france_data[, c("trstplt", "trstprl", "trstprt", "gndr", "domicil", "eisced", "yrbrn")])
france_clean <- france_data %>%
mutate(
trstplt = ifelse(trstplt > 10 | trstplt < 0, NA, trstplt),
trstprl = ifelse(trstprl > 10 | trstprl < 0, NA, trstprl),
trstprt = ifelse(trstprt > 10 | trstprt < 0, NA, trstprt),
trust = scales::rescale(
trstplt + trstprl + trstprt,
to = c(0, 100),
na.rm = TRUE
),
generation = case_when(
yrbrn >= 1928 & yrbrn <= 1945 ~ "Interwar Generation",
yrbrn >= 1946 & yrbrn <= 1964 ~ "Baby Boomers",
yrbrn >= 1965 & yrbrn <= 1980 ~ "Generation X",
yrbrn >= 1981 & yrbrn <= 1996 ~ "Millennials",
yrbrn >= 1997 & yrbrn <= 2005 ~ "Generation Z",
TRUE ~ NA_character_
),
generation = factor(generation,
levels = c("Interwar Generation", "Baby Boomers",
"Generation X", "Millennials", "Generation Z")),
education = case_when(
eisced %in% c(0, 1, 2) ~ "Lower Education",
eisced %in% c(3, 4) ~ "Secondary Education",
eisced %in% c(5, 6, 7) ~ "Tertiary Education",
TRUE ~ NA_character_
),
education = factor(education,
levels = c("Lower Education", "Secondary Education", "Tertiary Education")),
geo_context = case_when(
domicil %in% 1:3 ~ "Urban",
domicil %in% 4:5 ~ "Rural",
TRUE ~ NA_character_
),
geo_context = factor(geo_context)
)
total_obs_before <- nrow(france_clean)
france_clean <- france_clean %>%
filter(!is.na(trust) & !is.na(generation) & !is.na(education) & !is.na(geo_context))
total_obs_after <- nrow(france_clean)
retained_percentage <- round((total_obs_after / total_obs_before) * 100, 1)
cat("Total observations before filtering:", total_obs_before, "\n")
## Total observations before filtering: 19038
cat("Complete cases retained:", total_obs_after, "\n")
## Complete cases retained: 16583
cat("Percentage of observations retained:", retained_percentage, "%\n")
## Percentage of observations retained: 87.1 %
summary(france_clean$trust)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.00 20.00 36.67 35.40 50.00 100.00
table(france_clean$generation, useNA = "ifany")
##
## Interwar Generation Baby Boomers Generation X Millennials
## 2954 5758 4599 2775
## Generation Z
## 497
table(france_clean$education, useNA = "ifany")
##
## Lower Education Secondary Education Tertiary Education
## 5411 6337 4835
table(france_clean$geo_context, useNA = "ifany")
##
## Rural Urban
## 5985 10598
ess_clean <- ess %>%
mutate(
trstplt = ifelse(trstplt > 10 | trstplt < 0, NA, trstplt),
trstprl = ifelse(trstprl > 10 | trstprl < 0, NA, trstprl),
trstprt = ifelse(trstprt > 10 | trstprt < 0, NA, trstprt),
trust = scales::rescale(
trstplt + trstprl + trstprt,
to = c(0, 100),
na.rm = TRUE
)
)
country_trust_trends <- ess_clean %>%
filter(!is.na(trust)) %>%
group_by(cntry, essround) %>%
summarize(
mean_trust = mean(trust, na.rm = TRUE),
n = n(),
year = 2002 + (first(essround) - 1) * 2,
.groups = "drop"
)
country_trust_trends %>%
select(essround, year) %>%
distinct() %>%
arrange(essround)
latest_round <- max(country_trust_trends$essround, na.rm = TRUE)
latest_trust <- country_trust_trends %>%
filter(essround == latest_round) %>%
arrange(mean_trust)
highest_trust_country <- latest_trust %>% slice_max(order_by = mean_trust, n = 1) %>% pull(cntry)
lowest_trust_country <- latest_trust %>% slice_min(order_by = mean_trust, n = 1) %>% pull(cntry)
similar_countries <- c("FR", "DE", "GB")
descriptive_stats <- france_clean %>%
summarize(
mean_trust = mean(trust, na.rm = TRUE),
sd_trust = sd(trust, na.rm = TRUE),
min_trust = min(trust, na.rm = TRUE),
max_trust = max(trust, na.rm = TRUE),
pct_interwar = mean(generation == "Interwar Generation", na.rm = TRUE) * 100,
pct_boomers = mean(generation == "Baby Boomers", na.rm = TRUE) * 100,
pct_genx = mean(generation == "Generation X", na.rm = TRUE) * 100,
pct_millennials = mean(generation == "Millennials", na.rm = TRUE) * 100,
pct_genz = mean(generation == "Generation Z", na.rm = TRUE) * 100,
pct_lower = mean(education == "Lower Education", na.rm = TRUE) * 100,
pct_secondary = mean(education == "Secondary Education", na.rm = TRUE) * 100,
pct_tertiary = mean(education == "Tertiary Education", na.rm = TRUE) * 100,
pct_urban = mean(geo_context == "Urban", na.rm = TRUE) * 100,
pct_rural = mean(geo_context == "Rural", na.rm = TRUE) * 100
)
total_obs <- nrow(france_clean)
descriptive_table <- descriptive_stats %>%
pivot_longer(cols = everything(), names_to = "Statistic", values_to = "Value") %>%
mutate(
Category = case_when(
str_detect(Statistic, "trust") ~ "Institutional Trust",
str_detect(Statistic, "interwar|boomers|genx|millennials|genz") ~ "Generation",
str_detect(Statistic, "lower|secondary|tertiary") ~ "Education",
str_detect(Statistic, "urban|rural") ~ "Geographic Context",
TRUE ~ "Other"
),
Statistic = case_when(
Statistic == "mean_trust" ~ "Mean",
Statistic == "sd_trust" ~ "Standard deviation",
Statistic == "min_trust" ~ "Minimum",
Statistic == "max_trust" ~ "Maximum",
Statistic == "pct_interwar" ~ "Interwar Generation (1928-1945)",
Statistic == "pct_boomers" ~ "Baby Boomers (1946-1964)",
Statistic == "pct_genx" ~ "Generation X (1965-1980)",
Statistic == "pct_millennials" ~ "Millennials (1981-1996)",
Statistic == "pct_genz" ~ "Generation Z (1997-2005)",
Statistic == "pct_lower" ~ "Lower",
Statistic == "pct_secondary" ~ "Secondary",
Statistic == "pct_tertiary" ~ "Tertiary",
Statistic == "pct_urban" ~ "Urban",
Statistic == "pct_rural" ~ "Rural",
TRUE ~ Statistic
)
) %>%
arrange(Category, Statistic) %>%
gt(groupname_col = "Category") %>%
fmt_number(
columns = Value,
decimals = 1
) %>%
cols_label(
Statistic = "Measure",
Value = "Value"
) %>%
tab_header(
title = md("**Table 1. Descriptive Statistics of Key Variables**^1^"),
subtitle = "France (ESS Data, 2004-2020)"
) %>%
fmt_percent(
columns = Value,
rows = !(Statistic %in% c("Mean", "Standard deviation", "Minimum", "Maximum")),
decimals = 1,
scale_values = FALSE
) %>%
tab_source_note(
source_note = md(paste0("^1^*N* = ", total_obs, " complete cases."))
) %>%
tab_style(
style = list(
cell_text(weight = "bold")
),
locations = cells_column_labels()
) %>%
tab_style(
style = list(
cell_text(weight = "bold")
),
locations = cells_row_groups()
) %>%
cols_width(
Statistic ~ px(400),
Value ~ px(100)
) %>%
tab_options(
column_labels.border.top.width = px(3),
column_labels.border.top.color = "black",
column_labels.border.bottom.width = px(2),
column_labels.border.bottom.color = "black",
table.border.bottom.color = "black",
table.border.bottom.width = px(3),
table.width = pct(90),
data_row.padding = px(3),
row_group.padding = px(4),
source_notes.font.size = px(10),
source_notes.padding = px(3),
column_labels.hidden = FALSE,
row_group.border.top.width = px(2),
row_group.border.bottom.width = px(2),
row_group.border.top.color = "black",
row_group.border.bottom.color = "black"
)
descriptive_table
Table 1. Descriptive Statistics of Key Variables1 | |
France (ESS Data, 2004-2020) | |
Measure | Value |
---|---|
Education | |
Lower | 32.6% |
Secondary | 38.2% |
Tertiary | 29.2% |
Generation | |
Baby Boomers (1946-1964) | 34.7% |
Generation X (1965-1980) | 27.7% |
Generation Z (1997-2005) | 3.0% |
Interwar Generation (1928-1945) | 17.8% |
Millennials (1981-1996) | 16.7% |
Geographic Context | |
Rural | 36.1% |
Urban | 63.9% |
Institutional Trust | |
Maximum | 100.0 |
Mean | 35.4 |
Minimum | 0.0 |
Standard deviation | 19.4 |
1N = 16583 complete cases. |
Table 1 presents the descriptive statistics of our key variables after retaining only complete cases (N = 16,583). In terms of educational attainment, the sample shows a relatively balanced distribution with the largest segment having secondary education (38.2%), followed by lower education (32.6%), and tertiary education (29.2%). This distribution aligns well with France’s educational landscape during this period, providing a solid foundation for examining educational differences in institutional trust.
The generational composition reflects France’s demographic structure, with Baby Boomers (1946-1964) constituting the largest group at 34.7%, followed by Generation X (1965-1980) at 27.7%. The Interwar Generation (1928-1945) makes up 17.8% of the sample, while Millennials (1981-1996) represent 16.7%. As expected, Generation Z (1997-2005) forms the smallest cohort at 3.0%, given that many in this generation would have been too young to participate in earlier survey waves. Regarding geographic context, nearly two-thirds of respondents (63.9%) reside in urban areas, compared to 36.1% in rural settings. This urban-rural distribution reflects France’s substantial urbanization, while still providing adequate representation of rural populations to enable meaningful comparisons.
Our outcome variable, institutional trust, displays considerable variation across the sample. Measured on a standardized scale from 0 to 100, it shows a mean of 35.4 with a standard deviation of 19.4. This relatively low average trust score (well below the midpoint of 50) suggests a general tendency toward institutional skepticism among French citizens, though the substantial standard deviation indicates meaningful variation that our predictors may help explain. These descriptive statistics provide the foundation for our subsequent analyses, where we will examine how these demographic factors relate to institutional trust in France.
For our analysis, we employ a multiple regression modeling approach to examine how institutional trust varies across generations, education levels, and geographic contexts in France. We follow a progressive model-building strategy:
This approach allows us to systematically explore how different factors contribute to institutional trust while controlling for other important variables.
First, let’s examine the distribution of institutional trust across our sample.
french_blue <- "#0055A4"
trust_hist <- ggplot(france_clean, aes(x = trust)) +
geom_histogram(
aes(y = after_stat(count)/sum(after_stat(count))),
bins = 30,
fill = french_blue,
color = "black",
alpha = 0.8,
linewidth = 0.2
) +
geom_vline(
xintercept = mean(france_clean$trust, na.rm = TRUE),
linetype = "dashed",
color = "black",
linewidth = 0.7
) +
labs(
title = "Figure 1. Distribution of Institutional Trust in France",
subtitle = "Scale based on combined trust in politicians, parliament, and parties (0-100)",
x = "Institutional Trust Score",
y = "Proportion"
) +
scale_y_continuous(labels = scales::percent_format()) +
theme_classic() +
theme(
plot.title = element_text(face = "bold", size = 14),
plot.subtitle = element_text(color = "grey50", size = 12),
axis.title = element_text(face = "bold"),
axis.text = element_text(size = 10, color = "black"),
axis.line = element_line(color = "black", linewidth = 0.7),
axis.ticks = element_line(color = "black", linewidth = 0.7),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),
plot.margin = margin(20, 20, 20, 20)
)
trust_hist
ggsave("trust_distribution.pdf", trust_hist, width = 10, height = 6)
Next, let’s compare trust levels across generations.
generation_trust <- france_clean %>%
filter(!is.na(generation), !is.na(trust)) %>%
group_by(generation) %>%
summarize(
mean_trust = mean(trust, na.rm = TRUE),
sd_trust = sd(trust, na.rm = TRUE),
n = n(),
se_trust = sd_trust / sqrt(n),
ci_lower = mean_trust - 1.96 * se_trust,
ci_upper = mean_trust + 1.96 * se_trust,
.groups = "drop"
)
french_blues <- c("#003399", "#647695", "#8A9CBC", "#B0C4E2", "#D6E4FF")
generation_plot <- ggplot(generation_trust, aes(y = generation, x = mean_trust, fill = generation)) +
geom_col(alpha = 0.9, width = 0.7, color = "black", linewidth = 0.3) +
geom_errorbarh(
aes(xmin = ci_lower, xmax = ci_upper),
height = 0.2,
color = "black",
linewidth = 0.7
) +
scale_fill_manual(values = french_blues) +
scale_x_continuous(limits = c(0, 50), expand = c(0, 0)) +
labs(
title = "Figure 2. Institutional Trust by Generation in France",
subtitle = "Average trust score with 95% confidence intervals",
y = NULL,
x = "Mean Institutional Trust Score (0-100)",
caption = "Source: European Social Survey (2004-2020)"
) +
theme_classic() +
theme(
plot.title = element_text(face = "bold", size = 14),
plot.subtitle = element_text(color = "grey50", size = 12),
axis.title.x = element_text(face = "bold"),
axis.text.y = element_text(face = "bold"),
axis.text = element_text(color = "black"),
axis.line = element_line(color = "black", linewidth = 0.7),
axis.ticks = element_line(color = "black", linewidth = 0.7),
legend.position = "none",
panel.grid = element_blank(),
plot.margin = margin(20, 20, 20, 20)
)
generation_plot
ggsave("trust_by_generation.pdf", generation_plot, width = 10, height = 6)
Now, let’s examine differences by education level.
education_trust <- france_clean %>%
filter(!is.na(education), !is.na(trust)) %>%
group_by(education) %>%
summarize(
mean_trust = mean(trust, na.rm = TRUE),
sd_trust = sd(trust, na.rm = TRUE),
n = n(),
se_trust = sd_trust / sqrt(n),
ci_lower = mean_trust - 1.96 * se_trust,
ci_upper = mean_trust + 1.96 * se_trust,
.groups = "drop"
)
french_reds <- c("#9D2235", "#CE2B37", "#EF4151")
education_plot <- ggplot(education_trust, aes(y = education, x = mean_trust, fill = education)) +
geom_col(alpha = 0.9, width = 0.7, color = "black", linewidth = 0.3) +
geom_errorbarh(
aes(xmin = ci_lower, xmax = ci_upper),
height = 0.2,
color = "black",
linewidth = 0.7
) +
scale_fill_manual(values = french_reds) +
scale_x_continuous(limits = c(0, 50), expand = c(0, 0)) +
labs(
title = "Figure 3. Institutional Trust by Education Attainment in France",
subtitle = "Average trust score with 95% confidence intervals",
y = NULL,
x = "Mean Institutional Trust Score (0-100)",
caption = "Source: European Social Survey (2004-2020)"
) +
theme_classic() +
theme(
plot.title = element_text(face = "bold", size = 14),
plot.subtitle = element_text(color = "grey50", size = 12),
axis.title.x = element_text(face = "bold"),
axis.text.y = element_text(face = "bold"),
axis.text = element_text(color = "black"),
axis.line = element_line(color = "black", linewidth = 0.7),
axis.ticks = element_line(color = "black", linewidth = 0.7),
legend.position = "none",
panel.grid = element_blank(),
plot.margin = margin(20, 20, 20, 20)
)
education_plot
ggsave("trust_by_education.pdf", education_plot, width = 10, height = 6)
Compare urban and rural trust levels.
geo_trust <- france_clean %>%
filter(!is.na(geo_context), !is.na(trust)) %>%
group_by(geo_context) %>%
summarize(
mean_trust = mean(trust, na.rm = TRUE),
sd_trust = sd(trust, na.rm = TRUE),
n = n(),
se_trust = sd_trust / sqrt(n),
ci_lower = mean_trust - 1.96 * se_trust,
ci_upper = mean_trust + 1.96 * se_trust,
.groups = "drop"
)
greens <- c("#006341", "#00843D")
geo_plot <- ggplot(geo_trust, aes(y = geo_context, x = mean_trust, fill = geo_context)) +
geom_col(alpha = 0.9, width = 0.7, color = "black", linewidth = 0.3) +
geom_errorbarh(
aes(xmin = ci_lower, xmax = ci_upper),
height = 0.2,
color = "black",
linewidth = 0.7
) +
scale_fill_manual(values = greens) +
scale_x_continuous(limits = c(0, 50), expand = c(0, 0)) +
labs(
title = "Figure 4. Institutional Trust by Geographic Context in France",
subtitle = "Average trust score with 95% confidence intervals",
y = NULL,
x = "Mean Institutional Trust Score (0-100)",
caption = "Source: European Social Survey (2004-2020)"
) +
theme_classic() +
theme(
plot.title = element_text(face = "bold", size = 14),
plot.subtitle = element_text(color = "grey50", size = 12),
axis.title.x = element_text(face = "bold"),
axis.text.y = element_text(face = "bold"),
axis.text = element_text(color = "black"),
axis.line = element_line(color = "black", linewidth = 0.7),
axis.ticks = element_line(color = "black", linewidth = 0.7),
legend.position = "none",
panel.grid = element_blank(),
plot.margin = margin(20, 20, 20, 20)
)
geo_plot
ggsave("trust_by_geo_context.pdf", geo_plot, width = 10, height = 6)
For variation, here would be the boxplot version for all three preceding graphs:
generation_boxplot <- ggplot(france_clean, aes(x = trust, y = generation, fill = generation)) +
geom_boxplot(alpha = 0.8, outlier.size = 1, outlier.alpha = 0.5, width = 0.7) +
scale_fill_manual(values = french_blues) +
scale_x_continuous(limits = c(0, 100)) +
labs(
title = "Figure 5. Distribution of Institutional Trust by Generation",
subtitle = "Boxplots showing median, quartiles, and range",
x = "Institutional Trust Score (0-100)",
y = NULL,
caption = "Source: European Social Survey (2004-2020)"
) +
theme_classic() +
theme(
plot.title = element_text(face = "bold", size = 14),
plot.subtitle = element_text(color = "grey50", size = 12),
axis.title.x = element_text(face = "bold"),
axis.text.y = element_text(face = "bold"),
axis.text = element_text(color = "black"),
axis.line = element_line(color = "black", linewidth = 0.7),
axis.ticks = element_line(color = "black", linewidth = 0.7),
legend.position = "none",
panel.grid.major.x = element_line(color = "grey95", linewidth = 0.3),
plot.margin = margin(20, 20, 20, 20)
)
generation_boxplot
ggsave("trust_by_generation_boxplot.pdf", generation_boxplot, width = 10, height = 6)
education_boxplot <- ggplot(france_clean, aes(x = trust, y = education, fill = education)) +
geom_boxplot(alpha = 0.8, outlier.size = 1, outlier.alpha = 0.5, width = 0.7) +
scale_fill_manual(values = french_reds) +
scale_x_continuous(limits = c(0, 100)) +
labs(
title = "Figure 6. Distribution of Institutional Trust by Education Level",
subtitle = "Boxplots showing median, quartiles, and range",
x = "Institutional Trust Score (0-100)",
y = NULL,
caption = "Source: European Social Survey (2004-2020)"
) +
theme_classic() +
theme(
plot.title = element_text(face = "bold", size = 14),
plot.subtitle = element_text(color = "grey50", size = 12),
axis.title.x = element_text(face = "bold"),
axis.text.y = element_text(face = "bold"),
axis.text = element_text(color = "black"),
axis.line = element_line(color = "black", linewidth = 0.7),
axis.ticks = element_line(color = "black", linewidth = 0.7),
legend.position = "none",
panel.grid.major.x = element_line(color = "grey95", linewidth = 0.3),
plot.margin = margin(20, 20, 20, 20)
)
education_boxplot
ggsave("trust_by_education_boxplot.pdf", education_boxplot, width = 10, height = 6)
geo_boxplot <- ggplot(france_clean, aes(x = trust, y = geo_context, fill = geo_context)) +
geom_boxplot(alpha = 0.8, outlier.size = 1, outlier.alpha = 0.5, width = 0.7) +
scale_fill_manual(values = greens) +
scale_x_continuous(limits = c(0, 100)) +
labs(
title = "Figure 7. Distribution of Institutional Trust by Geographic Context",
subtitle = "Boxplots showing median, quartiles, and range",
x = "Institutional Trust Score (0-100)",
y = NULL,
caption = "Source: European Social Survey (2004-2020)"
) +
theme_classic() +
theme(
plot.title = element_text(face = "bold", size = 14),
plot.subtitle = element_text(color = "grey50", size = 12),
axis.title.x = element_text(face = "bold"),
axis.text.y = element_text(face = "bold"),
axis.text = element_text(color = "black"),
axis.line = element_line(color = "black", linewidth = 0.7),
axis.ticks = element_line(color = "black", linewidth = 0.7),
legend.position = "none",
panel.grid.major.x = element_line(color = "grey95", linewidth = 0.3),
plot.margin = margin(20, 20, 20, 20)
)
geo_boxplot
ggsave("trust_by_geo_context_boxplot.pdf", geo_boxplot, width = 10, height = 6)
You would want to describe all visuals. You can do so one by one OR group some that make sense together. Do avoid just “eyeballing” the results. Refer to specific values. Here is how we can pull specific values for the graphs above:
overall_trust <- france_clean %>%
summarize(
mean = mean(trust, na.rm = TRUE),
median = median(trust, na.rm = TRUE),
sd = sd(trust, na.rm = TRUE)
)
generation_means <- france_clean %>%
group_by(generation) %>%
summarize(
mean_trust = round(mean(trust, na.rm = TRUE), 1),
n = n(),
.groups = "drop"
) %>%
arrange(desc(mean_trust))
education_means <- france_clean %>%
group_by(education) %>%
summarize(
mean_trust = round(mean(trust, na.rm = TRUE), 1),
n = n(),
.groups = "drop"
) %>%
arrange(desc(mean_trust))
geo_means <- france_clean %>%
group_by(geo_context) %>%
summarize(
mean_trust = round(mean(trust, na.rm = TRUE), 1),
n = n(),
.groups = "drop"
) %>%
arrange(desc(mean_trust))
cat("Overall institutional trust statistics:\n")
## Overall institutional trust statistics:
cat("Mean:", round(overall_trust$mean, 1), "\n")
## Mean: 35.4
cat("Median:", round(overall_trust$median, 1), "\n")
## Median: 36.7
cat("Standard deviation:", round(overall_trust$sd, 1), "\n\n")
## Standard deviation: 19.4
cat("Mean trust by generation (highest to lowest):\n")
## Mean trust by generation (highest to lowest):
print(generation_means[, c("generation", "mean_trust")])
## # A tibble: 5 × 2
## generation mean_trust
## <fct> <dbl>
## 1 Generation Z 43.8
## 2 Interwar Generation 36.9
## 3 Millennials 35.5
## 4 Baby Boomers 34.7
## 5 Generation X 34.4
cat("\n")
cat("Mean trust by education level (highest to lowest):\n")
## Mean trust by education level (highest to lowest):
print(education_means[, c("education", "mean_trust")])
## # A tibble: 3 × 2
## education mean_trust
## <fct> <dbl>
## 1 Tertiary Education 38.9
## 2 Lower Education 34.9
## 3 Secondary Education 33.1
cat("\n")
cat("Mean trust by geographic context (highest to lowest):\n")
## Mean trust by geographic context (highest to lowest):
print(geo_means[, c("geo_context", "mean_trust")])
## # A tibble: 2 × 2
## geo_context mean_trust
## <fct> <dbl>
## 1 Urban 36.4
## 2 Rural 33.6
cat("\n")
edu_diff <- max(education_means$mean_trust) - min(education_means$mean_trust)
cat("Difference between highest and lowest education groups:", round(edu_diff, 1), "points\n")
## Difference between highest and lowest education groups: 5.8 points
gen_range <- max(generation_means$mean_trust) - min(generation_means$mean_trust)
cat("Range across generations:", round(gen_range, 1), "points\n")
## Range across generations: 9.4 points
geo_diff <- geo_means$mean_trust[1] - geo_means$mean_trust[2]
cat("Urban-rural difference:", round(geo_diff, 1), "points\n")
## Urban-rural difference: 2.8 points
boxplot_stats_gen <- france_clean %>%
group_by(generation) %>%
summarize(
median = median(trust, na.rm = TRUE),
q25 = quantile(trust, 0.25, na.rm = TRUE),
q75 = quantile(trust, 0.75, na.rm = TRUE),
iqr = q75 - q25,
.groups = "drop"
)
boxplot_stats_edu <- france_clean %>%
group_by(education) %>%
summarize(
median = median(trust, na.rm = TRUE),
q25 = quantile(trust, 0.25, na.rm = TRUE),
q75 = quantile(trust, 0.75, na.rm = TRUE),
iqr = q75 - q25,
.groups = "drop"
)
boxplot_stats_geo <- france_clean %>%
group_by(geo_context) %>%
summarize(
median = median(trust, na.rm = TRUE),
q25 = quantile(trust, 0.25, na.rm = TRUE),
q75 = quantile(trust, 0.75, na.rm = TRUE),
iqr = q75 - q25,
.groups = "drop"
)
print("Boxplot statistics by generation:")
## [1] "Boxplot statistics by generation:"
print(boxplot_stats_gen)
## # A tibble: 5 × 5
## generation median q25 q75 iqr
## <fct> <dbl> <dbl> <dbl> <dbl>
## 1 Interwar Generation 36.7 23.3 50 26.7
## 2 Baby Boomers 36.7 20 50 30
## 3 Generation X 36.7 20 50 30
## 4 Millennials 36.7 23.3 50 26.7
## 5 Generation Z 46.7 33.3 56.7 23.3
print("Boxplot statistics by education:")
## [1] "Boxplot statistics by education:"
print(boxplot_stats_edu)
## # A tibble: 3 × 5
## education median q25 q75 iqr
## <fct> <dbl> <dbl> <dbl> <dbl>
## 1 Lower Education 36.7 20 50 30
## 2 Secondary Education 33.3 20 46.7 26.7
## 3 Tertiary Education 40 26.7 53.3 26.7
print("Boxplot statistics by geo_context:")
## [1] "Boxplot statistics by geo_context:"
print(boxplot_stats_geo)
## # A tibble: 2 × 5
## geo_context median q25 q75 iqr
## <fct> <dbl> <dbl> <dbl> <dbl>
## 1 Rural 33.3 20 50 30
## 2 Urban 36.7 23.3 50 26.7
Figure 1 presents the distribution of institutional trust in France, measured on a scale from 0-100 based on combined trust in politicians, parliament, and political parties. The distribution shows that French citizens express moderate to low trust in political institutions, with a mean of 35.4 and median of 36.7. The standard deviation of 19.4 points indicates substantial variation in trust attitudes across the population.
Figure 2 (and Figure 5) displays institutional trust across generations. Generation Z reports the highest average trust (43.8), with a median of 46.7 and a narrower interquartile range (IQR = 23.3) than other generations. The Interwar Generation shows the second highest average trust (36.9), while Generation X exhibits the lowest (34.4). Notably, most generations share the same median value (36.7), except for Generation Z, suggesting that differences are driven by the distribution’s shape rather than a shift in central tendency for most cohorts.
Figure 3 (and Figure 6) illustrates differences in trust across education levels. Tertiary-educated respondents report higher trust (mean = 38.9, median = 40.0) compared to those with lower education (mean = 34.9, median = 36.7) and secondary education (mean = 33.1, median = 33.3). The IQR for tertiary education (26.7) is identical to that of secondary education but smaller than lower education (30.0), indicating similar variability in the middle 50% of responses despite different central tendencies.
Figure 4 (and Figure 7) shows differences between urban and rural contexts, with urban residents reporting moderately higher trust (mean = 36.4, median = 36.7) than rural residents (mean = 33.6, median = 33.3). The IQR for urban residents (26.7) is slightly smaller than for rural residents (30.0), suggesting somewhat less variability in trust among urban dwellers.
Now, let’s create the spaghetti graph showing trust trends across countries. For the report, we would already have met the expected requirement. But I want to show you an example to compare your case (whether you do single country or multiple countries). This is notably helpful to identify what “kind of case” you have – rooted in your data, as opposed to only the literature.
spaghetti_plot <- ggplot(country_trust_trends,
aes(x = year, y = mean_trust, group = cntry, color = cntry)) +
geom_line(
data = country_trust_trends %>%
filter(!cntry %in% c("FR", "NO", "BG")),
alpha = 0.2,
color = "grey70",
linewidth = 0.5
) +
geom_line(
data = country_trust_trends %>%
filter(cntry %in% c("FR", "NO", "BG")),
linewidth = 1.5
) +
geom_point(
data = country_trust_trends %>%
filter(cntry %in% c("FR", "NO", "BG") &
essround == latest_round),
size = 4
) +
geom_text_repel(
data = country_trust_trends %>%
filter(cntry %in% c("FR", "NO", "BG") &
essround == latest_round),
aes(label = case_when(
cntry == "FR" ~ "France",
cntry == "NO" ~ "Norway",
cntry == "BG" ~ "Bulgaria"
)),
nudge_x = 1,
direction = "y",
segment.color = "black",
segment.size = 0.5,
box.padding = 0.5,
size = 4,
fontface = "bold"
) +
scale_color_manual(
values = c(
"FR" = "#003399",
"NO" = "#BA0C2F",
"BG" = "#009B74"
)
) +
scale_x_continuous(
breaks = seq(2004, 2020, by = 2),
limits = c(2004, 2021)
) +
scale_y_continuous(
limits = c(0, 100)
) +
labs(
title = "Figure 8. Institutional Trust Trends Across Europe (2004-2020)",
subtitle = "France compared to highest (Norway) and lowest (Bulgaria) trust countries",
x = "Year",
y = "Mean Institutional Trust Score (0-100)",
caption = "Source: European Social Survey"
) +
theme_classic() +
theme(
plot.title = element_text(face = "bold", size = 14),
plot.subtitle = element_text(color = "grey50", size = 12),
axis.title = element_text(face = "bold"),
axis.text = element_text(color = "black"),
axis.line = element_line(color = "black", linewidth = 0.7),
legend.position = "none",
panel.grid.major.y = element_line(color = "grey90", linewidth = 0.3),
plot.margin = margin(20, 20, 20, 20)
)
spaghetti_plot
ggsave("trust_trends_europe.pdf", spaghetti_plot, width = 10, height = 6)
similar_plot <- ggplot(
country_trust_trends %>%
filter(cntry %in% similar_countries),
aes(x = year, y = mean_trust, group = cntry, color = cntry)
) +
geom_line(linewidth = 1.5) +
geom_point(
data = country_trust_trends %>%
filter(cntry %in% similar_countries & essround == latest_round),
size = 4
) +
geom_text_repel(
data = country_trust_trends %>%
filter(cntry %in% similar_countries & essround == latest_round),
aes(label = case_when(
cntry == "FR" ~ "France",
cntry == "DE" ~ "Germany",
cntry == "GB" ~ "United Kingdom"
)),
nudge_x = 1,
direction = "y",
segment.color = "black",
segment.size = 0.5,
box.padding = 0.5,
size = 4,
fontface = "bold"
) +
scale_color_manual(
values = c(
"FR" = "#003399",
"DE" = "#FFCC00",
"GB" = "#7D26CD"
)
) +
scale_x_continuous(
breaks = seq(2002, 2020, by = 2),
limits = c(2004, 2021)
) +
scale_y_continuous(
limits = c(0, 60)
) +
labs(
title = "Figure 9. Institutional Trust Trends in France, Germany, and UK (2004-2020)",
subtitle = "Comparison of similar Western European countries",
x = "Year",
y = "Mean Institutional Trust Score (0-100)",
caption = "Source: European Social Survey"
) +
theme_classic() +
theme(
plot.title = element_text(face = "bold", size = 14),
plot.subtitle = element_text(color = "grey50", size = 12),
axis.title = element_text(face = "bold"),
axis.text = element_text(color = "black"),
axis.line = element_line(color = "black", linewidth = 0.7),
axis.ticks = element_line(color = "black", linewidth = 0.7),
legend.position = "none",
panel.grid.major.y = element_line(color = "grey90", linewidth = 0.3),
plot.margin = margin(20, 20, 20, 20)
)
similar_plot
ggsave("trust_trends_similar_countries.pdf", similar_plot, width = 10, height = 6)
Again, to speak more concretely about the figures, let’s calculate relevant values:
fr_no_bg_trends <- country_trust_trends %>%
filter(cntry %in% c("FR", "NO", "BG")) %>%
arrange(cntry, year)
country_means <- fr_no_bg_trends %>%
group_by(cntry) %>%
summarize(
mean_trust_overall = mean(mean_trust, na.rm = TRUE),
.groups = "drop"
)
country_change <- fr_no_bg_trends %>%
group_by(cntry) %>%
arrange(cntry, year) %>%
summarize(
first_year = first(year),
last_year = last(year),
first_trust = first(mean_trust),
last_trust = last(mean_trust),
abs_change = last_trust - first_trust,
pct_change = (last_trust - first_trust) / first_trust * 100,
.groups = "drop"
)
country_volatility <- fr_no_bg_trends %>%
group_by(cntry) %>%
arrange(cntry, year) %>%
mutate(
trust_change = c(NA, diff(mean_trust))
) %>%
summarize(
volatility = sd(trust_change, na.rm = TRUE),
.groups = "drop"
)
similar_countries_trends <- country_trust_trends %>%
filter(cntry %in% c("FR", "DE", "GB")) %>%
arrange(cntry, year)
similar_means <- similar_countries_trends %>%
group_by(cntry) %>%
summarize(
mean_trust_overall = mean(mean_trust, na.rm = TRUE),
.groups = "drop"
)
similar_change <- similar_countries_trends %>%
group_by(cntry) %>%
arrange(cntry, year) %>%
summarize(
first_year = first(year),
last_year = last(year),
first_trust = first(mean_trust),
last_trust = last(mean_trust),
abs_change = last_trust - first_trust,
pct_change = (last_trust - first_trust) / first_trust * 100,
.groups = "drop"
)
print("Trust trends for France, Norway, and Bulgaria:")
## [1] "Trust trends for France, Norway, and Bulgaria:"
print(country_means)
## # A tibble: 3 × 2
## cntry mean_trust_overall
## <chr> <dbl>
## 1 BG 20.0
## 2 FR 35.5
## 3 NO 54.5
print(country_change)
## # A tibble: 3 × 7
## cntry first_year last_year first_trust last_trust abs_change pct_change
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 BG 2006 2020 18.7 21.9 3.23 17.3
## 2 FR 2004 2020 37.2 39.3 2.09 5.62
## 3 NO 2004 2020 46.7 62.0 15.3 32.9
print(country_volatility)
## # A tibble: 3 × 2
## cntry volatility
## <chr> <dbl>
## 1 BG 3.11
## 2 FR 2.60
## 3 NO 1.31
print("Trust trends for France, Germany, and UK:")
## [1] "Trust trends for France, Germany, and UK:"
print(similar_means)
## # A tibble: 3 × 2
## cntry mean_trust_overall
## <chr> <dbl>
## 1 DE 39.7
## 2 FR 35.5
## 3 GB 37.5
print(similar_change)
## # A tibble: 3 × 7
## cntry first_year last_year first_trust last_trust abs_change pct_change
## <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 DE 2004 2020 34.8 42.2 7.40 21.2
## 2 FR 2004 2020 37.2 39.3 2.09 5.62
## 3 GB 2004 2020 37.8 36.1 -1.68 -4.43
Figure 8 presents institutional trust trends across European countries from 2004 to 2020, highlighting France in comparison with Norway (highest trust) and Bulgaria (lowest trust). The figure reveals substantial and persistent differences between these countries. Norway consistently maintains the highest trust levels (overall mean of 54.5), substantially above France (mean 35.5) and Bulgaria (mean 20.0). This hierarchy remains stable throughout the period, with minimal overlap in trust levels between the three countries.
Over the 16-year period, all three countries show positive trends in institutional trust, though with varying magnitudes. Norway demonstrates the most substantial increase, with trust levels rising from 46.7 in 2004 to 62.0 in 2020, representing a 15.3-point (32.9%) increase. Bulgaria shows modest growth from 18.7 to 21.9 points, a 3.2-point (17.3%) increase. France exhibits the most stability, with trust increasing only slightly from 37.2 to 39.3 points, a 2.1-point (5.6%) increase.
The volatility of trust trends also differs across countries. Bulgaria shows the highest year-to-year variability (SD of changes = 3.1), suggesting less stable institutional attitudes. France demonstrates moderate volatility (SD = 2.6), while Norway shows the most consistent trend (SD = 1.3) despite its steeper overall increase.
Figure 9 compares institutional trust in France with similar Western European countries (Germany and the UK). Germany displays the highest average trust (39.7), followed by the UK (37.5) and France (35.5), though these differences are considerably smaller than those observed between Norway and Bulgaria. The most striking contrast is in the trajectories: Germany shows substantial growth from 34.8 to 42.2 points (7.4-point increase, 21.2%), while France shows modest growth (2.1 points, 5.6%), and the UK exhibits a slight decline from 37.8 to 36.1 points (-1.7 points, -4.4%).
These cross-national patterns suggest that while institutional trust varies substantially across European countries, France maintains a relatively moderate and stable position. The contrast between Germany’s strong upward trajectory and France’s modest growth may indicate differences in how political and economic developments have influenced public trust in these neighboring countries.
Now, let’s build our progressive regression models to understand the factors influencing institutional trust.
model1 <- lm(trust ~ generation, data = france_clean)
model2 <- lm(trust ~ generation + education, data = france_clean)
model3 <- lm(trust ~ generation + education + geo_context, data = france_clean)
model4 <- lm(trust ~ generation + education + geo_context + education * geo_context, data = france_clean)
regression_table <- tab_model(
model1, model2, model3, model4,
title = "Table 2. Regression Models Predicting Institutional Trust in France",
dv.labels = c("Model 1: Generations",
"Model 2: + Education",
"Model 3: + Geography",
"Model 4: With Interaction"),
pred.labels = c(
"(Intercept)" = "Intercept",
"generationBaby Boomers" = "Baby Boomers",
"generationGeneration X" = "Generation X",
"generationMillennials" = "Millennials",
"generationGeneration Z" = "Generation Z",
"educationSecondary Education" = "Secondary Education",
"educationTertiary Education" = "Tertiary Education",
"geo_contextUrban" = "Urban",
"educationSecondary Education:geo_contextUrban" = "Secondary Education × Urban",
"educationTertiary Education:geo_contextUrban" = "Tertiary Education × Urban"
),
string.est = "Estimate",
string.ci = "95% CI",
string.p = "p-value",
p.style = "numeric",
collapse.ci = TRUE,
show.aic = TRUE,
show.r2 = TRUE,
show.obs = TRUE,
digits = 2,
digits.p = 3,
file = "regression_models.html"
)
regression_table
Model 1: Generations | Model 2: + Education | Model 3: + Geography | Model 4: With Interaction | |||||
---|---|---|---|---|---|---|---|---|
Predictors | Estimate | p-value | Estimate | p-value | Estimate | p-value | Estimate | p-value |
Intercept |
36.87 (36.17 – 37.57) |
<0.001 |
36.42 (35.68 – 37.16) |
<0.001 |
34.97 (34.14 – 35.81) |
<0.001 |
34.90 (33.91 – 35.88) |
<0.001 |
Baby Boomers |
-2.19 (-3.05 – -1.34) |
<0.001 |
-2.57 (-3.43 – -1.70) |
<0.001 |
-2.48 (-3.34 – -1.61) |
<0.001 |
-2.48 (-3.35 – -1.62) |
<0.001 |
Generation X |
-2.46 (-3.35 – -1.57) |
<0.001 |
-3.64 (-4.56 – -2.71) |
<0.001 |
-3.59 (-4.51 – -2.66) |
<0.001 |
-3.59 (-4.52 – -2.67) |
<0.001 |
Millennials |
-1.38 (-2.38 – -0.38) |
0.007 |
-2.49 (-3.51 – -1.46) |
<0.001 |
-2.65 (-3.67 – -1.62) |
<0.001 |
-2.65 (-3.68 – -1.63) |
<0.001 |
Generation Z |
6.91 (5.08 – 8.75) |
<0.001 |
7.02 (5.20 – 8.84) |
<0.001 |
6.89 (5.07 – 8.71) |
<0.001 |
6.90 (5.09 – 8.72) |
<0.001 |
Secondary Education |
-1.04 (-1.75 – -0.32) |
0.005 |
-0.97 (-1.68 – -0.25) |
0.008 |
-0.67 (-1.81 – 0.47) |
0.248 | ||
Tertiary Education |
5.10 (4.32 – 5.88) |
<0.001 |
4.95 (4.17 – 5.73) |
<0.001 |
4.77 (3.45 – 6.09) |
<0.001 | ||
Urban |
2.26 (1.65 – 2.87) |
<0.001 |
2.39 (1.34 – 3.45) |
<0.001 | ||||
Secondary Education × Urban |
-0.49 (-1.91 – 0.94) |
0.504 | ||||||
Tertiary Education × Urban |
0.24 (-1.34 – 1.82) |
0.765 | ||||||
Observations | 16583 | 16583 | 16583 | 16583 | ||||
R2 / R2 adjusted | 0.008 / 0.008 | 0.025 / 0.025 | 0.028 / 0.028 | 0.029 / 0.028 | ||||
AIC | 145237.144 | 144945.513 | 144894.579 | 144897.611 |
Table 2 presents a systematic analysis of factors associated with institutional trust in France. The models progressively incorporate generational cohorts, educational attainment, and geographic context to explore how these key social dimensions relate to trust in political institutions.
Model 1 examines generational differences in institutional trust, with the Interwar Generation as the reference category. The results reveal a distinctive pattern: the intercept (36.87) indicates that the Interwar Generation maintains moderate trust levels, while subsequent generations—Baby Boomers (-2.19), Generation X (-2.46), and Millennials (-1.38)—all report significantly lower trust. Most striking is Generation Z’s substantially higher trust (6.91 points above the reference group). This suggests instead a non-monotonic relationship where both the oldest and youngest citizens express greater institutional confidence than middle-aged cohorts.
When educational attainment is incorporated in Model 2, we observe a pronounced educational gradient in institutional trust. Compared to those with lower education, respondents with secondary education report slightly reduced trust (-1.04), while those with tertiary education demonstrate markedly higher trust levels (5.10). The introduction of education strengthens the negative coefficient for Generation X (-3.64 compared to -2.46 previously), suggesting that adjusting for educational differences unmasks an even stronger generational effect. The persistence of generational effects after adjusting for education indicates these cohort differences reflect more than compositional educational disparities across generations.
Model 3 introduces geographic context, revealing that urban residents report significantly higher institutional trust than their rural counterparts (2.26 points). The inclusion of geographic context slightly attenuates the educational coefficients, with tertiary education shifting from 5.10 to 4.95, suggesting some relationship between urbanization and educational distribution. The geographic effect itself is substantial, comparable in magnitude to some generational effects, highlighting the importance of territorial disparities in institutional attitudes. After adjusting for urban-rural differences, generational patterns remain remarkably stable, indicating these cohort effects operate consistently across geographic contexts.
In Model 4, we test whether education’s relationship with institutional trust differs between urban and rural contexts. Neither interaction term reaches statistical significance, with estimates of -0.49 for secondary education and 0.24 for tertiary education. These results suggest the association between education and institutional trust operates similarly across geographic settings, despite potentially different institutional experiences in urban versus rural environments. The main effects remain stable in this interaction model, reinforcing the robustness of the identified patterns.
Comparing model fit statistics reveals that each additional predictor improves explanatory power, with R² increasing from 0.008 in Model 1 to 0.029 in Model 4. The AIC values indicate that Model 3 provides the optimal balance between parsimony and explanatory power (AIC = 144894.579), outperforming the interaction model (which does not add any significant predictors). Further, the addition of education adds the most explanatory power (from 0.008 to 0.025 between Model 1 and Model 2), suggesting that it is an important predictor of institutional trust. However, despite identifying these significant patterns, the modest R² values indicate substantial unexplained variation in institutional trust.
Let’s create visualizations to better understand the regression results.
plot_coef_model4 <- plot_model(model3,
show.values = TRUE,
value.offset = 0.3,
colors = c(okabe_ito[2], okabe_ito[3], okabe_ito[6])) +
labs(
title = "Figure 10. Predictors of Institutional Trust in France",
subtitle = "Coefficient estimates with 95% confidence intervals",
x = "Estimate",
y = NULL
) +
theme_minimal() +
theme(
plot.title = element_text(face = "bold", size = 14),
plot.subtitle = element_text(color = "grey50", size = 12),
panel.grid.minor = element_blank(),
plot.margin = margin(20, 20, 20, 20)
)
plot_coef_model4
ggsave("coefficient_plot.pdf", plot_coef_model4, width = 10, height = 6)
Here is the standardized version:
plot_coef_model3 <- plot_model(model3,
show.values = TRUE,
value.offset = 0.3,
type = "std2",
colors = c(okabe_ito[2], okabe_ito[3], okabe_ito[6])) +
labs(
title = "Figure 11. Standardized Predictors of Institutional Trust in France",
subtitle = "Coefficient estimates with 95% confidence intervals",
x = "Estimate",
y = NULL
) +
theme_minimal() +
theme(
plot.title = element_text(face = "bold", size = 14),
plot.subtitle = element_text(color = "grey50", size = 12),
panel.grid.minor = element_blank(),
plot.margin = margin(20, 20, 20, 20)
)
plot_coef_model3
ggsave("coefficient_plot_standardized.pdf", plot_coef_model3, width = 10, height = 6)
Now, let’s visualize one of our key results (i.e., education) through marginal effects:
education_effect_plot <- plot_model(model3, type = "pred", terms = "education") +
labs(
title = "Figure 12. Effect of Education on Institutional Trust",
subtitle = "Adjusted for generation and geographic context",
x = NULL,
y = "Predicted Trust Score (0-100)"
) +
scale_y_continuous(limits = c(30, 45), breaks = seq(30, 45, 3)) +
theme_classic() +
theme(
plot.title = element_text(face = "bold", size = 14),
plot.subtitle = element_text(color = "grey50", size = 12),
axis.title.y = element_text(face = "bold"),
axis.text = element_text(color = "black", size = 10),
axis.text.x = element_text(face = "bold", vjust = 0.5),
axis.line = element_line(color = "black", linewidth = 0.7),
panel.grid.major.y = element_line(color = "grey90", linewidth = 0.3),
panel.grid.minor = element_blank(),
plot.margin = margin(20, 20, 20, 20)
)
education_effect_plot
ggsave("education_effect_plot.pdf", education_effect_plot, width = 10, height = 6)
As well as visually showcase the interaction effect:
interaction_plot <- interact_plot(model4,
pred = "education",
modx = "geo_context",
interval = TRUE,
int.width = 0.95,
colors = c("#003399", "#9D2235"),
legend.main = "Geographic Context") +
labs(
title = "Figure 13. Education Effects on Institutional Trust by Geographic Context",
subtitle = "Interaction effects with 95% confidence intervals",
y = "Predicted Trust Score (0-100)",
x = NULL
) +
scale_y_continuous(limits = c(30, 45)) +
theme_classic() +
theme(
plot.title = element_text(face = "bold", size = 14),
plot.subtitle = element_text(color = "grey50", size = 12),
axis.title.y = element_text(face = "bold"),
axis.text = element_text(color = "black"),
axis.text.x = element_text(face = "bold"),
axis.line = element_line(color = "black", linewidth = 0.7),
legend.position = "bottom",
legend.title = element_text(face = "bold"),
legend.background = element_rect(fill = "white", color = "grey90"),
panel.grid.major.y = element_line(color = "grey90", linewidth = 0.3),
panel.grid.minor = element_blank(),
plot.margin = margin(20, 20, 20, 20)
)
interaction_plot
ggsave("education_geo_interaction.pdf", interaction_plot, width = 10, height = 6)
model_gen_edu <- lm(trust ~ generation * education + geo_context, data = france_clean)
summary(model_gen_edu)
##
## Call:
## lm(formula = trust ~ generation * education + geo_context, data = france_clean)
##
## Residuals:
## Min 1Q Median 3Q Max
## -45.46 -13.83 1.22 13.76 67.90
##
## Coefficients:
## Estimate Std. Error t value
## (Intercept) 34.2382 0.4909 69.753
## generationBaby Boomers -2.6076 0.6235 -4.182
## generationGeneration X -1.9324 0.7818 -2.472
## generationMillennials 1.9764 0.9355 2.113
## generationGeneration Z 9.0213 1.3715 6.578
## educationSecondary Education 1.2085 0.8261 1.463
## educationTertiary Education 6.4703 1.0387 6.229
## geo_contextUrban 2.2012 0.3111 7.076
## generationBaby Boomers:educationSecondary Education -1.1011 1.0101 -1.090
## generationGeneration X:educationSecondary Education -3.6136 1.1339 -3.187
## generationMillennials:educationSecondary Education -6.3589 1.2919 -4.922
## generationGeneration Z:educationSecondary Education -3.7531 2.0330 -1.846
## generationBaby Boomers:educationTertiary Education 0.3667 1.2321 0.298
## generationGeneration X:educationTertiary Education -2.2610 1.2969 -1.743
## generationMillennials:educationTertiary Education -6.9315 1.4431 -4.803
## generationGeneration Z:educationTertiary Education -6.1733 2.7573 -2.239
## Pr(>|t|)
## (Intercept) < 0.0000000000000002 ***
## generationBaby Boomers 0.00002899699310 ***
## generationGeneration X 0.01346 *
## generationMillennials 0.03465 *
## generationGeneration Z 0.00000000004922 ***
## educationSecondary Education 0.14348
## educationTertiary Education 0.00000000048011 ***
## geo_contextUrban 0.00000000000155 ***
## generationBaby Boomers:educationSecondary Education 0.27569
## generationGeneration X:educationSecondary Education 0.00144 **
## generationMillennials:educationSecondary Education 0.00000086345201 ***
## generationGeneration Z:educationSecondary Education 0.06490 .
## generationBaby Boomers:educationTertiary Education 0.76600
## generationGeneration X:educationTertiary Education 0.08130 .
## generationMillennials:educationTertiary Education 0.00000157642316 ***
## generationGeneration Z:educationTertiary Education 0.02518 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 19.07 on 16567 degrees of freedom
## Multiple R-squared: 0.03157, Adjusted R-squared: 0.0307
## F-statistic: 36.01 on 15 and 16567 DF, p-value: < 0.00000000000000022
gen_edu_interaction <- plot_model(
model_gen_edu,
type = "pred",
terms = c("generation", "education"),
colors = c(okabe_ito[1], okabe_ito[2], okabe_ito[3])
) +
labs(
title = "Figure 14. Educational Differences in Institutional Trust Across Generations",
subtitle = "Predicted values with 95% confidence intervals",
x = NULL,
y = "Predicted Trust Score (0-100)",
color = "Education Level"
) +
scale_y_continuous(limits = c(20, 55), breaks = seq(20, 55, 5)) +
theme_classic() +
theme(
plot.title = element_text(face = "bold", size = 14),
plot.subtitle = element_text(color = "grey50", size = 12),
axis.title.y = element_text(face = "bold"),
axis.text = element_text(color = "black"),
axis.text.x = element_text(face = "bold"),
axis.line = element_line(color = "black", linewidth = 0.7),
legend.position = "bottom",
legend.title = element_text(face = "bold"),
legend.background = element_rect(fill = "white", color = "grey90"),
panel.grid.major.y = element_line(color = "grey90", linewidth = 0.3),
panel.grid.minor = element_blank(),
plot.margin = margin(20, 20, 20, 20)
)
gen_edu_interaction
ggsave("generation_education_interaction.pdf", gen_edu_interaction, width = 10, height = 6)
gen_edu_interaction_horizontal <- plot_model(
model_gen_edu,
type = "pred",
terms = c("generation", "education"),
colors = c(okabe_ito[1], okabe_ito[2], okabe_ito[3]), # Okabe-Ito palette
axis.labels = c("Generation Z", "Millennials", "Generation X", "Baby Boomers", "Interwar Generation")
) +
coord_flip() + # Flip the coordinates to make horizontal
labs(
title = "Figure 15. Educational Differences in Institutional Trust Across Generations",
subtitle = "Predicted values with 95% confidence intervals",
y = "Predicted Trust Score (0-100)",
x = NULL,
color = "Education Level"
) +
scale_y_continuous(limits = c(20, 55), breaks = seq(20, 55, 5)) +
theme_classic() +
theme(
plot.title = element_text(face = "bold", size = 14),
plot.subtitle = element_text(color = "grey50", size = 12),
axis.title.y = element_text(face = "bold"),
axis.text = element_text(color = "black"),
axis.text.y = element_text(face = "bold"),
axis.line = element_line(color = "black", linewidth = 0.7),
legend.position = "bottom",
legend.title = element_text(face = "bold"),
legend.background = element_rect(fill = "white", color = "grey90"),
panel.grid.major.x = element_line(color = "grey90", linewidth = 0.3),
panel.grid.minor = element_blank(),
plot.margin = margin(20, 20, 20, 20)
)
gen_edu_interaction_horizontal
ggsave("generation_education_interaction_horizontal.pdf", gen_edu_interaction_horizontal, width = 10, height = 7)
The last two graphs show an interesting pattern for education and Gen Z/Millennials specifically. In your report, you would want to model first. But here I am leveraging the visual relative to Edu x Geo to show the analytical visual differences – and how you can note the pattern even without modeling. But to confirm, here is the modeling output:
model5 <- lm(trust ~ generation + education + geo_context + education * generation, data = france_clean)
tab_model(model5)
trust | |||
---|---|---|---|
Predictors | Estimates | CI | p |
(Intercept) | 34.24 | 33.28 – 35.20 | <0.001 |
generation [Baby Boomers] | -2.61 | -3.83 – -1.39 | <0.001 |
generation [Generation X] | -1.93 | -3.46 – -0.40 | 0.013 |
generation [Millennials] | 1.98 | 0.14 – 3.81 | 0.035 |
generation [Generation Z] | 9.02 | 6.33 – 11.71 | <0.001 |
education [Secondary Education] |
1.21 | -0.41 – 2.83 | 0.143 |
education [Tertiary Education] |
6.47 | 4.43 – 8.51 | <0.001 |
geo context [Urban] | 2.20 | 1.59 – 2.81 | <0.001 |
generation [Baby Boomers] × education [Secondary Education] |
-1.10 | -3.08 – 0.88 | 0.276 |
generation [Generation X] × education [Secondary Education] |
-3.61 | -5.84 – -1.39 | 0.001 |
generation [Millennials] × education [Secondary Education] |
-6.36 | -8.89 – -3.83 | <0.001 |
generation [Generation Z] × education [Secondary Education] |
-3.75 | -7.74 – 0.23 | 0.065 |
generation [Baby Boomers] × education [Tertiary Education] |
0.37 | -2.05 – 2.78 | 0.766 |
generation [Generation X] × education [Tertiary Education] |
-2.26 | -4.80 – 0.28 | 0.081 |
generation [Millennials] × education [Tertiary Education] |
-6.93 | -9.76 – -4.10 | <0.001 |
generation [Generation Z] × education [Tertiary Education] |
-6.17 | -11.58 – -0.77 | 0.025 |
Observations | 16583 | ||
R2 / R2 adjusted | 0.032 / 0.031 |
Although measured with a lot of uncertainty (note: this is due to the much lower sample size for Gen Z relative to all other generational categories), Gen Z x tertiary education is a key result. When modeled, Millennials also really stands out.
To enhance our understanding of the relationships identified in our regression models, we present several visualizations that illustrate the magnitude and direction of effects. Figure 10 (and Figure 11) displays the coefficient estimates from Model 3, revealing the relative importance of different predictors. Generation Z shows the strongest positive association with institutional trust (6.89 points higher than the Interwar Generation), followed by tertiary education (4.95 points higher than lower education). Urban residence is associated with a 2.26-point increase in trust compared to rural settings, highlighting the geographic dimension of institutional attitudes.
Figure 11 presents standardized coefficient estimates (using Gelman’s method of dividing by two standard deviations), allowing for more direct comparisons of effect sizes across variables measured on different scales. This standardization reveals that Generation Z demonstrates the strongest positive association with institutional trust (0.36 standard deviations), followed by tertiary education (0.26 standard deviations). Urban residence shows a modest but significant positive relationship (0.12 standard deviations). Conversely, Generation X exhibits the strongest negative association (-0.19 standard deviations), followed by Millennials (-0.14) and Baby Boomers (-0.13). Secondary education shows a weak negative relationship (-0.05) compared to lower education. The asterisks indicate high statistical significance (*** p<0.001, ** p<0.01) for all these relationships, strengthening confidence in our findings about the generational and educational dimensions of institutional trust in France.
Figure 12 illustrates the education gradient in institutional trust after adjusting for generational differences and geographic context. The predicted trust values range from 34.0 for those with secondary education to 39.9 for those with tertiary education, representing a substantial 5.9-point difference. This visualization demonstrates the non-linear nature of the education effect—tertiary education corresponds with markedly higher trust, while secondary education shows slightly lower trust than the reference category.
Figure 13 examines the relationship between education and geographic context, revealing that the urban-rural divide persists across all education levels with minimal variation in magnitude. The urban advantage is consistent: 2.39 points for lower education, 1.91 points for secondary education, and 2.64 points for tertiary education. These similar values across education levels align with the non-significant interaction terms in Model 4, confirming that the urban effect operates uniformly regardless of educational attainment.
Figure 14/15 presents the relationship between generation and education, revealing an important pattern: education stratifies institutional trust within almost all generations, but this stratification is notably reduced in Generation Z. The educational gradient spans 11.63 points in the Interwar Generation and 10.81 points among Baby Boomers, but only 7.80 points in Generation Z. This 3.8-point difference in the education effect between older and younger generations suggests that while education remains associated with higher trust across all cohorts, its stratifying power appears diminished in the younger generations.
This weakened educational stratification among Generation Z merits particular attention. While tertiary education corresponds with substantially higher trust in all generations, the gap between education levels is less pronounced in Generation Z, potentially indicating changing patterns in how education shapes institutional attitudes among younger French citizens.
These visualizations provide critical insights beyond the regression coefficients alone. They demonstrate the substantial magnitude of educational differences, the consistent urban-rural divide, and the intergenerational variation in how education relates to institutional trust in France—with particular attention to the potentially changing role of education in the youngest generation.