-
-
Save anonymous/1b6c312f28f5b370c14c37ec867d329c to your computer and use it in GitHub Desktop.
R-Code zum Blogeintrag "Wer hat Angst vorm Pukelsheim?" im UZH-Seminar "Politischer Datenjournalismus" 2016: http://pwipdm.uzh.ch/wordpress/?p=7201
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Copyright (C) 2016 Salim Brüggemann | |
# This program is free software: you can redistribute it and/or modify | |
# it under the terms of the GNU General Public License as published by | |
# the Free Software Foundation, either version 3 of the License, or | |
# (at your option) any later version. | |
# This program is distributed in the hope that it will be useful, | |
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
# GNU General Public License for more details. | |
# To get a copy of the GNU General Public License see <http://www.gnu.org/licenses/>. | |
############################################################################### | |
# Blogeintrag im UZH-Seminar "Politischer Datenjournalismus" 2016: http://pwipdm.uzh.ch/wordpress/?p=7201 | |
# Autor: Salim Brüggemann, 08-915-126 | |
############################################################################### | |
remove(list = ls(all = TRUE)) | |
setwd("~/Dokumente/Ausbildung/Studium UZH/Master/2. Semester/Politischer Datenjournalismus/Blogeinträge/3. Blogeintrag") | |
library(dplyr) | |
library(DT) | |
library(ggiraph) | |
library(grid) | |
library(plotly) | |
library(RBazi) | |
library(reshape2) | |
library(rgdal) | |
library(rgeos) | |
library(viridis) | |
# Funktion für Parteifarben definieren | |
get_party_color <- function(party, alpha = 180) { | |
switch(party, | |
"AL" = rgb(238, 29, 35, alpha, maxColorValue = 255), | |
"AL | PdA | solidaritéS" = rgb(238, 29, 35, alpha, maxColorValue = 255), | |
"BDP" = rgb(255, 221, 0, alpha, maxColorValue = 255), | |
"CSP" = rgb(0, 150, 167, alpha, maxColorValue = 255), | |
"CSP Obwalden" = rgb(215, 45, 12, alpha, maxColorValue = 255), | |
"CVP" = rgb(255, 133, 12, alpha, maxColorValue = 255), | |
"EDU" = rgb(102, 51, 0, alpha, maxColorValue = 255), | |
#"EDU" = rgb(0, 0, 0, alpha, maxColorValue = 255), | |
"EVP" = rgb(204, 153, 255, alpha,maxColorValue = 255), | |
#"EVP" = rgb(0, 97, 172, alpha,maxColorValue = 255), | |
"FDP" = rgb(16, 79, 160, alpha, maxColorValue = 255), | |
"glp" = rgb(195, 217, 63, alpha, maxColorValue = 255), | |
"Grüne" = rgb(132, 180, 20, alpha, maxColorValue = 255), | |
"Junge CVP" = rgb(255, 133, 12, alpha, maxColorValue = 255), | |
"Junge glp" = rgb(195, 217, 63, alpha, maxColorValue = 255), | |
"Junge Grüne" = rgb(132, 180, 20, alpha, maxColorValue = 255), | |
#"Junge Grüne" = rgb(44, 79, 57, alpha, maxColorValue = 255), | |
"Junge SVP" = rgb(63, 123, 23, alpha, maxColorValue = 255), | |
#"Junge SVP" = rgb(0, 145, 61, alpha, maxColorValue = 255), | |
"Jungfreisinnige" = rgb(16, 79, 160, alpha, maxColorValue = 255), | |
#"Jungfreisinnige" = rgb(0, 76, 147, alpha, maxColorValue = 255), | |
"JUSO" = rgb(238, 42, 59, alpha, maxColorValue = 255), | |
#"JUSO" = rgb(238, 42, 46, alpha, maxColorValue = 255), | |
"LDP" = rgb(0, 73, 144, alpha, maxColorValue = 255), | |
"Lega" = rgb(102, 51, 0, alpha, maxColorValue = 255), | |
"MCG" = rgb(249, 250, 0, alpha, maxColorValue = 255), | |
"PdA" = rgb(255, 0, 0, alpha, maxColorValue = 255), | |
"Piraten" = rgb(0, 0, 0, alpha, maxColorValue = 255), | |
#"Piraten" = rgb(249, 178, 0, alpha, maxColorValue = 255), | |
"SP" = rgb(238, 42, 59, alpha, maxColorValue = 255), | |
"SVP" = rgb(63, 123, 23, alpha, maxColorValue = 255)) | |
} | |
# Angaben zur Anzahl (un)gültiger Wahlzettel/Stimmender gemäss Bundeskanzlei definieren (S. 3 von https://www.admin.ch/opc/de/federal-gazette/2015/7927.pdf) | |
ballot_papers_2015 <- list(voters_eligible = 5283556, | |
voters_participating = 2563052, | |
spoiled_ballot_papers = 30665, | |
empty_ballot_papers = 10885) | |
# Wahlergebnisse der Nationalratswahlen 2015 einlesen | |
data_tickets_2015 <- read.csv("Nationalratswahlen 2015 – Erhaltene Stimmen und Stärke der Wahllisten nach Kantonen.csv", stringsAsFactors = FALSE) | |
data_candidates_2015 <- read.csv("Nationalratswahlen 2015 – Erhaltene Stimmen der Kandidierenden nach Kantonen.csv", stringsAsFactors = FALSE) | |
data_mandates_2015 <- read.csv("Nationalratswahlen 2015 – Mandatsverteilung nach Parteien und Kanton.csv", stringsAsFactors = FALSE) | |
data_voters_share_2015 <- read.csv("Nationalratswahlen 2015 – Parteistärken.csv", stringsAsFactors = FALSE) | |
# Unterschiedliche Namen vergeben an Kleinstparteien, die vom BFS nur pauschal als "Übrige" aufgeführt sind (damit deren Stimmen später nicht fälschlich einer einzigen fiktiven Partei "Übrige" zugerechnet werden) | |
data_tickets_2015$Unterpartei[data_tickets_2015$Unterpartei == ""] <- paste0("_sonstige_", 1:length(data_tickets_2015$Unterpartei[data_tickets_2015$Unterpartei == ""])) | |
# Variable für national ausgeglichen gewichtete Stimmenzahl in Wahllistendatensatz einfügen | |
data_tickets_2015$Erhaltene_Stimmen_NZZ <- data_tickets_2015$Erhaltene_Stimmen / data_tickets_2015$Kantonaler_Sitzanspruch | |
# Art der Listenberücksichtigung im Wahlergebnis (real) bestimmen | |
data_tickets_2015$Listenberücksichtigung <- "" | |
for ( i in unique(data_tickets_2015$Kanton) ) { | |
parties_directly_considered <- data_mandates_2015$Partei[data_mandates_2015[, i] != 0] | |
lists_considered <- unique(data_tickets_2015$Listenverbindung[data_tickets_2015$Kanton == i & data_tickets_2015$Unterpartei %in% parties_directly_considered]) | |
lists_considered <- lists_considered[lists_considered != ""] | |
data_tickets_2015$Listenberücksichtigung[data_tickets_2015$Kanton == i & data_tickets_2015$Unterpartei %in% parties_directly_considered] <- "direkt" | |
data_tickets_2015$Listenberücksichtigung[data_tickets_2015$Kanton == i & !(data_tickets_2015$Unterpartei %in% parties_directly_considered) & data_tickets_2015$Listenverbindung %in% lists_considered] <-"indirekt" | |
data_tickets_2015$Listenberücksichtigung[data_tickets_2015$Kanton == i & !(data_tickets_2015$Unterpartei %in% parties_directly_considered) & !(data_tickets_2015$Listenverbindung %in% lists_considered)] <- "nicht" | |
} | |
# Doppeltproportionale Sitzzuteilung mittels RBazi berechnen | |
## Stimmdaten-Matrix generieren | |
subparty_votes_v1 <- | |
data_tickets_2015 %>% | |
select(Kanton, Unterpartei, Erhaltene_Stimmen) %>% | |
group_by(Kanton, Unterpartei) %>% | |
summarise(sum(Erhaltene_Stimmen)) %>% | |
rename(Erhaltene_Stimmen = `sum(Erhaltene_Stimmen)`) %>% | |
ungroup() %>% | |
acast(Kanton ~ Unterpartei, value.var = "Erhaltene_Stimmen") | |
### NAs durch 0 ersetzen | |
subparty_votes_v1[is.na(subparty_votes_v1)] <- 0 | |
## Sitzzahlen-Vektor generieren | |
seat_numbers <- | |
data_tickets_2015 %>% | |
select(Kanton, Kantonaler_Sitzanspruch) %>% | |
distinct(Kanton) %>% | |
arrange(Kanton) %>% | |
select(Kantonaler_Sitzanspruch) %>% | |
unlist() | |
## Zuteilung berechnen | |
apportionment_biprob_2015_v1 <- bipropApp(districtMagnitudes = seat_numbers, | |
votes = subparty_votes_v1, | |
appMethod = "DivStd", | |
nzz = TRUE) | |
## Oberzuteilung extrahieren | |
superapportionment_v1 <- apportionment_biprob_2015_v1$Superapportionment$DivStdh200$apportionment | |
superapportionment_v1 <- superapportionment_v1[grep(x = names(superapportionment_v1), pattern = "^(?!_sonstige.*)", perl = TRUE)] | |
## Parteien ermitteln, welche keinen eigenen Sitz erreichten | |
names(superapportionment_v1[superapportionment_v1 == 0]) | |
## Stimmen für diese Parteien den jeweiligen Mutter- bzw. listenverbundenen Parteien zurechnen, sofern eindeutig möglich | |
data_tickets_2015$Unterpartei_v2 <- data_tickets_2015$Unterpartei | |
data_tickets_2015$Unterpartei_v2[data_tickets_2015$Unterpartei_v2 == "Junge EVP"] <- "EVP" | |
data_tickets_2015$Unterpartei_v2[data_tickets_2015$Unterpartei_v2 == "Junge EDU"] <- "EDU" | |
data_tickets_2015$Unterpartei_v2[data_tickets_2015$Unterpartei_v2 == "SD" & data_tickets_2015$Kanton == "BE"] <- "EDU" | |
## Doppeltproportionale Sitzzuteilung nochmals anhand der aktualisierten geänderten Daten berechnen | |
### Stimmdaten-Matrix generieren | |
subparty_votes_v2 <- | |
data_tickets_2015 %>% | |
select(Kanton, Unterpartei_v2, Erhaltene_Stimmen) %>% | |
group_by(Kanton, Unterpartei_v2) %>% | |
summarise(sum(Erhaltene_Stimmen)) %>% | |
rename(Erhaltene_Stimmen = `sum(Erhaltene_Stimmen)`) %>% | |
ungroup() %>% | |
acast(Kanton ~ Unterpartei_v2, value.var = "Erhaltene_Stimmen") | |
#### NAs durch 0 ersetzen | |
subparty_votes_v2[is.na(subparty_votes_v2)] <- 0 | |
### Zuteilung berechnen | |
apportionment_biprob_2015_v2 <- bipropApp(districtMagnitudes = seat_numbers, | |
votes = subparty_votes_v2, | |
appMethod = "DivStd", | |
nzz = TRUE) | |
##3 Oberzuteilung extrahieren | |
superapportionment_v2 <- apportionment_biprob_2015_v2$Superapportionment$DivStdh200$apportionment | |
superapportionment_v2 <- superapportionment_v2[grep(x = names(superapportionment_v2), pattern = "^(?!_sonstige.*)", perl = TRUE)] | |
### Wiederum Parteien ermitteln, welche keinen eigenen Sitz erreichten | |
names(superapportionment_v2[superapportionment_v2 == 0]) # neu erreicht die "Junge BDP" keinen eigenen Sitz mehr | |
## Stimmen für die "Junge BDP" der Mutterpartei zurechnen | |
data_tickets_2015$Unterpartei_v3 <- data_tickets_2015$Unterpartei_v2 | |
data_tickets_2015$Unterpartei_v3[data_tickets_2015$Unterpartei_v3 == "Junge BDP"] <- "BDP" | |
## Doppeltproportionale Sitzzuteilung ein drittes Mal anhand der aktualisierten geänderten Daten berechnen | |
### Stimmdaten-Matrix generieren | |
subparty_votes_v3 <- | |
data_tickets_2015 %>% | |
select(Kanton, Unterpartei_v3, Erhaltene_Stimmen) %>% | |
group_by(Kanton, Unterpartei_v3) %>% | |
summarise(sum(Erhaltene_Stimmen)) %>% | |
rename(Erhaltene_Stimmen = `sum(Erhaltene_Stimmen)`) %>% | |
ungroup() %>% | |
acast(Kanton ~ Unterpartei_v3, value.var = "Erhaltene_Stimmen") | |
#### NAs durch 0 ersetzen | |
subparty_votes_v3[is.na(subparty_votes_v3)] <- 0 | |
### Zuteilung berechnen | |
apportionment_biprob_2015_v3 <- bipropApp(districtMagnitudes = seat_numbers, | |
votes = subparty_votes_v3, | |
appMethod = "DivStd", | |
nzz = TRUE) | |
### Oberzuteilung extrahieren | |
superapportionment_v3 <- apportionment_biprob_2015_v3$Superapportionment$DivStdh200$apportionment | |
superapportionment_v3 <- superapportionment_v3[grep(x = names(superapportionment_v3), pattern = "^(?!_sonstige.*)", perl = TRUE)] | |
### Wiederum Parteien ermitteln, welche keinen eigenen Sitz erreichten | |
names(superapportionment_v3[superapportionment_v3 == 0]) # keine Veränderung mehr, passt! | |
# Dataframe mit Sitzverteilungen und Co. generieren | |
data_mandates_plus_2015 <- data.frame(Partei = sort(names(superapportionment_v3[superapportionment_v3 != 0])), | |
Jungpartei = FALSE, | |
Parteiblock = "", | |
Mandate_real = rowSums(data_mandates_2015[2:length(data_mandates_2015)]), | |
Mandate_biproportional = superapportionment_v3[superapportionment_v3 != 0][sort(names(superapportionment_v3[superapportionment_v3 != 0]))], | |
stringsAsFactors = FALSE) | |
data_mandates_plus_2015$Differenz <- data_mandates_plus_2015$Mandate_biproportional - data_mandates_plus_2015$Mandate_real | |
data_mandates_plus_2015$Jungpartei[grep(x = data_mandates_plus_2015$Partei, pattern = "(JUSO|Jung.*)")] <- TRUE | |
data_voters_share_biprop_2015 <- | |
data_tickets_2015 %>% | |
group_by(Unterpartei_v3) %>% | |
summarise(sum(Erhaltene_Stimmen_NZZ) / sum(data_tickets_2015$Erhaltene_Stimmen_NZZ) * 100) %>% | |
rename(Parteistärke_biproportional = `sum(Erhaltene_Stimmen_NZZ)/sum(data_t...`) %>% | |
rename(Partei = Unterpartei_v3) %>% | |
ungroup() | |
data_mandates_plus_2015 <- merge(data_mandates_plus_2015, data_voters_share_biprop_2015, by = ("Partei"), all.x = TRUE) | |
data_mandates_plus_2015 <- merge(data_mandates_plus_2015, data_voters_share_2015, by = ("Partei"), all.x = TRUE) | |
# Parteien nach Links-Rechts-Schema anordnen | |
order_political_orientation_2015 <- c("AL | PdA | solidaritéS", | |
"JUSO", | |
"SP", | |
"Junge Grüne", | |
"Grüne", | |
"Piraten", | |
"Junge glp", | |
"glp", | |
"CSP Obwalden", | |
"EVP", | |
"Junge CVP", | |
"CVP", | |
"BDP", | |
"LDP", | |
"Jungfreisinnige", | |
"FDP", | |
"MCG", | |
"Lega", | |
"SVP", | |
"Junge SVP", | |
"EDU") | |
data_mandates_plus_2015 <- slice(data_mandates_plus_2015, match(order_political_orientation_2015, Partei)) | |
data_mandates_plus_2015$Parteiblock[1:5] <- "Linke" | |
data_mandates_plus_2015$Parteiblock[6:16] <- "Mitte" | |
data_mandates_plus_2015$Parteiblock[17:21] <- "Rechte" | |
data_mandates_plus_2015$Parteifarbe <- "" | |
for ( i in data_mandates_plus_2015$Partei ) { | |
data_mandates_plus_2015$Parteifarbe[data_mandates_plus_2015$Partei == i] <- get_party_color(i) | |
} | |
# Bar Plots zur Sitzverteilung real vs. biproportional mittels Plotly generieren | |
## Parteifarbpaletten definieren | |
young_party_colors <- colorRampPalette(subset(data_mandates_plus_2015, Jungpartei == TRUE)$Parteifarbe) | |
old_party_colors <- colorRampPalette(subset(data_mandates_plus_2015, Jungpartei == FALSE)$Parteifarbe) | |
all_party_colors <- colorRampPalette(data_mandates_plus_2015$Parteifarbe) | |
## Bar Plot zu offiziellen gegenüber biproportionalen Sitzzahldifferenzen mittels Plotly erzeugen | |
bar_plot_2015 <- | |
plot_ly(data = subset(data_mandates_plus_2015, Jungpartei == TRUE), | |
x = Partei, | |
y = Differenz, | |
text = paste0(Partei, | |
"<br><br>Parteistärke", | |
"<br>biproportional: ", round(Parteistärke_biproportional, digits = 1), " %", | |
"<br><br>Anzahl Mandate", | |
"<br>offiziell: ", Mandate_real, | |
"<br>biproportional: ", Mandate_biproportional), | |
marker = list(line = list(color = "#00ffff", | |
width = 4), | |
color = young_party_colors(length(Partei))), | |
name = "Jungparteien", | |
legendgroup = "Jungparteien", | |
type = "bar", | |
hoverinfo = "text") %>% | |
add_trace(data = subset(data_mandates_plus_2015, Jungpartei == FALSE), | |
x = Partei, | |
y = Differenz, | |
text = paste0(Partei, | |
"<br><br>Parteistärke", | |
ifelse(is.na(Parteistärke_real), "", paste0("<br>offiziell: ", round(Parteistärke_real, digits = 1), " %")), | |
"<br>biproportional: ", round(Parteistärke_biproportional, digits = 1), " %", | |
"<br><br>Anzahl Mandate", | |
"<br>offiziell: ", Mandate_real, | |
"<br>biproportional: ", Mandate_biproportional), | |
marker = list(color = old_party_colors(length(Partei))), | |
name = "restliche Parteien", | |
legendgroup = "restliche Parteien", | |
type = "bar", | |
hoverinfo = "text") %>% | |
layout(title = "Differenz zwischen offizieller und biproportionaler Sitzverteilung im Nationalrat<br>「2015 - 2019」", | |
xaxis = list(title = ""), | |
yaxis = list(title = "Differenz an Nationalratsmandaten"), | |
font = list(family = "Liberation Serif"), | |
margin = list(l = 60, r = 0, t = 40, b = 140, pad = 0, autoexpand = TRUE), | |
width = 800, | |
height = 800) %>% | |
config(displaylogo = FALSE, | |
showLink = FALSE, | |
displayModeBar = FALSE) | |
bar_plot_2015 | |
plotly_POST(bar_plot_2015, filename = "DDJ-Blogeintrag 3/Barplot 2015", fileopt = "overwrite", sharing = "public") | |
## Bar Plot zu offizieller gegenüber biproportionaler Sitzzahlen mittels Plotly erzeugen | |
bar_plot_2_2015 <- | |
plot_ly(data = data_mandates_plus_2015, | |
x = Partei, | |
y = Mandate_real, | |
marker = list(color = all_party_colors(length(Partei))), | |
opacity = 0.5, | |
name = "offiziell", | |
legendgroup = "offiziell", | |
type = "bar", | |
hoverinfo = "none") %>% | |
add_trace(data = data_mandates_plus_2015, | |
x = Partei, | |
y = Mandate_biproportional, | |
text = paste0(Partei, | |
"<br><br>Parteistärke", | |
ifelse(is.na(Parteistärke_real), "", paste0("<br>offiziell: ", round(Parteistärke_real, digits = 1), " %")), | |
"<br>biproportional: ", round(Parteistärke_biproportional, digits = 1), " %", | |
"<br><br>Anzahl Mandate", | |
"<br>offiziell: ", Mandate_real, | |
"<br>biproportional: ", Mandate_biproportional), | |
marker = list(color = all_party_colors(length(Partei))), | |
name = "biproportional", | |
legendgroup = "biproportional", | |
type = "bar", | |
hoverinfo = "text") %>% | |
layout(title = "Sitzverteilung im Nationalrat<br>「2015 - 2019」", | |
xaxis = list(title = ""), | |
yaxis = list(title = "Nationalratsmandate"), | |
font = list(family = "Liberation Serif"), | |
margin = list(l = 60, r = 0, t = 40, b = 140, pad = 0, autoexpand = TRUE), | |
width = 800, | |
height = 800) %>% | |
config(displaylogo = FALSE, | |
showLink = FALSE, | |
displayModeBar = FALSE) | |
bar_plot_2_2015 | |
plotly_POST(bar_plot_2_2015, filename = "DDJ-Blogeintrag 3/Barplot 2 2015", fileopt = "overwrite", sharing = "public") | |
# Pie Chart zu offizieller vs. biproportionaler Sitzverteilung nach Parteiblöcken mittels Plotly erzeugen | |
party_blocks <- data.frame(Parteiblock = unique(data_mandates_plus_2015$Parteiblock), | |
Parteien = "", | |
stringsAsFactors = FALSE) | |
for ( i in unique(data_mandates_plus_2015$Parteiblock) ) { | |
party_blocks$Parteien_real[party_blocks$Parteiblock == i] <- paste(data_mandates_plus_2015$Partei[data_mandates_plus_2015$Parteiblock==i & data_mandates_plus_2015$Mandate_real != 0], collapse = "<br>") | |
party_blocks$Parteien_biproportional[party_blocks$Parteiblock == i] <- paste(data_mandates_plus_2015$Partei[data_mandates_plus_2015$Parteiblock==i & data_mandates_plus_2015$Mandate_biproportional != 0], collapse = "<br>") | |
} | |
## Parteistärken pro Parteiblock berechnen | |
Parteiblockstärke_biproportional <- list(Linke = sum(data_mandates_plus_2015$Parteistärke_biproportional[data_mandates_plus_2015$Parteiblock == "Linke"]), | |
Mitte = sum(data_mandates_plus_2015$Parteistärke_biproportional[data_mandates_plus_2015$Parteiblock == "Mitte"]), | |
Rechte = sum(data_mandates_plus_2015$Parteistärke_biproportional[data_mandates_plus_2015$Parteiblock == "Rechte"])) | |
Parteiblockstärke_real <- list(Linke = sum(data_mandates_plus_2015$Parteistärke_real[data_mandates_plus_2015$Parteiblock == "Linke"], na.rm = TRUE), | |
Mitte = sum(data_mandates_plus_2015$Parteistärke_real[data_mandates_plus_2015$Parteiblock == "Mitte"], na.rm = TRUE), | |
Rechte = sum(data_mandates_plus_2015$Parteistärke_real[data_mandates_plus_2015$Parteiblock == "Rechte"], na.rm = TRUE)) | |
pie_chart_real_2015 <- | |
plot_ly(type = "pie", | |
data = data_mandates_plus_2015 %>% group_by(Parteiblock) %>% summarise(sum(Mandate_real)), | |
values = `sum(Mandate_real)`, | |
labels = Parteiblock, | |
marker = list(colors = c("#ff3399", "#14b8b8", "#4d2800")), | |
text = paste0(party_blocks$Parteien_real[party_blocks$Parteiblock == Parteiblock], | |
"<br><br>Anzahl Mandate: ", `sum(Mandate_real)`, | |
"<br><br>Parteiblockstärke: ", round(as.numeric(Parteiblockstärke_real[Parteiblock]), digits = 1), " %"), | |
textinfo = "label+value", | |
hoverinfo = "text", | |
insidetextfont = list(family = "Liberation Serif", | |
color = "#ffffff"), | |
name = "Mandate real", | |
legendgroup = "Mandate", | |
direction = "counterclockwise", | |
#hole = 0.5, | |
opacity = 0.6, | |
pull = 0.02, | |
rotation = 122.5, | |
hoverinfo = "text", | |
domain = list(x = c(0, 0.48), y = c(0, 1))) %>% | |
add_trace(type = "pie", | |
data = data_mandates_plus_2015 %>% group_by(Parteiblock) %>% summarise(sum(Mandate_biproportional)), | |
values = `sum(Mandate_biproportional)`, | |
labels = Parteiblock, | |
marker = list(colors = c("#ff3399", "#14b8b8", "#4d2800")), | |
text = paste0(party_blocks$Parteien_biproportional[party_blocks$Parteiblock == Parteiblock], | |
"<br><br>Anzahl Mandate: ", `sum(Mandate_biproportional)`, | |
"<br><br>Parteiblockstärke: ", round(as.numeric(Parteiblockstärke_biproportional[Parteiblock]), digits = 1), " %"), | |
textinfo = "label+value", | |
hoverinfo = "text", | |
insidetextfont = list(family = "Liberation Serif", | |
color = "#ffffff"), | |
name = "Mandate biproportional", | |
legendgroup = "Mandate", | |
direction = "counterclockwise", | |
#hole = 0.5, | |
pull = 0.02, | |
rotation = 117, | |
hoverinfo = "text", | |
domain = list(x = c(0.52, 1), y = c(0, 1))) %>% | |
layout(title = "Sitzverteilung im Nationalrat<br>「2015 - 2019」", | |
margin = list(l = 0, r = 0, t = 40, b = 0, pad = 0, autoexpand = TRUE), | |
width = 800, | |
height = 490, | |
showlegend = FALSE, | |
font = list(family = "Liberation Serif"), | |
annotations = list(list(x = 0.205 , y = 1, text = "offiziell", showarrow = F, xref='paper', yref='paper', font = list(size = 16)), | |
list(x = 0.82 , y = 1, text = "biproportional", showarrow = F, xref='paper', yref='paper', font = list(size = 16)))) %>% | |
config(displaylogo = FALSE, | |
showLink = FALSE, | |
displayModeBar = FALSE) | |
pie_chart_real_2015 | |
plotly_POST(pie_chart_real_2015, filename = "DDJ-Blogeintrag 3/Pie Chart 2015", fileopt = "overwrite", sharing = "public") | |
# Anzahl (nicht) berücksichtigter (Pseudo-)WählerInnen/Wahlzettel berechnen (real vs. biproportional) | |
## real direkt, indirekt und nicht berücksichtigte berechnen | |
data_voter_consideration_2015 <- | |
data_tickets_2015 %>% | |
group_by(Kanton, Listenberücksichtigung) %>% | |
summarise(sum(Erhaltene_Stimmen_NZZ)) %>% | |
ungroup() %>% | |
dcast(Kanton ~ Listenberücksichtigung, value.var = "sum(Erhaltene_Stimmen_NZZ)") %>% | |
rename(real_direkt = direkt) %>% | |
rename(real_indirekt = indirekt) %>% | |
rename(real_nicht = nicht) | |
## biproportional direkt und nicht berücksichtigte berechnen | |
### (Pseudo-)WählerInnen/Wahlzettel pro Partei und Kanton berechnen | |
data_votes_nzz_2015 <- | |
data_tickets_2015 %>% | |
group_by(Kanton, Unterpartei_v3) %>% | |
summarise(sum(Erhaltene_Stimmen_NZZ)) %>% | |
ungroup() %>% | |
dcast(Kanton ~ Unterpartei_v3, value.var = "sum(Erhaltene_Stimmen_NZZ)") | |
### biproportional nicht berücksichtigte Parteien ermitteln | |
biprop_unconsidered_parties <- names(apportionment_biprob_2015_v3$Superapportionment$DivStdh200$apportionment[apportionment_biprob_2015_v3$Superapportionment$DivStdh200$apportionment == 0]) | |
### direkt und nicht berücksichtigte (Pseudo-)WählerInnen/Wahlzettel pro Kanton berechnen | |
data_voter_consideration_2015$biproportional_direkt <- NA | |
data_voter_consideration_2015$biproportional_nicht <- NA | |
for ( i in data_votes_nzz_2015$Kanton ) { | |
biprop_unconsidered_parties_cantonal <- biprop_unconsidered_parties | |
# In "BE" "SD" raus, da dort berücksichtigt (an "EDU" geflossen) | |
if ( i == "BE" ) biprop_unconsidered_parties_cantonal <- biprop_unconsidered_parties_cantonal[biprop_unconsidered_parties_cantonal != "SD"] | |
data_voter_consideration_2015$biproportional_direkt[data_voter_consideration_2015$Kanton == i] <- sum(subset(data_votes_nzz_2015, Kanton == i)[, !(colnames(data_votes_nzz_2015) %in% biprop_unconsidered_parties_cantonal) & colnames(data_votes_nzz_2015) != "Kanton"], na.rm = TRUE) | |
data_voter_consideration_2015$biproportional_nicht[data_voter_consideration_2015$Kanton == i] <- sum(subset(data_votes_nzz_2015, Kanton == i)[, colnames(data_votes_nzz_2015) %in% biprop_unconsidered_parties_cantonal], na.rm = TRUE) | |
} | |
### (Pseudo-)WählerInnen/Wahlzettel-Total pro Kanton berechnen | |
data_voter_consideration_2015$total <- (data_tickets_2015 %>% group_by(Kanton) %>% summarise(sum(Erhaltene_Stimmen_NZZ)))$`sum(Erhaltene_Stimmen_NZZ)` | |
### Bereinigung/Rundung auf Ganzzahlen | |
data_voter_consideration_2015[is.na(data_voter_consideration_2015)] <- 0 | |
data_voter_consideration_2015$total <- as.integer(round(data_voter_consideration_2015$total)) | |
data_voter_consideration_2015$real_direkt <- as.integer(round(data_voter_consideration_2015$real_direkt)) | |
data_voter_consideration_2015$real_indirekt <- as.integer(round(data_voter_consideration_2015$real_indirekt)) | |
data_voter_consideration_2015$real_nicht <- as.integer(round(data_voter_consideration_2015$real_nicht)) | |
data_voter_consideration_2015$biproportional_direkt <- as.integer(round(data_voter_consideration_2015$biproportional_direkt)) | |
data_voter_consideration_2015$biproportional_nicht <- as.integer(round(data_voter_consideration_2015$biproportional_nicht)) | |
# Choropleth Plots (Inspiration: https://gist.github.com/hrbrmstr/e3d0dc87eaacf7bbece7 | Kartendaten: https://github.com/ruedin/swisscantonsmod/) | |
switzerland <- readOGR(dsn = "./swisscantons", layer = "ch-cantons") | |
data_plot_switzerland <- fortify(switzerland) | |
data_voter_consideration_2015$id[c(1, 3, 2, 5, 6, 4, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 19, 18, 20, 21, 22, 24, 23, 26, 25)] <- 0:25 | |
# data_voter_consideration_2015$Zentrum_Koordinate_x <- data.frame(gCentroid(switzerland, byid = TRUE))[c(1, 3, 2, 5, 6, 4, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 19, 18, 20, 21, 22, 24, 23, 26, 25), "x"] | |
# data_voter_consideration_2015$Zentrum_Koordinate_y <- data.frame(gCentroid(switzerland, byid = TRUE))[c(1, 3, 2, 5, 6, 4, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 19, 18, 20, 21, 22, 24, 23, 26, 25), "y"] | |
data_plot_switzerland <- merge(data_plot_switzerland, data_voter_consideration_2015, by = "id") | |
## Funktion zur Generierung eines interaktiven htmlwidgets-Plots mittels ggraph | |
plot_ggiraph <- function(ggplot_object) { | |
ggiraph(code = print(ggplot_object), | |
hover_css = "fill:orangered;r:6pt", | |
tooltip_offx = 20, | |
tooltip_offy = 0, | |
tooltip_opacity = 0.9, | |
width = "100%", | |
height = "700px") | |
} | |
## Funktion zum ggplot-theming definieren | |
theme_map <- function(base_size = 9, base_family = "") { | |
theme_bw(base_size = base_size, base_family = base_family) %+replace% | |
theme(axis.line = element_blank(), | |
axis.text = element_blank(), | |
axis.ticks = element_blank(), | |
axis.title = element_blank(), | |
panel.background = element_blank(), | |
panel.border = element_blank(), | |
panel.grid = element_blank(), | |
panel.margin = unit(0, "lines"), | |
plot.background = element_blank(), | |
plot.title = element_text(size = 9, hjust = 0.5, vjust=-1), | |
plot.subtitle = element_text(size = 7, hjust = 0.5), | |
legend.text = element_text(size = 6), | |
legend.title = element_text(size = 7), | |
legend.key.width = unit(30, "points"), | |
legend.title.align = 1, | |
legend.justification = c(0, 0), | |
legend.position = "bottom" | |
) | |
} | |
## offiziell nicht berücksichtigte Wahlzettel plotten mittels ggplot2 | |
choropleth_plot_real <- ggplot(mapping = aes(data_id = Kanton, | |
tooltip = paste0("<span style=\"color: #ff4500;\"><strong>", Kanton, "</strong></span>", | |
"<br><br>Total Wahlzettel: <em>", total, "</em>", | |
"<br><br>Unberücksichtigte Wahlzettel", | |
"<br><span style=\"color: #ff4500;\">offiziell: </span><em>", real_nicht, "</em> bzw. <span style=\"color: #ff4500;\"><strong>", round((real_nicht / total) * 100, digits = 1), " %</strong></span>", | |
"<br><span style=\"color: #00e600;\">biproportional: </span><em>", biproportional_nicht, "</em> bzw. <span style=\"color: #00e600;\"><strong>", round((biproportional_nicht / total) * 100, digits = 1), " %</strong></span>", | |
"<br><br>Anzahl Sitze: ", colSums(data_mandates_2015[, Kanton])))) + | |
geom_map_interactive(data = subset(data_plot_switzerland, Kanton != "AR"), | |
map = subset(data_plot_switzerland, Kanton != "AR"), | |
mapping = aes(map_id = id, | |
x = long, | |
y = lat, | |
# group = group, | |
fill = (real_nicht / total) * 100), | |
color = "#ffffff", | |
size = 0.3) + | |
# Workaround zur korrekten Darstellung (AR muss nach SG geplottet werden; offenbar, weil es (wie AI) komplett von SG umschlossen ist (aber vor AI geplottet wird?)) | |
geom_map_interactive(data = subset(data_plot_switzerland, Kanton == "AR"), | |
map = subset(data_plot_switzerland, Kanton == "AR"), | |
mapping = aes(map_id = id, | |
x = long, | |
y = lat, | |
# group = group, | |
fill = (real_nicht / total) * 100), | |
color = "#ffffff", | |
size = 0.3) + | |
# geom_text_interactive(data = data_voter_consideration_2015, | |
# mapping = aes(label = Kanton, | |
# x = Zentrum_Koordinate_x, | |
# y = Zentrum_Koordinate_y), | |
# size = 3) + | |
coord_map() + | |
scale_fill_viridis() + | |
labs(x = "", | |
y = "", | |
fill = "offiziell unberücksichtigte \nWahlzettel in % ", | |
title = "Offiziell unberücksichtigte Wahlzettel", | |
subtitle = "\nNationalratswahlen 「2015」") + | |
# ggtitle(expression(atop("Anteil unberücksichtigter Wahlzettel nach Kantonen", atop(italic("Nationalratswahlen 「2015」"), "")))) + | |
theme_map() | |
choropleth_plot_real | |
plot_ggiraph(choropleth_plot_real) | |
## biproportional nicht berücksichtigte Wahlzettel plotten mittels ggplot2 | |
choropleth_plot_biprop <- ggplot(mapping = aes(data_id = Kanton, | |
tooltip = paste0("<span style=\"color: #ff4500;\"><strong>", Kanton, "</strong></span>", | |
"<br><br>Total Wahlzettel: <em>", total, "</em>", | |
"<br><br>Unberücksichtigte Wahlzettel", | |
"<br><span style=\"color: #ff4500;\">offiziell: </span><em>", real_nicht, "</em> bzw. <span style=\"color: #ff4500;\"><strong>", round((real_nicht / total) * 100, digits = 1), " %</strong></span>", | |
"<br><span style=\"color: #00e600;\">biproportional: </span><em>", biproportional_nicht, "</em> bzw. <span style=\"color: #00e600;\"><strong>", round((biproportional_nicht / total) * 100, digits = 1), " %</strong></span>", | |
"<br><br>Anzahl Sitze: ", colSums(data_mandates_2015[, Kanton])))) + | |
geom_map_interactive(data = subset(data_plot_switzerland, Kanton != "AR"), | |
map = subset(data_plot_switzerland, Kanton != "AR"), | |
mapping = aes(map_id = id, | |
x = long, | |
y = lat, | |
# group = group, | |
fill = (biproportional_nicht / total) * 100), | |
color = "#ffffff", | |
size = 0.3) + | |
# Workaround zur korrekten Darstellung (AR muss nach SG geplottet werden; offenbar, weil es (wie AI) komplett von SG umschlossen ist (aber vor AI geplottet wird?)) | |
geom_map_interactive(data = subset(data_plot_switzerland, Kanton == "AR"), | |
map = subset(data_plot_switzerland, Kanton == "AR"), | |
mapping = aes(map_id = id, | |
x = long, | |
y = lat, | |
# group = group, | |
fill = (biproportional_nicht / total) * 100), | |
color = "#ffffff", | |
size = 0.3) + | |
# geom_text_interactive(data = data_voter_consideration_2015, | |
# mapping = aes(label = Kanton, | |
# x = Zentrum_Koordinate_x, | |
# y = Zentrum_Koordinate_y), | |
# size = 3) + | |
coord_map() + | |
# gleiche Skala sicherstellen | |
scale_fill_viridis(end = max(data_voter_consideration_2015$biproportional_nicht / data_voter_consideration_2015$total) / | |
max(data_voter_consideration_2015$real_nicht / data_voter_consideration_2015$total)) + | |
labs(x = "", | |
y = "", | |
fill = "biproportional unberücksichtigte \nWahlzettel in % ", | |
title = "Biproportional unberücksichtigte Wahlzettel", | |
subtitle = "\nNationalratswahlen 「2015」") + | |
# ggtitle(expression(atop("Anteil unberücksichtigter Wahlzettel nach Kantonen", atop(italic("Nationalratswahlen 「2015」"), "")))) + | |
theme_map() | |
choropleth_plot_biprop | |
plot_ggiraph(choropleth_plot_biprop) | |
# offiziell vs. biproportional gewählte KandidatInnen bestimmen | |
## offiziell Gewählte | |
data_elected_candidates_real_2015 <- | |
data_candidates_2015 %>% | |
filter(Gewählt == "G") %>% | |
select(Vorname, Name, Geburtsjahr, Partei, Kanton, Erhaltene_Stimmen) | |
### bereinigen | |
data_elected_candidates_real_2015$Name <- paste(data_elected_candidates_real_2015$Vorname, data_elected_candidates_real_2015$Name) | |
data_elected_candidates_real_2015$Vorname <- NULL | |
data_elected_candidates_real_2015$Partei[data_elected_candidates_real_2015$Partei == "GPS"] <- "Grüne" | |
data_elected_candidates_real_2015$Partei[data_elected_candidates_real_2015$Partei == "GLP"] <- "glp" | |
data_elected_candidates_real_2015$Partei[data_elected_candidates_real_2015$Partei == "MCR"] <- "MCG" | |
data_elected_candidates_real_2015$Partei[data_elected_candidates_real_2015$Partei == "PdA"] <- "AL | PdA | solidaritéS" | |
data_elected_candidates_real_2015$Partei[data_elected_candidates_real_2015$Name == "Christoph Eymann"] <- "LDP" | |
data_elected_candidates_real_2015$Partei[data_elected_candidates_real_2015$Name == "Karl Vogler"] <- "CSP Obwalden" | |
## biproportional Gewählte | |
subapportionment_v3 <- apportionment_biprob_2015_v3$Subapportionment1$apportionment | |
data_elected_candidates_biprop_2015_raw <- data.frame() | |
### Listennummern der Listen mit einem Mandat je Kanton bestimmen und daraufhin KandidatInnen mit den meisten Stimmen je Listennummer-Konglomerat bestimmen | |
for ( canton in rownames(subapportionment_v3) ) { | |
elected_parties <- colnames(subapportionment_v3)[subapportionment_v3[canton, ] != 0] | |
for ( party in elected_parties ) { | |
mandate_list_numbers <- unique(data_tickets_2015$Listen_Nr_numerisch[data_tickets_2015$Kanton == canton & data_tickets_2015$Unterpartei_v3 == party]) | |
possible_candidates <- arrange(subset(data_candidates_2015, Kanton == canton & Listen_Nr_numerisch %in% mandate_list_numbers), desc(Erhaltene_Stimmen)) | |
possible_candidates$Partei <- party | |
nr_of_seats <- subapportionment_v3[canton, party] | |
data_elected_candidates_biprop_2015_raw <- rbind(data_elected_candidates_biprop_2015_raw, possible_candidates[1:nr_of_seats, ]) | |
} | |
} | |
### bereinigen | |
data_elected_candidates_biprop_2015 <- | |
data_elected_candidates_biprop_2015_raw %>% | |
select(Vorname, Name, Geburtsjahr, Partei, Kanton, Erhaltene_Stimmen) | |
data_elected_candidates_biprop_2015$Name <- paste(data_elected_candidates_biprop_2015$Vorname, data_elected_candidates_biprop_2015$Name) | |
data_elected_candidates_biprop_2015$Vorname <- NULL | |
data_elected_candidates_biprop_2015$Partei[data_elected_candidates_biprop_2015$Partei == "GPS"] <- "Grüne" | |
data_elected_candidates_biprop_2015$Partei[data_elected_candidates_biprop_2015$Partei == "GLP"] <- "glp" | |
data_elected_candidates_biprop_2015$Partei[data_elected_candidates_biprop_2015$Partei == "MCR"] <- "MCG" | |
data_elected_candidates_biprop_2015$Partei[data_elected_candidates_biprop_2015$Partei == "PdA"] <- "AL | PdA | solidaritéS" | |
data_elected_candidates_biprop_2015$Partei[data_elected_candidates_biprop_2015$Name == "Christoph Eymann"] <- "LDP" | |
data_elected_candidates_biprop_2015$Partei[data_elected_candidates_biprop_2015$Name == "Karl Vogler"] <- "CSP Obwalden" | |
## biproportional Abgewählte bestimmen | |
data_candidates_out <- | |
anti_join(data_elected_candidates_real_2015, data_elected_candidates_biprop_2015) %>% | |
arrange(Partei, Kanton, Name) | |
## biproportional neu Gewählte bestimmen | |
data_candidates_in <- | |
anti_join(data_elected_candidates_biprop_2015, data_elected_candidates_real_2015) %>% | |
arrange(Partei, Kanton, Name) | |
# interaktive HTML-Tabelle mit den Ab- und den neu Gewählten erstellen mittels DT | |
datatable(data_candidates_out, | |
autoHideNavigation = TRUE, | |
rownames = FALSE, | |
options = list(dom = "ftipr")) | |
datatable(data_candidates_in, | |
autoHideNavigation = TRUE, | |
rownames = FALSE, | |
options = list(dom = "ftipr")) | |
# Diverse Berechnungen | |
## Durchschnittsalter der Parlamentarier real vs. biproportional? | |
mean(rep(2016, 200) - data_elected_candidates_real_2015$Geburtsjahr) | |
mean(rep(2016, 200) - data_elected_candidates_biprop_2015$Geburtsjahr) # Differenz von 1 Jahr! | |
## der/die jüngste ParlamentarierIn? | |
2016 - max(data_elected_candidates_real_2015$Geburtsjahr) | |
2016 - max(data_elected_candidates_biprop_2015$Geburtsjahr) | |
## der/die älteste ParlamentarierIn? | |
2016 - min(data_elected_candidates_real_2015$Geburtsjahr) | |
2016 - min(data_elected_candidates_biprop_2015$Geburtsjahr) # genau gleich! | |
## Durchschnittsalter der 10 jüngsten ParlamentarierInnen? | |
mean(rep(2016, 10) - arrange(data_elected_candidates_real_2015, desc(Geburtsjahr))$Geburtsjahr[1:10]) | |
mean(rep(2016, 10) - arrange(data_elected_candidates_biprop_2015, desc(Geburtsjahr))$Geburtsjahr[1:10]) # Differenz von über 5 Jahren! | |
## Differenz zwischen offizieller Anzahl gültig Stimmender und derjenigen beim Biprop-NZZ-Verfahren berechnen | |
(ballot_papers_2015$voters_participating - ballot_papers_2015$empty_ballot_papers - ballot_papers_2015$spoiled_ballot_papers) - sum(apportionment_biprob_2015_v3$Superapportionment$input$votes) | |
## Gesamtzahl nicht berücksichtigter Stimmen beim Biprop-NZZ-Verfahren berechnen (auf 2 verschiedene "Arten") | |
sum(apportionment_biprob_2015_v3$Superapportionment$input$votes[apportionment_biprob_2015_v3$Superapportionment$DivStdh200$apportionment == 0]) | |
sum(data_voter_consideration_2015$biproportional_nicht) | |
## Gesamtzahl nicht berücksichtigter Stimmen beim offiziellen Wahlverfahren berechnen | |
sum(data_voter_consideration_2015$real_nicht) # mehr als das 3.5-fache als beim Doppelproporz (und dort konnten aufgrund von Mehrdeutigkeiten noch nicht einmal alle Listenverbindungen berücksichtigt werden; würden diese auch noch berücksichtigt, wäre es mehr als das 5.5-fache) | |
## Gesamtzahl hypothetisch nicht berücksichtigter Stimmen beim Biprop-NZZ-Verfahren berechnen, wenn alle mit Listenverbindung berücksichtigt würden | |
round(sum(data_tickets_2015$Erhaltene_Stimmen_NZZ[data_tickets_2015$Unterpartei_v3 %in% biprop_unconsidered_parties & data_tickets_2015$Listenverbindung == ""])) | |
## Gesamtzahl ungültiger Stimmen | |
ballot_papers_2015$spoiled_ballot_papers | |
## checken, obs gegenläufige Sitzverschiebungen auf 1. Ebene gab | |
### maximale Stimmenzahlen je Kanton bestimmen | |
data_votes_maxima <- | |
data_candidates_2015 %>% | |
group_by(Kanton) %>% | |
summarise(Stimmenmaximum = max(Erhaltene_Stimmen)) | |
### die entsprechenden KandidatInnen eruieren | |
data_candidates_votes_maxima <- inner_join(data_candidates_2015, | |
data_votes_maxima, | |
by = c("Kanton" = "Kanton", "Erhaltene_Stimmen" = "Stimmenmaximum")) | |
### checken, ob alle 26 Bestplatzierten im Doppelproporz weiterhin gewählt würden | |
anti_join(data_candidates_votes_maxima, data_elected_candidates_biprop_2015) # jap! |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment