Vorbemerkungen

Dieses Dokument beschreibt die Vorprozessierung und explorative Analyse des Datensatzes, der Grundlage des auf srf.ch veröffentlichten Artikel Wenn fehlende Parlamentarier den Unterschied machen ist.

SRF Data legt Wert darauf, dass die Datenvorprozessierung und -Analyse nachvollzogen und überprüft werden kann. SRF Data glaubt an das Prinzip offener Daten, aber auch offener und nachvollziehbarer Methoden. Zum anderen soll es Dritten ermöglicht werden, auf dieser Vorarbeit aufzubauen und damit weitere Auswertungen oder Applikationen zu generieren.

Die Endprodukte des vorliegenden Scripts, neben der vorliegenden explorativen Analyse, sind folgende Files:

  • cases.xls: Der Datensatz, der alle Fälle aufzeigt, bei denen Fraktionen Abstimmungen verloren, die sie hätten gewinnen können, wenn keine Fraktionsmitglieder gefehlt hätten. In diesem Datensatz kann eine bestimmte Abstimmung mehrfach vorkommen, wenn mehrere Fraktionen die Chance gehabt hätten, den Ausgang einer Abstimmung zu beeinflussen.
  • cases_unique.xls: Der Datensatz weist jede der betroffenen Abstimmungen auf, unabhängig von und ohne Angabe zur betroffenen Fraktion.

R-Script & Daten

Die Vorprozessierung und Analyse wurde im Statistikprogramm R vorgenommen. Das zugrunde liegende Script sowie die prozessierten Daten können unter diesem Link heruntergeladen werden. Durch Ausführen von main.Rmd kann der hier beschriebene Prozess nachvollzogen und der für den Artikel verwendete Datensatz generiert werden. Dabei werden Daten aus dem Ordner input eingelesen und Ergebnisse in den Ordner output geschrieben.

SRF Data verwendet das rddj-template von Timo Grossenbacher als Grundlage für seine R-Scripts. Entstehen bei der Ausführung dieses Scripts Probleme, kann es helfen, die Anleitung von rddj-template zu studieren.

Debug-Informationen: This report was generated on 2018-12-07 10:23:59. R version: 3.4.4 on x86_64-pc-linux-gnu. For this report, CRAN packages as of 2018-01-01 were used.

GitHub

Der Code für die vorliegende Datenprozessierung ist auf https://github.com/srfdata/2018-02-parlament-absenzen zur freien Verwendung verfügbar.

Lizenz

Creative Commons Lizenzvertrag
2018-02-parlament-absenzen von SRF Data ist lizenziert unter einer Creative Commons Namensnennung - Weitergabe unter gleichen Bedingungen 4.0 International Lizenz.

Weitere Projekte

Code & Daten von SRF Data sind unter http://srfdata.github.io verfügbar.

Haftungsausschluss

Die veröffentlichten Informationen sind sorgfältig zusammengestellt, erheben aber keinen Anspruch auf Aktualität, Vollständigkeit oder Richtigkeit. Es wird keine Haftung übernommen für Schäden, die durch die Verwendung dieses Scripts oder der daraus gezogenen Informationen entstehen. Dies gilt ebenfalls für Inhalte Dritter, die über dieses Angebot zugänglich sind.

Originalquelle

Die Originalquelle ist die Abstimmungsdatenbank des Parlamentsdienstes der Schweiz. Dort veröffentlicht der Parlamentsdienst Namenslisten mit dem Abstimmungsverhalten aller Mitglieder des Nationalrats seit Beginn der 49. Legislaturperiode.

Vorbereitungen

## [1] "package package:rmarkdown detached"

Packages definieren

# von https://mran.revolutionanalytics.com/web/packages/checkpoint/vignettes/using-checkpoint-with-knitr.html
# alle Packages, die nicht gebraucht werden,
# können hier entfernt werden (auskommentieren reicht nicht!)
# Wichtig: wenn neues Package installiert werden soll,
# scanForPackages = T setzen im checkpoint() call im nächsten Chunk
# tidyverse: see https://blog.rstudio.org/2016/09/15/tidyverse-1-0-0/
cat(
  "
library(rstudioapi) # temporary bugfix, might be removed when renewing date
library(tidyverse) # ggplot2, dplyr, tidyr, readr, purrr, tibble
library(magrittr) # pipes
library(stringr) # string manipulation
library(readxl) # excel
library(WriteXLS) #write excel
library(scales) # scales for ggplot2
library(jsonlite) # json
library(forcats) # easier factor handling,
library(lintr) # code linting, auf keinen Fall entfernen ;-)
library(styler) # code formatting
library(rmarkdown) # muss für automatisches knitting 
# in deploy.sh eingebunden werden",
  file = "manifest.R"
)

Packages installieren

# if checkpoint is not yet installed, install it (for people using this
# system for the first time)
if (!require(checkpoint)) {
  if (!require(devtools)) {
    install.packages("devtools", repos = "http://cran.us.r-project.org")
    require(devtools)
  }
  devtools::install_github("RevolutionAnalytics/checkpoint",
                           ref = "v0.3.2", # could be adapted later,
                           # as of now (beginning of July 2017
                           # this is the current release on CRAN)
                           repos = "http://cran.us.r-project.org")
  require(checkpoint)
}
## Loading required package: checkpoint
## 
## checkpoint: Part of the Reproducible R Toolkit from Microsoft
## https://mran.microsoft.com/documents/rro/reproducibility/
# nolint start
if (!dir.exists("~/.checkpoint")) {
  dir.create("~/.checkpoint")
}
# nolint end
# install packages for the specified CRAN snapshot date
checkpoint(snapshotDate = package_date,
           project = path_to_wd,
           verbose = T,
           scanForPackages = T,
           use.knitr = F,
           R.version = R_version)
## Scanning for packages used in this project
## rmarkdown files found and will not be parsed. Set use.knitr = TRUE
## - Discovered 13 packages
## All detected packages already installed
## checkpoint process complete
## ---
rm(package_date)

Packages laden

source("manifest.R")
## ── Attaching packages ────────────────────────────────── tidyverse 1.2.1 ──
## ✔ ggplot2 2.2.1     ✔ purrr   0.2.4
## ✔ tibble  1.4.1     ✔ dplyr   0.7.4
## ✔ tidyr   0.7.2     ✔ stringr 1.2.0
## ✔ readr   1.1.1     ✔ forcats 0.2.0
## ── Conflicts ───────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## 
## Attaching package: 'magrittr'
## The following object is masked from 'package:purrr':
## 
##     set_names
## The following object is masked from 'package:tidyr':
## 
##     extract
## 
## Attaching package: 'scales'
## The following object is masked from 'package:purrr':
## 
##     discard
## The following object is masked from 'package:readr':
## 
##     col_factor
## 
## Attaching package: 'jsonlite'
## The following object is masked from 'package:purrr':
## 
##     flatten
unlink("manifest.R")
sessionInfo()
## R version 3.4.4 (2018-03-15)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Ubuntu 18.04.1 LTS
## 
## Matrix products: default
## BLAS: /opt/R/R-3.4.4/lib64/R/lib/libRblas.so
## LAPACK: /opt/R/R-3.4.4/lib64/R/lib/libRlapack.so
## 
## locale:
##  [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
##  [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
##  [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
##  [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
##  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
## [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
##  [1] rmarkdown_1.8    styler_1.0.0     lintr_1.0.2      jsonlite_1.5    
##  [5] scales_0.5.0     WriteXLS_4.0.0   readxl_1.0.0     magrittr_1.5    
##  [9] forcats_0.2.0    stringr_1.2.0    dplyr_0.7.4      purrr_0.2.4     
## [13] readr_1.1.1      tidyr_0.7.2      tibble_1.4.1     ggplot2_2.2.1   
## [17] tidyverse_1.2.1  rstudioapi_0.7   checkpoint_0.4.0
## 
## loaded via a namespace (and not attached):
##  [1] reshape2_1.4.3   haven_1.1.0      lattice_0.20-35  colorspace_1.3-2
##  [5] htmltools_0.3.6  yaml_2.1.16      rlang_0.1.6      pillar_1.0.1    
##  [9] foreign_0.8-69   glue_1.2.0       modelr_0.1.1     bindrcpp_0.2    
## [13] bindr_0.1        plyr_1.8.4       munsell_0.4.3    gtable_0.2.0    
## [17] cellranger_1.1.0 rvest_0.3.2      psych_1.7.8      evaluate_0.10.1 
## [21] knitr_1.18       rex_1.1.2        parallel_3.4.4   broom_0.4.3     
## [25] Rcpp_0.12.14     backports_1.1.2  mnormt_1.5-5     hms_0.4.0       
## [29] digest_0.6.13    stringi_1.1.6    grid_3.4.4       rprojroot_1.3-1 
## [33] cli_1.0.0        tools_3.4.4      lazyeval_0.2.1   crayon_1.3.4    
## [37] pkgconfig_2.0.1  xml2_1.1.1       lubridate_1.7.1  assertthat_0.2.0
## [41] httr_1.3.1       R6_2.2.2         nlme_3.1-131.1   compiler_3.4.4

Zusätzliche Scripts laden

# falls Logik auf andere Scripts ausgelagert werden soll (z.B. der Übersichtlichkeit halber), hier einkommentieren

Vorprozessierung der Daten

Daten einlesen und transformieren

Für jede Abstimmung im Nationalrat liegen Daten zum Abstimmungsverhalten der einzelnen Ratsmitglieder vor. Der Parlamentsdienst stellt die Daten sessionsweise als xlsx-Dateien zur Verfügung. Daraus sollen die oben beschriebenen Datensätze erstellt werden.

# Leere Datensätze erstellen, um mit dem Loop aufzufüllen
cases <- data.frame()
abstimmungen <- data.frame()
parlamentarier <- data.frame()

# Loop, der die einzelnen xlsx-Files jeder Session einliest und verarbeitet
temp <- list.files(
  path = "input",
  pattern = "*.xlsx"
)

# Beginn des Loops
for (i in temp) {
  
  # Schritt 1: Abstimmung und Abstimmungsverhalten der Parlamentarier einlesen und zu einem Datensatz zusammenfassen

  # xlsx-Dateien zu Abstimmungen einlesen und Bereich auswählen
  votes_loop <- read_excel(
    paste(
      "input/",
      i,
      sep = ""
    ),
    col_names = TRUE,
    range = anchored("A9", dim = c(NA, NA))
  )
  
  # Zur einfacheren Verarbeitung nur das Datum der Abstimmungen speichern
  votes_loop %<>%
    mutate(VoteDate = str_sub(VoteDate, 0, 10))

  # Liste aller Abstimmungen wird bei jedem Durchgang des Loops ergänzt
  abstimmungen %<>% 
    bind_rows(
      select(votes_loop, 
             VoteDate:VoteSubmissionText, 
             Decision:Präsident) 
      )
  # Transformieren des Datensatzes, alle Parlamentarier und Abstimmungen einzeln auflisten
  votes_loop %<>%
    gather(
      BioId,
      VoteDecision,
      13:(ncol(votes_loop) - 7)
    )
  
  
  # Schritt 2: Ein Datensatz aller Parlamentarier erstellen

  # xlsx-Dateien zu Parlamentariern einlesen, Bereich auswählen und neu benennen
  parl_loop <-
      read_excel(
        paste(
          "input/",
          i,
          sep = ""
          ),
        col_names = F,
        range = anchored("M1", dim = c(8, NA))
        )

    parl_loop %<>%
      t() %>%
      as.data.frame() %>%
      rename(
        Id = V1,
        BioId = V2,
        CouncillorName = V3,
        Rat = V4,
        Fraktion = V5,
        Kanton = V6,
        Geburtsdatum = V7,
        Vereidigungsdatum = V8) %>%
      mutate(BioId = as.character(BioId),
             Id = as.character(Id),
             CouncillorName = as.character(CouncillorName),
             Geburtsdatum = as.character(Geburtsdatum),
             Vereidigungsdatum = as.character(Vereidigungsdatum),
             Fraktion = as.character(Fraktion))

  # Datensatz aller Parlamentarier wird bei jedem Durchgang des Loops ergänzt
   parlamentarier %<>%
    bind_rows(
      parl_loop)


  # Schritt 3: Das Abstimmungsverhalten der Fraktionen zu jeder einzelnen Abstimmung berechnen Datensatz zu Abstimmungen und Parlamentarieren mergen und anpassen
  data_merged <-
    full_join(
    votes_loop,
    parl_loop,
    by = "BioId"
  )

  # Datensatz spezifieren, rekodieren und Abstimmungen nach Fraktionen gruppieren
  data_merged %<>%
    select(
      BioId,
      Id,
      Name = CouncillorName,
      Rat = Rat.x,
      Fraktion,
      Kanton,
      Date = VoteDate,
      Kommission,
      Dept.,
      AffairId,
      AffairTitle,
      VoteId = VoteRegistrationNumber,
      VoteMeaningYes,
      VoteMeaningNo,
      DivisionText,
      SubmissionText = VoteSubmissionText,
      VoteDecision,
      RatJa = Ja,
      RatNein = Nein,
      RatEnth = `Enth.`,
      RatEntsch = `Entschuldigung gem. Art. 57 Abs. 4`,
      RatNicht = `Hat nicht teilgenommen`,
      RatPres = Präsident) %>%
    mutate(Ja = case_when(VoteDecision ==
                            "Ja" ~ 1, TRUE ~ 0),
           Nein = case_when(VoteDecision ==
                              "Nein" ~ 1, TRUE ~ 0),
           Ent = case_when(VoteDecision ==
                             "Enthaltung" ~ 1, TRUE ~ 0),
           Abw = case_when(VoteDecision ==
                             "Hat nicht teilgenommen" ~ 1, TRUE ~ 0),
           AbwEnt = case_when(VoteDecision ==
                                "Entschuldigt" ~ 1, TRUE ~ 0),
           Pres = case_when(VoteDecision ==
                              "Der Präsident stimmt nicht" ~ 1, TRUE ~ 0)) %>%
    group_by(VoteId, Fraktion) %>%
    summarise(
      Ja = sum(Ja),
      Nein = sum(Nein),
      Enthaltung = sum(Ent),
      Abwesend = sum(Abw),
      Entschuldigt = sum(AbwEnt),
      Präsident = sum(Pres),
      RatJa = mean(RatJa),
      RatNein = mean(RatNein),
      RatEnthaltung = mean(RatEnth),
      RatEntschuldigt = mean(RatEntsch),
      RatAbwesend = mean(RatNicht)
    )


  # Schritt 4: Potenzial berechnen, um die für die Auswertung relevanten Fälle zu finden

  # Das Potenzial A ist die Differenz zwischen der Anzahl der fehlenden Personen einer Fraktion und der Stimmendifferenz im Rat. Das Potenzial gibt darüber Auskunft, ob eine Fraktion eine Abstimmung hätte gewinnen können, wenn alle Mitglieder anwesend gewesen wären. In der Regel trifft dies zu für Fraktionen und Abstimmungen mit einem Potenzial A > 0. Allerdings gibt es 2 Ausnahmen, bei denen auch Potenzial A = 0 ausreicht. Aus diesem Grund werden vorerst auch diese Fälle mitberücksichtigt. (Wird später für die Berechnung von Potenzial B und Potenzial C relevant.)

  # Berechnen, wie eine Abstimmungen aus Sicht der Fraktionsmehrheit ausging
  data_merged %<>%
    mutate(RatDifferenz = RatJa - RatNein,
           Fehlende = Abwesend, #+ Entschuldigt,
           PotentialA = Fehlende - abs(RatDifferenz)) %>%
    mutate(Ausgang = ( Ja / ( Ja + Nein ) - 0.5 ) /
             ( RatJa / ( RatJa + RatNein )  - 0.5 ) ) %>%
    mutate(Ausgang = case_when(Ausgang <= 0 ~ "Verloren",
                               TRUE ~ "Gewonnen")) %>%
    filter(PotentialA > -1 &
             Ausgang == "Verloren")

  # Fälle, in denen das Potenzial A mindestens 0 beträg und die Abstimmung aus Sicht der Fraktionsmehr verloren wurde, an den Datensatz "cases" anhängen
  cases %<>%
    bind_rows(data_merged)
}

# Nicht weiter benötigte Datensätze löschen
rm(data_merged)
rm(votes_loop)
rm(parl_loop)

# Rekodieren der CVP-Fraktion
cases %<>% 
  mutate(Fraktion = recode(Fraktion, "CE" = "C"))

# Jeden Parlamentarier nur einmal im Datensatz
parlamentarier %<>% 
  distinct(BioId, .keep_all = T)

Spezialfälle berechnen

Eine Abstimmungen gilt dann als verschenkt, wenn die Anzahl der Abwesenden einer Fraktion grösser ist als die totale Stimmendifferenz (d.h. “Potenzial A” > 0). Wie bereits erwähnt, gibt es aber zwei Ausnahmen:

1. Abstimmungen mit Stichentscheid

Abstimmungen, bei denen der Präsident aufgrund von Stimmengleichheit im Rat den Stichentscheid fällt. Ist die Anzahl der Abwesenden in diesem Fall gleich gross wie die totale Stimmendifferenz (“Potenzial A” = 0), gilt die Abstimmung dennoch als verschenkt. Obwohl die Stimmendifferenz = 1, hätte 1 zusätzliches Fraktionsmitglied schon gereicht, um die Abstimmung zu gewinnen (da der Präsident dann nicht mitgestimmt hätte). Für diese Fälle gilt “Potenzial B” = 1.

# Dazu wird zuerst berechnet, in welchen Abstimmungen der Präsident den Stichentscheid fällte. (Der Präsident kann bei einer Reihe von besonderen Abstimmungen mitstimmen, nicht nur wenn im Rat Stimmenparität herrscht. Weil wir aber nur die Stichentscheide wollen, suchen wir also all jene Entscheide, bei denen der Präsident mitstimmte und die Stimmendifferenz 1 beträgt.)

abstimmungen %<>%
  mutate(Differenz = Ja - Nein)

# Subset erstellen für Abstimmungen, die der Präsident per Stichentscheid entschied
abstimmungen_pres <- abstimmungen %>%
  select(
    Dept.,
    AffairId,
    VoteRegistrationNumber,
    Ja,
    Nein,
    Differenz,
    Präsident
  )

abstimmungen_pres %<>% 
  mutate(Differenz = abs(Differenz))

abstimmungen_pres %<>% 
  filter(Differenz == 1 &
           Präsident == "True") %>%
  select(
    VoteRegistrationNumber,
    PresDec = Präsident
  )

# In einer neuen Variable PotentialB wird vermerkt, ob ein Stichentscheid des Präsidenten stattfand
cases %<>% 
  left_join(
    abstimmungen_pres,
    by = c("VoteId" = "VoteRegistrationNumber")) %>%
  mutate(PotentialB = case_when(PresDec == "True" &
                               PotentialA == 0  ~ 1, 
                               TRUE ~ 0))
rm(abstimmungen_pres)

2. Fraktionszugehörigkeit des NR-Präsidenten

Ein weiterer Spezialfall sind Abstimmungen, bei denen die Anzahl der Abwesenden einer Fraktion gleich gross ist wie die totale Stimmendifferenz (d.h. “Potenzial A” = 0) und der NR-Präsident der eigenen Fraktion angehört. Wären in diesem Fall alle Fraktionsteilnehmer anwesend, entschiede der Präsident per Stichentscheid. Da dieser der eigenen Fraktion angehört, wird davon ausgegangen, dass er ebenfalls im Sinne der Fraktionsmehrheit stimmt. In diesem Fall gilt “Potenzial C” = 1.

# Diese Fälle werden in der Variable PotentialC vermerkt.
cases %<>%
  mutate(PotentialC = case_when(Präsident == 1  ~ 1, 
                               TRUE ~ 0))

Spezialfälle berücksichtigen

Erst die Variable PotentialTotal beinhaltet alle Spezialfälle. Weist eine Abstimmung für eine Fraktion also eine Gesamtpotenzial von mind. 1 auf, gilt die Abstimmung als verschenkte Chance. Es können also alle Abstimmungen und Fraktionen entfernt werden, bei denen das PotentialTotal 0 oder kleiner ist.

# Variable PotentialTotal berechnen und berücksichtigen
cases %<>%
  mutate(PotentialTotal = 
           PotentialA + 
           PotentialB + 
           PotentialC) %>%
  filter(PotentialTotal > 0)

Datensatz vereinheitlichen

# Datensatz zu "verschenkten Abstimmungen" mit Zusatzdaten zu Abstimmungen (Titel, Text, etc.) kombinieren
abstimmungen_t <- abstimmungen %>%
  select(
    Date = VoteDate,
    Kommission,
    Dept = `Dept.`,
    `AffairId`,
    Title = AffairTitle,
    VoteMeaningYes,
    VoteMeaningNo,
    DivisionText,
    VoteSubmissionText,
    VoteId = VoteRegistrationNumber
  )

cases %<>% left_join(
  abstimmungen_t,
  by = "VoteId"
)
rm(abstimmungen_t)

# Umbrüche entfernen
cases %<>%
  mutate(Title = str_replace_all(Title, "[\r\n]", " "),
         VoteMeaningYes = str_replace_all(VoteMeaningYes, "[\r\n]", " "),
         VoteMeaningNo = str_replace_all(VoteMeaningNo, "[\r\n]", " "),
         DivisionText = str_replace_all(DivisionText, "[\r\n]", " "),
         VoteSubmissionText = str_replace_all(VoteSubmissionText, "[\r\n]", 
                                              " "))

Threshold für Einigkeit

Die Analyse basiert auf der Annahme, dass die abwesenden Parlamentarier gleich stimmen wie die Mehrheit der anwesenden Fraktionsmitglieder. Diese Annahme ist aber nur realistisch, wenn es in der Fraktion klare Mehrheitsverhältnisse gibt. Mit dem Threshold wird festgelegt, wie gross die Mehrheit in Prozent mindestens sein soll, damit der Fall in der Analyse berücksichtigt wird.

# Der Threshold wird bei 75% angelegt.
ts1 <- 75
cases %<>%
  mutate(JaAnteil = Ja / (Ja + Nein)) %>%
  filter(JaAnteil > (ts1 / 100) | 
           JaAnteil < (100 - ts1) / 100)

Es gibt auch Fälle, wo sich ein Teil der Fraktion der Stimme enthält. Je grösser der Anteil der anwesenden Fraktionsmitglieder, der sich einer Stimme enthält, desto unwahrscheinlicher wird die Annahme, dass die abwesendenen Mitglieder mit der stimmenden Fraktionsmehrheit gestimmt hätten. Vielmehr hätten wohl auch sie sich ihrer Stimme enthalten. Deshalb wird ein Threshold gesetzt, der bestimmt, wie gross der Anteil der Enthaltungen maximal sein darf, bevor eine Abstimmung nicht mehr berücksichtigt wird.

# Der Threshold wird bei 25% angelegt.
ts2 <- 25
cases %<>%
  mutate(EnthAnteil = Enthaltung / (Ja + Nein + Enthaltung)) %>%
  filter(EnthAnteil < ( ts2 / 100) )

Datensätze speichern

# Das File "cases.xls" enthält alle Fälle, in denen Fraktionen wegen Schwänzern Abstimmungen verloren, die sie eigentlich hätten gewinnen können (inkl. Link zum offiziellem Abstimmungsprotokoll).
cases %<>%
  mutate(legislatur = case_when(VoteId < 12604 ~ 49,
                                VoteId >= 12604 ~ 50)) %>%
  mutate(URL = paste(
    "https://www.parlament.ch/poly/Abstimmung/",
    legislatur,
    "/out/vote_",
    legislatur,
    "_",
    VoteId,
    ".pdf",
    sep = ""))
  
WriteXLS(
  cases,
  ExcelFileName = "output/cases.xls"
)

# Das File "cases_unique.xls" enthält alle Abstimmungen, die anders ausgegangen wären, wenn eine der unterlegenen Fraktionen vollständig anwesend gewesen wäre.
cases_unique <-
  cases %>% 
  distinct(VoteId, .keep_all = T)

cases_unique %<>% 
  select(
    Date:VoteSubmissionText,
    VoteId,
    RatJa:Fehlende)

WriteXLS(
  cases_unique,
  ExcelFileName = "output/cases_unique.xls"
) # alle betroffenen Abstimmungen

Plots

Ein paar einfache Plots als Überblick über die Daten.

Plot: “Verlorene Abstimmungen wegen abwesenden Fraktionsmitgliedern”"

plot <- ggplot(cases, aes(Fraktion)) +
  geom_histogram(stat = "count") +
  labs(
    x = "Fraktion",
    y = "Anzahl",
    title = "Verlorene Abstimmungen wegen abwesenden Fraktionsmitgliedern"
  ) +
  theme_minimal()
## Warning: Ignoring unknown parameters: binwidth, bins, pad
plot

Plot: “SVP: Verschenkte Abstimmungen nach Departement”

dplot <- filter(cases, Fraktion == "V")
plot <- ggplot(dplot, aes(Dept)) +
  geom_histogram(stat = "count") +
  labs(
    x = "Dept.",
    y = "Anzahl",
    title = "SVP: Verschenkte Abstimmungen nach Departement"
  ) +
  theme_minimal()
## Warning: Ignoring unknown parameters: binwidth, bins, pad
plot

rm(dplot)

Plot: “SP: Verschenkte Abstimmungen nach Departement”

dplot <- filter(cases, Fraktion == "S")
plot <- ggplot(dplot, aes(Dept)) +
  geom_histogram(stat = "count") +
  labs(
    x = "Dept.",
    y = "Anzahl",
    title = "SP: Verschenkte Abstimmungen nach Departement"
  ) +
  theme_minimal()
## Warning: Ignoring unknown parameters: binwidth, bins, pad
plot

rm(dplot)

Plot: “Verschenkte Abstimmungen nach Art der Vorlage”

dplot <- 
  cases %>% 
  distinct(VoteId, .keep_all = T)

dplot %<>%
  mutate(differenz = (RatJa - RatNein),
         form = case_when(grepl("Motion", 
                                VoteMeaningYes, 
                                ignore.case = T) ~ "Motion",
                          grepl("Postulat", 
                                VoteMeaningYes, 
                                ignore.case = T) ~ "Postulat",
                          grepl("Gesamtabstimmung", 
                                DivisionText,
                                ignore.case = T) ~ "Gesamtabstimmung"))

plot <- ggplot(dplot, aes(form)) +
  geom_histogram(stat = "count") +
  labs(
    x = "Art der Vorlage",
    y = "Anzahl",
    title = "Verschenkte Abstimmungen nach Art der Vorlage"
  ) +
  theme_minimal()
## Warning: Ignoring unknown parameters: binwidth, bins, pad
plot

rm(dplot)

Plot: “Verschenkte Abstimmungen nach Jahr”

dplot <- 
  cases %>% 
  distinct(VoteId, .keep_all = T)

dplot %<>%
  mutate(Date = str_sub(Date, 0, 4))

plot <- ggplot(dplot, aes(Date)) +
  geom_histogram(stat = "count") +
  labs(
    x = "Jahr",
    y = "Anzahl",
    title = "Verschenkte Abstimmungen nach Jahr"
  ) +
  theme_minimal()
## Warning: Ignoring unknown parameters: binwidth, bins, pad
plot

Linting

Der Code in diesem RMarkdown wird mit lintr automatisch auf den Wickham’schen tidyverse style guide überprüft.

lintr::lint(
  "main.Rmd", linters =
    lintr::with_defaults(
      commented_code_linter = NULL,
      trailing_whitespace_linter = NULL
    )
)
## main.Rmd:221:1: style: lines should not be more than 80 characters.
##   # Schritt 1: Abstimmung und Abstimmungsverhalten der Parlamentarier einlesen und zu einem Datensatz zusammenfassen
## ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
## main.Rmd:236:31: style: Variable and function names should be all lowercase.
##     mutate(VoteDate = str_sub(VoteDate, 0, 10))
##                               ^~~~~~~~
## main.Rmd:242:14: style: Variable and function names should be all lowercase.
##              VoteDate:VoteSubmissionText, 
##              ^~~~~~~~
## main.Rmd:242:23: style: Variable and function names should be all lowercase.
##              VoteDate:VoteSubmissionText, 
##                       ^~~~~~~~~~~~~~~~~~
## main.Rmd:245:1: style: lines should not be more than 80 characters.
##   # Transformieren des Datensatzes, alle Parlamentarier und Abstimmungen einzeln auflisten
## ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
## main.Rmd:248:7: style: Variable and function names should be all lowercase.
##       BioId,
##       ^~~~~
## main.Rmd:249:7: style: Variable and function names should be all lowercase.
##       VoteDecision,
##       ^~~~~~~~~~~~
## main.Rmd:280:35: style: Variable and function names should be all lowercase.
##       mutate(BioId = as.character(BioId),
##                                   ^~~~~
## main.Rmd:282:44: style: Variable and function names should be all lowercase.
##              CouncillorName = as.character(CouncillorName),
##                                            ^~~~~~~~~~~~~~
## main.Rmd:293:1: style: lines should not be more than 80 characters.
##   # Schritt 3: Das Abstimmungsverhalten der Fraktionen zu jeder einzelnen Abstimmung berechnen Datensatz zu Abstimmungen und Parlamentarieren mergen und anpassen
## ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
## main.Rmd:301:1: style: lines should not be more than 80 characters.
##   # Datensatz spezifieren, rekodieren und Abstimmungen nach Fraktionen gruppieren
## ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
## main.Rmd:304:7: style: Variable and function names should be all lowercase.
##       BioId,
##       ^~~~~
## main.Rmd:306:14: style: Variable and function names should be all lowercase.
##       Name = CouncillorName,
##              ^~~~~~~~~~~~~~
## main.Rmd:310:14: style: Variable and function names should be all lowercase.
##       Date = VoteDate,
##              ^~~~~~~~
## main.Rmd:313:7: style: Variable and function names should be all lowercase.
##       AffairId,
##       ^~~~~~~~
## main.Rmd:314:7: style: Variable and function names should be all lowercase.
##       AffairTitle,
##       ^~~~~~~~~~~
## main.Rmd:315:16: style: Variable and function names should be all lowercase.
##       VoteId = VoteRegistrationNumber,
##                ^~~~~~~~~~~~~~~~~~~~~~
## main.Rmd:316:7: style: Variable and function names should be all lowercase.
##       VoteMeaningYes,
##       ^~~~~~~~~~~~~~
## main.Rmd:317:7: style: Variable and function names should be all lowercase.
##       VoteMeaningNo,
##       ^~~~~~~~~~~~~
## main.Rmd:318:7: style: Variable and function names should be all lowercase.
##       DivisionText,
##       ^~~~~~~~~~~~
## main.Rmd:319:24: style: Variable and function names should be all lowercase.
##       SubmissionText = VoteSubmissionText,
##                        ^~~~~~~~~~~~~~~~~~
## main.Rmd:320:7: style: Variable and function names should be all lowercase.
##       VoteDecision,
##       ^~~~~~~~~~~~
## main.Rmd:324:19: style: Words within variable and function names should be separated by '_' rather than '.'.
##       RatEntsch = `Entschuldigung gem. Art. 57 Abs. 4`,
##                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
## main.Rmd:324:19: style: Variable and function names should not be longer than 30 characters.
##       RatEntsch = `Entschuldigung gem. Art. 57 Abs. 4`,
##                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
## main.Rmd:327:27: style: Variable and function names should be all lowercase.
##     mutate(Ja = case_when(VoteDecision ==
##                           ^~~~~~~~~~~~
## main.Rmd:329:29: style: Variable and function names should be all lowercase.
##            Nein = case_when(VoteDecision ==
##                             ^~~~~~~~~~~~
## main.Rmd:331:28: style: Variable and function names should be all lowercase.
##            Ent = case_when(VoteDecision ==
##                            ^~~~~~~~~~~~
## main.Rmd:333:28: style: Variable and function names should be all lowercase.
##            Abw = case_when(VoteDecision ==
##                            ^~~~~~~~~~~~
## main.Rmd:335:31: style: Variable and function names should be all lowercase.
##            AbwEnt = case_when(VoteDecision ==
##                               ^~~~~~~~~~~~
## main.Rmd:337:29: style: Variable and function names should be all lowercase.
##            Pres = case_when(VoteDecision ==
##                             ^~~~~~~~~~~~
## main.Rmd:339:14: style: Variable and function names should be all lowercase.
##     group_by(VoteId, Fraktion) %>%
##              ^~~~~~
## main.Rmd:345:26: style: Variable and function names should be all lowercase.
##       Entschuldigt = sum(AbwEnt),
##                          ^~~~~~
## main.Rmd:347:20: style: Variable and function names should be all lowercase.
##       RatJa = mean(RatJa),
##                    ^~~~~
## main.Rmd:348:22: style: Variable and function names should be all lowercase.
##       RatNein = mean(RatNein),
##                      ^~~~~~~
## main.Rmd:349:28: style: Variable and function names should be all lowercase.
##       RatEnthaltung = mean(RatEnth),
##                            ^~~~~~~
## main.Rmd:350:30: style: Variable and function names should be all lowercase.
##       RatEntschuldigt = mean(RatEntsch),
##                              ^~~~~~~~~
## main.Rmd:351:26: style: Variable and function names should be all lowercase.
##       RatAbwesend = mean(RatNicht)
##                          ^~~~~~~~
## main.Rmd:355:1: style: lines should not be more than 80 characters.
##   # Schritt 4: Potenzial berechnen, um die für die Auswertung relevanten Fälle zu finden
## ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
## main.Rmd:357:1: style: lines should not be more than 80 characters.
##   # Das Potenzial A ist die Differenz zwischen der Anzahl der fehlenden Personen einer Fraktion und der Stimmendifferenz im Rat. Das Potenzial gibt darüber Auskunft, ob eine Fraktion eine Abstimmung hätte gewinnen können, wenn alle Mitglieder anwesend gewesen wären. In der Regel trifft dies zu für Fraktionen und Abstimmungen mit einem Potenzial A > 0. Allerdings gibt es 2 Ausnahmen, bei denen auch Potenzial A = 0 ausreicht. Aus diesem Grund werden vorerst auch diese Fälle mitberücksichtigt. (Wird später für die Berechnung von Potenzial B und Potenzial C relevant.)
## ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
## main.Rmd:361:27: style: Variable and function names should be all lowercase.
##     mutate(RatDifferenz = RatJa - RatNein,
##                           ^~~~~
## main.Rmd:361:35: style: Variable and function names should be all lowercase.
##     mutate(RatDifferenz = RatJa - RatNein,
##                                   ^~~~~~~
## main.Rmd:363:40: style: Variable and function names should be all lowercase.
##            PotentialA = Fehlende - abs(RatDifferenz)) %>%
##                                        ^~~~~~~~~~~~
## main.Rmd:365:16: style: Variable and function names should be all lowercase.
##              ( RatJa / ( RatJa + RatNein )  - 0.5 ) ) %>%
##                ^~~~~
## main.Rmd:365:26: style: Variable and function names should be all lowercase.
##              ( RatJa / ( RatJa + RatNein )  - 0.5 ) ) %>%
##                          ^~~~~
## main.Rmd:365:34: style: Variable and function names should be all lowercase.
##              ( RatJa / ( RatJa + RatNein )  - 0.5 ) ) %>%
##                                  ^~~~~~~
## main.Rmd:368:12: style: Variable and function names should be all lowercase.
##     filter(PotentialA > -1 &
##            ^~~~~~~~~~
## main.Rmd:371:1: style: lines should not be more than 80 characters.
##   # Fälle, in denen das Potenzial A mindestens 0 beträg und die Abstimmung aus Sicht der Fraktionsmehr verloren wurde, an den Datensatz "cases" anhängen
## ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
## main.Rmd:387:12: style: Variable and function names should be all lowercase.
##   distinct(BioId, .keep_all = T)
##            ^~~~~
## main.Rmd:412:5: style: Variable and function names should be all lowercase.
##     AffairId,
##     ^~~~~~~~
## main.Rmd:413:5: style: Variable and function names should be all lowercase.
##     VoteRegistrationNumber,
##     ^~~~~~~~~~~~~~~~~~~~~~
## main.Rmd:427:5: style: Variable and function names should be all lowercase.
##     VoteRegistrationNumber,
##     ^~~~~~~~~~~~~~~~~~~~~~
## main.Rmd:436:33: style: Variable and function names should be all lowercase.
##   mutate(PotentialB = case_when(PresDec == "True" &
##                                 ^~~~~~~
## main.Rmd:437:32: style: Variable and function names should be all lowercase.
##                                PotentialA == 0  ~ 1, 
##                                ^~~~~~~~~~
## main.Rmd:461:12: style: Variable and function names should be all lowercase.
##            PotentialA + 
##            ^~~~~~~~~~
## main.Rmd:462:12: style: Variable and function names should be all lowercase.
##            PotentialB + 
##            ^~~~~~~~~~
## main.Rmd:463:12: style: Variable and function names should be all lowercase.
##            PotentialC) %>%
##            ^~~~~~~~~~
## main.Rmd:464:10: style: Variable and function names should be all lowercase.
##   filter(PotentialTotal > 0)
##          ^~~~~~~~~~~~~~
## main.Rmd:473:12: style: Variable and function names should be all lowercase.
##     Date = VoteDate,
##            ^~~~~~~~
## main.Rmd:476:5: style: Variable and function names should be all lowercase.
##     `AffairId`,
##     ^~~~~~~~~~
## main.Rmd:477:13: style: Variable and function names should be all lowercase.
##     Title = AffairTitle,
##             ^~~~~~~~~~~
## main.Rmd:478:5: style: Variable and function names should be all lowercase.
##     VoteMeaningYes,
##     ^~~~~~~~~~~~~~
## main.Rmd:479:5: style: Variable and function names should be all lowercase.
##     VoteMeaningNo,
##     ^~~~~~~~~~~~~
## main.Rmd:480:5: style: Variable and function names should be all lowercase.
##     DivisionText,
##     ^~~~~~~~~~~~
## main.Rmd:481:5: style: Variable and function names should be all lowercase.
##     VoteSubmissionText,
##     ^~~~~~~~~~~~~~~~~~
## main.Rmd:482:14: style: Variable and function names should be all lowercase.
##     VoteId = VoteRegistrationNumber
##              ^~~~~~~~~~~~~~~~~~~~~~
## main.Rmd:494:43: style: Variable and function names should be all lowercase.
##          VoteMeaningYes = str_replace_all(VoteMeaningYes, "[\r\n]", " "),
##                                           ^~~~~~~~~~~~~~
## main.Rmd:495:42: style: Variable and function names should be all lowercase.
##          VoteMeaningNo = str_replace_all(VoteMeaningNo, "[\r\n]", " "),
##                                          ^~~~~~~~~~~~~
## main.Rmd:496:41: style: Variable and function names should be all lowercase.
##          DivisionText = str_replace_all(DivisionText, "[\r\n]", " "),
##                                         ^~~~~~~~~~~~
## main.Rmd:497:47: style: Variable and function names should be all lowercase.
##          VoteSubmissionText = str_replace_all(VoteSubmissionText, "[\r\n]", 
##                                               ^~~~~~~~~~~~~~~~~~
## main.Rmd:510:10: style: Variable and function names should be all lowercase.
##   filter(JaAnteil > (ts1 / 100) | 
##          ^~~~~~~~
## main.Rmd:511:12: style: Variable and function names should be all lowercase.
##            JaAnteil < (100 - ts1) / 100)
##            ^~~~~~~~
## main.Rmd:522:10: style: Variable and function names should be all lowercase.
##   filter(EnthAnteil < ( ts2 / 100) )
##          ^~~~~~~~~~
## main.Rmd:531:33: style: Variable and function names should be all lowercase.
##   mutate(legislatur = case_when(VoteId < 12604 ~ 49,
##                                 ^~~~~~
## main.Rmd:532:33: style: Variable and function names should be all lowercase.
##                                 VoteId >= 12604 ~ 50)) %>%
##                                 ^~~~~~
## main.Rmd:536:6: warning: Do not use absolute paths.
##     "/out/vote_",
##      ^~~~
## main.Rmd:539:5: style: Variable and function names should be all lowercase.
##     VoteId,
##     ^~~~~~
## main.Rmd:551:12: style: Variable and function names should be all lowercase.
##   distinct(VoteId, .keep_all = T)
##            ^~~~~~
## main.Rmd:555:10: style: Variable and function names should be all lowercase.
##     Date:VoteSubmissionText,
##          ^~~~~~~~~~~~~~~~~~
## main.Rmd:556:5: style: Variable and function names should be all lowercase.
##     VoteId,
##     ^~~~~~
## main.Rmd:557:5: style: Variable and function names should be all lowercase.
##     RatJa:Fehlende)
##     ^~~~~
## main.Rmd:621:12: style: Variable and function names should be all lowercase.
##   distinct(VoteId, .keep_all = T)
##            ^~~~~~
## main.Rmd:624:23: style: Variable and function names should be all lowercase.
##   mutate(differenz = (RatJa - RatNein),
##                       ^~~~~
## main.Rmd:624:31: style: Variable and function names should be all lowercase.
##   mutate(differenz = (RatJa - RatNein),
##                               ^~~~~~~
## main.Rmd:626:33: style: Variable and function names should be all lowercase.
##                                 VoteMeaningYes, 
##                                 ^~~~~~~~~~~~~~
## main.Rmd:629:33: style: Variable and function names should be all lowercase.
##                                 VoteMeaningYes, 
##                                 ^~~~~~~~~~~~~~
## main.Rmd:632:33: style: Variable and function names should be all lowercase.
##                                 DivisionText,
##                                 ^~~~~~~~~~~~
## main.Rmd:652:12: style: Variable and function names should be all lowercase.
##   distinct(VoteId, .keep_all = T)
##            ^~~~~~
# lintr::lint("scripts/my_script.R", linters =
#               lintr::with_defaults(
#                 commented_code_linter = NULL,
#                 trailing_whitespace_linter = NULL
#                 )
#             )