In political science, various dimensionality reduction methods are used to derive polarization patterns from voting data. The best known scaling method is probably (D)W-NOMINATE, whose predecessor NOMINATE (Nominal Three-Step Estimation) was developed in the 1980s. We want to use this procedure to examine how polarised the National Council was in the 50th legislative period.
Since large queries can lead to server-side errors that result in the loss of all data already downloaded, we fetch the voting data step stepwise and combine the locally stored files after the download.
For this purpose we use the following function:
get_voting_buffered <- function(id) {
# Create folder
folder <- "voting50"
if(!dir.exists(folder)) dir.create(folder)
# Download
dt <- swissparl::get_data("Voting", Language = "DE", IdSession = id)
# Save
saveRDS(dt, paste0(folder, "/", id, ".rds"))
}
It is applied using purrr
’s walk
function to all sessions of the
50th term.
# Get Session IDs
sessions50 <- swissparl::get_data("Session", Language = "DE", LegislativePeriodNumber = 50)
# Apply Function to Session IDs
purrr::walk(sessions50$ID, get_voting_buffered)
# Combine to One Dataset
v50 <- purrr::map_dfr(list.files("voting50", full.names = T), readRDS)
We find ~893,000 entries. These are now extended to include further information on the members of the Council.
# Get Councillor Data
council_members <- swissparl::get_data(
table = "MemberCouncil",
Language = "DE",
PersonNumber = unique(v50$PersonNumber)
)
# Join with Voting Data
v50 <- left_join(
v50,
council_members %>%
select(PersonNumber, GenderAsString, CantonAbbreviation, PartyAbbreviation),
by = "PersonNumber"
)
Before we can analyze our data with the NOMINATE scaling method, we have
to convert it into an object of the class rollcall
. For this we use
the package pscl
…this may take a little getting used to.
# Load Packages
library(pscl)
library(dplyr)
library(tidyr)
# Wide Form
v50w <- v50 %>%
mutate(ID = paste0(LastName, " (", ParlGroupNameAbbreviation, "/", CantonAbbreviation, ")" )) %>%
select(ID, GenderAsString, ParlGroupNameAbbreviation, PartyAbbreviation, IdVote, Decision) %>%
mutate(Decision_new = case_when(
Decision == 1 ~ 1,
Decision == 2 ~ 6,
!Decision %in% c(1, 2) ~ 9
)
) %>%
select(-Decision) %>%
pivot_wider(names_from = IdVote, values_from = Decision_new)
# Generate rcObject
rc50 <- rollcall(
data = v50w %>% select(-(1:4)),
yea = 1,
nay = 6,
missing = 9,
notInLegis = NA,
legis.names = v50w$ID,
vote.names = names(v50w %>% select(-(1:4))),
legis.data = v50w %>% select(2:4)
)
To calculate the relative positions of the politicians on 2 dimensions
we use W-NOMINATE from Poole and Rosenthal. The algorithm is implented
in the R-Package wnominate
. The calculation takes about 10-15 minutes.
library(wnominate)
res <- wnominate::wnominate(
rcObject = rc50,
minvotes = ceiling(0.25 * length(unique(v50$IdVote))),
polarity = c("Wobmann (Fraktion V/SO)", "Wobmann (Fraktion V/SO)")
)
We now use the calculated coordinates to visualize the political polarization in the National Council in the 50th legislative period.
# Packages
library(ggplot2)
library(hrbrthemes)
# 2D-Plot
res$legislators %>%
mutate(ParlGroupNameAbbreviation = factor(
ParlGroupNameAbbreviation,
levels = c("Fraktion S", "Fraktion V", "Fraktion RL", "Fraktion C", "Fraktion G", "Fraktion GL", "Fraktion BD")
)
) %>%
ggplot(aes(x = coord1D, y = -coord2D)) +
stat_density_2d(
aes(alpha = stat(level), fill = ParlGroupNameAbbreviation),
geom = "polygon",
show.legend = FALSE
) +
geom_point(
aes(color = ParlGroupNameAbbreviation),
alpha = 0.6,
size = 1
) +
expand_limits(x = c(-1, 1), y = c(-1, 1)) +
coord_equal() +
scale_fill_manual(values = c("#EE2A3B", "#00A55A", "#104FA0", "#FF850C", "#85B229", "#C2D93F", "#FFDD00")) +
scale_color_manual(
values = c("#EE2A3B", "#00A55A", "#104FA0", "#FF850C", "#85B229", "#C2D93F", "#FFDD00"),
guide = guide_legend(override.aes = list(shape = 15, size = 4, alpha = 1))
) +
labs(
title = "Polarization in the National Council",
subtitle = "W-NOMINATE Scores of the 50th Parliamentary Term",
caption = "Data: Parliamentary Services of the Federal Assembly, Bern",
color = ""
) +
theme_ipsum_rc() +
theme(
legend.justification = "bottom",
axis.title = element_blank(),
panel.grid.minor = element_blank()
)
Quite homogeneous parliamentary groups. Let’s now foucs on the first W-NOMINATE dimension (left-right spectrum).
# Package
library(ggridges)
# 1D-Plot
res$legislators %>%
mutate(ParlGroupNameAbbreviation = factor(
ParlGroupNameAbbreviation,
levels = rev(c("Fraktion S", "Fraktion G", "Fraktion GL", "Fraktion BD", "Fraktion C", "Fraktion RL", "Fraktion V"))
)
) %>%
ggplot(aes(x = coord1D, y = ParlGroupNameAbbreviation, fill = ParlGroupNameAbbreviation)) +
stat_density_ridges(quantile_lines = TRUE, quantiles = 2, alpha = 0.7) +
scale_fill_manual(values = rev(c("#EE2A3B", "#85B229", "#C2D93F", "#FFDD00", "#FF850C", "#104FA0", "#00A55A"))) +
labs(
title = "Polarization in the National Council",
subtitle = "W-NOMINATE 1st Dimension Scores of the 50th Parliamentary Term",
caption = "Data: Parliamentary Services of the Federal Assembly, Bern"
) +
theme_ipsum_rc() +
theme(
legend.position = "none",
axis.title = element_blank(),
panel.grid.minor = element_blank()
)
For a more elaborate analysis check out Clau Dermont’s Aus bipolar wird tripolar: Polarisierung bei Parlamentsabstimmungen.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.