Notes

This document illustrates the preprocessing of the dataset visualized in this article on srf.ch.

SRF Data attaches great importance to transparent and reproducible data preprocessing and -analysis. SRF Data believes in the principles of open data but also open and reproducible methods. Third parties should be empowered to build on the work of SRF Data and to generate new analyses and applications.

R-Script & processed data

The preprocessing and analysis of the data was conducted in the R project for statistical computing. The RMarkdown script used to generate this document and all the resulting data can be downloaded under this link. Through executing main.Rmd, the herein described process can be reproduced and this document can be generated. In the course of this, data from the folder ìnput will be processed and results will be written to output.

Attention: Please set your working directory in the first code chunk!

GitHub

The code for the herein described process can also be freely downloaded from https://github.com/srfdata/2015-09-elections-political-shifts. Criticism in the form of GitHub issues and pull requests is very welcome!

License

Creative Commons License
2015-09-elections-political-shifts by SRF Data is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.

Exclusion of liability

The published information has been collated carefully, but no guarantee is offered of its completeness, correctness or up-to-date nature. No liability is accepted for damage or loss incurred from the use of this script or the information drawn from it. This exclusion of liability also applies to third-party content that is accessible via this offer.

Other projects

All code & data from SRF Data is available under http://srfdata.github.io.

Data description

Original data source

Michael Hermann, Mario Nowak (Forschungsstelle sotomo) with data from the Swiss Federal Statistical Office (FSO).

Shifts in political space

The shifts on the left-right- and the progressive-conservative-axes are available from 1981 to 2014 (1-year-steps) in the spatial aggregation levels of:

  • municipalities (gem)
  • cantons (kt)
  • municipality types (detailed description) (agglotyp)
  • language regions (sprachreg)

They are computed from national ballots results using a statistical dimensionality reduction method. A detailed methodology description (in German) can be found in the box at the end of the article on srf.ch. Important: The dimensionless values always signify the relative deviation from the median in a given year -> not absolute, semantical positions.

Shifts in party strengths

The shifts in party strengths for the national council are available from 1971 to 2014 (4-year-steps) in the spatial aggregation levels of:

  • municipalities (gem)
  • cantons (kt)
  • municipality types (detailed description) (agglotyp)
  • language regions (sprachreg)

Spatial aggregation & municipality harmonization

For all computations and spatial aggregations, the municipalities of 2015 were used (“Gemeindestand”). In order to account for municipality mergers and splits, the data were fully harmonized to the level of 2015.

Description of output

The following sections describe the results of the data preprocessing as stored in the output folder.

output/polit_raum_*.csv

Contains position on the left-right- and the progressive-conservative-axes from 1981 to 2014 (1-year-steps) in the above mentioned spatial aggregation levels.

Attribute Type Description
jahr Integer Year
*_id Integer / String ID of the referenced spatial unit
dim Integer Political space dimension (axis). Dimension 21 signifies left (negative) and right (positive), 22 signifies progressive (positive) and conservative (negative).
wert Double Value (dimensionless). See explanation above.
anz_gultig Double Number of people who voted validly in a given year and spatial unit (yearly average, rounded).
anz_stber Double Total number of people allowed to vote in a given year and spatial unit (yearly average, rounded).

output/nrw_*.csv

Contains party strengths in percent for the national council from 1971 to 2014 (4-year-steps) in the above mentioned spatial aggregation levels.

Attribute Type Description
jahr Integer Year of national council election
*_id Integer / String ID of the referenced spatial unit
partei Integer Party ID, references ID in output/parties.csv
wert Double Party strength in percent

Lookup tables

output/lut_*.csv

Contains full names and further information (e.g. translations) for the spatial aggregation levels of municipalities (gem) and municipality types (agglotyp).

Goes without description.

output/parties.csv

Contains party classifications made by SRF Data with the help of political scientists, used throughout all projects related to elections.

Attribute Type Description
ID Integer Unique identifier
Abbr_* String Abbreviation in German (D), French (F), English (E), Romansh (R), Italian (I), respectively
Legend_* String Abbreviation, but with slightly more information, used for frontend purposes
Name_* String Full name
Sortorder Integer Used for frontend purposes solely
OLD_ID String “Official” ID as given in https://github.com/srfdata/2015-06-elections-partystrengths/blob/master/analysis/input/parteienstaerke_mod_2.xlsx (sheet “Parteien”), used for combining party strengths for party groupings

Preparations

Define packages

# von https://mran.revolutionanalytics.com/web/packages/checkpoint/vignettes/using-checkpoint-with-knitr.html
cat("library(magrittr)
library(tidyr)
library(dplyr)
library(readxl)
library(animation)
library(extrafont)
library(jsonlite)
library(ggplot2)", 
file = "manifest.R")
package_date <- "2015-10-01"

Install packages

if(!require(checkpoint)) {
  if(!require(devtools)){
    install.packages("devtools", repos = "http://cran.us.r-project.org")
    require(devtools)
  }
  devtools::install_github("checkpoint", username = "RevolutionAnalytics", ref = "v0.3.2", 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/
dir.create("~/.checkpoint")
## Warning in dir.create("~/.checkpoint"): '/home/tgrossen/.checkpoint'
## already exists
checkpoint(snapshotDate = package_date, project = path_to_wd, verbose = T, scanForPackages = T, use.knitr = F, R.version = "3.1.3")
## Scanning for packages used in this project
## rmarkdown files found and will not be parsed. Set use.knitr = TRUE
## - Discovered 9 packages
## All detected packages already installed
## checkpoint process complete
## ---
rm(package_date)

Load packages

source("manifest.R")
## 
## Attaching package: 'tidyr'
## The following object is masked from 'package:magrittr':
## 
##     extract
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
## Registering fonts with R
## 
## Attaching package: 'jsonlite'
## The following object is masked from 'package:utils':
## 
##     View
unlink("manifest.R")
sessionInfo()
## R version 3.1.3 (2015-03-09)
## Platform: x86_64-unknown-linux-gnu (64-bit)
## Running under: Ubuntu 16.04.2 LTS
## 
## locale:
##  [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
##  [3] LC_TIME=de_CH.UTF-8        LC_COLLATE=en_US.UTF-8    
##  [5] LC_MONETARY=de_CH.UTF-8    LC_MESSAGES=en_US.UTF-8   
##  [7] LC_PAPER=de_CH.UTF-8       LC_NAME=C                 
##  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
## [11] LC_MEASUREMENT=de_CH.UTF-8 LC_IDENTIFICATION=C       
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] ggplot2_1.0.1    jsonlite_0.9.17  extrafont_0.17   animation_2.4   
## [5] readxl_0.1.0     dplyr_0.4.3      tidyr_0.3.1      magrittr_1.5    
## [9] checkpoint_0.4.0
## 
## loaded via a namespace (and not attached):
##  [1] assertthat_0.1   backports_1.1.0  colorspace_1.2-6 DBI_0.3.1       
##  [5] digest_0.6.12    evaluate_0.10.1  extrafontdb_1.0  grid_3.1.3      
##  [9] gtable_0.1.2     htmltools_0.3.6  knitr_1.16       MASS_7.3-39     
## [13] munsell_0.4.2    parallel_3.1.3   plyr_1.8.3       proto_0.3-10    
## [17] R6_2.1.1         Rcpp_0.12.12     reshape2_1.4.1   rmarkdown_1.6   
## [21] rprojroot_1.2    Rttf2pt1_1.3.3   scales_0.3.0     stringi_1.1.5   
## [25] stringr_1.2.0    tools_3.1.3      yaml_2.1.14

Read in data & prepare it

# We mostly need the following datasets

# lut_agglotyp.csv
lut_agglotyp <- read.csv2(file = "input/lut_agglotyp.csv", sep = ";", stringsAsFactors = F)

# polit_raum_agglotyp.csv
polit_raum_agglotyp <- read.csv2(file = "input/polit_raum_agglotyp.csv", sep = ";", stringsAsFactors = F)

# anz_gultig_agglotyp.csv
anz_gultig_agglotyp <- read.csv2(file = "input/anz_gultig_agglotyp.csv", sep = ";", stringsAsFactors = F)

# anz_stber_agglotyp.csv
anz_stber_agglotyp <- read.csv2(file = "input/anz_stber_agglotyp.csv", sep = ";", stringsAsFactors = F)

# add valid votes
polit_raum_agglotyp %<>%
  inner_join(anz_gultig_agglotyp, by = c("jahr", "agglotyp")) %>% 
  rename(anz_gultig = anz) %>%
  inner_join(anz_stber_agglotyp, by = c("jahr", "agglotyp")) %>% 
  rename(anz_stber = anz) %>% 
  mutate(share_vote = anz_gultig / anz_stber)

polit_raum_agglotyp %<>% 
  mutate(wert = as.numeric(wert), agglotyp = factor(agglotyp, levels = lut_agglotyp$typ_id, labels = lut_agglotyp$typ)) 
polit_raum_agglotyp_left_right <- polit_raum_agglotyp %>% 
  filter(dim == 22)
polit_raum_agglotyp_cons_prog <- polit_raum_agglotyp %>% 
  filter(dim == 21) 

# lut_gem.csv
lut_gem <- read.csv2(file = "input/lut_gem.csv", sep = ";", stringsAsFactors = F)

# polit_raum_gem.csv
polit_raum_gem <- read.csv2(file = "input/polit_raum_gem.csv", sep = ";", stringsAsFactors = F)

polit_raum_gem %<>% 
  mutate(wert = as.numeric(wert)) 

# join in agglotyp
polit_raum_gem %<>% 
  inner_join(lut_gem, by = "gnr") %>% 
  mutate(agglotyp_id = factor(agglotyp_id, levels = lut_agglotyp$typ_id, labels = lut_agglotyp$typ)) %>% 
  select(-agglotyp) %>% 
  rename(agglotyp = agglotyp_id)

# join in agglotyp values
polit_raum_gem %<>%
  inner_join(polit_raum_agglotyp, by = c("jahr", "dim", "agglotyp")) %>% 
  select(-anz_gultig) %>% 
  rename(wert_gde = wert.x, wert_agglotyp = wert.y)

# polit_raum_sprachreg.csv
polit_raum_sprachreg <- read.csv2(file = "input/polit_raum_sprachreg.csv", sep = ";", stringsAsFactors = F)

# lut_sprachreg.csv
lut_sprachreg <- read.csv2(file = "input/lut_sprachreg.csv", sep = ";", stringsAsFactors = F)
## Warning in read.table(file = file, header = header, sep = sep, quote
## = quote, : incomplete final line found by readTableHeader on 'input/
## lut_sprachreg.csv'
# anz_gultig_sprachreg.csv
anz_gultig_sprachreg <- read.csv2(file = "input/anz_gultig_sprachreg.csv", sep = ";", stringsAsFactors = F)

# anz_stber_sprachreg.csv
anz_stber_sprachreg <- read.csv2(file = "input/anz_stber_sprachreg.csv", sep = ";", stringsAsFactors = F)

# add valid votes
polit_raum_sprachreg %<>%
  rename(jahr = Jahr) %>% 
  inner_join(anz_gultig_sprachreg, by = c("jahr", "sprachreg")) %>% 
  rename(anz_gultig = anz) %>% 
  inner_join(anz_stber_sprachreg, by = c("jahr", "sprachreg")) %>% 
  rename(anz_stber = anz) %>% 
  mutate(share_vote = anz_gultig / anz_stber)

polit_raum_sprachreg %<>% 
  mutate(wert = as.numeric(wert), sprachreg = factor(sprachreg, levels = lut_sprachreg$sprachreg, labels = lut_sprachreg$label_sprachreg)) 

# parties
nrw_agglotyp <- read.csv2(file = "input/nrw_71_agglotyp.csv", sep = ";", stringsAsFactors = F)
nrw_agglotyp %<>% 
  mutate(wert = as.numeric(wert), agglotyp = factor(agglotyp, levels = lut_agglotyp$typ_id, labels = lut_agglotyp$typ)) 


nrw_gem <- read.csv2(file = "input/nrw_71_gem.csv", sep = "\t", stringsAsFactors = F)

Plausibility checks

# correct number of rows?
nrow(polit_raum_agglotyp) == 34 * 8 * 2
## [1] TRUE
nrow(polit_raum_gem) == 34 * 2324 * 2
## [1] TRUE
length(unique(nrw_agglotyp$partei))
## [1] 12
length(unique(nrw_agglotyp$jahr))
## [1] 11
nrow(nrw_agglotyp) == 11 * 8 * 12 
## [1] TRUE

Analysis

Group sizes

In the following analyses, it is important to note that different municipality types are home to a differing amount of people and are thus not equally “important” for the political landscape of Switzerland. The following chart gives an overview.

anz_stber <- anz_stber_agglotyp %>% 
  filter(jahr == 2014)
anz_stber %<>% 
  mutate(agglotyp = factor(agglotyp, levels = lut_agglotyp$typ_id, labels = lut_agglotyp$typ)) 
ggplot(anz_stber, aes(x = agglotyp, y = anz)) +
  geom_bar(stat = "identity") +
  ylab("Number of people allowed to vote") +
  xlab("Municipality type") +
  theme(axis.text.x = element_text(angle = 90))

Progressive-conservative dimension

# developments in the agglomeration types - dim cons/progr
ggplot(polit_raum_agglotyp_cons_prog, aes(x = jahr, y = wert, color = agglotyp, size = anz_gultig, alpha = anz_gultig)) +
    geom_line() +
    geom_hline(yintercept = 0) + 
    ylim(-10, 10) +
    ylab("Value (Positive: progressive; negative: conservative)") +
    xlab("Year") +
    ggtitle(label = "Political shifts on the progressive-conservative-axis") +
    guides(color = guide_legend(ncol = 2), alpha = F) +
    theme(legend.title = element_text()) +
    scale_color_discrete(name = "Type of municipality") +
    scale_size_continuous(name = "Number of valid votes, also conveyed with opacity")

Most important findings:

  • Big cities performed a significant and constant shift to being more progressive in the last 35 years, a shift which has slowed down in the last 10 years.
  • Municipalities with high status and with low status moved into the opposite direction, the latter starting further “down”. The same is also the case for municipalities with middle status. Note: All these municipalities belong to agglomeration which is not part of the core of agglomerations (i.e. not the same as “cities” / typical “agglo” as we would call it)
  • Most voters live in small agglomeration municipalities - these stayed mostly the same over the last 35 years.
  • Rural municipalities performed a significant and relatively stark shift to being more progressive in the last 10 years.

Left-right dimension

# developments in the agglomeration types - dim left/right
ggplot(polit_raum_agglotyp_left_right, aes(x = jahr, y = wert, color = agglotyp, size = anz_gultig, alpha = anz_gultig)) +
    geom_line() +
    geom_hline(yintercept = 0) + 
    ylim(-10, 10) + 
    ylab("Value (Negative: left; positive: right)") +
    xlab("Year") +
    ggtitle(label = "Political shifts on the left-right-axis") +
    guides(color = guide_legend(ncol = 2), alpha = F) +
    theme(legend.title = element_text()) +
    scale_color_discrete(name = "Type of municipality") +
    scale_size_continuous(name = "Number of valid votes, also conveyed with opacity") +
    coord_flip()
## Warning: Removed 1 rows containing missing values (geom_path).

Most important findings:

  • Big cities were already very “left”, and even went further away into the left in the last 15 years.
  • The time between approximately 1995 and 2005 can be considered to be more “harmonic” than before and after - the difference between the left and right were not so stark then.
  • Both small agglomeration municipalities and rural municipalities considerably shifted to the right in the last 10 year - but not nearly as strong as big cities moved to the left.
  • Municipalities of high and middle status both shifted slightly to the left in the last 10 years, the former being more right wing than the latter.
  • Another relatively strong shift could be observed in low-status-municipalities, but it happened already in the early 90es. During that time, also higher status municipalities considerably shifted to the right.

Both together

# need to spread it first
polit_raum_agglotyp_spread <- polit_raum_agglotyp %>% 
  spread(dim, wert) %>% 
  rename(leftright = `22`, progcons = `21`)

library(grid)

# note: we use geom_path here instead of geom_line 
ggplot(polit_raum_agglotyp_spread, aes(x = leftright, y = progcons, color = agglotyp)) +
    geom_path(arrow = arrow(ends = "last", type = "closed", length = unit(0.13, "inches"))) +
    geom_hline(yintercept = 0) + 
    geom_vline(xintercept = 0) + 
    xlab("Value (Negative: left; positive: right)") +
    ylab("Value (Positive: progressive; negative: conservative)") +
    ggtitle(label = "Political shifts on both axes") +
    guides(color = guide_legend(ncol = 2)) +
    theme(legend.title = element_text(size = 14), axis.text.x = element_blank(), axis.text.y = element_blank(), axis.ticks.x = element_blank(), axis.ticks.y = element_blank(), plot.margin = unit(c(1,1,1,1), "lines"), legend.text = element_text(size = 14), legend.box = "vertical") +
    guides(color = guide_legend(ncol = 1)) + 
    scale_color_discrete(name = "Type of municipality") +
    scale_alpha_continuous(name = "Year") +
    xlim(-10, 10) +
    ylim(-10, 10)
## Warning: Removed 1 rows containing missing values (geom_path).

Most important findings:

  • The plot clearly shows one thing: Different municipality types occupy different “areas” in socio-political space. Only between small cities and municipalities of low and middle status the distinction is not that clear.
    • Big cities: Progressive-left (tendency becoming stronger)
    • Middle cities: Progressive-left, although less strong
    • High-status-municipalities: Progressive-right
    • Rural municipalities: Conservative-right
    • Small agglomeration municipalities: Conservative-right, less pronounced than the above though
  • The plot also shows in terms of big shifts: Big cities, low-status-municipalities, high-status-municipalities and rural municipalities have “wandered” the most, in more or less that order
  • The plot also shows in terms of abrupt shifts: Small cities, small agglomeration municipalities, rural municipalities have made the “sharpest turns” recently - mostly to the right

Exemplary municipalities

The following shows the three most similar (i.e. typical) municipalities for each of the municipality types (in terms of euclidean distance to both dimensions).

polit_raum_gem %>% 
  group_by(gnr, gemeinde, agglotyp) %>% 
  summarize(euclidean = as.numeric(dist(rbind(wert_gde, wert_agglotyp)))) %>% 
  ungroup() %>% 
  group_by(agglotyp) %>% 
  arrange(euclidean) %>% 
  slice(1:3) %>% 
  as.data.frame()
##     gnr             gemeinde                    agglotyp euclidean
## 1   261               Zürich                  Grossstadt 11.129542
## 2  2701                Basel                  Grossstadt 17.415019
## 3   351                 Bern                  Grossstadt 18.507061
## 4  1061               Luzern                 Mittelstadt 14.350345
## 5  3203           St. Gallen                 Mittelstadt 21.588594
## 6  3901                 Chur                 Mittelstadt 23.955018
## 7  2581                Olten                  Kleinstadt 15.096612
## 8  6002            Brig-Glis                  Kleinstadt 22.036277
## 9  4671          Kreuzlingen                  Kleinstadt 22.607275
## 10    1      Aeugst am Albis         statushohe Gemeinde  9.867958
## 11 1051          Adligenswil         statushohe Gemeinde 13.091849
## 12  732             Bellmund         statushohe Gemeinde 14.267109
## 13 2861              Sissach     statusmittlere Gemeinde  8.285717
## 14  198                Uster     statusmittlere Gemeinde  9.818788
## 15 2886             Hölstein     statusmittlere Gemeinde 10.841969
## 16 2824          Frenkendorf        statustiefe Gemeinde  9.214266
## 17 2309     Wünnewil-Flamatt        statustiefe Gemeinde 11.835844
## 18  247            Schlieren        statustiefe Gemeinde 14.985783
## 19 2523             Horriwil Kleinagglomerationsgemeinde  9.263439
## 20  420 Rüdtligen-Alchenflüh Kleinagglomerationsgemeinde  9.603676
## 21 2904            Neunkirch Kleinagglomerationsgemeinde  9.762454
## 22 2302           St. Antoni          ländliche Gemeinde  6.047818
## 23  785            Meiringen          ländliche Gemeinde  8.255822
## 24 1068              Vitznau          ländliche Gemeinde  8.338027
# doublecheck
gnr_1 <- polit_raum_gem %>% 
  filter(gnr == 1) %>% 
  select(wert_gde, wert_agglotyp) %>% 
  as.data.frame()

dist(rbind(gnr_1$wert_gde, gnr_1$wert_agglotyp))
##          1
## 2 9.867958
gnr_2861 <- polit_raum_gem %>% 
  filter(gnr == 2861) %>% 
  select(wert_gde, wert_agglotyp) %>% 
  as.data.frame()

dist(rbind(gnr_2861$wert_gde, gnr_2861$wert_agglotyp))
##          1
## 2 8.285717
# seems to work ok

# plot them
most_similar_gnr <- polit_raum_gem %>% 
  group_by(gnr, gemeinde, agglotyp) %>% 
  summarize(euclidean = as.numeric(dist(rbind(wert_gde, wert_agglotyp)))) %>% 
  ungroup() %>% 
  group_by(agglotyp) %>% 
  arrange(euclidean) %>% 
  slice(1) %>% 
  ungroup() %>% 
  select(gnr)

most_similar <- polit_raum_gem %>% 
  filter(gnr %in% most_similar_gnr$gnr) %>% 
  select(jahr, gemeinde, dim, wert_gde, agglotyp) %>% 
  spread(dim, wert_gde) %>% 
  rename(leftright = `22`, progcons = `21`) %>% 
  arrange(jahr, agglotyp)

ggplot(most_similar, aes(x = leftright, y = progcons, color = agglotyp)) +
    geom_path(arrow = arrow(ends = "last", type = "closed", length = unit(0.13, "inches"))) +
    geom_hline(yintercept = 0) + 
    geom_vline(xintercept = 0) + 
    xlab("Value (Negative: left; positive: right)") +
    ylab("Value (Positive: progressive; negative: conservative)") +
    ggtitle(label = "Political shifts on both axes for most typical municipalities") +
    guides(color = guide_legend(ncol = 2)) +
    theme(legend.title = element_text()) +
    scale_color_discrete(name = "Type of municipality (colors identical with the plot above)") +
    scale_alpha_continuous(name = "Year") +
    xlim(-10, 10) +
    ylim(-10, 10)

Voting participation

All values

ggplot(polit_raum_agglotyp, aes(x = jahr, y = share_vote, color = agglotyp, alpha = anz_gultig)) +
    geom_line(size = 2) + 
    ylab("Average share of people who voted") +
    xlab("Year") +
    ggtitle(label = "Voting participation") +
    # scale_color_manual(values = distinct_colors) +
    guides(color = guide_legend(ncol = 2)) +
    theme(legend.title = element_text()) +
    scale_color_discrete(name = "Type of municipality") +
    scale_alpha_continuous(name = "Number of valid votes") +
    scale_y_continuous(labels = scales::percent)

Note: The stark peak in 1989 is most likely due to the fact that, in this year, only one vote was used.

Smoothed

ggplot(polit_raum_agglotyp, aes(x = jahr, y = share_vote, color = agglotyp, alpha = anz_gultig)) +
    geom_line(aes(alpha = anz_gultig), stat = "smooth", se = F, method = "loess", size = 2) + 
    ylab("Average share of people who voted") +
    xlab("Year") +
    ggtitle(label = "Voting participation (loess smoother)") +
    guides(color = guide_legend(ncol = 2)) +
    theme(legend.title = element_text()) +
    scale_color_discrete(name = "Type of municipality") +
    scale_alpha_continuous(name = "Number of valid votes") +
    scale_y_continuous(labels = scales::percent)

Most important findings:

  • Generally, voting participation is increasing in the last decade.
  • It also seems that municipality types used to be more “coherent” in terms of voting participation than nowadays.
  • Nowadays, i.e. in the last 10-15 years, differences in voting behavior can be observed:
    • Municipalities of high status seem to have a considerably higher voting participation than those of low status (in 2014, the former had almost 60%, the latter less than 50%)
    • Participation in urban areas seems to be higher than in rural areas.
    • These patterns seem to be consistent throughout time.

Language regions

# need to spread it first
polit_raum_sprachreg_spread <- polit_raum_sprachreg %>% 
  spread(dim, wert) %>% 
  rename(leftright = `22`, progcons = `21`) 

# note: we use geom_path here instead of geom_line 
ggplot(polit_raum_sprachreg_spread, aes(x = leftright, y = progcons, color = sprachreg)) +
    geom_path(arrow = arrow(ends = "last", type = "closed", length = unit(0.13, "inches"))) +
    geom_hline(yintercept = 0) + 
    geom_vline(xintercept = 0) + 
    xlab("Value (Negative: left; positive: right)") +
    ylab("Value (Positive: progressive; negative: conservative)") +
    ggtitle(label = "Political shifts on both axes") +
    guides(color = guide_legend(ncol = 2)) +
    theme(legend.title = element_text()) +
    scale_color_discrete(name = "Language region") +
    scale_alpha_continuous(name = "Year") +
    xlim(-10, 10) +
    ylim(-10, 10)
## Warning: Removed 11 rows containing missing values (geom_path).

Left-right shift looks interesting for the French-speaking region, let’s explore further:

polit_raum_sprachreg_left_right <- polit_raum_sprachreg %>% 
  filter(dim == 22)
  
ggplot(polit_raum_sprachreg_left_right, aes(x = jahr, y = wert, color = sprachreg, size = anz_gultig, alpha = anz_gultig)) +
    geom_line() +
    geom_hline(yintercept = 0) + 
    ylim(-10, 10) + 
    ylab("Value (Negative: left; positive: right)") +
    xlab("Year") +
    ggtitle(label = "Political shifts on the left-right-axis") +
    guides(color = guide_legend(ncol = 2), alpha = F) +
    theme(legend.title = element_text()) +
    scale_color_discrete(name = "Language region") +
    scale_size_continuous(name = "Number of valid votes, also conveyed with opacity") +
    coord_flip()
## Warning: Removed 11 rows containing missing values (geom_path).

Most important findings:

  • Just as types of muncipalities, different language regions also have very different “socio-political footprints”.
    • German-speaking: Slightly conservative-right
    • French-speaking: Progressive-left
    • Italian-speaking: Conservative-left
    • Rumantsch-speaking: Right
  • The strongest shifts can be observed for the French- and Italian-speaking parts of Switzerland: They both shifted to the right considerably. For the French-speaking region, this shift was rather abrupt in the beginning of the millenium.

Party strengths

SVP & SP

nrw_agglotyp_svp_sp <- nrw_agglotyp %>% 
  filter(partei == 4 | partei == 3) %>% 
  mutate(partei = factor(partei, levels = c(3,4), labels = c("SP", "SVP")))

ggplot(nrw_agglotyp_svp_sp, aes(x = jahr, y = wert, color = agglotyp)) +
    geom_line() +
    ylab("Party strength (in percent)") +
    xlab("Year") +
    ggtitle(label = "Party strength of SP and SVP") +
    guides(color = guide_legend(ncol = 2), alpha = F) +
    theme(legend.title = element_text()) +
    scale_color_discrete(name = "Type of municipality") +
    facet_grid(partei ~ .)

CVP & FDP

nrw_agglotyp_cvp_fdp <- nrw_agglotyp %>% 
  filter(partei == 2 | partei == 1) %>% 
  mutate(partei = factor(partei, levels = c(2,1), labels = c("CVP", "FDP")))

ggplot(nrw_agglotyp_cvp_fdp, aes(x = jahr, y = wert, color = agglotyp)) +
    geom_line() +
    ylab("Party strength (in percent)") +
    xlab("Year") +
    ggtitle(label = "Party strength of CVP and FDP") +
    guides(color = guide_legend(ncol = 2), alpha = F) +
    theme(legend.title = element_text()) +
    scale_color_discrete(name = "Type of municipality") +
    facet_grid(partei ~ .)

GPS

nrw_agglotyp_gps <- nrw_agglotyp %>% 
  filter(partei == 13) %>% 
  mutate(partei = factor(partei, levels = c(13), labels = c("GPS")))

ggplot(nrw_agglotyp_gps, aes(x = jahr, y = wert, color = agglotyp)) +
    geom_line() +
    ylab("Party strength (in percent)") +
    xlab("Year") +
    ggtitle(label = "Party strength of the Greens") +
    guides(color = guide_legend(ncol = 2), alpha = F) +
    theme(legend.title = element_text()) +
    scale_color_discrete(name = "Type of municipality") +
    facet_grid(partei ~ .)

Save GIF animation of political movement of agglotypes

# note: we use geom_path here instead of geom_line 
# todos:
# - add styling
# - add source
# - 
setwd("output/")
plotIteration <- function(cur_year){
  polit_raum_agglotyp_spread_range <- polit_raum_agglotyp_spread %>% 
    filter(jahr <= cur_year)
  dimplot <- ggplot(polit_raum_agglotyp_spread_range, aes(x = leftright, y = progcons, color = agglotyp)) +
      geom_path(size = 1, arrow = arrow(ends = "last", type = "closed", length = unit(0.05, "inches"))) +
      geom_hline(yintercept = 0, color = "#94928d") + 
      geom_vline(xintercept = 0, color = "#94928d") + 
      xlab("Links <> Rechts") +
      ylab("Konservativ <> Progressiv") +
      ggtitle(label = cur_year) +
      guides(color = guide_legend(ncol = 2)) +
      scale_color_discrete(name = "Raumtyp") +
      scale_alpha_continuous(name = "Jahr") +
      xlim(-10, 10) +
      ylim(-10, 10) +
      theme_bw() +
      theme(title = element_text(size = 16, family = "SRG SSR Type", face = "bold"), 
            text = element_text(color = "#222222", family = "SRG SSR Type"),
            legend.title = element_blank(),  
            axis.text.x = element_blank(), 
            axis.text.y = element_blank(), 
            axis.ticks.x = element_blank(), 
            axis.ticks.y = element_blank(), 
            legend.text = element_text(size = 13), 
            legend.margin = unit(0, "cm"),
            plot.margin = unit(c(0.5,0,0.5,0.5), "cm"),
            axis.title.x = element_text(size = 14), 
            axis.title.y = element_text(size = 14), 
            legend.box = "vertical", 
            panel.grid = element_blank(), 
            plot.background = element_rect(fill = "#f5f5f2"), 
            panel.background = element_rect(fill = "#f5f5f2"), 
            legend.background = element_rect(fill = "#f5f5f2"),  
            legend.key = element_blank(),
            axis.line = element_line(color = "#94928d")) +
        annotate("text", x = 8, y = -10, label = "Quelle: Sotomo", family = "SRG SSR Type", color = "#555555", size = 4) +
        guides(color = guide_legend(ncol = 1))
  print(dimplot)
}
saveGIF({
  for (cur_year in c(1981:2014, rep(2014, 10))){
    plotIteration(cur_year)
  }
}, interval = 0.1, movie.name = "movement.gif", ani.width = 624, ani.height = 480, clean = T)
## geom_path: Each group consist of only one observation. Do you need to adjust the group aesthetic?
## Warning: Removed 1 rows containing missing values (geom_path).

## Warning: Removed 1 rows containing missing values (geom_path).

## Warning: Removed 1 rows containing missing values (geom_path).

## Warning: Removed 1 rows containing missing values (geom_path).

## Warning: Removed 1 rows containing missing values (geom_path).

## Warning: Removed 1 rows containing missing values (geom_path).

## Warning: Removed 1 rows containing missing values (geom_path).

## Warning: Removed 1 rows containing missing values (geom_path).

## Warning: Removed 1 rows containing missing values (geom_path).

## Warning: Removed 1 rows containing missing values (geom_path).

## Warning: Removed 1 rows containing missing values (geom_path).
## Executing: 
## 'convert' -loop 0 -delay 10 Rplot1.png Rplot2.png Rplot3.png
##     Rplot4.png Rplot5.png Rplot6.png Rplot7.png Rplot8.png
##     Rplot9.png Rplot10.png Rplot11.png Rplot12.png Rplot13.png
##     Rplot14.png Rplot15.png Rplot16.png Rplot17.png Rplot18.png
##     Rplot19.png Rplot20.png Rplot21.png Rplot22.png Rplot23.png
##     Rplot24.png Rplot25.png Rplot26.png Rplot27.png Rplot28.png
##     Rplot29.png Rplot30.png Rplot31.png Rplot32.png Rplot33.png
##     Rplot34.png Rplot35.png Rplot36.png Rplot37.png Rplot38.png
##     Rplot39.png Rplot40.png Rplot41.png Rplot42.png Rplot43.png
##     Rplot44.png 'movement.gif'
## Output at: movement.gif
## [1] TRUE
setwd("..")
movement.gif

movement.gif

Preprocessing for visualization

parties <- read.csv2(file = "input/parties.csv", sep = ",", stringsAsFactors = F)

# remove unnecessary attributes
lut_gem_new <- lut_gem %>% select(-agglotyp)
# add translations to lut_gem
lut_gem_new %<>% rename(name_official = gemeinde)

# first, load municipality name translations (src http://www.bfs.admin.ch/bfs/portal/de/index/infothek/nomenklaturen/blank/blank/gem_liste/03.Document.90142.xls, slightly edited to make it easier to read in)
translated_names <- read_excel("input/gdenamen_translation.xlsx", sheet = 1)[1:75,]

translated_names %<>%
  rename(name_official = Name_Official, name_d = Name_D, name_f = Name_F, name_i = Name_I)

lut_gem_new %<>%
  left_join(translated_names, by = "name_official") %>% 
  rename(id = gnr) %>% 
  rename(kt_id = kt) %>% 
  rename(sprachreg_id = sprachreg)

# further attribute selection
lut_agglotyp %<>% 
  select(typ_id, typ) %>% 
  rename(id = typ_id)

nrw_agglotyp <- read.csv2(file = "input/nrw_71_agglotyp.csv", sep = ";", stringsAsFactors = F)
nrw_agglotyp %<>% mutate(wert = as.numeric(wert)) %>% 
  rename(agglotyp_id = agglotyp)

nrw_gem %<>% mutate(wert = as.numeric(Wert)) %>% 
  rename(gem_id = bfs, partei = Partei) %>% 
  select(-Wert)

nrw_sprachreg <- read.csv2(file = "input/nrw_71_sprachreg.csv", sep = ";", stringsAsFactors = F)
nrw_sprachreg %<>% mutate(wert = as.numeric(Wert)) %>% 
  rename(sprachreg_id = sprachreg, partei = Partei) %>% 
  select(-Wert)

nrw_kt <- read.csv2(file = "input/nrw_71_kt.csv", sep = ";", stringsAsFactors = F)
nrw_kt %<>% mutate(wert = as.numeric(Wert)) %>% 
  rename(kt_id = kt, partei = Partei) %>% 
  select(-Wert)

polit_raum_agglotyp <- read.csv2(file = "input/polit_raum_agglotyp.csv", sep = ";", stringsAsFactors = F)
polit_raum_agglotyp %<>%
  inner_join(anz_gultig_agglotyp, by = c("jahr", "agglotyp")) %>% 
  rename(anz_gultig = anz) %>%
  inner_join(anz_stber_agglotyp, by = c("jahr", "agglotyp")) %>% 
  rename(anz_stber = anz) 
polit_raum_agglotyp %<>% mutate(wert = as.numeric(wert)) 
polit_raum_agglotyp %<>% rename(agglotyp_id = agglotyp)

polit_raum_sprachreg <- read.csv2(file = "input/polit_raum_sprachreg.csv", sep = ";", stringsAsFactors = F)
polit_raum_sprachreg %<>% rename(jahr = Jahr)
polit_raum_sprachreg %<>%
  inner_join(anz_gultig_sprachreg, by = c("jahr", "sprachreg")) %>% 
  rename(anz_gultig = anz) %>%
  inner_join(anz_stber_sprachreg, by = c("jahr", "sprachreg")) %>% 
  rename(anz_stber = anz) 
polit_raum_sprachreg %<>% mutate(wert = as.numeric(wert)) 
polit_raum_sprachreg %<>% rename(sprachreg_id = sprachreg)

polit_raum_gem <- read.csv2(file = "input/polit_raum_gem.csv", sep = ";", stringsAsFactors = F)
anz_gultig_gem <- read.csv2(file = "input/anz_gultig_gem.csv", sep = ";", stringsAsFactors = F)
anz_stber_gem <- read.csv2(file = "input/anz_stber_gem.csv", sep = ";", stringsAsFactors = F)
polit_raum_gem %<>%
  inner_join(anz_gultig_gem, by = c("jahr", "gnr")) %>% 
  rename(anz_gultig = anz) %>%
  inner_join(anz_stber_gem, by = c("jahr", "gnr")) %>% 
  rename(anz_stber = anz) 
polit_raum_gem %<>% rename(gem_id = gnr)
polit_raum_gem %<>% mutate(wert = as.numeric(wert)) 

polit_raum_kt <- read.csv2(file = "input/polit_raum_kt.csv", sep = ";", stringsAsFactors = F)
anz_gultig_kt <- read.csv2(file = "input/anz_gultig_kt.csv", sep = ";", stringsAsFactors = F)
anz_stber_kt <- read.csv2(file = "input/anz_stber_kt.csv", sep = ";", stringsAsFactors = F)
polit_raum_kt %<>%
  inner_join(anz_gultig_kt, by = c("jahr", "kt")) %>% 
  rename(anz_gultig = anz) %>%
  inner_join(anz_stber_kt, by = c("jahr", "kt")) %>% 
  rename(anz_stber = anz) 
polit_raum_kt %<>% mutate(wert = as.numeric(wert)) 
polit_raum_kt %<>% rename(kt_id = kt)


# write out the other files
write.table(parties, file = "output/parties.csv", row.names = F, na = "", sep = ",", quote = T)

write.table(lut_gem_new, file = "output/lut_gem.csv", row.names = F, na = "", sep = ",", quote = T)
write.table(lut_agglotyp, file = "output/lut_agglotyp.csv", row.names = F, na = "", sep = ",", quote = T)

write.table(nrw_agglotyp, file = "output/nrw_agglotyp.csv", row.names = F, na = "", sep = ",", quote = T)
write.table(nrw_gem, file = "output/nrw_gem.csv", row.names = F, na = "", sep = ",", quote = T)
write.table(nrw_kt, file = "output/nrw_kt.csv", row.names = F, na = "", sep = ",", quote = T)
write.table(nrw_sprachreg, file = "output/nrw_sprachreg.csv", row.names = F, na = "", sep = ",", quote = T)

# write.table(nrw_71_gem, file = "output/nrw_71_gem.csv", row.names = F, na = "", sep = ",", quote = T) -> will follow, needs to be converted to wide format
write.table(polit_raum_agglotyp, file = "output/polit_raum_agglotyp.csv", row.names = F, na = "", sep = ",", quote = T)
write.table(polit_raum_gem, file = "output/polit_raum_gem.csv", row.names = F, na = "", sep = ",", quote = T)
write.table(polit_raum_sprachreg, file = "output/polit_raum_sprachreg.csv", row.names = F, na = "", sep = ",", quote = T)
write.table(polit_raum_kt, file = "output/polit_raum_kt.csv", row.names = F, na = "", sep = ",", quote = T)

Special analysis for SRF Tagesschau

The results of this analysis were published in the episode of Saturday, September 26th 2015, 19:30

Party strength losses and wins: Where?

# SP
# between 2003 and 2011
nrw_agglotyp_svp_sp %>% 
  filter(partei == "SP" & jahr >= 2003 & jahr <= 2011) %>% 
  group_by(agglotyp) %>% 
  summarize(diff = first(wert) - last(wert)) %>% 
  arrange(desc(diff))
## Source: local data frame [8 x 2]
## 
##                      agglotyp     diff
##                        (fctr)    (dbl)
## 1                  Grossstadt 6.224438
## 2     statusmittlere Gemeinde 5.124154
## 3                  Kleinstadt 4.786880
## 4 Kleinagglomerationsgemeinde 4.618933
## 5        statustiefe Gemeinde 4.170542
## 6                 Mittelstadt 4.132426
## 7         statushohe Gemeinde 3.395582
## 8          ländliche Gemeinde 3.154744

Between 2003 and 2011, the SP lost the most in big cities (6.2 percentage points), followed by middle-status municipalities (5.1). The party lost the least in rural municipalities (3.2).

# SVP
# between 1991 and 2007: the rise of SVP
nrw_agglotyp_svp_sp %>% 
  filter(partei == "SVP" & jahr >= 1991 & jahr <= 2007) %>% 
  group_by(agglotyp) %>% 
  summarize(diff = first(wert) - last(wert)) %>% 
  arrange(desc(diff))
## Source: local data frame [8 x 2]
## 
##                      agglotyp      diff
##                        (fctr)     (dbl)
## 1                  Grossstadt -12.51254
## 2         statushohe Gemeinde -15.82503
## 3     statusmittlere Gemeinde -15.99945
## 4          ländliche Gemeinde -16.25189
## 5                 Mittelstadt -18.72282
## 6        statustiefe Gemeinde -19.28740
## 7 Kleinagglomerationsgemeinde -19.34388
## 8                  Kleinstadt -19.54538
nrw_agglotyp_svp_sp %>% 
  filter(partei == "SVP" & jahr >= 2007 & jahr <= 2011) %>% 
  group_by(agglotyp) %>% 
  summarize(diff = first(wert) - last(wert)) %>% 
  arrange(desc(diff))
## Source: local data frame [8 x 2]
## 
##                      agglotyp     diff
##                        (fctr)    (dbl)
## 1        statustiefe Gemeinde 3.742414
## 2                  Grossstadt 3.677293
## 3     statusmittlere Gemeinde 2.693762
## 4                 Mittelstadt 2.421912
## 5 Kleinagglomerationsgemeinde 2.398610
## 6                  Kleinstadt 2.068013
## 7          ländliche Gemeinde 1.688183
## 8         statushohe Gemeinde 1.664444

Between 1991 and 2007, the SVP gained tremendously. They won the most voters in little cities (19.5 percentage points), closely followed by municipalities of small agglomerations (19.3) and low-status municipalities (19.3). The won the least in big cities (12.5). From 2007 to 2011, the party lost again, the most in low-status municipalities (3.7) and big cities (3.7), the least in high-status municipalities and rural municipalities (1.7 both).

# CVP: The slow decline
# between 1971 and 2011
nrw_agglotyp_cvp_fdp %>% 
  filter(partei == "CVP" & jahr >= 1971 & jahr <= 2011) %>% 
  group_by(agglotyp) %>% 
  summarize(diff = first(wert) - last(wert)) %>% 
  arrange(desc(diff))
## Source: local data frame [8 x 2]
## 
##                      agglotyp      diff
##                        (fctr)     (dbl)
## 1          ländliche Gemeinde 13.636592
## 2 Kleinagglomerationsgemeinde 10.426281
## 3                  Kleinstadt  8.311346
## 4                 Mittelstadt  7.847546
## 5        statustiefe Gemeinde  6.525694
## 6     statusmittlere Gemeinde  6.197461
## 7         statushohe Gemeinde  4.866242
## 8                  Grossstadt  4.348259

Between 1971 and 2011, the CVP continuously and steadily lost votes. The party lost the most in rural municipalities (13.6 percentage points), followed by municipalities of small agglomerations (10.4). They lost the least in big cities (4.3).

# FDP: The slow decline
# between 1979 and 2011
nrw_agglotyp_cvp_fdp %>% 
  filter(partei == "FDP" & jahr >= 1979 & jahr <= 2011) %>% 
  group_by(agglotyp) %>% 
  summarize(diff = first(wert) - last(wert)) %>% 
  arrange(desc(diff))
## Source: local data frame [8 x 2]
## 
##                      agglotyp      diff
##                        (fctr)     (dbl)
## 1         statushohe Gemeinde 16.938460
## 2                 Mittelstadt 16.183982
## 3                  Kleinstadt 15.119465
## 4 Kleinagglomerationsgemeinde 11.310805
## 5                  Grossstadt 11.192368
## 6        statustiefe Gemeinde 10.857251
## 7     statusmittlere Gemeinde 10.778217
## 8          ländliche Gemeinde  9.314937

Between 1979 and 2011, the FDP continuously and steadily lost votes. The party lost the most in high-status municipalities (16.9 percentage points), followed by middle cities (16.2). They lost the least in rural municipalities (9.3).