#######################################################
## Open Data Kooperation: univie & Parlament ##########
## Showcase 10: Einsprüche des Bundesrats #############
## code by Laurenz Ennser-Jedenastik & Daniel Bliem ###
### Kontakt: laurenz.ennser@univie.ac.at ##############
#######################################################
# Leeren der Arbeitsumgebung
rm(list = ls())
# Installieren und Laden der erforderlichen Pakete.
necessary.packages <- c(
"httr",
"jsonlite",
"RJSONIO",
"RCurl",
"httr2",
"purrr",
"tibble",
"rjson",
"tidyverse",
"gridExtra",
"stringr",
"showtext")
new.packages <- necessary.packages[!(necessary.packages %in% installed.packages()[,"Package"])]
if(length(new.packages)) install.packages(new.packages)
lapply(necessary.packages, library, character.only = TRUE)
# Definieren des Arbeitsverzeichnisses
setwd("Pfad/zu/Ihrem/Arbeitsverzeichnis")
### 1. Datensatz: Einsprüche des Bundesrats
# Definition von API-UR und Body des API-Calls
req <- request("https://www.parlament.gv.at/Filter/api/filter/data/101?js=eval&showAll=true&export=true")
t1 <-
req %>%
req_body_raw('{
"NRBR": [
"NR"
],
"GP_CODE": [
"XXVII", "XXVI", "XXV", "XXIV", "XXIII", "XXII", "XXI", "XX"
],
"VHG": [
"EBR"
]
}') %>%
req_perform()
# Die Antwort in einen Dataframe laden
df1 <- t1 %>% resp_body_json()
# Loop zur Umwandlung von relativen Links aus den Ergebnislisten des API-Calls (Filter) zu absoluten Links
nrows1 <- length(df1$rows)
vorne <- "https://www.parlament.gv.at"
hinten <- "?json=true"
all.urls <- rep(NA, nrows1)
all.GPs <- rep(NA, nrows1)
all.numbers <- rep(NA, nrows1)
# Iterieren durch die Reihen, um aus den Links herunterladbare URLs zu machen
for (i in 1:nrows1) {
all.urls[i] <- paste0(vorne, df1[["rows"]][[i]][[15]], hinten)
all.GPs[i] <- df1[["rows"]][[i]][[1]]
all.numbers[i] <- df1[["rows"]][[i]][[8]]
}
for (i in 1:nrows1){
download.file(all.urls[i], destfile = paste0(all.GPs[i], "_", all.numbers[i], ".json"))
}
# Erstellen der leeren Variablen für den Dataframe
files.in.dir <- list.files(path = "Pfad/zu/Ihrem/Speicherort")
files.in.dir <- files.in.dir[grep(".json", files.in.dir)] # Sicherstellen, dass nur JSON-Files in der Liste sind
df01 <- as.data.frame(rep(NA, length(files.in.dir)))
df01$name <- NA
df01$cit <- NA
df01$cit_vhg <- NA
df01$gp <- NA
df01$einlangen <- NA
df01$statustext <- NA
count.files <- length(files.in.dir)
# Funktion zur Ermittlung des Labels
get_label <- function(bubble) {
if (!is.null(bubble[["label"]])) {
return(bubble[["label"]])
} else {
return(NA)
}
}
# Iterieren durch alle Dateien im Verzeichnis und extrahieren relevanter Informationen
for (i in 1:count.files) {
getfile <- fromJSON(file = files.in.dir[i])
print(files.in.dir[i])
# Extrahieren der passenden Daten der JSON-Datei für jede Variable und speichern dieser im Dataframe
df01$name[i] <- getfile[["meta"]][["description"]]
df01$cit[i] <- getfile[["meta"]][["openGraph"]][["description"]]
df01$cit_vhg[i] <- getfile[["content"]][["reference"]][[1]][["zitation"]]
df01$gp[i] <- getfile[["content"]][["gp_code"]]
df01$einlangen[i] <- getfile[["content"]][["phase"]][[1]][["stages"]][[1]][["date"]]
df01$statustext[i] <- getfile[["content"]][["status"]][["description"]]
}
# Umformen der "status"-Variable
for (i in 1:nrow(df01)) {
if (str_detect(df01$statustext[i], "Beharrungsbeschlusses")) {
df01$status[i] <- "Beharrungsbeschluss"
} else if (str_detect(df01$statustext[i], "Zugewiesen")) {
df01$status[i] <- "An Ausschuss zugewiesen"
} else if (str_detect(df01$statustext[i], "Beschlossen")) {
df01$status[i] <- "Einspruch erfolgreich"
} else {
df01$status[i] <- "Gerade in Behandlung"
}
}
# Speichern des Datensatzes
save(df01, file = "Ihr_Dateiname.RData")
### 2. Datensatz: Mitglieder des Bundesrats
# Definieren des neuen Speicherorts für die JSON Files
setwd("Pfad/zu/Ihrem/neuen/Speicherort")
# Definition von API-UR und Body des API-Calls
req2 <- request("https://www.parlament.gv.at/Filter/api/json/post?jsMode=EVAL&FBEZ=WFW_007&listeId=undefined&showAll=true&export=true")
t2 <-
req2 %>%
req_body_raw('{
"NRBR": [
"BR"
],
"R_WF": [
"WP"
],
"M": [
"M"
],
"W": [
"W"
]
}') %>%
req_perform()
# Die Antwort in einen Dataframe laden
df2 <- t2 %>% resp_body_json()
# Loop zur Umwandlung von relativen Links aus den Ergebnislisten des API-Calls (Filter) zu absoluten Links
nrows2 <- length(df2$rows)
vorne <- "https://www.parlament.gv.at"
hinten <- "?json=true"
all.urls <- rep(NA, nrows2)
all.numbers <- rep(NA, nrows2)
# Iterieren durch die Reihen, um aus den Links herunterladbare URLs zu machen
for (i in 1:nrows2) {
all.urls[i] <- paste0(vorne, df2[["rows"]][[i]][[7]], hinten)
all.numbers[i] <- df2[["rows"]][[i]][[1]]
}
for (i in 1:nrows2){
download.file(all.urls[i], destfile = paste0("BR_", all.numbers[i], ".json"))
}
# Erstellen der leeren Variablen für den Dataframe
files.in.dir.2 <- list.files(path = "Pfad/zu/Ihrem/neuen/Speicherort", pattern = "*.json", full.names = TRUE)
count.files.2 <- length(files.in.dir.2)
df02 <- data.frame(name = character(),
nr = character(),
frak = character(),
von = character(),
bis = character(),
stringsAsFactors = FALSE)
# Iterieren durch alle Dateien im Verzeichnis und extrahieren relevanter Informationen
for (i in 1:count.files.2) {
getfile <- fromJSON(file = files.in.dir.2[i])
print(files.in.dir.2[i])
name <- getfile[["content"]][["headingbox"]][["title"]]
nr <- getfile[["content"]][["personInfo"]][["pad_intern"]]
mandates <- NULL
# Prüfen ob "biografie" oder "banner" existiert
if (!is.null(getfile[["content"]][["biografie"]])) {
mandates <- getfile[["content"]][["biografie"]][["mandatefunktionen"]][["mandate"]]
} else if (!is.null(getfile[["content"]][["banner"]])) {
mandates <- getfile[["content"]][["banner"]][["mandate"]]
}
# Wenn Mandate gefunden wurden
if (!is.null(mandates)) {
for (j in seq_along(mandates)) {
if (mandates[[j]][["mandat"]] == "Mitglied des Bundesrates") {
frak <- mandates[[j]][["wahlpartei"]]
von <- mandates[[j]][["mandatVon"]]
bis <- mandates[[j]][["mandatBis"]]
# Neue Zeile zum DataFrame hinzufügen
df02 <- rbind(df02, data.frame(name = name,
nr = nr,
frak = frak,
von = von,
bis = bis,
stringsAsFactors = FALSE))
}
}
}
}
# Setzen fehlender "bis" Werte auf "29.09.2024" (aktuelle Abgeordnete)
df02$bis[is.na(df02$bis) | df02$bis == ""] <- "29.09.2024"
# Variablen in Datum-Format umwandeln
df02$von <- as.Date(df02$von, format = "%d.%m.%Y")
df02$bis <- as.Date(df02$bis, format = "%d.%m.%Y")
# Filtern des Untersuchungszeitraums: XX. GP bis XXVII. GP
df02 <- df02 %>% filter(bis >= as.Date("1996-01-15"))
df02 <- df02 %>% filter(von >= as.Date("2024-09-28"))
# Vereinheitlichen der FPÖ-Fraktion
df02 <- df02 %>% mutate(frak = ifelse(frak == "F", "FPÖ", frak))
# Korrigieren einiger Abgeordneter, die als fraktionslos geführt wurden
fraktion_mapping <- c(
"Andreas Lackner" = "GRÜNE",
"Christoph Längle, BA" = "FPÖ",
"Claudia Hauschildt-Buschberger" = "GRÜNE",
"Cornelia Michalke" = "FPÖ",
"David Stögmüller" = "GRÜNE",
"Dipl.-Ing. Dr. Adi Gross" = "GRÜNE",
"Dr. Dietmar Schmittner" = "FPÖ",
"Dr. Ewa Ernst-Dziedzic" = "GRÜNE",
"Dr. Heidelinde Reiter" = "GRÜNE",
"Dr. Jennifer Kickert" = "GRÜNE",
"Dr. Peter Böhm" = "FPÖ",
"Edmund Tauchner" = "FPÖ",
"Efgani Dönmez, PMM" = "GRÜNE",
"Elisabeth Kerschbaum" = "GRÜNE",
"Elmar Podgorschek" = "FPÖ",
"Harald Vilimsky" = "FPÖ",
"Ing. Siegfried Kampl" = "BZÖ",
"Johann Ertl" = "FPÖ",
"Jutta Arztmann" = "FPÖ",
"Mag. John Gudenus" = "FPÖ",
"Mag. Nicole Schreyer" = "GRÜNE",
"Mag. Walter Ebner" = "BZÖ",
"Marco Schreuder" = "GRÜNE",
"Monika Mühlwerth" = "FPÖ",
"Peter Mitterer" = "BZÖ",
"Peter Zwanziger" = "BZÖ",
"Stefan Schennach" = "SPÖ",
"Stefan Zaggl-Kasztner" = "SPÖ",
"Werner Herbert" = "FPÖ"
)
# Aktualisieren der 'frak' Spalte in df02 basierend auf dieser Liste
df02$frak <- ifelse(df02$frak == "OF" & !is.na(fraktion_mapping[df02$name]),
fraktion_mapping[df02$name],
df02$frak)
# Speichern des Datensatzes
save(df02, file = "Ihr_Dateiname.RData")
### Erstellen einer Variable, die anzeigt, ob eine Partei in einem gegebenen Jahr in der Regierung oder Opposition ist
# Etablieren der Regierungsperioden für alle Parteien
govperiods <- data.frame(
Partei = c("ÖVP", "ÖVP", "SPÖ", "SPÖ", "FPÖ", "FPÖ", "GRÜNE", "BZÖ"),
Startdatum = as.Date(c("1996-01-15", "2020-01-07", "1996-01-15", "2007-01-11", "2000-02-04", "2017-12-18", "2020-01-07", "2005-04-17")),
Enddatum = as.Date(c("2019-05-28", "2024-09-29", "2000-02-03", "2017-12-17", "2007-01-10", "2019-05-27", "2024-09-29", "2007-01-10"))
)
# Funktion um Regierungsperioden mit Mandatsperioden der Abgeordneten abgleichen
split_periods <- function(frak, von, bis, name, govperiods) {
periods <- govperiods %>% filter(Partei == frak)
results <- list()
for (i in 1:nrow(periods)) {
overlap_start <- max(von, periods$Startdatum[i])
overlap_end <- min(bis, periods$Enddatum[i])
if (!is.na(overlap_start) && !is.na(overlap_end) && overlap_start <= overlap_end) {
if (von < overlap_start) {
# Falls es eine Lücke vor der Regierungsperiode gibt, diese als Oppositionszeit hinzufügen
results[[length(results) + 1]] <- data.frame(
frak = frak,
von = von,
bis = overlap_start - 1,
gov = 0,
name = name
)
}
# Regierungszeit hinzufügen
results[[length(results) + 1]] <- data.frame(
frak = frak,
von = overlap_start,
bis = overlap_end,
gov = 1,
name = name
)
von <- overlap_end + 1
}
}
if (von <= bis) {
# Falls es noch verbleibende Zeit nach der letzten Regierungsperiode gibt, diese als Oppositionszeit hinzufügen
results[[length(results) + 1]] <- data.frame(
frak = frak,
von = von,
bis = bis,
gov = 0,
name = name
)
}
return(do.call(rbind, results))
}
# Anwendung der Funktion auf den Datensatz
df02_split <- do.call(rbind,
lapply(1:nrow(df02), function(i) {
split_periods(df02$frak[i], df02$von[i], df02$bis[i], df02$name[i], govperiods)
}))
# Fraktionlose Abgeordnete auf Opposition setzen
df02_split <- df02_split %>% mutate(gov = if_else(frak == "OF", 0, gov))
# Benutzerdefinierte Schriftart hinzufügen
font_add("Lato",
regular = "Pfad/zu/Ihrer/Schriftart.ttf",
bold = "Pfad/zu/Ihrer/Schriftart.ttf")
# Schriftart aktivieren
showtext_auto()
# Erstellen eines eigenen Themes für die Grafiken
custom_theme <- function() {
theme_minimal() +
theme(
plot.background = element_rect(fill = "white", color = NA),
panel.grid.major = element_line(color = alpha("gray", 0.25)),
panel.grid.minor = element_line(color = "white"),
panel.border = element_blank(),
plot.title = element_text(color = "#132843", size = 55, face = "bold", family = "Lato", hjust = 0),
plot.subtitle = element_text(color = "#132843", size = 38, family = "Lato", margin = margin(b = 20), hjust = 0),
axis.title.x = element_text(face = "bold", family = "Lato", size = 35, margin = margin(t = 15)),
axis.title.y = element_text(face = "bold", family = "Lato", size = 35, margin = margin(r = 15)),
axis.text = element_text(family = "Lato", size = 30),
legend.text = element_text(family = "Lato", face = "bold", size = 35),
plot.margin = margin(1.2, 0.7, 0.7, 0.7, "cm"),
plot.title.position = "plot",
plot.caption.position = "plot",
plot.caption = element_text(size = 27)
)
}
# Speicherort für die Grafiken festlegen
setwd("Pfad/zu/Ihrem/Speicherort")
### 1. Grafik: Sitzanzahl der Regierungsparteien im Bundesrat
# Daten vorbereiten: Datum pro Tag und Anzahl der Abgeordneten in Regierung als neue Variablen
df02_plot1 <- df02_split %>%
mutate(date_seq = map2(von, bis, seq, by = "day")) %>%
unnest(date_seq) %>%
group_by(date_seq) %>%
summarise(
num_in_gov = sum(gov == 1))
# Filtern des Untersuchungszeitraums: XX. GP bis XXVII. GP
df02_plot1$date_seq <- as.Date(df02_plot1$date_seq, format = "%d.%m.%Y")
df02_plot1 <- df02_plot1 %>% filter(date_seq >= as.Date("1996-01-15"))
# Ignorieren von kurzweiligen Nichtbesetzungen von Mandaten (unter 1 Monat)
df02_smoothed <- df02_plot1 %>%
arrange(date_seq) %>%
mutate(
change_id = cumsum(
lag(num_in_gov, default = first(num_in_gov)) != num_in_gov)
) %>%
group_by(change_id) %>%
mutate(period_length = n()) %>%
ungroup() %>%
filter(period_length > 30)
# Manuelle Festlegung der Werte der bentöigten Mandate für eine Mehrheit
majority_needed_df <- data.frame(
start_date = as.Date(c("1996-01-15", "2002-12-07", "2013-08-13")),
end_date = as.Date(c("2002-12-06", "2013-08-12", "2024-09-29")),
majority_needed = c(33, 32, 31)
)
# Erstellen der konstanten Linie für die Grafik aus diesen Daten
majority_needed_line <- majority_needed_df %>%
rowwise() %>%
do(data.frame(date_seq = seq(.$start_date, .$end_date, by = "day"),
majority_needed = .$majority_needed)) %>%
ungroup()
# Entfernen des Zeitraums von 29.05.2019 bis 06.01.2020 (Bierlein)
df02_smoothed_na <- df02_smoothed %>%
mutate(num_in_gov = ifelse(date_seq >= as.Date("2019-05-29") & date_seq <= as.Date("2020-01-06"), NA, num_in_gov))
# Erstellen der Grafik
grafik1 <- ggplot(df02_smoothed_na, aes(x = date_seq)) +
geom_line(data = majority_needed_line, aes(x = date_seq, y = majority_needed, color = "Mandate benötigt für Mehrheit"),
size = 1.5, alpha = 1) +
geom_line(aes(y = num_in_gov, color = "Mandate der Regierungsfraktionen"), size = 1.5, alpha = 0.8) +
scale_color_manual(values = c("Mandate der Regierungsfraktionen" = "#b8a552", "Mandate benötigt für Mehrheit" = "#132843")) +
scale_x_date(date_breaks = "1 year", date_labels = "%Y", limits = c(as.Date("1996-01-01"), as.Date("2024-12-31")), expand = c(0.03, 0.03)) +
scale_y_continuous(breaks = seq(0, max(70), by = 5), limits = c(25, 55)) +
labs(
title = "Mandatsaufteilung im Bundesrat",
subtitle = "Jän. 1996 – Okt. 2024",
x = "",
y = "",
caption = "Quelle: parlament.gv.at",
color = ""
) +
custom_theme() +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
legend.position = "bottom"
)
ggsave("SC10_Grafik01.jpg", grafik1, width = 10, height = 7.5, units = "in", dpi = 300)
### 2. Grafik: Einsprüche pro Tagung
# Variable auf Datum-Format ändern
df01$einlangen <- as.Date(df01$einlangen, format = "%d.%m.%Y")
# Funktion zur Berechnung des Tagungsjahres
calculate_tagung <- function(date) {
if (!inherits(date, "Date")) {
stop("Eingabewert ist kein Datum.")
}
year <- format(date, "%Y")
month <- as.numeric(format(date, "%m"))
if (month >= 9) {
tagung <- paste(year, substr(as.numeric(year) + 1, 3, 4), sep = "/")
} else {
tagung <- paste(substr(as.numeric(year) - 1, 1, 4), substr(year, 3, 4), sep = "/")
}
return(tagung)
}
# Berechnen der Tagungsvariable für den Datensatz
df01$tagung <- sapply(df01$einlangen, calculate_tagung)
# Definieren aller Tagungen
alle_tagungen <- c("1996/97", "1997/98", "1998/99", "1999/00", "2000/01", "2001/02", "2002/03", "2003/04", "2004/05", "2005/06",
"2006/07", "2007/08", "2008/09", "2009/10", "2010/11", "2011/12", "2012/13", "2013/14", "2014/15", "2015/16",
"2016/17", "2017/18", "2018/19", "2019/20", "2020/21", "2021/22", "2022/23", "2023/24"
)
# Konvertieren der Spalte 'tagung' in einen Faktor mit den definierten Levels
df01$tagung <- factor(df01$tagung, levels = alle_tagungen)
# Jeder Tagung ein Datum geben
convert_tagung_to_date <- function(tagung) {
year_start <- as.numeric(substr(tagung, 1, 4))
mid_date <- as.Date(paste(year_start, "12-31", sep = "-"))
return(mid_date)
}
# Anzahl an Einsprüchen für jede Tagung festlegen
tagungen_dates <- data.frame(
tagung = alle_tagungen,
mid_date = as.Date(sapply(alle_tagungen, convert_tagung_to_date))
)
# Zusammenführen der vorhandenen Daten mit den Tagungen, um alle Tagungen einzuschließen
df01_summary <- df01 %>%
mutate(mid_date = as.Date(sapply(tagung, convert_tagung_to_date))) %>%
group_by(mid_date, tagung) %>%
summarise(counts = n(), .groups = 'drop') %>%
right_join(tagungen_dates, by = c("mid_date", "tagung")) %>%
replace_na(list(counts = 0))
# Erstellen einer Datenbasis für die Perioden, in denen die Regierung keine Mehrheit hatte
rects <- data.frame(
xmin = as.Date(c("2005-10-01", "2019-05-01")),
xmax = as.Date(c("2007-06-30", "2021-06-30")),
ymin = -Inf,
ymax = Inf
)
# Grafik erstellen mit zwei schattierten Phasen, wo die Regierung keine Mehrheit im Bundesrat hatte
grafik2 <- ggplot() +
geom_bar(data = df01_summary, aes(x = mid_date, y = counts), stat = "identity", width = 250, fill = "#132843") +
geom_rect(data = rects, aes(xmin = xmin, xmax = xmax, ymin = ymin, ymax = ymax), fill = "grey80", alpha = 0.4) +
annotate("text", x = as.Date("2013-01-01"), y = Inf, label = "Regierungsfraktionen haben\nkeine Mehrheit im Bundesrat",
vjust = 3.3, hjust = 0.46, size = 13, color = "black", fontface = "bold", lineheight = 0.35) +
annotate("segment", x = as.Date("2009-06-01"), xend = as.Date("2006-12-01"),
y = 19, yend = 17, color = "black", size = 1, arrow = arrow(length = unit(0.2, "cm"))) +
annotate("segment", x = as.Date("2017-03-01"), xend = as.Date("2020-01-01"),
y = 19, yend = 17, color = "black", size = 1, arrow = arrow(length = unit(0.2, "cm"))) +
labs(x = "", y = "",
title = "Einsprüche des Bundesrats",
subtitle = "Pro Tagung",
caption = "Quelle: parlament.gv.at") +
custom_theme() +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5),
legend.position = "") +
scale_x_date(breaks = tagungen_dates$mid_date, labels = alle_tagungen, expand = c(0.03, 0.03))
ggsave("SC10_Grafik02.jpg", grafik2, width = 10, height = 7.5, units = "in", dpi = 300)
### 3. Grafik: Resultat der Einsprüche
# Reihenfolge der Balken festlegen
df01$status <- factor(df01$status, levels = c("Beharrungsbeschluss", "An Ausschuss zugewiesen", "Einspruch erfolgreich"))
# Erstellen der Grafik
grafik3 <- ggplot(df01, aes(x = status)) +
geom_bar(width = 0.2, fill = "#132843") +
labs(x = "", y = "",
title = "Einsprüche des Bundesrats",
subtitle = "Nach Art der Behandlung; Jän. 1996 – Okt. 2024",
caption = "Quelle: parlament.gv.at") +
custom_theme() +
scale_x_discrete(drop = FALSE)
ggsave("SC10_Grafik03.jpg", grafik3, width = 10, height = 7.5, units = "in", dpi = 300)