########################################################
### Open Data Kooperation: univie & Parlament ##########
### Showcase 2: Ursprünge von Gesetzen #################
### code by Laurenz Ennser-Jedenastik & Isabel Kiani ###
### überarbeitet: Oktober 2024 ################################
### Kontakt: laurenz.ennser@univie.ac.at ###############
########################################################
# Environment leeren
rm(list = ls())
# Installieren der erforderlichen Pakete
necessary.packages <- c("httr",
"jsonlite",
"RJSONIO",
"RCurl",
"httr2",
"purrr",
"tidyr",
"dplyr",
"tibble",
"rjson",
"stringr",
"progress")
new.packages <- necessary.packages[!(necessary.packages %in% installed.packages()[,"Package"])]
if(length(new.packages)) install.packages(new.packages)
# Laden der erforderlichen Packete
lapply(necessary.packages, library, character.only = TRUE)
# Arbeitsumgebung für das Script und Speicherung von Ergebnissen des API-Calls
setwd("Pfad/zu/Ihrem/Arbeitsverzeichnis")
# Definition von API-UR und Body des API-Calls
req <- request("https://www.parlament.gv.at/Filter/api/filter/data/101?showAll=true")
t1 <-
req %>%
req_body_raw('{"GP_CODE":["XXVII", "XXVI", "XXV", "XXIV", "XXIII", "XXII", "XXI", "XX"],"DOKTYP":["A","RV", "BUA", "GABR", "GABR13", "BRA"]}') %>%
req_perform()
df1 <- t1 %>% resp_body_json()
# Loop zur Umwandlung von relativen Links aus den Ergebnislisten des API-Calls (Filter) zu absoluten Links, sowie der Download der Files in die entsprechend WorkingDirectory
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)
all.doctypes <- rep(NA, nrows1)
# Ladebalken erstellen
pb <- progress_bar$new(
format = " Downloading [:bar] :percent :elapsed",
total = nrows1, clear = FALSE, width = 60
)
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]][[3]]
all.doctypes[i] <- df1[["rows"]][[i]][[6]]
suppressMessages( # Ausgabe während dem Download unterdrücken
download.file(all.urls[i], destfile = paste0(all.GPs[i], "_", all.numbers[i], "_", all.doctypes[i], ".json"), quiet = TRUE)
)
pb$tick() # Fortschrittsbalken aktualisieren
}
#############################
# END OF DATA COLLECTION #
#############################
#-------------------------------------
#############################
# START OF DATA ANALYSIS #
#############################
# Environment leeren
rm(list = ls())
# Liste aller Files im Arbeitsverzeichnis erstellen.
setwd("Pfad/zu/Ihrem/Arbeitsverzeichnis") # Arbeitsumgebung des zuvor gespeicherten Skripts und der Ergebnisse des API-Calls
files.in.dir <- list.files(getwd())
json.only <- grep("json", files.in.dir) # Liste auf JSON-Files beschränken (falls andere Files präsent)
files.in.dir <- files.in.dir[json.only]
nfiles <- length(files.in.dir) # Objekt definieren, das Anzahl der Files enthält
# Loop zum Erzeugen eines Dataframes (df), der pro Zeile einen Verhandlungsgegenstand enthält. In die Spalten werden für die Analyse relevante Variablen geschrieben.
df <- data.frame(title = rep(NA, nfiles)) # leeren Dataframe erzeugen
for (i in 1:nfiles) {
getfile <- fromJSON(file = files.in.dir[i]) # JSON-File einlesen
df$title[i] <- getfile$meta$title # Titel des Verhandlungsgegenstandes
df$filename[i] <- files.in.dir[i] # Dateiname des Verhandlungsgegenstandes
df$citation[i] <- getfile$content$zitation # Zitation des Verhandlungsgegenstandes
df$type[i] <- getfile$content$typ # Art des Verhandlungsgegenstandes
df$doktyp[i] <- getfile$content$doktyp # Art des Verhandlungsgegenstandes
df$gp[i] <- getfile$content$gp_code # Gesetzgebungsperiode
df$bgbl[i] <- ifelse(is.null(getfile$content$status$bgbllinks[[1]]$title)==TRUE, NA, getfile$content$status$bgbllinks[[1]]$title) # Bundesgesetzblatt
df$sponsorcount[i] <- ifelse(is.null(getfile$content$names)==TRUE, NA, length(getfile$content$names)) # Anzahl der Antragsteller:innen
nphases <- length(getfile$content$phase) # Anzahl an Phasen des Gesetzgebungsprozesses (relevant für Ermittlung von Start- und Enddatum)
df$nphases[i] <- nphases
nstages <- length(getfile$content$phase[[nphases]]$stages) # Anzahl an Unter-Phasen des Gesetzgebungsprozesses (relevant für Ermittlung von Start- und Enddatum)
df$nstages[i] <- nstages
nstagesinphase3 <- ifelse(nphases<3, NA, length(getfile$content$phase[[3]]$stages)) # Anzahl an Unter-Phasen des Gesetzgebungsprozesses (relevant für Ermittlung von Start- und Enddatum)
df$nstagesinphase3[i] <- nstagesinphase3
df$startdate[i] <- ifelse(nstages==0, NA, getfile$content$phase[[1]]$stages[[1]]$date) # Einbringungsdatum/Startdatum des Gesetzgebungsprozesses
df$enddate[i] <- ifelse(nstages==0, NA, getfile$content$phase[[nphases]]$stages[[nstages]]$date) # Enddatum des Gesetzgebungsprozesses
nrbeschlusslocation1 <- ifelse(nstages==0, NA, grep("Gesetzesvorschlag in dritter Lesung <b>angenommen", getfile$content$phase)) # Phase lokalisieren, wo Gesetzesbeschluss dokumentiert ist
nrbeschlusslocation2 <- ifelse(nphases<3, NA, grep("Gesetzesvorschlag in dritter Lesung <b>angenommen", getfile$content$phase[[nrbeschlusslocation1]]$stages)) # Unter-Phase lokalisieren, wo Gesetzesbeschluss dokumentiert ist
df$nrbeschlussdatum[i] <- ifelse(nphases<3 | is.na(nrbeschlusslocation2), NA, getfile$content$phase[[nrbeschlusslocation1]]$stages[[nrbeschlusslocation2]]$date) # Beschlussdatum im Nationalrat auslesen
}
# Div. Umformatierungen von Datumsvariablen
df$startdate <- as.Date(df$startdate, "%d.%m.%Y") # Standardformat
df$enddate <- as.Date(df$enddate, "%d.%m.%Y")
df$endmonth <- as.numeric(substr(df$enddate, 6, 7))
df$startmonth <- as.numeric(substr(df$startdate, 6, 7))
df$endyear <- as.numeric(substr(df$enddate, 1, 4))
df$nrbeschlussdatum <- as.Date(df$nrbeschlussdatum, "%d.%m.%Y")
df$nrbeschlussjahr <- as.numeric(substr(df$nrbeschlussdatum, 1, 4))
# Variable bgbl.TF: TRUE/FALSE: Existiert ein Eintrag im Bundesgesetzblatt (d.h. wurde der Gesetzesvorschlag Gesetz)? (NB: keine perfekte Identifikation von Beschlüssen!)
df$bgbl.TF <- !is.na(df$bgbl)
# Variable für Dateien mit "-U1.json" oder "-E1.json" im Dateinamen erzeugen (werden bei Analyse ausgeschlossen, handelt sich meist um Beilagen)
df$u1files <- 0
df$u1files[grep("-U1.json", df$filename)] <- 1
df$u1files[grep("-E1.json", df$filename)] <- 1 # nur ein Fall
# Vereinfachte Variable Art des Verhandlungsgegenstandes => nur Gesetzesanträge (d.h. keine Staatsverträge, Bundesrechnungsabschlüsse, Beschlüsse Eur. Rat/Rat der EU, Regierungsvorlagen für Volksbefragungen)
zuordnung <- c("RV" = "RV", "A" = "A", "BUA" = "BUA", "BRA" = "BUA", "EBR" = "EBR", "GABR" = "GABR", "GABR13" = "GABR")
df$doktyp2 <- zuordnung[df$doktyp]
### Überprüfung: Einsprüche des Bundesrates (Einsprüche, auf die kein Beharrungsbeschluss folgt, müssen aus Gesamtanalyse herausgenommen werden)
# setwd("")
# ebr <- data.frame(filename = df$filename[df$doktyp2=="EBR"])
# for (i in 1:nrow(ebr)) {
# ebrfile <- fromJSON(file = ebr$filename[i])
# # print(grep("Wiederholung des Gesetzesbeschlusses", ebrfile))
# ebr$beschlusswiederholung[i] <- length(grep("Wiederholung des Gesetzesbeschlusses", ebrfile))
# ebr$title[i] <- ebrfile$meta$title
# dashlocations <- unlist(gregexpr("/", ebrfile$content$reference[[1]]$url))
# ebr$refgp[i] <- substr(ebrfile$content$reference[[1]]$url, dashlocations[2]+1, dashlocations[3]-1)
# ebr$refart[i] <- substr(ebrfile$content$reference[[1]]$url, dashlocations[3]+1, dashlocations[4]-1)
# ebr$refnr[i] <- substr(ebrfile$content$reference[[1]]$url, dashlocations[4]+1, nchar(ebrfile$content$reference[[1]]$url))
# }
# ebr$refid <- paste0(ebr$refnr, "/", ebr$refart)
# ebr$refid <- gsub("/I", " d.B.", ebr$refid)
# erfolgreicheeinsprueche_id <- ebr$refid[ebr$beschlusswiederholung==0]
# erfolgreicheeinsprueche_gp <- ebr$refgp[ebr$beschlusswiederholung==0]
# # Überprüfung, ob Einsprüche ohne Beharrungsbeschluesse als Gesetzesbeschluesse erfasst sind ==> wenn alles FALSE, dann ok
# for (i in 1:length(erfolgreicheeinsprueche_id)) {
# print(df$bgbl.TF[df$citation == erfolgreicheeinsprueche_id[i] & df$gp == erfolgreicheeinsprueche_gp[i]]) ### wenn FALSE, dann ist alles gut
# }
# Loop zur Erzeugung von Variablen über Antragsteller:innen (bis zur Anzahl der Variable df$sponsorcount für jede kreierte Variable)
maxspons <- max(df$sponsorcount, na.rm = T) # Maximalzahl der in den Daten erfassten Antragsteller:innen (nötig für Loop)
for (i in 1:maxspons) { # Loop zum Erzeugen der (leeren) Variablen
df[[paste("name", i, sep="")]] <- NA # Variable: Name d. Antragsteller:in
df[[paste("id", i, sep="")]] <- NA # Variable: ID d. Antragsteller:in
df[[paste("party", i, sep="")]] <- NA # Variable: Klub der Antragsteller:in (Ein-Buchstaben-Kürzel: S, V, F, G, ...)
}
# Loop zum Befüllen der Antragsteller:innen-Variablen
for (j in 1:maxspons) {
counter <- which(df$sponsorcount>(j-1) & df$doktyp2=="A") # Counter auf Selbständige Anträge & Anzahl Antragsteller:innen > j-1 begrenzen
for (i in counter) {
getfile <- fromJSON(file = df$filename[i])
df[[paste("party", j, sep="")]][i] <- getfile$content$names[[j]]$frak_code
df[[paste("id", j, sep="")]][i] <- substr(getfile$content$names[[j]]$url, 9, nchar(getfile$content$names[[j]]$url))
df[[paste("name", j, sep="")]][i] <- getfile$content$names[[j]]$name
}
}
# Alle einbringenden Fraktionen in eine String-Variable zusammenfassen:
df$sponsor.all <- NA
df$sponsor.all[df$sponsorcount==1 & !is.na(df$sponsorcount)] <- df$party1[df$sponsorcount==1 & !is.na(df$sponsorcount)]
df$sponsor.all[df$sponsorcount==2 & !is.na(df$sponsorcount)] <- paste(df$party1[df$sponsorcount==2 & !is.na(df$sponsorcount)], df$party2[df$sponsorcount==2 & !is.na(df$sponsorcount)], sep="")
df$sponsor.all[df$sponsorcount==3 & !is.na(df$sponsorcount)] <- paste(df$party1[df$sponsorcount==3 & !is.na(df$sponsorcount)], df$party2[df$sponsorcount==3 & !is.na(df$sponsorcount)], df$party3[df$sponsorcount==3 & !is.na(df$sponsorcount)], sep="")
df$sponsor.all[df$sponsorcount==4 & !is.na(df$sponsorcount)] <- paste(df$party1[df$sponsorcount==4 & !is.na(df$sponsorcount)], df$party2[df$sponsorcount==4 & !is.na(df$sponsorcount)], df$party3[df$sponsorcount==4 & !is.na(df$sponsorcount)], df$party4[df$sponsorcount==4 & !is.na(df$sponsorcount)], sep="")
df$sponsor.all[df$sponsorcount==5 & !is.na(df$sponsorcount)] <- paste(df$party1[df$sponsorcount==5 & !is.na(df$sponsorcount)], df$party2[df$sponsorcount==5 & !is.na(df$sponsorcount)], df$party3[df$sponsorcount==5 & !is.na(df$sponsorcount)], df$party4[df$sponsorcount==5 & !is.na(df$sponsorcount)], df$party5[df$sponsorcount==5 & !is.na(df$sponsorcount)], sep="")
df$sponsor.all[df$sponsorcount==6 & !is.na(df$sponsorcount)] <- paste(df$party1[df$sponsorcount==6 & !is.na(df$sponsorcount)], df$party2[df$sponsorcount==6 & !is.na(df$sponsorcount)], df$party3[df$sponsorcount==6 & !is.na(df$sponsorcount)], df$party4[df$sponsorcount==6 & !is.na(df$sponsorcount)], df$party5[df$sponsorcount==6 & !is.na(df$sponsorcount)], df$party6[df$sponsorcount==6 & !is.na(df$sponsorcount)], sep="")
# Partei-Strings in eindeutige Reihenfolge bringen (z.B. "VSF", "FVS", "SFV", etc. werden allesamt zu "SVF")
ptyLETTERS <- c("S", "V", "F", "G", "L", "B", "T", "N", "J")
for (i in 1:nrow(df)) {
df$sponsors[i] <- str_c(ptyLETTERS[sort(match(str_split(df$sponsor.all[i], "")[[1]], ptyLETTERS))], collapse="")
}
# Neue Variable, die nur Antragsteller:innen beschlossener Gesetze aus selbständigen Anträgen erfasst
df$sponsorsA <- df$sponsors
df$sponsorsA[is.na(df$nrbeschlussdatum) | df$doktyp2!="A"] <- NA
# Erzeuge Vektor zu Tagungen (Tagung = Sitzungsperiode von September bis Juli, d.h. vom Ende einer bis zum Beginn der nächsten Sommerpause)
tagung <- paste(1996:2023, "/", substr(1997:2024, 3, 4), sep="")
tagung.start.ende <- as.Date(c("19.09.1996", "11.07.1997", "18.09.1997", "17.07.1998", "17.09.1998", "16.07.1999", "29.10.1999", "07.07.2000",
"05.09.2000", "06.07.2001", "26.09.2001", "11.07.2002", "19.09.2002", "10.07.2003", "02.09.2003", "09.07.2004",
"22.09.2004", "08.07.2005", "21.09.2005", "14.07.2006", "12.09.2006", "06.07.2007", "27.09.2007", "10.07.2008",
"12.09.2008", "10.07.2009", "01.09.2009", "09.07.2010", "22.09.2010", "08.07.2011", "13.09.2011", "06.07.2012",
"19.09.2012", "06.07.2013", "17.09.2013", "10.07.2014", "02.09.2014", "17.07.2015", "01.09.2015", "08.07.2016",
"13.09.2016", "13.07.2017", "20.09.2017", "05.07.2018", "07.09.2018", "03.07.2019", "19.09.2019", "09.07.2020",
"14.09.2020", "19.07.2021", "22.09.2021", "08.07.2022", "13.09.2022", "10.07.2023", "12.09.2023", "09.07.2024"), "%d.%m.%Y")
start.tagung <- tagung.start.ende[seq(1, 55, 2)]
ende.tagung <- tagung.start.ende[seq(2, 56, 2)]
# Tagungsvariable im Dataframe erzeugen, die Tagung des Beschlusses ausweist
df$tagung <- NA
for (i in 1:length(tagung)) {
df$tagung[start.tagung[i] <= df$nrbeschlussdatum & ende.tagung[i] >= df$nrbeschlussdatum] <- tagung[i]
}
# Variable amtierende Regierung: SPÖ-ÖVP (SV), ÖVP-FPÖ/BZÖ (VF), ÖVP-Grüne (VG) etc. (anhand des Einbringungsdatums):
df$reg <- NA
df$reg[df$startdate < as.Date("04.02.2000", "%d.%m.%Y")] <- "SV" # SPÖ-ÖVP-Koalition vor Februar 2000
df$reg[df$startdate >= as.Date("04.02.2000", "%d.%m.%Y") & df$startdate < as.Date("03.10.2006", "%d.%m.%Y")] <- "VF" # ÖVP-FPÖ/BZÖ-Koalitionen (Schüssel I & II)
df$reg[df$startdate >= as.Date("03.10.2006", "%d.%m.%Y") & df$startdate < as.Date("11.01.2007", "%d.%m.%Y")] <- "VB" # ÖVP-BZÖ-Koalition (erst ab Oktober 2006 ist Fraktionskürzel für BZÖ "B")
df$reg[df$startdate >= as.Date("11.01.2007", "%d.%m.%Y") & df$startdate < as.Date("18.12.2017", "%d.%m.%Y")] <- "SV" # SPÖ-ÖVP-Koalitionen zwischen Jänner 2007 und Dezember 2017
df$reg[df$startdate >= as.Date("18.12.2017", "%d.%m.%Y") & df$startdate < as.Date("22.05.2019", "%d.%m.%Y")] <- "VF" # ÖVP-FPÖ-Koalition ab Dezember 2017 (Kurz I)
df$reg[df$startdate >= as.Date("22.05.2019", "%d.%m.%Y") & df$startdate < as.Date("03.06.2019", "%d.%m.%Y")] <- "V" # ÖVP-Minderheitsregierung (Kurz, Löger)
df$reg[df$startdate >= as.Date("03.06.2019", "%d.%m.%Y") & df$startdate < as.Date("07.01.2020", "%d.%m.%Y")] <- "Bierlein" # Bundesregierung Bierlein ab Juni 2019
df$reg[df$startdate >= as.Date("07.01.2020", "%d.%m.%Y") & df$startdate < as.Date("23.10.2024", "%d.%m.%Y")] <- "VG" # ÖVP-Grüne-Koalition ab Jänner 2020
# Variable Regierungsantrag: Selbständige Anträge, die von beiden Koalitionsparteien ohne Oppositionsbeteiligung eingebracht wurden:
df$regA <- as.numeric(df$sponsorsA==df$reg)
####################
### AUSWERTUNGEN ###
####################
# Tabelle Gesetzesursprung pro Tagung (nur Gesetzesbeschlüsse; exkl. Anhänge, RV-Änderungen etc. (U1-Files):
table.mat <- table(df$doktyp2[df$u1files == 0], df$tagung[df$u1files == 0])
table.mat <- table.mat[rownames(table.mat) != "GABR", ]
# Tabelle exportieren:
setwd("Pfad/zu/Ihrem/Arbeitsverzeichnis") # Speicherung der Tabellen und Grafiken
write.table(t(table.mat), "showcase2_datenexport1.csv", sep=";", col.names = NA)
# Tabelle Selbständige Anträge gesplittet in reine Regierungsanträge (1) und andere (0):
table.mat2 <- table(df$regA, df$tagung)
row.names(table.mat2) <- c("Andere Antraege", "Koalitionsantraege")
# Tabelle exportieren:
write.table(t(table.mat2), "showcase2_datenexport2.csv", sep=";", col.names = NA)
# Grafik 1 erstellen:
jpeg("SC2_grafik1.jpg", height = 12, width = 22, units = "cm", res=600)
par(mar=c(4.5, 8, 4, 1))
bplot <- barplot(rowSums(table.mat[c(3, 1, 2), ]), col=c("indianred1", "darkseagreen3", "cornflowerblue"), border="white",
names.arg = c("Regierungsvorlagen", "Selbst. Antrag", "Ausschussantrag"), horiz = TRUE, las=1, cex.names=0.9, cex.axis=0.9,
xlim = c(0, 2500),
main = "Ursprung von Gesetzesbeschlüssen\n(September 1996 bis Juli 2024)", xlab="Anzahl Gesetzesbeschlüsse")
text(rowSums(table.mat[c(3, 1, 2), ]) / 2, bplot, rowSums(table.mat[c(3, 1, 2), ]))
dev.off()
# Grafik 2 erstellen:
jpeg("SC2_grafik2.jpg", height = 12, width = 18, units = "cm", res=600)
par(mar=c(4.5, 4, 2, 1))
bplot <- barplot(table.mat[c(3, 1, 2), ], ylim=c(0, 200), las=2, col=c("indianred1", "darkseagreen3", "cornflowerblue"), beside=TRUE,
border="white", cex.names=0.9, cex.axis=0.9, main = "Ursprung von Gesetzesbeschlüssen im Zeitverlauf",
ylab="Anzahl Gesetzesbeschlüsse")
legend(bplot[1], 200, c("Regierungsvorlagen", "Selbständige Anträge", "Ausschussanträge"), cex=0.8, bty="n",
fill = c("indianred1", "darkseagreen3", "cornflowerblue"), border = "white")
dev.off()
# Grafik 3 erstellen:
jpeg("SC2_grafik3.jpg", height = 12, width = 18, units = "cm", res=600)
par(mar=c(4.5, 3, 4, 1))
bplot2 <- barplot(table.mat2[2:1, ], ylim=c(0, 120), las=2, col=c("indianred1", "darkseagreen3"), border="white", cex.names=0.9, cex.axis=0.9,
main = "Gesetzesbeschlüsse auf Basis von Selbständigen Anträgen\nnach einbringenden Klubs", beside = TRUE, ylab="Anzahl Gesetzesbeschlüsse")
legend(bplot2[1], 120, c("Eingebracht von Klubs beider Regierungsparteien", "Eingebracht von Klubs in allen anderen Konstellationen"), cex=0.8, bty="n",
fill = c("indianred1", "darkseagreen3"), border = "white")
dev.off()