library(readr)
library(tidyverse)
library(dplyr)
library(geojsonio)
library(ggplot2)
library(plotly)
library(broom)
library(sp)
Im Datensatz werden verschiedene Symbole zur Markierung von NA-Werten verwendet. Diese mussten als solche erkennbar gemacht werden. Ebenfalls mussten die Abstimmungs-Tage als Datum erkannt werden.
swissvotes <- read_delim("DATASET_swissvotes.csv",
delim = ";", escape_double = FALSE, na = "NA", locale = locale(decimal_mark = ",", grouping_mark = "'"), trim_ws = TRUE, show_col_types = FALSE)
swissvotes[, 1:834][swissvotes[,1:834] == "."] <- NA
swissvotes[, 1:834][swissvotes[,1:834] == ""] <- NA
swissvotes$datum <- as.Date(swissvotes$datum, format = "%d.%m.%Y")
Um potentielle Fehler durch speziell formatierte Werte vorzubeugen, wurden diese umbenannt.
names(swissvotes)[names(swissvotes) == "kt-nein"] <- "kt_nein"
names(swissvotes)[names(swissvotes) == "kt-ja"] <- "kt_ja"
names(swissvotes)[names(swissvotes) == "volkja-proz"] <- "volkja_proz"
names(swissvotes)[names(swissvotes) == "sr-pos"] <- "sr_pos"
names(swissvotes)[names(swissvotes) == "nr-pos"] <- "nr_pos"
names(swissvotes)[names(swissvotes) == "bv-pos"] <- "bv_pos"
names(swissvotes)[names(swissvotes) == "br-pos"] <- "br_pos"
Um die Stimmbeteiligung der Kantone auf die Kantone in den Geo-Daten zu mappen, wurden die jeweiligen Spalten umbenannt.
swissvotes <- swissvotes %>% rename ("Zuerich" = "zh-bet",
"Bern" = "be-bet",
"Luzern" = "lu-bet",
"Uri" = "ur-bet",
"Schwyz" = "sz-bet",
"Obwalden" = "ow-bet",
"Nidwalden" = "nw-bet",
"Glarus" = "gl-bet",
"Zug" = "zg-bet",
"Fribourg" = "fr-bet",
"Solothurn" = "so-bet",
"Basel_Stadt" = "bs-bet",
"Basel_Landschaft" = "bl-bet",
"Schaffhausen" = "sh-bet",
"Appenzell_Ausserrhoden" = "ar-bet",
"Appenzell_Innerrhoden" = "ai-bet",
"St_Gallen" = "sg-bet",
"Graubuenden" = "gr-bet",
"Aargau" = "ag-bet",
"Thurgau" = "tg-bet",
"Ticino" = "ti-bet",
"Vaud" = "vd-bet",
"Valais" = "vs-bet",
"Neuchâtel" = "ne-bet",
"Genève" = "ge-bet",
"Jura" = "ju-bet")
Beim Einlesen des Datensatzes wurden einige Werte nicht als numerisch erkannt, diese wurden umformatiert.
cols.num <- c( "Zuerich", "Bern", "Luzern", "Uri", "Schwyz", "Obwalden", "Nidwalden", "Glarus", "Zug", "Fribourg", "Solothurn","Basel_Landschaft", "Basel_Stadt", "Schaffhausen", "Appenzell_Ausserrhoden", "Appenzell_Innerrhoden", "St_Gallen", "Graubuenden", "Aargau", "Thurgau", "Ticino", "Vaud","Valais", "Neuchâtel", "Genève", "Jura")
swissvotes[cols.num] <- sapply(swissvotes[cols.num],as.numeric)
swissvotes <- transform(swissvotes,
bet = as.numeric(bet))
Für die Choroplethen-Karte war nur ein Bruchteil der Daten relevant, es wurde daher ein neues Datenset erstellt.
slim_swissvotes <- subset(swissvotes, select = c(anr, datum, titel_kurz_d, Zuerich, Bern, Luzern, Uri, Schwyz, Obwalden, Nidwalden, Glarus, Zug, Fribourg, Solothurn, Basel_Landschaft, Basel_Stadt, Schaffhausen, Appenzell_Ausserrhoden, Appenzell_Innerrhoden, St_Gallen, Graubuenden, Aargau, Thurgau, Ticino, Vaud, Valais, Neuchâtel, Genève, Jura))
Reduzierung der Spalten, da die Kantons-Beteiligungen nicht jeweils eine eigene Spalte bilden sollen. Dafür gab es eine Erweiterung der Zeilen von 680 zu 17680.
swissvotes_beteiligung <- pivot_longer(slim_swissvotes, cols = cols.num, names_to = "Beteiligung_Kantone")
Pro Abstimmungs-Datum werden mehrere Abstimmungen gehalten. Die Stimmbeteiligung der verschiedenen Vorlagen unterscheidet sich dabei minim, weshalb entschieden wurde, den jeweiligen Durchschnitts-Wert pro Datum zu verwenden. So wird eine doppel- oder gar dreifach-Belegung der Daten in der Choroplethen-Karte vermieden.
swissvotes_mean <- swissvotes_beteiligung %>% # Group data
group_by(datum, Beteiligung_Kantone) %>%
dplyr::summarize(mean_bet = mean(value)) %>%
as.data.frame()
Beim ersten Versuch die Karte zu plotten, wurde bemerkt, dass Kantone ohne Daten jeweils nicht gezeichnet werden, das heisst, vor der Eigenständigkeit des Kanton Juras wird die Landesgrenze nicht korrekt dargestellt. Um dies zu vermeiden wird daher jeder NA-Wert im für die Darstellung relevanten Datensatz gleich 0 gesetzt.
swissvotes_mean[is.na(swissvotes_mean)] <- 0
Mit über 17000 Observationen wird eine Berechung aller Daten und vor allem ein Rendering der gesamten Grafiken extrem rechenaufwändig. Mit den zur Verfügung stehenden Ressourcen war dies nicht in einer sinnvollen Zeit berechenbar (memory fail nach 4 Stunden Rechenzeit). Um dennoch zu zeigen, dass (und wie) die Karte funktioniert, wurde ein minimalistischer Datensatz erstellt, der nur das Jahr 2020 und das Jahr 2019 betrachtet.
tiny <- swissvotes_mean[swissvotes_mean$datum < "2021-03-07" & swissvotes_mean$datum > "2018-11-25",]
Mit einem geojson-File werden die Kantonsgrenzen aller Schweizer Kantone generiert und in einem json-Format gespeichert. Über «g» wird die Projektion der Karte definiert.
kan <- rjson::fromJSON(file="kantone.geojson")
g <- list(
fitbounds = "locations",
projection = list(type = 'mercator'),
visible = FALSE
)
Mit plotly wird eine Choroplethen-Karte, verknüpft auf die oben eingelesenen Geo-Daten erstellt. Um die Differenz der verschiedenen Stimmbeteiligungen besser zu erkennen, wird für die Demonstration die Range der Farbskale von 20 % bis 80% Beteiligung beschränkt. Als Farbskala wird “Virdis” verwendet, da diese farbenblind sicher ist.
Erklärung einiger wichtiger Parameter:
type = Plot-Art, in unserem Fall eine
Choroplethenkarte dieser Plot-Typ braucht Geodaten in Form einer
geojson Datei
locations = Verknüpfung der Geodaten mit den jeweiligen
Kantonsdaten
z = Füllwert der einzelnen locations (Kantone), in
unserem Fall die mittlere Stimmbeteiligung und damit auch die Werte für
die Farbskala
zmin = minimal Wert der Fabskala
zmax = maximal Wert der Farbskala
colorscale = Farbwahl der Farbskala
frame = Erweiterung des Plots um einen dynamischen
Zeitstrahl, die Plots werden in einzelne Frames/Plots aufgeteilt
featureidkey = Festlegung, über welche Werte die
einzelnen Verknüpfungen gesucht werden sollen (aus beiden Datensätzen,
sowohl Swissvotes als auch die Geodaten)
plot_karte <- plot_ly()
plot_karte <- plot_karte %>% add_trace(
type="choropleth",
geojson=kan,
locations=~tiny$Beteiligung_Kantone,
z=~tiny$mean_bet,
zmin = 20,
zmax = 80,
colorscale="Viridis",
frame=~tiny$datum,
featureidkey="properties.NAME"
)
Kosmetik für die einzelnen Frames, Titel, Farbskalabezeichung und Position, sowie die Verzerrung der Karte wurden korrigiert (mit geo = g).
plot_karte <- plot_karte %>% colorbar(title = "Stimmbeteiligung (%)",x = 1, y = 0.5, len = 0.5, which = c(1:5))
plot_karte <- plot_karte %>% layout(
title = "Stimmbeteiligung"
)
#Berücksichtigung von geographischen Parameter (Projektion etc)
plot_karte <- plot_karte %>% layout(
geo = g
)
plot_karte