Site built with pkgdown {{#pkgdown}}{{version}}{{/pkgdown}}.
9 |
--------------------------------------------------------------------------------
/man/get_cantonalvotes.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/get_cantonalvotes.R
3 | \name{get_cantonalvotes}
4 | \alias{get_cantonalvotes}
5 | \title{Get cantonal results and counting status in real time or for selected dates or a time range in the past}
6 | \usage{
7 | get_cantonalvotes(
8 | geolevel = "municipality",
9 | votedates = NULL,
10 | from_date = NULL,
11 | to_date = NULL
12 | )
13 | }
14 | \arguments{
15 | \item{geolevel}{geographical level for which the results should be loaded. Options: "canton", "district", "municipality" or "zh_counting_districts".}
16 |
17 | \item{votedates}{dates of the ballots to be selected. Default: most recent ballot available. Format: "YYYY-MM-DD".}
18 |
19 | \item{from_date}{starting point in time from which vote results should be retrieved. Format: "YYYY-MM-DD".}
20 |
21 | \item{to_date}{end point in time to which vote results should be retrieved. Format: "YYYY-MM-DD".}
22 | }
23 | \value{
24 | a tibble containing the results
25 | }
26 | \description{
27 | \code{get_cantonalvotes} is one of the two main functions of swissvote package. It allows to retrieve the results and the counting status for national ballots.
28 | }
29 | \details{
30 | get_cantonalvotes - retrieve vote results for cantonal ballots at district- or municipality level for selected dates or a given date range.
31 | }
32 | \examples{
33 |
34 | # Select by range
35 | results <- get_cantonalvotes(
36 | geolevel = "district",
37 | from_date = "2019-01-01",
38 | to_date = "2019-12-31"
39 | )
40 |
41 | # Select specific votedate(s)
42 | get_cantonalvotes(votedates = "2019-02-10")
43 |
44 | # get the results at counting district level
45 | # yields the same result as the municipality level, with the
46 | # exception of Winterthur and Zurich,
47 | # where detailed counting district results are returned instead.
48 |
49 | get_cantonalvotes(votedate = "2019-09-22", geolevel = "zh_counting_districts")
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/man/plot_nationalvotes.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/plot_nationalvotes.R
3 | \name{plot_nationalvotes}
4 | \alias{plot_nationalvotes}
5 | \title{Plot National Votes}
6 | \usage{
7 | plot_nationalvotes(
8 | votedate = NULL,
9 | vote_id = NULL,
10 | geolevel = "municipality",
11 | measure = "result",
12 | standardize = T,
13 | lakes = T,
14 | language = "DE",
15 | theme = "srf"
16 | )
17 | }
18 | \arguments{
19 | \item{votedate}{date of the ballot. Default: most recent ballot available.}
20 |
21 | \item{vote_id}{id of the vote. Default: first id mentioned in the data set.}
22 |
23 | \item{geolevel}{geographical level. Options: "canton", "district", "municipality" or "zh_counting_districts".}
24 |
25 | \item{measure}{measure to color the administrative units. Options: "result" for the yes vote share or "turnout"
26 | for the voter turnout of a given vote.}
27 |
28 | \item{standardize}{if \code{TRUE}, the scale of the measure ranges from 0 to 100 percent. Recommended for comparisons
29 | between votes.}
30 |
31 | \item{lakes}{if \code{TRUE}, the largest Swiss lakes are shown in color on the map.}
32 |
33 | \item{language}{defines the language. Options: "DE" for German, "FR" for French, "IT" for Italian or "RM" for Romansh.}
34 |
35 | \item{theme}{defines basic appearance of the map. Five options are available: "srf" for a theme inspired by the
36 | plots of Swiss Radio and Television, and "A" to "E" for the viridis color scales magma, inferno, plasma, viridis and cividis.}
37 | }
38 | \value{
39 | a ggplot object
40 | }
41 | \description{
42 | \code{plot_nationalvotes} plots the results of national votes in a choropleth map using ggplot2.
43 | }
44 | \examples{
45 |
46 | # Plot the most recent national vote
47 | plot_nationalvotes()
48 | \donttest{
49 | # Plot a specific national vote at cantonal level
50 | plot_nationalvotes(
51 | votedate = "2014-02-09",
52 | vote_id = 5800,
53 | geolevel = "canton"
54 | )
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/man/get_nationalvotes.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/get_nationalvotes.R
3 | \name{get_nationalvotes}
4 | \alias{get_nationalvotes}
5 | \title{Get national results and counting status in real time or for selected dates or a time range in the past}
6 | \usage{
7 | get_nationalvotes(
8 | geolevel = "municipality",
9 | votedates = NULL,
10 | from_date = NULL,
11 | to_date = NULL,
12 | language = "DE"
13 | )
14 | }
15 | \arguments{
16 | \item{geolevel}{geographical level for which the results should be loaded. Options: "national", "canton", "district", "municipality" or "zh_counting_districts".#' @param votedates dates of the ballots to be selected. Default: most recent ballot available. Format: "YYYY-MM-DD".}
17 |
18 | \item{votedates}{dates of the ballots to be selected. Default: most recent ballot available. Format: "YYYY-MM-DD".}
19 |
20 | \item{from_date}{starting point in time from which vote results should be retrieved. Format: "YYYY-MM-DD".}
21 |
22 | \item{to_date}{end point in time to which vote results should be retrieved. Format: "YYYY-MM-DD".}
23 |
24 | \item{language}{defines the language of the vote title. Options: "DE" for German, "FR" for French, "IT" for Italian or "RM" for Romansh.}
25 | }
26 | \value{
27 | a tibble containing the results
28 | }
29 | \description{
30 | \code{get_nationalvotes} is one of the two main functions of swissvote package. It allows to retrieve the results and the counting status for national ballots.
31 | }
32 | \details{
33 | get_nationalvotes - retrieve vote results for national ballots at district- or municipality level for selected dates or a given date range.
34 | }
35 | \examples{
36 | \donttest{
37 | # Selection by range
38 | results <- get_nationalvotes(
39 | geolevel = "district",
40 | from_date = "2018-01-01",
41 | to_date = "2018-12-31"
42 | )
43 |
44 | # Selection by end date only
45 | get_nationalvotes(to_date = "1983-12-04")
46 | }
47 | # Selection of a specific vote date
48 | get_nationalvotes(votedates = "2014-02-09")
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/docs/bootstrap-toc.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap Table of Contents v0.4.1 (http://afeld.github.io/bootstrap-toc/)
3 | * Copyright 2015 Aidan Feldman
4 | * Licensed under MIT (https://github.com/afeld/bootstrap-toc/blob/gh-pages/LICENSE.md) */
5 |
6 | /* modified from https://github.com/twbs/bootstrap/blob/94b4076dd2efba9af71f0b18d4ee4b163aa9e0dd/docs/assets/css/src/docs.css#L548-L601 */
7 |
8 | /* All levels of nav */
9 | nav[data-toggle='toc'] .nav > li > a {
10 | display: block;
11 | padding: 4px 20px;
12 | font-size: 13px;
13 | font-weight: 500;
14 | color: #767676;
15 | }
16 | nav[data-toggle='toc'] .nav > li > a:hover,
17 | nav[data-toggle='toc'] .nav > li > a:focus {
18 | padding-left: 19px;
19 | color: #563d7c;
20 | text-decoration: none;
21 | background-color: transparent;
22 | border-left: 1px solid #563d7c;
23 | }
24 | nav[data-toggle='toc'] .nav > .active > a,
25 | nav[data-toggle='toc'] .nav > .active:hover > a,
26 | nav[data-toggle='toc'] .nav > .active:focus > a {
27 | padding-left: 18px;
28 | font-weight: bold;
29 | color: #563d7c;
30 | background-color: transparent;
31 | border-left: 2px solid #563d7c;
32 | }
33 |
34 | /* Nav: second level (shown on .active) */
35 | nav[data-toggle='toc'] .nav .nav {
36 | display: none; /* Hide by default, but at >768px, show it */
37 | padding-bottom: 10px;
38 | }
39 | nav[data-toggle='toc'] .nav .nav > li > a {
40 | padding-top: 1px;
41 | padding-bottom: 1px;
42 | padding-left: 30px;
43 | font-size: 12px;
44 | font-weight: normal;
45 | }
46 | nav[data-toggle='toc'] .nav .nav > li > a:hover,
47 | nav[data-toggle='toc'] .nav .nav > li > a:focus {
48 | padding-left: 29px;
49 | }
50 | nav[data-toggle='toc'] .nav .nav > .active > a,
51 | nav[data-toggle='toc'] .nav .nav > .active:hover > a,
52 | nav[data-toggle='toc'] .nav .nav > .active:focus > a {
53 | padding-left: 28px;
54 | font-weight: 500;
55 | }
56 |
57 | /* from https://github.com/twbs/bootstrap/blob/e38f066d8c203c3e032da0ff23cd2d6098ee2dd6/docs/assets/css/src/docs.css#L631-L634 */
58 | nav[data-toggle='toc'] .nav > .active > ul {
59 | display: block;
60 | }
61 |
--------------------------------------------------------------------------------
/docs/docsearch.js:
--------------------------------------------------------------------------------
1 | $(function() {
2 |
3 | // register a handler to move the focus to the search bar
4 | // upon pressing shift + "/" (i.e. "?")
5 | $(document).on('keydown', function(e) {
6 | if (e.shiftKey && e.keyCode == 191) {
7 | e.preventDefault();
8 | $("#search-input").focus();
9 | }
10 | });
11 |
12 | $(document).ready(function() {
13 | // do keyword highlighting
14 | /* modified from https://jsfiddle.net/julmot/bL6bb5oo/ */
15 | var mark = function() {
16 |
17 | var referrer = document.URL ;
18 | var paramKey = "q" ;
19 |
20 | if (referrer.indexOf("?") !== -1) {
21 | var qs = referrer.substr(referrer.indexOf('?') + 1);
22 | var qs_noanchor = qs.split('#')[0];
23 | var qsa = qs_noanchor.split('&');
24 | var keyword = "";
25 |
26 | for (var i = 0; i < qsa.length; i++) {
27 | var currentParam = qsa[i].split('=');
28 |
29 | if (currentParam.length !== 2) {
30 | continue;
31 | }
32 |
33 | if (currentParam[0] == paramKey) {
34 | keyword = decodeURIComponent(currentParam[1].replace(/\+/g, "%20"));
35 | }
36 | }
37 |
38 | if (keyword !== "") {
39 | $(".contents").unmark({
40 | done: function() {
41 | $(".contents").mark(keyword);
42 | }
43 | });
44 | }
45 | }
46 | };
47 |
48 | mark();
49 | });
50 | });
51 |
52 | /* Search term highlighting ------------------------------*/
53 |
54 | function matchedWords(hit) {
55 | var words = [];
56 |
57 | var hierarchy = hit._highlightResult.hierarchy;
58 | // loop to fetch from lvl0, lvl1, etc.
59 | for (var idx in hierarchy) {
60 | words = words.concat(hierarchy[idx].matchedWords);
61 | }
62 |
63 | var content = hit._highlightResult.content;
64 | if (content) {
65 | words = words.concat(content.matchedWords);
66 | }
67 |
68 | // return unique words
69 | var words_uniq = [...new Set(words)];
70 | return words_uniq;
71 | }
72 |
73 | function updateHitURL(hit) {
74 |
75 | var words = matchedWords(hit);
76 | var url = "";
77 |
78 | if (hit.anchor) {
79 | url = hit.url_without_anchor + '?q=' + escape(words.join(" ")) + '#' + hit.anchor;
80 | } else {
81 | url = hit.url + '?q=' + escape(words.join(" "));
82 | }
83 |
84 | return url;
85 | }
86 |
--------------------------------------------------------------------------------
/NEWS.md:
--------------------------------------------------------------------------------
1 |
2 | # swissdd 1.1.5
3 |
4 | * fix for `get_geodata(geolevel="canton")` added
5 |
6 | # swissdd 1.1.4.999
7 |
8 |
9 | * fix for `get_geodata(geolevel="zh_counting_districts")` and additional checks to ensure functions fail gracefully in case of availability and connection problems.
10 |
11 | # swissdd 1.1.3
12 |
13 | * fix for `get_geodata() / plot_nationalvotes()`
14 |
15 | * gentle failure of the swissdd-function in case of errors in the data source / problems with the API
16 |
17 | * bug fixes for get_geodata() / plot_nationalvotes()
18 |
19 | # swissdd 1.1.1
20 |
21 | * minor modification to the 'plot_cantonalvotes()'-function to ensure that the function works even when there are no results yet for an upcoming vote.
22 |
23 | # swissdd 1.1.0
24 |
25 | * major update : new functions to retrieve geodata of voting districts and plot vote results maps have been added by [David Zumbach](https://github.com/zumbov2) (Thanks a thousand David!)
26 |
27 | # swissdd 1.0.4
28 |
29 | * the `canton_json_to_dfr` has been adapted to work with the altered data structure of the json files provided via opendata.swiss containing the cantonal vote results.
30 |
31 | # swissdd 1.0.3
32 |
33 | * broken examples fixed
34 |
35 | # swissdd 1.0.2
36 |
37 | * Functions renamed (_*breaking change*_): to retrieve real time or archive results data via opendata.swiss, the function `get_nationalvotes` must be used. `get_swissvotes` is turned into the function that allows to get Swissvotes-Data.
38 |
39 | * New function that allows to retrieve Swissvotes-Data added (`get_swissvotes`)
40 |
41 | * option to retrieve counting district level data with the `get_nationalvotes()`- and `get_cantonalvotes()`-functions added (affects Zurich and Winterthur only)
42 |
43 | * functions adapted to new tidyr API, the package now depends on tidyr >= 1.0.0.
44 |
45 | # swissdd 1.0.1
46 |
47 | * Added parent level ids to district and municipality level results (canton / district)
48 | * New function that calculates correlations between votes added: `similar_votes()`
49 | * Better error handling with fallback if votedate information provided on opendata.swiss is corrupt
50 | * Improved speed of data-retrieval. Comparison:
51 |
52 | `system.time(get_swissvotes(to_date="1983-12-04"))`
53 |
54 | user system elapsed
55 |
56 | 2.492 0.160 47.024
57 |
58 | *comparison with swissdd version 1.0.0*
59 |
60 | user system elapsed
61 |
62 | 3.120 0.276 64.693
63 |
--------------------------------------------------------------------------------
/R/zzz.R:
--------------------------------------------------------------------------------
1 | # package startup-message (data source displayed)
2 |
3 | #' Title
4 | #'
5 | #' @param lib
6 | #' @param pkg
7 | #' @importFrom utils packageVersion
8 | #'
9 | #' @examples
10 |
11 | .onAttach <- function(lib, pkg)
12 | {
13 | packageStartupMessage('************************************************************')
14 | packageStartupMessage(paste('* swissdd',utils::packageVersion("swissdd"),' *'))
15 | packageStartupMessage('* developed by politan.ch *')
16 | packageStartupMessage('* *')
17 | packageStartupMessage('* Data sources: *')
18 | packageStartupMessage('* Federal Statistical Office *')
19 | packageStartupMessage('* https://www.bfs.admin.ch/ *')
20 | packageStartupMessage('* *')
21 | packageStartupMessage('* Swissvotes *')
22 | packageStartupMessage('* https://swissvotes.ch/ *')
23 | packageStartupMessage('* *')
24 | packageStartupMessage('************************************************************')
25 | }
26 |
27 | # suppress notes
28 |
29 | utils::globalVariables(c("id", "resultat","res","geoid",
30 | "kantId",
31 | "canton_id","canton_name",
32 | "district_id","district_name",
33 | "mun_id","mun_name",
34 | "langKey",
35 | "yes",
36 | "anr",
37 | "name",
38 | # "kantonname",
39 | "results2",
40 | "geoLevelnummer",
41 | "geoLevelname",
42 | "ktdata_zh",
43 | "jaStimmenInProzent",
44 | "cor","cov","correlation",
45 | "text",
46 | "measure", "vogeId", "geometry",
47 | "bezkId", "stimmbeteiligungInProzent",
48 | "text",#,"ktid"
49 | "packageVersion"
50 | ))
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://www.gnu.org/licenses/gpl-2.0.en.html)
2 | [](http://cran.rstudio.com/web/packages/swissdd/index.html)
3 | [](https://github.com/politanch/swissdd/actions?workflow=R-CMD-check)
4 |
5 | # swissdd
6 |
7 | ## the swiss direct democracy R package
8 |
9 |
10 |
11 |
12 | ### Installation
13 | Swissdd can be installed from github (on going updates) `devtools::install_github("politanch/swissdd")`.
14 |
15 |
16 | `swissdd` builds upon real time data service for federal and cantonal votes provided by the __Federal Statistical Office__ via [opendata.swiss](https://opendata.swiss/de/). It brings the results of popular votes, aggregated at the geographical level of choice, straight into R. Available levels are
17 |
18 | * national
19 | * cantons
20 | * districts
21 | * municipalities
22 |
23 | The package wraps the real time data on vote Sundays. As soon as the ballots close (from 12:00 on), the datastream is continuosly updated, until the data for all municipalities is complete and the final results are available. Additionally, it allows to access the archive and to retrieve the *harmonized* results of national votes since 1981. Thanks to a major contribution of [David Zumbach](https://github.com/zumbov2) the latest version contains new functions to retrieve geodata of administrative boundaries and plot vote results maps.
24 |
25 | It also allows to retrieve data from the [swissvotes-database](https://swissvotes.ch/page/home), the most comprehensive database on swiss popular votes.
26 |
27 | The documentation is available here: https://politanch.github.io/swissdd/
28 |
29 | The webservice of the FSO is documented on opendata.swiss (language settings can be found at the bottom of the page):
30 |
31 | https://opendata.swiss/de/dataset/echtzeitdaten-am-abstimmungstag-zu-eidgenoessischen-abstimmungsvorlagen
32 |
33 | https://opendata.swiss/de/dataset/echtzeitdaten-am-abstimmungstag-zu-kantonalen-abstimmungsvorlagen
34 |
35 | ## More data on Swiss politics
36 | - [DigDemLab](https://digdemlab.io/)
37 | - [swissparl](https://github.com/zumbov2/swissparl)
38 | - [swissvotes](https://swissvotes.ch/)
39 |
--------------------------------------------------------------------------------
/.github/workflows/pkgdown.yaml:
--------------------------------------------------------------------------------
1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
3 | on:
4 | push:
5 | branches: [main, master]
6 | pull_request:
7 | branches: [main, master]
8 | release:
9 | types: [published]
10 | workflow_dispatch:
11 |
12 | name: pkgdown
13 |
14 | jobs:
15 | pkgdown:
16 | runs-on: ubuntu-latest
17 | # Only restrict concurrency for non-PR jobs
18 | concurrency:
19 | group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }}
20 | env:
21 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
22 | permissions:
23 | contents: write
24 | steps:
25 | - uses: actions/checkout@v4
26 |
27 | - uses: r-lib/actions/setup-pandoc@v2
28 |
29 | - uses: r-lib/actions/setup-r@v2
30 | with:
31 | use-public-rspm: true
32 |
33 | - uses: r-lib/actions/setup-r-dependencies@v2
34 | with:
35 | extra-packages: any::pkgdown, local::.
36 | needs: website
37 |
38 | - uses: r-lib/actions/setup-pandoc@v1
39 |
40 | - name: Query dependencies
41 | run: |
42 | install.packages('remotes')
43 | saveRDS(remotes::dev_package_deps(dependencies = TRUE), ".github/depends.Rds", version = 2)
44 | writeLines(sprintf("R-%i.%i", getRversion()$major, getRversion()$minor), ".github/R-version")
45 | shell: Rscript {0}
46 |
47 | - name: Cache R packages
48 | uses: actions/cache@v2
49 | with:
50 | path: ${{ env.R_LIBS_USER }}
51 | key: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1-${{ hashFiles('.github/depends.Rds') }}
52 | restore-keys: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1-
53 |
54 | - name: Install dependencies
55 | run: |
56 | remotes::install_deps(dependencies = TRUE)
57 | install.packages(c("RSwissMaps","viridis"), type = "binary")
58 | remotes::install_github("politanch/swissdd")
59 | shell: Rscript {0}
60 |
61 |
62 | - name: Install package
63 | run: R CMD INSTALL .
64 |
65 | - name: Build site
66 | run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE)
67 | shell: Rscript {0}
68 |
69 | - name: Deploy to GitHub pages 🚀
70 | if: github.event_name != 'pull_request'
71 | uses: JamesIves/github-pages-deploy-action@v4.5.0
72 | with:
73 | clean: false
74 | branch: gh-pages
75 | folder: docs
76 |
--------------------------------------------------------------------------------
/R/get_poll.R:
--------------------------------------------------------------------------------
1 | #' Download poll data collected after a national vote by gfs.bern and the political science departements of the universities of Berne, Zurich, and Geneva.
2 | #'
3 | #' \code{get_poll} downloads exit poll data Please cite data.
4 | #'
5 | #' get_poll - retrieve poll data on votes. The unit of analysis are individuals.
6 | #'
7 | #' @param bfsnr number of identification of the vote, by default = NULL. Polls available after September 2020 (from voteid 6360 onwards). Bfsnr corresponds to anr in swissvotes data and has to be four digits (available through get_swissvotes).
8 | #' @param codebook by default = FALSE. If TRUE navigates your browser to the codebook if available.
9 | #' @importFrom RCurl getBinaryURL
10 | #' @export
11 | #'
12 | #' @return a tibble containing the results
13 | #'
14 | #' @examples
15 | #' results <- get_poll(bfsnr=6360, codebook=FALSE)
16 |
17 | get_poll <- function(bfsnr = NULL, codebook = F) {
18 |
19 | if(is.null(bfsnr)) stop("Identifier number of vote (voteid) has to be specified. See for the variable `anr` obtained through swissdd::get_swissvotes ")
20 | if(class(bfsnr)=="numeric"){
21 | if(nchar(bfsnr)<4) stop("vote identifier has to have four digits, for example 6360")
22 | if(bfsnr<6360) stop("Polls are available from November 2020 onwards. Hence, bfsnr cannot be smaller than 6360.")
23 | }
24 |
25 | #create download url
26 | if(nchar(bfsnr/10)==3) voteid <- paste0((bfsnr/10),".00")
27 | if(nchar(bfsnr/10)==5) voteid <- paste0((bfsnr/10),"0")
28 | download_url <- paste0("https://swissvotes.ch/vote/", voteid, "/nachbefragung.csv")
29 |
30 | safe_csv <-purrr::possibly(utils::read.csv, otherwise=tibble())
31 |
32 | #download data
33 |
34 | exitpoll <- try(utils::read.csv(
35 | file=download_url,
36 | sep=",",
37 | header=T,
38 | fileEncoding="latin1",
39 | stringsAsFactors = F
40 | ))
41 |
42 | if(class(exitpoll)=="try-error"){
43 | stop("Data is not (yet) available on swissvotes.\nFollow @swissvotes on Twitter for update information.")
44 | }else{
45 | message("Data is downloaded from swissvotes.")
46 | }
47 |
48 |
49 |
50 |
51 | if (codebook) {
52 | codebook_url <- z <- vector("list", 2)
53 | codebook_url[[1]] <- paste0("https://swissvotes.ch/vote/", voteid, "/nachbefragung-codebuch-de.xlsx")
54 | codebook_url[[2]] <- paste0("https://swissvotes.ch/vote/", voteid, "/nachbefragung-codebuch-de.pdf")
55 |
56 | #test which version is available (xlsx or pdf)
57 | for(i in 1:2){
58 | try(z[[i]] <- RCurl::getBinaryURL(codebook_url[[i]], failonerror = TRUE))
59 | }
60 |
61 | pdf_or_xlsx <- which(!is.null(z))
62 | utils::browseURL(codebook_url[[pdf_or_xlsx]])
63 | }
64 |
65 |
66 | return(exitpoll)
67 |
68 | }
69 |
70 |
--------------------------------------------------------------------------------
/vignettes/plot_correlations.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Find and plot correlated vote results"
3 | output: rmarkdown::html_vignette
4 | ---
5 |
6 | ```{r, include = FALSE}
7 | knitr::opts_chunk$set(
8 | collapse = TRUE,
9 | comment = "#>"
10 | )
11 | ```
12 |
13 | ```{r setup, include=FALSE}
14 | knitr::opts_chunk$set(echo = TRUE)
15 | ```
16 |
17 |
18 | ### What other votes correlate with the 2014 vote on immigration (mei)?
19 |
20 | For the sake of illustration we are interested in the result of the 2014 vote on immigration for Swiss municipalities. The initiative was voted upon on 9th of February 2014. There are different ways to find this exact vote. One of them is using the information in the title. An other solution would be to look up the `id` provided by the FSO.
21 |
22 | `unique(federalvotes$name[grep("Massen", federalvotes$name)])`
23 |
24 | First, we invoke the necessary packages and use the function `get_nationalvotes` to access the data. We further specify the unit of analysis as well as the range.
25 |
26 |
27 | ```{r echo=TRUE, warning=FALSE, message=FALSE}
28 | # installation from CRAN (stable)
29 | # install.packages("swissdd")
30 | # install.packages("dplyr")
31 |
32 | # installation from github (ongoing updates)
33 | # devtools::install_github("politanch/swissdd")
34 |
35 | library(swissdd)
36 | library(dplyr)
37 | library(ggplot2)
38 | library(tidyr)
39 |
40 | #get results of all votes between 2010-2019
41 | federalvotes <- get_nationalvotes(geolevel = "municipality",
42 | from_date = "2010-03-07",
43 | to_date = "2020-09-27")
44 |
45 | #get correlations for votes on municipal level with mei
46 | simvotes <- similar_votes(federalvotes, id=5800, from=.4, to=.6)
47 | simvotes
48 |
49 | #extract names of correlated votes
50 | ballotnames <- federalvotes %>%
51 | dplyr::select(name, id, mun_id)%>%
52 | filter(id%in%c(5800, simvotes[2,1]))%>%
53 | distinct(name)
54 |
55 |
56 | #get correlations for votes on municipal level with mei
57 | simvotes <- similar_votes(federalvotes, id=6310, from=.3, to=1)
58 | simvotes
59 |
60 | #extract names of correlated votes
61 | ballotnames <- federalvotes %>%
62 | dplyr::select(name, id, mun_id)%>%
63 | filter(id%in%c(6310, simvotes[1,1]))%>%
64 | distinct(name)
65 |
66 | #subset for correlated votes
67 | corrvotes <- federalvotes %>%
68 | filter(id%in%c(6310, simvotes[1,1]))%>%
69 | dplyr::select(id, jaStimmenInProzent, mun_id)%>%
70 | mutate(id=as.character(id))
71 |
72 | #plot
73 | corrvotes%>%
74 | pivot_wider(names_from="id", values_from="jaStimmenInProzent")%>%
75 | ggplot(aes(y=`6310`, x=`5800`))+
76 | geom_point(alpha=.2)+
77 | scale_y_continuous(limits=c(0,100))+
78 | scale_x_continuous(limits=c(0,100))+
79 | geom_abline(intercept = 0, slope=1, size=.1)+
80 | geom_smooth(method="lm", size=.3, color="orange")+
81 | labs(y="BGI, Yes Shares", x="MEI, Yes Shares", caption="politan.ch")+
82 | theme_bw()
83 |
84 | ```
85 |
--------------------------------------------------------------------------------
/vignettes/use_SwissvotesDB.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Retrieve Swissvotes-Data"
3 | output: rmarkdown::html_vignette
4 | ---
5 |
6 | ```{r, include = FALSE}
7 | knitr::opts_chunk$set(
8 | collapse = TRUE,
9 | comment = "#>"
10 | )
11 | ```
12 |
13 | ### How to Use Swissvotes Data
14 |
15 | First, you have to make sure that your machine is connected to the internet.
16 |
17 | ```{r echo=TRUE, warning=FALSE, message=FALSE}
18 | # installation from CRAN (stable)
19 | # install.packages("swissdd")
20 | # install.packages("dplyr")
21 |
22 | # installation from github (ongoing updates)
23 | # devtools::install_github("politanch/swissdd")
24 |
25 | Sys.setlocale('LC_ALL','C')
26 |
27 | library(swissdd)
28 | library(dplyr)
29 |
30 | ```
31 |
32 |
33 | By default, function extracts only the database provided by [Swissvotes](https://swissvotes.ch){target="_blank"}. However, you can specify that you want the codebook as well (or just the codebook for that matter). Additionally, there is a specification to save the citation. If you work with data from Swissvotes, we recommend to save the citation.
34 |
35 | If you don't specify anything the function will be executed with the following specifications.
36 |
37 | ```{r echo=TRUE, warning=FALSE, message=FALSE, eval=FALSE}
38 | #default
39 | swissvotesDB <- get_swissvotes(DB=T, savecitation=F, codebook=F)
40 | ```
41 |
42 | ```{r echo=FALSE, warning=FALSE, message=FALSE}
43 | #default
44 | swissvotesDB <- get_swissvotes()
45 | ```
46 |
47 | If you use the parameter `codebook=T` the function will download the data and direct your browser automatically to the codebook. If you're only interested in the codebook (you accessed the data already, for example), set the parameter `DB` to `FALSE`.
48 |
49 | Since we loaded the data already, there is no need to download it again.
50 |
51 | ```{r echo=TRUE, warning=FALSE, message=FALSE, eval=FALSE}
52 |
53 | get_swissvotes(DB=F, codebook=T)
54 |
55 | ```
56 |
57 | For illustration, we are interested in the upcoming vote on affordable housing (date of vote 9th of February, 2020). The variable `titel_kurz_d` or `titel_kurz_f` contains the title of a vote (shorter version than the official title `titel_off_d` or `titel_off_f`). Please note that the letter `d` indicates the German title, while the letter `f` gives you the French title. Since we don't know too much about the initiative we can read about it as well. First, we find the position in the data and then use the hyperlink provided to read about it.
58 |
59 | ```{r echo=TRUE, warning=FALSE, message=FALSE, eval=TRUE}
60 |
61 | #get position in dataset
62 | pos_ah <- grep("bezahlbare Wohnungen", swissvotesDB$titel_kurz_d)
63 |
64 | #extract url that directs you to more information about the vote
65 | mei_url <- swissvotesDB$anneepolitique[pos_ah]
66 |
67 | #mei_url
68 | #"https://anneepolitique.swiss/prozesse/56996-volksinitiative-mehr-bezahlbare-wohnungen"
69 | ```
70 |
71 | ```{r echo=TRUE, warning=FALSE, message=FALSE, eval=FALSE}
72 | #access url
73 | browseURL(mei_url)
74 | ```
75 |
76 |
77 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/index.md:
--------------------------------------------------------------------------------
1 | # swissdd
2 |
3 | [](https://cran.r-project.org/package=swissdd)
4 | [](https://www.gnu.org/licenses/gpl-2.0.en.html)
5 | [](http://cran.rstudio.com/web/packages/swissdd/index.html)
6 |
7 | ## the swiss direct democracy R package
8 |
9 |
10 | `swissdd` builds upon the real time data service for federal and cantonal votes provided by the __Federal Statistical Office__ (FSO) via [opendata.swiss](https://opendata.swiss/de/). It brings the results of popular votes, aggregated at the geographical level of choice, straight into R. Available levels are
11 |
12 | * national
13 | * cantons
14 | * districts
15 | * municipalities
16 |
17 | The package wraps the real time data on vote Sundays. As soon as the ballots close (from 12:00 on), the datastream is continuosly updated, until the data for all municipalities is complete and the final results are available. Additionally, it allows to access the archive and to retrieve the *harmonized* results of national votes since 1981. Additionally, it allows to access the archive and to retrieve the *harmonized* results of national votes since 1981, as well as loading data from the [swissvotes-database](https://swissvotes.ch/page/home), the most comprehensive database on swiss popular votes.
18 |
19 | Thanks to a major contribution of [David Zumbach](https://github.com/zumbov2) the latest version contains brand new functions to retrieve geodata of administrative boundaries provided by the FSO and to plot vote result maps.
20 |
21 | ```
22 | # installation from github
23 | devtools::install_github("politanch/swissdd")
24 |
25 | #realtimedata on vote-sundays or the data of the last votations
26 | federalvotes <- get_nationalvotes(geolevel = "district")
27 |
28 | #retrieve data for many votes from the archive, either by selecting indiviual dates...
29 | federalvotes <- get_nationalvotes(votedates=c("2019-02-10","1984-09-23"), geolevel = "district")
30 |
31 | #... or defining a range.
32 | federalvotes <- get_nationalvotes(from_date="2017-01-01",to_date="2018-01-01", geolevel = "district")
33 |
34 | # the results of cantonal votes are also available (2019-)
35 |
36 | cantonalvotes <- get_cantonalvotes(votedates="2019-02-10", geolevel = "municipality")
37 | ```
38 |
39 | The webservice of the FSO is documented on opendata.swiss (language settings can be found at the bottom of the page):
40 |
41 | https://opendata.swiss/de/dataset/echtzeitdaten-am-abstimmungstag-zu-eidgenoessischen-abstimmungsvorlagen
42 |
43 | https://opendata.swiss/de/dataset/echtzeitdaten-am-abstimmungstag-zu-kantonalen-abstimmungsvorlagen
44 |
45 | The geodata for the administrative boundaries can be found here:
46 |
47 | https://opendata.swiss/de/dataset/geodaten-zu-den-eidgenoessischen-abstimmungsvorlagen
48 |
49 | ## More data on Swiss politics
50 | - [DigDemLab](https://digdemlab.io/)
51 | - [swissparl](https://github.com/zumbov2/swissparl)
52 | - [swissvotes](https://swissvotes.ch/)
53 |
--------------------------------------------------------------------------------
/docs/extra.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Raleway|Ubuntu+Mono');
2 |
3 | h1, h2, h3, h4, h5{
4 | color: #293c55;
5 | }
6 |
7 | body{
8 | font-family: 'Raleway', sans-serif;
9 | }
10 |
11 | code{
12 | font-family: 'Ubuntu Mono', monospace;
13 | }
14 |
15 | p>a {
16 | color: #FF6B00;
17 | }
18 |
19 | li>a {
20 | color: #FF6B00;
21 | }
22 |
23 | .sourceCode>a{
24 | color: #293c55;
25 | }
26 |
27 | .st{
28 | color: #B03A5B;
29 | }
30 |
31 | .op{
32 | color: #666666;
33 | }
34 |
35 | .dv, .dt{
36 | color: #191919;
37 | }
38 |
39 | .btn-copy-ex{
40 | background-color: #B03A5B;
41 | border-color: #293c55;
42 | }
43 |
44 | .btn-copy-ex:hover{
45 | background-color: #293c55;
46 | border-color: #B03A5B;
47 | }
48 |
49 |
50 | pre {
51 | box-shadow:
52 | rgba(0, 0, 0, 0.1) 0 2px 3px 1px,
53 | rgba(0, 0, 0, 0.1) 0 1px 3px 1px,
54 | rgba(0, 0, 0, 0.2) 0 1px 1px -1px;
55 | }
56 |
57 | .navbar-default{
58 | border-color: #293c55;
59 | background-color: #FF6B00;
60 | }
61 |
62 | .navbar-default .navbar-nav>li>a {
63 | color:#fff;
64 | }
65 |
66 | .navbar-default .navbar-link {
67 | color:#fff
68 | }
69 |
70 | .label-default {
71 | background-color: #293c55;
72 | }
73 |
74 | .navbar-default .navbar-nav > .active > a,
75 | .navbar-default .navbar-nav > .active > a:hover,
76 | .navbar-default .navbar-nav > .active > a:focus {
77 | color: #FF6B00;
78 | background-color: #293c55;
79 | }
80 |
81 | .navbar-default .navbar-nav > li > a:hover,
82 | .navbar-default .navbar-nav > li > a:focus {
83 | color: #FF5400;
84 | }
85 |
86 | .navbar-default .navbar-nav .open .dropdown-menu>li>a, .navbar-default .navbar-nav .open .dropdown-menu {
87 | border-color: #FFFFFF;
88 | background-color: #293c55;
89 | color : #FF6B00;
90 | }
91 |
92 |
93 | .navbar-default .navbar-nav > li > a:hover, .navbar-default .navbar-nav > li > a:focus {
94 | color: #293c55;
95 | background-color: #FF6B00;
96 | }
97 |
98 | .navbar-default .navbar-nav > .open > a, .navbar-default .navbar-nav > .open > a:hover, .navbar-default .navbar-nav > .open > a:focus {
99 |
100 | background-color: #293c55;
101 | color: #ffffff;
102 |
103 | }
104 |
105 | .navbar-default .navbar-nav > .dropdown > a .caret {
106 | color: #999999;
107 | }
108 | .navbar-default .navbar-nav > .dropdown > a:hover .caret,
109 | .navbar-default .navbar-nav > .dropdown > a:focus .caret {
110 | color: #B03A5B;
111 | }
112 |
113 | .navbar-default .navbar-nav>.open>a,
114 | .navbar-default .navbar-nav>.open>a:hover,
115 | .navbar-default .navbar-nav>.open>a:focus{
116 | color: #d8d8d8;
117 | }
118 |
119 | .navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,
120 | .navbar-default .navbar-nav .open .dropdown-menu{
121 | color: #dce6f2;
122 | }
123 |
124 | .nav-pills>li.active>a,
125 | .nav-pills>li.active>a:hover,
126 | .nav-pills>li.active>a:focus {
127 | color: #FFFFFF;
128 | background-color: #435b7b;
129 | }
130 |
131 | a:hover, a:focus {
132 | color: #293c55;
133 | }
134 |
135 | /*fix to move article titles below nav bar*/
136 |
137 | .contents .page-header {
138 | margin-top: calc(60px + 1em);
139 | }
--------------------------------------------------------------------------------
/pkgdown/extra.css:
--------------------------------------------------------------------------------
1 | @import url('https://fonts.googleapis.com/css?family=Raleway|Ubuntu+Mono');
2 |
3 | h1, h2, h3, h4, h5{
4 | color: #293c55;
5 | }
6 |
7 | body{
8 | font-family: 'Raleway', sans-serif;
9 | }
10 |
11 | code{
12 | font-family: 'Ubuntu Mono', monospace;
13 | }
14 |
15 | p>a {
16 | color: #FF6B00;
17 | }
18 |
19 | li>a {
20 | color: #FF6B00;
21 | }
22 |
23 | .sourceCode>a{
24 | color: #293c55;
25 | }
26 |
27 | .st{
28 | color: #B03A5B;
29 | }
30 |
31 | .op{
32 | color: #666666;
33 | }
34 |
35 | .dv, .dt{
36 | color: #191919;
37 | }
38 |
39 | .btn-copy-ex{
40 | background-color: #B03A5B;
41 | border-color: #293c55;
42 | }
43 |
44 | .btn-copy-ex:hover{
45 | background-color: #293c55;
46 | border-color: #B03A5B;
47 | }
48 |
49 |
50 | pre {
51 | box-shadow:
52 | rgba(0, 0, 0, 0.1) 0 2px 3px 1px,
53 | rgba(0, 0, 0, 0.1) 0 1px 3px 1px,
54 | rgba(0, 0, 0, 0.2) 0 1px 1px -1px;
55 | }
56 |
57 | .navbar-default{
58 | border-color: #293c55;
59 | background-color: #FF6B00;
60 | }
61 |
62 | .navbar-default .navbar-nav>li>a {
63 | color:#fff;
64 | }
65 |
66 | .navbar-default .navbar-link {
67 | color:#fff
68 | }
69 |
70 | .label-default {
71 | background-color: #293c55;
72 | }
73 |
74 | .navbar-default .navbar-nav > .active > a,
75 | .navbar-default .navbar-nav > .active > a:hover,
76 | .navbar-default .navbar-nav > .active > a:focus {
77 | color: #FF6B00;
78 | background-color: #293c55;
79 | }
80 |
81 | .navbar-default .navbar-nav > li > a:hover,
82 | .navbar-default .navbar-nav > li > a:focus {
83 | color: #FF5400;
84 | }
85 |
86 | .navbar-default .navbar-nav .open .dropdown-menu>li>a, .navbar-default .navbar-nav .open .dropdown-menu {
87 | border-color: #FFFFFF;
88 | background-color: #293c55;
89 | color : #FF6B00;
90 | }
91 |
92 |
93 | .navbar-default .navbar-nav > li > a:hover, .navbar-default .navbar-nav > li > a:focus {
94 | color: #293c55;
95 | background-color: #FF6B00;
96 | }
97 |
98 | .navbar-default .navbar-nav > .open > a, .navbar-default .navbar-nav > .open > a:hover, .navbar-default .navbar-nav > .open > a:focus {
99 |
100 | background-color: #293c55;
101 | color: #ffffff;
102 |
103 | }
104 |
105 | .navbar-default .navbar-nav > .dropdown > a .caret {
106 | color: #999999;
107 | }
108 | .navbar-default .navbar-nav > .dropdown > a:hover .caret,
109 | .navbar-default .navbar-nav > .dropdown > a:focus .caret {
110 | color: #B03A5B;
111 | }
112 |
113 | .navbar-default .navbar-nav>.open>a,
114 | .navbar-default .navbar-nav>.open>a:hover,
115 | .navbar-default .navbar-nav>.open>a:focus{
116 | color: #d8d8d8;
117 | }
118 |
119 | .navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,
120 | .navbar-default .navbar-nav .open .dropdown-menu{
121 | color: #dce6f2;
122 | }
123 |
124 | .nav-pills>li.active>a,
125 | .nav-pills>li.active>a:hover,
126 | .nav-pills>li.active>a:focus {
127 | color: #FFFFFF;
128 | background-color: #435b7b;
129 | }
130 |
131 | a:hover, a:focus {
132 | color: #293c55;
133 | }
134 |
135 | /*fix to move article titles below nav bar*/
136 |
137 | .contents .page-header {
138 | margin-top: calc(60px + 1em);
139 | }
--------------------------------------------------------------------------------
/R/get_swissvotes.R:
--------------------------------------------------------------------------------
1 | #' Download additional data collected by annee politique suisse (the complete SwissVotes-Database)
2 | #'
3 | #' \code{get_swissvotes} downloads additional data collected by annee politique suisse. It allows for completely downloading their database. Please cite data.
4 | #'
5 | #' get_swissvotes - retrieve data on votes. The unit of analysis are votes.
6 | #'
7 | #' @param DB get database
8 | #' @param savecitation by default = FALSE. Saves the citation within a .txt file in the working directory if TRUE.
9 | #' @param codebook by default = FALSE. If TRUE navigates your browser to the codebook.
10 | #'
11 | #' @export
12 | #'
13 | #' @return a tibble containing the results
14 | #'
15 | #' @examples
16 | #'
17 | # results <- get_swissvotes(DB=TRUE, savecitation=FALSE, codebook=FALSE)
18 | #'
19 | #' # See codebook only
20 | #' get_swissvotes(codebook=FALSE)
21 | #'
22 | get_swissvotes <- function(DB = T, savecitation = F, codebook = F) {
23 |
24 | if (DB) {
25 |
26 |
27 | safe_csv <- purrr::safely(utils::read.csv, otherwise=tibble::tibble(anr=NA))
28 |
29 | download <- suppressWarnings(safe_csv(
30 | "https://swissvotes.ch/page/dataset/swissvotes_dataset.csv",
31 | sep=";",
32 | stringsAsFactors = F
33 | ))
34 |
35 | swissvotesDB <- download$result
36 |
37 | if(!is.null(download$error)) message(paste0(download$error))
38 |
39 | }
40 | if (codebook) utils::browseURL("https://swissvotes.ch/page/dataset/codebook-de.pdf")
41 |
42 | if(DB) {
43 |
44 | message("To cite swissvotes data in publications, please use:\n
45 | Linder, Wolf, Christian Bolliger und Yvan Rielle (2010):
46 | Verzeichnisse der Literatur, der Quellen und der Abkuerzungen fuer die Kurzbeschreibungen zu den Abstimmungen 1848-2007.
47 | In: Linder, Wolf, Christian Bolliger und Yvan Rielle (Hg.): Handbuch der eidgenoessischen Volksabstimmungen 1848-2007. Bern: Haupt. S. 713-729.\n
48 |
49 | A BibTeX entry for LaTeX users is
50 |
51 | @Book{,
52 | author = {Linder, Wolf and Bolliger, Christian and Rielle, Yvan},
53 | title = {Handbuch der eidgenoessischen Volksabstimmungen 1848-2007},
54 | publisher = {Haupt, Bern},
55 | year = {2010},
56 | url = {https://swissvotes.ch},
57 | }")
58 |
59 | if(savecitation) {
60 | hb <- "@Book{,
61 | author = {Linder, Wolf and Bolliger, Christian and Rielle, Yvan},
62 | title = {Handbuch der eidgenoessischen Volksabstimmungen 1848-2007},
63 | publisher = {Haupt, Bern},
64 | year = {2010},
65 | url = {https://swissvotes.ch},
66 | }"
67 |
68 |
69 | utils::write.table(hb, "swissvotes.txt", col.names=F, row.names=F, quote=F)
70 |
71 | }
72 |
73 | #change id of ballot according swissdd codebook.
74 |
75 | swissvotesDB <- swissvotesDB %>%
76 | dplyr::mutate(anr = as.numeric(anr)*10)
77 |
78 | return(swissvotesDB)
79 |
80 | }
81 |
82 | }
83 |
84 | #identify non ASCII chars
85 | # tools::showNonASCII("@Book{,
86 | # author = {Linder, Wolf and Bolliger, Christian and Rielle, Yvan},
87 | # title = {Handbuch der eidgenoöessischen Volksabstimmungen 1848-2007},
88 | # publisher = {Haupt, Bern},
89 | # year = {2010},
90 | # url = {https://swissvotes.ch},
91 | # }")
92 |
--------------------------------------------------------------------------------
/R/get_cantonalvotes.R:
--------------------------------------------------------------------------------
1 | #' Get cantonal results and counting status in real time or for selected dates or a time range in the past
2 | #'
3 | #' \code{get_cantonalvotes} is one of the two main functions of swissvote package. It allows to retrieve the results and the counting status for national ballots.
4 | #'
5 | #' get_cantonalvotes - retrieve vote results for cantonal ballots at district- or municipality level for selected dates or a given date range.
6 | #'
7 | #' @param geolevel geographical level for which the results should be loaded. Options: "canton", "district", "municipality" or "zh_counting_districts".
8 | #' @param votedates dates of the ballots to be selected. Default: most recent ballot available. Format: "YYYY-MM-DD".
9 | #' @param from_date starting point in time from which vote results should be retrieved. Format: "YYYY-MM-DD".
10 | #' @param to_date end point in time to which vote results should be retrieved. Format: "YYYY-MM-DD".
11 | #'
12 | #' @importFrom purrr map_dfr
13 | #' @importFrom lubridate ymd
14 | #'
15 | #' @export
16 | #'
17 | #' @return a tibble containing the results
18 | #'
19 | #' @examples
20 | #'
21 | #' # Select by range
22 | #' results <- get_cantonalvotes(
23 | #' geolevel = "district",
24 | #' from_date = "2019-01-01",
25 | #' to_date = "2019-12-31"
26 | #' )
27 | #'
28 | #' # Select specific votedate(s)
29 | #' get_cantonalvotes(votedates = "2019-02-10")
30 | #'
31 | #' # get the results at counting district level
32 | #' # yields the same result as the municipality level, with the
33 | #' # exception of Winterthur and Zurich,
34 | #' # where detailed counting district results are returned instead.
35 | #'
36 | #' get_cantonalvotes(votedate = "2019-09-22", geolevel = "zh_counting_districts")
37 | #'
38 | get_cantonalvotes <- function(geolevel = "municipality", votedates = NULL, from_date = NULL, to_date = NULL) {
39 |
40 | # Check inputs
41 | check_geolevel(geolevel, available_geolevels = c("canton", "district", "municipality", "zh_counting_districts"))
42 | if (!is.null(votedates) & (!is.null(from_date) | !is.null(to_date))) stop("Please select the vote dates either with 'votedates' OR via a range ('from_date' / 'to_date').")
43 |
44 | # Parse dates
45 | if (!is.null(votedates)) votedates <- lubridate::ymd(votedates)
46 | if (!is.null(from_date)) from_date <- lubridate::ymd(from_date)
47 | if (!is.null(to_date)) to_date <- lubridate::ymd(to_date)
48 |
49 | # Call base api
50 | call_res <- call_api_base(geolevel = "canton")
51 | available_dates <- available_votedates(geolevel = "canton", call_res)
52 |
53 | # Define vote dates to be fetched
54 | if (is.null(votedates) & is.null(from_date) & is.null(to_date)) votedates <- max(available_dates)
55 | if (!is.null(from_date) | !is.null(to_date)) {
56 |
57 | if (is.null(from_date)) from_date <- lubridate::ymd(min(available_dates))
58 | if (is.null(to_date)) to_date <- lubridate::ymd(max(available_dates))
59 | votedates <- available_dates[available_dates >= from_date & available_dates <= to_date]
60 |
61 | }
62 |
63 | # Check votedates
64 | # remove in order to avoid failure if votedates are lacking
65 | check_votedates(votedates, available_dates)
66 |
67 | # Iterate over dates and create dataframe
68 | votedata <- purrr::map_dfr(
69 | votedates,
70 | canton_json_to_dfr,
71 | geolevel = geolevel,
72 | call_res = call_res
73 | )
74 |
75 | # Return
76 | return(votedata)
77 |
78 | }
--------------------------------------------------------------------------------
/R/get_nationalvotes.R:
--------------------------------------------------------------------------------
1 | #' Get national results and counting status in real time or for selected dates or a time range in the past
2 | #'
3 | #' \code{get_nationalvotes} is one of the two main functions of swissvote package. It allows to retrieve the results and the counting status for national ballots.
4 | #'
5 | #' get_nationalvotes - retrieve vote results for national ballots at district- or municipality level for selected dates or a given date range.
6 | #'
7 | #' @param geolevel geographical level for which the results should be loaded. Options: "national", "canton", "district", "municipality" or "zh_counting_districts".#' @param votedates dates of the ballots to be selected. Default: most recent ballot available. Format: "YYYY-MM-DD".
8 | #' @param votedates dates of the ballots to be selected. Default: most recent ballot available. Format: "YYYY-MM-DD".
9 | #' @param from_date starting point in time from which vote results should be retrieved. Format: "YYYY-MM-DD".
10 | #' @param to_date end point in time to which vote results should be retrieved. Format: "YYYY-MM-DD".
11 | #' @param language defines the language of the vote title. Options: "DE" for German, "FR" for French, "IT" for Italian or "RM" for Romansh.
12 | #'
13 | #' @importFrom purrr map_dfr
14 | #' @importFrom lubridate ymd
15 | #'
16 | #' @export
17 | #'
18 | #' @return a tibble containing the results
19 | #'
20 | #' @examples
21 | #' \donttest{
22 | #' # Selection by range
23 | #' results <- get_nationalvotes(
24 | #' geolevel = "district",
25 | #' from_date = "2018-01-01",
26 | #' to_date = "2018-12-31"
27 | #' )
28 | #'
29 | #' # Selection by end date only
30 | #' get_nationalvotes(to_date = "1983-12-04")
31 | #' }
32 | #' # Selection of a specific vote date
33 | #' get_nationalvotes(votedates = "2014-02-09")
34 | #'
35 | get_nationalvotes <- function(geolevel = "municipality", votedates = NULL, from_date = NULL, to_date = NULL, language = "DE") {
36 |
37 | # Check inputs
38 | check_geolevel(geolevel, available_geolevels = c("national", "canton", "district", "municipality", "zh_counting_districts"))
39 | if (!is.null(votedates) & (!is.null(from_date) | !is.null(to_date))) stop("Please select the vote dates either with 'votedates' OR via a range ('from_date' / 'to_date').")
40 |
41 | # Parse dates
42 | if (!is.null(votedates)) votedates <- lubridate::ymd(votedates)
43 | if (!is.null(from_date)) from_date <- lubridate::ymd(from_date)
44 | if (!is.null(to_date)) to_date <- lubridate::ymd(to_date)
45 |
46 | # Call API
47 | call_res <- call_api_base(geolevel = "national")
48 | available_dates <- available_votedates(geolevel = "national", call_res)
49 |
50 | # Define vote dates to be fetched
51 | if (is.null(votedates) & is.null(from_date) & is.null(to_date)) votedates <- max(available_dates)
52 | if (!is.null(from_date) | !is.null(to_date)) {
53 |
54 | if (is.null(from_date)) from_date <- lubridate::ymd(min(available_dates))
55 | if (is.null(to_date)) to_date <- lubridate::ymd(max(available_dates))
56 | votedates <- available_dates[available_dates >= from_date & available_dates <= to_date]
57 |
58 | }
59 |
60 | # Check votedates
61 | check_votedates(votedates, available_dates)
62 |
63 | # Iterate over dates and create data
64 | votedata <- purrr::map_dfr(
65 | votedates,
66 | swiss_json_to_dfr,
67 | geolevel = geolevel,
68 | language = language,
69 | call_res = call_res
70 | )
71 |
72 | # Return
73 | return(votedata)
74 |
75 | }
--------------------------------------------------------------------------------
/.github/workflows/check_test.yaml:
--------------------------------------------------------------------------------
1 | name: 'check-r-package'
2 | description: 'Action to check R package with rcmdcheck. Assumes that rcmdcheck has already been installed.'
3 | author: 'Jim Hester'
4 | inputs:
5 | args:
6 | description: 'Arguments to pass to the `args` parameter of rcmdcheck. Must be an R expression. Note that it often needs to be quoted in YAML, see the README for details.'
7 | default: 'c("--no-manual", "--as-cran")'
8 | build_args:
9 | description: 'Arguments to pass to the `build_args` parameter of rcmdcheck. Note that it often needs to be quoted in YAML, see the README for details.'
10 | default: '"--no-manual"'
11 | error-on:
12 | description: 'What type of result should cause a build error? Note that it often needs to be quoted in YAML, see the README for details.'
13 | default: '"warning"'
14 | check-dir:
15 | description: 'Where should the check output go? Note that it often needs to be quoted in YAML, see the README for details.'
16 | default: '"check"'
17 | working-directory:
18 | description: 'Using the working-directory keyword, you can specify the working directory of where "rcmdcheck::rcmdcheck" is run.'
19 | default: '.'
20 | upload-snapshots:
21 | description: 'Whether to upload all testthat snapshots as an artifact.'
22 | default: false
23 | upload-results:
24 | description: 'Whether to upload check results for successful runs too.'
25 | default: false
26 |
27 | runs:
28 | using: "composite"
29 | steps:
30 | - name: Check
31 | id: rcmdcheck
32 | run: |
33 | ## --------------------------------------------------------------------
34 | options(crayon.enabled = TRUE)
35 | cat("LOGNAME=", Sys.info()[["user"]], "\n", sep = "", file = Sys.getenv("GITHUB_ENV"), append = TRUE)
36 | if (Sys.getenv("_R_CHECK_FORCE_SUGGESTS_", "") == "") Sys.setenv("_R_CHECK_FORCE_SUGGESTS_" = "false")
37 | if (Sys.getenv("_R_CHECK_CRAN_INCOMING_", "") == "") Sys.setenv("_R_CHECK_CRAN_INCOMING_" = "false")
38 | cat("check-dir-path=", file.path(getwd(), (${{ inputs.check-dir }})), "\n", file = Sys.getenv("GITHUB_OUTPUT"), sep = "", append = TRUE)
39 | check_results <- rcmdcheck::rcmdcheck(args = (${{ inputs.args }}), build_args = (${{ inputs.build_args }}), error_on = (${{ inputs.error-on }}), check_dir = (${{ inputs.check-dir }}))
40 | shell: Rscript {0}
41 | working-directory: ${{ inputs.working-directory }}
42 |
43 | - name: Show testthat output
44 | if: always()
45 | run: |
46 | ## --------------------------------------------------------------------
47 | echo ::group::Show testthat output
48 | find check -name 'testthat.Rout*' -exec cat '{}' \; || true
49 | echo ::endgroup::
50 | shell: bash
51 | working-directory: ${{ inputs.working-directory }}
52 |
53 | - name: Upload check results
54 | if: failure() || inputs.upload-results != 'false'
55 | uses: actions/upload-artifact@v4
56 | with:
57 | name: ${{ runner.os }}-${{ runner.arch }}-r${{ matrix.config.r }}-${{ matrix.config.id || strategy.job-index }}-results
58 | path: ${{ steps.rcmdcheck.outputs.check-dir-path }}
59 |
60 | - name: Upload snapshots
61 | if: inputs.upload-snapshots != 'false'
62 | uses: actions/upload-artifact@v4
63 | with:
64 | name: ${{ runner.os }}-${{ runner.arch }}-r${{ matrix.config.r }}-${{ matrix.config.id || strategy.job-index }}-testthat-snapshots
65 | path: ${{ steps.rcmdcheck.outputs.check-dir-path }}/**/tests*/testthat/_snaps
66 | if-no-files-found: ignore
67 |
--------------------------------------------------------------------------------
/docs/pkgdown.js:
--------------------------------------------------------------------------------
1 | /* http://gregfranko.com/blog/jquery-best-practices/ */
2 | (function($) {
3 | $(function() {
4 |
5 | $('.navbar-fixed-top').headroom();
6 |
7 | $('body').css('padding-top', $('.navbar').height() + 10);
8 | $(window).resize(function(){
9 | $('body').css('padding-top', $('.navbar').height() + 10);
10 | });
11 |
12 | $('[data-toggle="tooltip"]').tooltip();
13 |
14 | var cur_path = paths(location.pathname);
15 | var links = $("#navbar ul li a");
16 | var max_length = -1;
17 | var pos = -1;
18 | for (var i = 0; i < links.length; i++) {
19 | if (links[i].getAttribute("href") === "#")
20 | continue;
21 | // Ignore external links
22 | if (links[i].host !== location.host)
23 | continue;
24 |
25 | var nav_path = paths(links[i].pathname);
26 |
27 | var length = prefix_length(nav_path, cur_path);
28 | if (length > max_length) {
29 | max_length = length;
30 | pos = i;
31 | }
32 | }
33 |
34 | // Add class to parent
, and enclosing
if in dropdown
35 | if (pos >= 0) {
36 | var menu_anchor = $(links[pos]);
37 | menu_anchor.parent().addClass("active");
38 | menu_anchor.closest("li.dropdown").addClass("active");
39 | }
40 | });
41 |
42 | function paths(pathname) {
43 | var pieces = pathname.split("/");
44 | pieces.shift(); // always starts with /
45 |
46 | var end = pieces[pieces.length - 1];
47 | if (end === "index.html" || end === "")
48 | pieces.pop();
49 | return(pieces);
50 | }
51 |
52 | // Returns -1 if not found
53 | function prefix_length(needle, haystack) {
54 | if (needle.length > haystack.length)
55 | return(-1);
56 |
57 | // Special case for length-0 haystack, since for loop won't run
58 | if (haystack.length === 0) {
59 | return(needle.length === 0 ? 0 : -1);
60 | }
61 |
62 | for (var i = 0; i < haystack.length; i++) {
63 | if (needle[i] != haystack[i])
64 | return(i);
65 | }
66 |
67 | return(haystack.length);
68 | }
69 |
70 | /* Clipboard --------------------------*/
71 |
72 | function changeTooltipMessage(element, msg) {
73 | var tooltipOriginalTitle=element.getAttribute('data-original-title');
74 | element.setAttribute('data-original-title', msg);
75 | $(element).tooltip('show');
76 | element.setAttribute('data-original-title', tooltipOriginalTitle);
77 | }
78 |
79 | if(ClipboardJS.isSupported()) {
80 | $(document).ready(function() {
81 | var copyButton = "";
82 |
83 | $(".examples, div.sourceCode").addClass("hasCopyButton");
84 |
85 | // Insert copy buttons:
86 | $(copyButton).prependTo(".hasCopyButton");
87 |
88 | // Initialize tooltips:
89 | $('.btn-copy-ex').tooltip({container: 'body'});
90 |
91 | // Initialize clipboard:
92 | var clipboardBtnCopies = new ClipboardJS('[data-clipboard-copy]', {
93 | text: function(trigger) {
94 | return trigger.parentNode.textContent;
95 | }
96 | });
97 |
98 | clipboardBtnCopies.on('success', function(e) {
99 | changeTooltipMessage(e.trigger, 'Copied!');
100 | e.clearSelection();
101 | });
102 |
103 | clipboardBtnCopies.on('error', function() {
104 | changeTooltipMessage(e.trigger,'Press Ctrl+C or Command+C to copy');
105 | });
106 | });
107 | }
108 | })(window.jQuery || window.$)
109 |
--------------------------------------------------------------------------------
/vignettes/plot_voteshares.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Plot vote shares"
3 | output: rmarkdown::html_vignette
4 | ---
5 |
6 | ```{r, include = FALSE}
7 | knitr::opts_chunk$set(
8 | collapse = TRUE,
9 | comment = "#>"
10 | )
11 | ```
12 |
13 |
14 | This functionality has been kindly added by [David Zumbach](https://twitter.com/davidzumbach){target="_blank"}.
15 |
16 | The function `get_geodata` allows users to download geographic information for different administrative units in Switzerland. The function returns a *simple feature* `data.frame`. Users should specify the level of administrative units using the argument `geolevel`. The following options are available:
17 |
18 | * `national` to download administrative boundaries of Switzerland
19 | * `cantonal` to download administrative boundaries of cantons
20 | * `municipality` to download administrative boundaries of municipalities/communities
21 | * `zh_counting_districts` to download administrative boundaries of *Zählkreise* for the City of Zurich
22 | * `lakes` to download boundaries of major lakes
23 |
24 | An illustration of how one could utilize the functionionality of `swissdd` to visualize vote outcomes for any national vote is given here.
25 |
26 | ### Plot voteshares "by hand"
27 |
28 | Producing a map "by hand" allows for more flexibility. For example, you could simply download the geo-information and plot something entirely else. If you never ever want to produce a plot by hand, skip to the next sectionn.
29 |
30 |
31 | ```{r echo=TRUE, warning=FALSE, message=FALSE}
32 | # installation from CRAN (stable)
33 | # install.packages("swissdd")
34 | # install.packages("dplyr")
35 |
36 | # installation from github (ongoing updates)
37 | # devtools::install_github("politanch/swissdd")
38 |
39 |
40 | library(swissdd)
41 | packageVersion("swissdd")
42 | library(dplyr)
43 | library(ggplot2)
44 | library(sf)
45 | library(viridis)
46 |
47 | #download geo information
48 | geo_canton <- get_geodata(geolevel = "canton")
49 | geo_canton$canton_id <- as.numeric(geo_canton$canton_id)
50 |
51 | # download data from API on the vote calles «Swiss coalition for Corporate justice SCCJ»
52 | kovi_nat <- get_nationalvotes(votedates="2020-11-29", geolevel = "canton")%>%
53 | dplyr::filter(id == 6360)%>%
54 | dplyr::select(canton_id, jaStimmenInProzent)%>%
55 | mutate(canton_id=as.numeric(canton_id))
56 | ```
57 |
58 | Combine the two `data.frames`.
59 | ```{r echo=TRUE, warning=FALSE, message=FALSE}
60 | can_df <- left_join(geo_canton, kovi_nat, by="canton_id")
61 | ```
62 |
63 | Plot the whole thing accordingly.
64 |
65 | ```{r echo=TRUE, warning=FALSE, message=FALSE}
66 | ggplot(can_df)+
67 | geom_sf(aes(fill=jaStimmenInProzent), color="white", size=.1)
68 | ```
69 |
70 | With this you can start prettifiyng the map according to your liking. Wee add the lakes as well here:
71 |
72 | ```{r echo=TRUE, warning=FALSE, message=FALSE}
73 | lakes <- swissdd::get_geodata(geolevel = "lakes")
74 | ggplot(can_df)+
75 | geom_sf(aes(fill=jaStimmenInProzent), color="white", size=.1)+
76 | geom_sf(data=lakes, fill="aliceblue", color=NA)+
77 | scale_fill_viridis(option="D",
78 | name = "Yes-Share",
79 | guide=guide_colorbar(title.position="top",
80 | direction = "horizontal",
81 | barheight = unit(2, units = "mm"),
82 | barwidth = unit(50, units = "mm"),
83 | title.hjust =0.5))+
84 | theme_void()+
85 | theme(legend.position="bottom")+
86 | labs(title="Result for Vote on Corporate Justice SCCJ", subtitle="November 29, 2020",
87 | caption="data: FSO/{swissdd}")
88 | ```
89 |
90 |
91 | ### Plot voteshares with built-in function
92 |
93 | If for some reason you don't want to create your map from scratch you could also rely on the function `plot_nationalvotes()` which allows you to quickly plot turnout rates or yes-shares. In order to run this function you have to specify the administrative level as well as the official identification number of the vote you're interested in.
94 |
95 | ```{r echo=TRUE, warning=FALSE, message=FALSE}
96 |
97 | plot_nationalvotes(votedate="2020-11-29", vote_id=6360, geolevel="canton")
98 | ```
99 |
--------------------------------------------------------------------------------
/R/similar_votes.R:
--------------------------------------------------------------------------------
1 | #' Obtain similarities a vote result shares with other votes
2 | #'
3 | #' \code{similar_votes} allows to obtain correlations of specified vote with other votes.
4 | #'
5 | #'
6 | #' @param federalvotes tibble or data.frame that is returned by 'get_swissvotes'.
7 | #' @param id identification number of the vote, needs four digits. Vote 626 (Zersiedelungsinitiative) needs 6260.
8 | #' @param corr set to TRUE by default. If FALSE return the variance-covariance matrix.
9 | #' @param from lower limit of correlations.
10 | #' @param to upper limit of correlations.
11 | #' @importFrom tibble rownames_to_column
12 | #' @importFrom tibble as_tibble
13 | #' @importFrom dplyr select
14 | #' @importFrom dplyr "%>%"
15 | #' @importFrom dplyr mutate
16 | #' @importFrom dplyr mutate_all
17 | #' @importFrom dplyr filter
18 | #' @importFrom dplyr rename
19 | #' @importFrom dplyr arrange
20 | #' @importFrom tidyr spread
21 | #' @importFrom stats cor
22 | #' @importFrom stats cov
23 | #' @export
24 | #' @rdname similar_votes
25 | #' @return a tibble containing the results
26 | #' @examples
27 | #' \donttest{
28 | #'
29 | #' fedvotes <- get_nationalvotes(geolevel = "canton",from_date = "2010-03-07",to_date="2019-02-10")
30 | #'
31 | #' #Find correlating votes for the 'Zersiedelungsinitiative', 2019-02-10
32 | #' results <- similar_votes(fedvotes, id=6260)
33 | #'
34 | #'
35 | #' #Zersiedelungsinitiative, 2019-02-10, filter stronger correlations (>0.5)
36 | #' results <- similar_votes(fedvotes, id=6260, from = 0.5)
37 | #'
38 | #' }
39 | #'
40 |
41 | similar_votes <- function(federalvotes=NULL, id=NULL, corr=TRUE, from=NULL, to=NULL){
42 |
43 | if(is.null(federalvotes)){
44 | stop("Need tibble returned by 'get_swissvotes'")
45 | }
46 |
47 | if(!is.null(id)){
48 | if(nchar(id)!=4) stop("bfnr needs to be four digits")
49 | to_return <- "col"
50 | }else{
51 | to_return <- "mat"
52 | }
53 |
54 |
55 | #this block may be redundant when ktid == geoLevelnummer
56 | var_mun <- "mun_id"%in%colnames(federalvotes)
57 | var_dist <- "district_id"%in%colnames(federalvotes)
58 |
59 | if(var_mun){
60 |
61 | fed <- federalvotes %>%
62 | dplyr::select(id, mun_id, jaStimmenInProzent) %>%
63 | mutate(id=paste0("V_", id))%>%
64 | tidyr::spread(id, jaStimmenInProzent)%>%
65 | dplyr::select(-mun_id)
66 |
67 | }else if(var_dist){
68 |
69 | fed <- federalvotes %>%
70 | dplyr::select(id, district_id, jaStimmenInProzent) %>%
71 | mutate(id=paste0("V_", id))%>%
72 | tidyr::spread(id, jaStimmenInProzent)%>%
73 | dplyr::select(-district_id)
74 |
75 | }else {
76 | fed <- federalvotes %>%
77 | dplyr::select(id,canton_id, jaStimmenInProzent) %>%
78 | mutate(id=paste0("V_", id))%>%
79 | tidyr::spread(id, jaStimmenInProzent)%>%
80 | dplyr::select(-canton_id)
81 |
82 | }
83 |
84 | if(corr==TRUE){
85 | fedcor <- cor(fed, use="complete.obs")
86 | }else{
87 | if(!is.null(id)) {
88 | to_return <- "mat"
89 | message("Ignores vote id and returns full variance-covariance matrix")
90 | }
91 | fedcor <- cov(fed)
92 | }
93 |
94 |
95 |
96 | if(to_return=="col"){
97 |
98 | position <- grep(id, colnames(fedcor))
99 |
100 | dat <- fedcor[,position]
101 | dat <- dat[-position]
102 |
103 | dat <- as.data.frame(dat)%>%
104 | tibble::rownames_to_column("id") %>%
105 | mutate(id=gsub("V_", "", id))%>%
106 | rename(correlation=dat) %>%
107 | arrange(-correlation)
108 |
109 | #if range is specified
110 | if(!is.null(from)){
111 | if(!is.null(to)){
112 | dat <- dat %>%
113 | filter(correlation<=to & correlation >= from)
114 | }else{
115 | dat <- dat %>%
116 | filter(correlation >= from)
117 | }
118 | }else{
119 | if(!is.null(to)){
120 | dat <- dat %>%
121 | filter(correlation<=to)
122 | }
123 | }
124 |
125 | return(as_tibble(dat))
126 |
127 | }
128 |
129 | if(to_return=="mat"){
130 | return(as_tibble(fedcor))
131 | }
132 |
133 | }
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
--------------------------------------------------------------------------------
/pkgdown/templates/head.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{{pagetitle}}} • {{#site}}{{title}}{{/site}}
6 |
7 | {{#has_favicons}}
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | {{/has_favicons}}
16 |
17 |
18 |
19 |
20 | {{#yaml}}
21 | {{#bootswatch}}{{/bootswatch}}
22 | {{^bootswatch}}{{/bootswatch}}
23 | {{/yaml}}
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | {{#yaml}}{{#docsearch}}
41 |
42 |
43 |
44 |
45 |
46 | {{/docsearch}}{{/yaml}}
47 |
48 | {{#extra}}
49 | {{#css}}{{/css}}
50 | {{#js}}{{/js}}
51 | {{/extra}}
52 |
53 |
54 | {{#opengraph}}
55 | {{#description}}
56 |
57 | {{/description}}
58 | {{#image}}
59 |
60 | {{/image}}
61 |
62 | {{/opengraph}}
63 |
64 |
65 |
66 | {{#yaml}}{{#noindex}}{{/noindex}}{{/yaml}}
67 | {{#development}}{{#in_dev}}{{/in_dev}}{{/development}}
68 |
69 |
70 |
71 |
72 |
73 |
77 |
78 | {{#yaml}}{{#ganalytics}}
79 |
80 |
81 |
87 | {{/ganalytics}}{{/yaml}}
88 |
89 |
90 |
--------------------------------------------------------------------------------
/R/plot_cantonalvotes.R:
--------------------------------------------------------------------------------
1 | #' Plot Cantonal Votes
2 | #'
3 | #' \code{plot_cantonalvotes} plots the results of cantonal votes in a choropleth map using ggplot2.
4 | #'
5 | #' @param votedate date of the ballot. Default: most recent ballot available.
6 | #' @param vote_id id of the vote. Default: first id mentioned in the data set.
7 | #' @param geolevel geographical level. Options: district", "municipality" or "zh_counting_districts".
8 | #' @param measure measure to color the administrative units. Options: "result" for the yes vote share or "turnout"
9 | #' for the voter turnout of a given vote.
10 | #' @param standardize if \code{TRUE}, the scale of the measure ranges from 0 to 100 percent. Recommended for comparisons
11 | #' between votes.
12 | #' @param language defines the language. Options: "DE" for German, "FR" for French, "IT" for Italian or "RM" for Romansh.
13 | #' @param theme defines basic appearance of the map. Five options are available: "srf" for a theme inspired by the
14 | #' plots of Swiss Radio and Television, and "A" to "E" for the viridis color scales magma, inferno, plasma, viridis and cividis.
15 | #'
16 | #' @return a ggplot object
17 | #'
18 | #' @importFrom lubridate ymd
19 | #' @importFrom dplyr left_join rename bind_rows
20 | #' @importFrom tibble tibble
21 | #'
22 | #' @examples
23 | #'
24 | #' # Plot the most recent cantonal vote
25 | #' plot_cantonalvotes()
26 | #'
27 | #' # Plot a specific cantonal vote
28 | #' plot_cantonalvotes(votedate = "2020-02-09", vote_id = 104945)
29 | #'
30 | #' @export
31 | plot_cantonalvotes <- function(votedate = NULL, vote_id = NULL, geolevel = "municipality", measure = "result",
32 | standardize = T, language = "DE", theme = "srf") {
33 |
34 | # Check inputs
35 | check_geolevel(geolevel, available_geolevels = c("district", "municipality", "zh_counting_districts"))
36 | check_measure(measure, available_measures = c("result", "turnout"))
37 | check_theme(theme, available_themes = c("srf", "A", "B", "C", "D", "E"))
38 |
39 | # API calls
40 | call_res_base <- call_api_base(geolevel = "canton")
41 | call_res_geodata <- call_api_geodata()
42 | available_dates <- available_votedates(geolevel = "canton", call_res_base)
43 |
44 | # check status of api calls and fail gracefully in case of errors
45 | # check status of api calls /available votedates and fail gracefully in case of errors
46 | if(httr::http_error(call_res_base)==FALSE &
47 | httr::http_error(call_res_geodata)==FALSE&
48 | is.null(available_dates)==FALSE & length(available_dates)>0) {
49 |
50 | # Handle votedate
51 | if (!is.null(votedate)) votedate <- lubridate::ymd(votedate)
52 | if (is.null(votedate)) votedate <- max(available_dates)
53 | check_votedate(votedate, available_dates)
54 |
55 | # Fetch vote data
56 | vote_data <- get_cantonalvotes(geolevel = geolevel, votedates = votedate)
57 |
58 | # Check whether the data does already contain at least a single result
59 |
60 |
61 | # Subset vote data
62 | if (is.null(vote_id)) vote_id <- unique(vote_data[["id"]])[1]
63 | vote_data <- vote_data[vote_data[["id"]] == vote_id,]
64 | # Stop if there is no data for the chosen vote id
65 | if (nrow(vote_data) == 0) message ("No data found for the specified 'vote_id'")
66 | # warning if no results are available yet for the chosen vote id
67 | if (all(is.na(vote_data$jaStimmenInProzent))==TRUE) message ("No results available yet for the specified 'vote_id'")
68 |
69 |
70 | #fetch geodata for the requested geolevel
71 | geodata <- get_geodata(geolevel = geolevel, call_res = call_res_geodata)
72 |
73 | # stop here and return an invisible null if geodata cannot be fetched
74 | if(is.null(geodata)) {return(invisible(NULL))}
75 |
76 |
77 | # Join geo with vote data
78 | if (geolevel == "municipality") {
79 |
80 | pd <- dplyr::left_join(
81 | geodata,
82 | vote_data,
83 | by = "mun_id"
84 | )
85 |
86 | }
87 | if (geolevel == "district") {
88 |
89 | pd <- dplyr::left_join(
90 | geodata,
91 | vote_data,
92 | by = "district_id"
93 | )
94 |
95 | }
96 | if (geolevel == "zh_counting_districts") {
97 |
98 | pd <- dplyr::left_join(
99 | geodata,
100 | vote_data,
101 | by = "mun_id"
102 | )
103 |
104 | }
105 |
106 | # Select relevant rows
107 | pd <- pd[!is.na(pd[["id"]]),]
108 |
109 | # Measure
110 | if (measure == "result") {
111 |
112 | pd <- pd %>% dplyr::rename(measure = jaStimmenInProzent)
113 | legend_title <- "jaStimmenInProzent"
114 |
115 | }
116 | if (measure == "turnout") {
117 |
118 | pd <- pd %>% dplyr::rename(measure = stimmbeteiligungInProzent)
119 | legend_title <- "stimmbeteiligungInProzent"
120 |
121 | }
122 |
123 | # Pseudo-standardization
124 | if (standardize) {
125 |
126 | pd2 <- tibble::tibble(
127 | id = vote_id,
128 | measure = c(0, 100)
129 | )
130 |
131 | pd <- pd %>% dplyr::bind_rows(pd2)
132 |
133 | }
134 |
135 | # Base plot
136 | plot_map_cantonal(pd, legend_title = legend_title, language = language, theme = theme)
137 |
138 | } else {
139 | message(paste("Data :",httr::http_status(call_res_base),
140 | "Geodata :",httr::http_status(call_res_geodata),
141 | "Votedates status:", !is.null(available_dates)
142 | ))
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/docs/bootstrap-toc.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap Table of Contents v0.4.1 (http://afeld.github.io/bootstrap-toc/)
3 | * Copyright 2015 Aidan Feldman
4 | * Licensed under MIT (https://github.com/afeld/bootstrap-toc/blob/gh-pages/LICENSE.md) */
5 | (function() {
6 | 'use strict';
7 |
8 | window.Toc = {
9 | helpers: {
10 | // return all matching elements in the set, or their descendants
11 | findOrFilter: function($el, selector) {
12 | // http://danielnouri.org/notes/2011/03/14/a-jquery-find-that-also-finds-the-root-element/
13 | // http://stackoverflow.com/a/12731439/358804
14 | var $descendants = $el.find(selector);
15 | return $el.filter(selector).add($descendants).filter(':not([data-toc-skip])');
16 | },
17 |
18 | generateUniqueIdBase: function(el) {
19 | var text = $(el).text();
20 | var anchor = text.trim().toLowerCase().replace(/[^A-Za-z0-9]+/g, '-');
21 | return anchor || el.tagName.toLowerCase();
22 | },
23 |
24 | generateUniqueId: function(el) {
25 | var anchorBase = this.generateUniqueIdBase(el);
26 | for (var i = 0; ; i++) {
27 | var anchor = anchorBase;
28 | if (i > 0) {
29 | // add suffix
30 | anchor += '-' + i;
31 | }
32 | // check if ID already exists
33 | if (!document.getElementById(anchor)) {
34 | return anchor;
35 | }
36 | }
37 | },
38 |
39 | generateAnchor: function(el) {
40 | if (el.id) {
41 | return el.id;
42 | } else {
43 | var anchor = this.generateUniqueId(el);
44 | el.id = anchor;
45 | return anchor;
46 | }
47 | },
48 |
49 | createNavList: function() {
50 | return $('
133 |
134 |
135 |
136 |
137 |
138 |
139 |
--------------------------------------------------------------------------------
/R/swiss_json_to_dfr.R:
--------------------------------------------------------------------------------
1 | #' Transform an opendata.swiss national results json into a tibble
2 | #'
3 | #' \code{swiss_json_to_dfr} transforms the json containing the results of a selected federal votedate into a tibble.
4 | #'
5 | #' @param votedate date of the ballot. Default: most recent ballot available. To select multiple ballots use the 'get_swissvotes'-function. Format = YYYYMMDD
6 | #' @param geolevel geographical level for which the results should be loaded. Options: "national", "canton", "district" or "municipality".
7 | #' @param dataurl url of the dataset on opendata.swiss
8 | #' @param index selection by index of the resource (last published = 1).
9 | #' @param language defines the language of the vote title. Options: "DE" for German, "FR" for French, "IT" for Italian or "RM" for Romansh.
10 | #' @param call_res result of a previous call to the base API. Optional argument.
11 | #'
12 | #' @importFrom httr add_headers GET http_error content
13 | #' @importFrom jsonlite fromJSON
14 | #' @importFrom tibble tibble
15 | #' @importFrom purrr map_chr map
16 | #' @importFrom dplyr "%>%" bind_cols rename filter bind_rows case_when
17 | #' @importFrom tidyr unnest unpack
18 | #' @importFrom lubridate ymd
19 | #'
20 | #' @return a tibble containing the results
21 | #'
22 | #' @export
23 | #'
24 | #' @examples
25 | #'
26 | #' # Transform the json of the most recent vote
27 | #' results <- swiss_json_to_dfr()
28 | #'
29 | #' # Transform the json of a selected votedate
30 | #' swiss_json_to_dfr(votedate = "2019-02-10")
31 | #'
32 | swiss_json_to_dfr <- function(votedate = NULL, geolevel = "municipality", dataurl = NULL, index = NULL, language = "DE", call_res) {
33 |
34 | # Check inputs
35 | check_geolevel(geolevel, available_geolevels = c("national", "canton", "district", "municipality", "zh_counting_districts"))
36 | check_language(language, available_languages = c("DE", "FR", "IT", "RM"))
37 |
38 | # Get URL if required
39 | if (is.null(dataurl)) {
40 |
41 | # Call API if required
42 | if (missing(call_res)) call_res <- call_api_base(geolevel = "national")
43 |
44 | # Handle votedate
45 | available_dates <- available_votedates(geolevel = "national", call_res)
46 | if (is.null(votedate)) votedate <- max(available_dates)
47 | votedate <- lubridate::ymd(votedate)
48 | check_votedate(votedate, available_dates)
49 |
50 | # Fail gracefully when available votedates cannot be parsed.
51 | if(length(available_dates)<1){
52 |
53 | message("Available Votedates cannot be parsed. There might be a technical issue with the opendata.swiss API.")
54 |
55 | return(invisible(NULL))
56 |
57 | }
58 |
59 | # Get URL
60 | urls <- get_vote_urls(geolevel = "national", call_res = call_res)
61 | dataurl <- urls[urls[["date"]] == votedate,][["download_url"]]
62 |
63 | }
64 |
65 | # Index
66 | if (!is.null(index)) dataurl <- dataurl[index]
67 | if (length(dataurl) > 1) stop("This is not a vectorised function. Only one URL can be queried at a time.")
68 |
69 | # Fetch, check and extract vote data
70 | res <- httr::GET(dataurl, httr::add_headers(`User-Agent` = "Mozilla/5.0"))
71 | res_data <- check_api_call(res)
72 | # res_data <- suppressWarnings(jsonlite::fromJSON(httr::content(res, as = "text", encoding = "UTF-8")))
73 |
74 | if(!is.null(res_data)){
75 |
76 | # Simplify data
77 | data_national <- res_data[["schweiz"]][["vorlagen"]]
78 | data_cantons <- res_data[["schweiz"]][["vorlagen"]][["kantone"]]
79 |
80 | # Language index
81 | lang_ind <- dplyr::case_when(
82 | language == "DE" ~ 1,
83 | language == "FR" ~ 2,
84 | language == "IT" ~ 3,
85 | language == "RM" ~ 4
86 | )
87 |
88 | # Geolevel specific extraction
89 | if (geolevel == "national") {
90 |
91 | findata <- tibble::tibble(
92 | id = data_national[["vorlagenId"]],
93 | name = purrr::map_chr(data_national[["vorlagenTitel"]], c(2, lang_ind))
94 | ) %>%
95 | dplyr::bind_cols(data_national[["resultat"]])
96 |
97 | }
98 | if (geolevel == "canton") {
99 |
100 | findata <- tibble::tibble(
101 | canton_id = purrr::map(data_cantons, 1),
102 | canton_name = purrr::map(data_cantons, 2),
103 | name = purrr::map_chr(data_national[["vorlagenTitel"]], c(2, lang_ind)),
104 | id = data_national[["vorlagenId"]],
105 | res = purrr::map(data_cantons, 3)
106 | ) %>%
107 | tidyr::unnest(c(canton_id, canton_name, res))
108 |
109 | }
110 | if (geolevel == "district") {
111 |
112 | findata <- tibble::tibble(
113 | name = purrr::map_chr(data_national[["vorlagenTitel"]], c(2, lang_ind)),
114 | id = data_national[["vorlagenId"]],
115 | canton_id = purrr::map(data_cantons, 1),
116 | canton_name = purrr::map(data_cantons, 2),
117 | res = purrr::map(res_data[["schweiz"]][["vorlagen"]][["kantone"]], "bezirke")
118 | ) %>%
119 | tidyr::unnest(c(res, canton_id, canton_name)) %>%
120 | tidyr::unnest(res) %>%
121 | dplyr::rename(
122 | district_id = geoLevelnummer,
123 | district_name = geoLevelname
124 | ) %>%
125 | tidyr::unpack(resultat)
126 |
127 | }
128 | if (geolevel == "zh_counting_districts" & is.list(data_cantons[[1]]$zaehlkreise)) {
129 |
130 | zaehlkreise <- tibble::tibble(
131 | name = purrr::map_chr(data_national[["vorlagenTitel"]], c(2, lang_ind)),
132 | id = data_national[["vorlagenId"]],
133 | canton_id = "1",
134 | canton_name = data_cantons[[1]][["geoLevelname"]][[1]],
135 | res = purrr::map(data_cantons, "zaehlkreise")
136 | ) %>%
137 | tidyr::unnest(res) %>%
138 | tidyr::unnest(res) %>%
139 | tidyr::unpack(resultat) %>%
140 | dplyr::rename(
141 | mun_id = geoLevelnummer,
142 | mun_name = geoLevelname
143 | )
144 |
145 | }
146 | if (geolevel %in% c("municipality", "zh_counting_districts")){
147 |
148 | findata <- tibble::tibble(
149 | name = purrr::map_chr(data_national[["vorlagenTitel"]], c(2, lang_ind)),
150 | id = data_national[["vorlagenId"]],
151 | canton_id = purrr::map(data_cantons, 1),
152 | canton_name = purrr::map(data_cantons, 2),
153 | res = purrr::map(res_data[["schweiz"]][["vorlagen"]][["kantone"]], "gemeinden")
154 | ) %>%
155 | tidyr::unnest(c(res, canton_id, canton_name)) %>%
156 | tidyr::unnest(res) %>%
157 | tidyr::unpack(resultat) %>%
158 | dplyr::rename(
159 | mun_id = geoLevelnummer,
160 | mun_name = geoLevelname
161 | )
162 |
163 | # Add results for counting districts
164 | if(geolevel == "zh_counting_districts" & is.list(data_cantons[[1]]$zaehlkreise)){
165 |
166 | findata <- findata %>%
167 | dplyr::filter(!mun_id %in% c(261, 230)) %>%
168 | dplyr::bind_rows(zaehlkreise)
169 |
170 | }
171 |
172 |
173 | }
174 |
175 | # Add votedate
176 | if (is.null(votedate)) {
177 |
178 | urls <- get_vote_urls(geolevel = "national", call_res = call_res)
179 | votedate <- urls[urls[["download_url"]] == dataurl,][["date"]]
180 |
181 | }
182 | findata$votedate <- lubridate::ymd(votedate)
183 |
184 | # Return
185 | return(findata)
186 |
187 | }
188 |
189 | }
--------------------------------------------------------------------------------
/vignettes/predict_voteshare.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Predict vote shares"
3 | output: rmarkdown::html_vignette
4 | ---
5 |
6 | ```{r, include = FALSE}
7 | knitr::opts_chunk$set(
8 | collapse = TRUE,
9 | comment = "#>"
10 | )
11 | ```
12 |
13 | ## How to get Swissvotes Data
14 |
15 | First, you have to make sure that your machine is connected to the internet. The function `get_swissvotes` fetches all data stored within the swissvotes database. Swissvotes offers one of the most comprehensive data platforms in relation to Swiss referendums and initiatives.
16 |
17 | 
18 |
19 | # Predict vote outcomes with {width=120px} Data
20 |
21 | This blogpost shows how to perform a simple prediction of vote outcomes based on Swissvotes data in just a few steps.
22 |
23 | ## 1. Retrieve and prepare the data
24 |
25 | First, you have to make sure that your machine is connected to the internet. The function `get_swissvotes` fetches all data stored within the swissvotes database. Swissvotes offers one of the most comprehensive data platforms in relation to Swiss referendums and initiatives.
26 |
27 | ```{r echo=TRUE, warning=FALSE, message=FALSE}
28 |
29 | # installation from CRAN (stable)
30 | # install.packages("swissdd")
31 | # install.packages("dplyr")
32 |
33 | # installation from github (ongoing updates)
34 | # devtools::install_github("politanch/swissdd")
35 |
36 | library(swissdd)
37 | library(dplyr)
38 | library(ggplot2)
39 | ```
40 |
41 |
42 | By default, the function simply extracts the database provided by [Swissvotes](https://swissvotes.ch){target="_blank"}. However, you can specify that you want the codebook as well (or just the codebook for that matter). Additionally, there is a specification to save the citation. If you work with data from Swissvotes, please cite the data accordingly.
43 |
44 | We first extract the data and the select the variables we need to make predictions. For some reason we may believe that the National Council represents the people. Therefore, we hope to see a pattern between the final votes in the National Council and the actual yes shares on a voting day.
45 |
46 | We download and prettify the data. After that we calculate the share of yes votes in the National Council and create a scatterplot to inspect if our intuition about the relationship can be seen in the data.
47 |
48 | ```{r echo=TRUE, warning=FALSE, message=FALSE, eval=TRUE}
49 |
50 | #extract data from database
51 | swissvotesdata <- get_swissvotes(DB=T, savecitation=F, codebook=F) %>%
52 | #select variables we need
53 | dplyr::select(anr, volkja.proz, nrja, nrnein) %>%
54 | #make numeric and rename variables
55 | mutate(nrja = as.numeric(nrja),
56 | nrnein = as.numeric(nrnein),
57 | volkja.proz= volkja.proz/100)%>%
58 | rename(bfsnr=anr,
59 | yesshare = volkja.proz)
60 |
61 | #clean data
62 | swissvotesdata[swissvotesdata=="."] <- NA
63 |
64 | #calculate share of yes votes in national council
65 | swissvotesdata <- swissvotesdata %>%
66 | mutate(nr_yesshare = nrja / (nrja+nrnein))
67 |
68 | ```
69 |
70 |
71 | By default, function extracts only the database provided by [Swissvotes](https://swissvotes.ch){target="_blank"}. However, you can specify that you want the codebook as well (or just the codebook for that matter). Additionally, there is a specification to save the citation. If you work with data from Swissvotes, please cite the data accordingly.
72 |
73 | First, we extract data from the database and select the variables we need to create our prediction. For some reason we might assume that the National Council represents the people. It this is the case, it should be reflected in a similar pattern in yes shares in the National Council and later on in the actual yes share of a popular vote.
74 |
75 | ## 2. Exploratory Analysis : is our assumption accurate?
76 |
77 | After downloading and preparing the data, we calculate the share of yes votes in the National Council and create a scatterplot to inspect if our intuition about the relationship can be seen in the data.
78 |
79 | ```{r echo=TRUE, warning=FALSE, message=FALSE}
80 |
81 | plot1 <- ggplot(data=swissvotesdata, aes(x=nr_yesshare, y= yesshare))+
82 | geom_hline(yintercept=.5, linetype="dashed", color="grey70")+
83 | geom_point()+
84 | geom_abline(intercept = 0, slope=1, size=.1)+
85 | scale_y_continuous(limits=c(0,1), labels=scales::percent)+
86 | scale_x_continuous(limits=c(0,1), labels=scales::percent)+
87 | geom_smooth(method="lm", se=T, size=.5, color="#FF6B00")+
88 | labs(y="Actual Outcome of Vote", x="Yes Share in National Council", caption="politan.ch")+
89 | theme_bw()
90 |
91 | plot1
92 | ```
93 |
94 | There seems to be a positive correlation (what a surprise!)... In a next step, we estimate a very basic model and then predict the vote shares for the two national votes on February 9th, 2020. In order to do so, we split the data into a training set and a prediction set.
95 |
96 | There seems to be a positive correlation (what a surprise!)...
97 |
98 | ## 3. Predict the results of the upcoming votes
99 |
100 | In the last step we estimate a very basic model and then predict the vote shares for the two national votes on February 9th, 2020. In order to do so, we split the data into a training set and a prediction set.
101 |
102 | ```{r echo=TRUE, warning=FALSE, message=FALSE, eval=TRUE}
103 | #split data
104 | estimation_data <- swissvotesdata[!swissvotesdata$bfsnr%in%c(6290, 6300),]
105 | prediction_data <- swissvotesdata[swissvotesdata$bfsnr%in%c(6290, 6300),]
106 |
107 | #estimate a model
108 | fit_m1 <- lm(yesshare ~ nr_yesshare, data=estimation_data)
109 |
110 | #predict
111 | prediction <- predict(fit_m1, newdata = prediction_data, interval="predict") %>% as.data.frame
112 | prediction$bfsnr <- c(6290, 6300)
113 | ```
114 |
115 | We can now plot the predictions for the two ballots. Note, it is crucial to correctly report uncertainty, hence, we have to include the prediction interval as well.
116 |
117 | ```{r echo=TRUE, warning=FALSE, message=FALSE, eval=TRUE}
118 |
119 | ggplot(prediction, aes(x=as.factor(bfsnr), y=fit, ymin=lwr, ymax=upr))+
120 | geom_pointrange(aes(color="#FF6B00"))+
121 | geom_text(aes(label=round(fit*100, 1)), nudge_x = 0.05)+
122 | scale_y_continuous(limits=c(0,1), labels=scales::percent)+
123 | scale_x_discrete(labels=c("Housing\nInitiative", "Law against\ndiscrimination"))+
124 | geom_hline(yintercept=.5, linetype="dashed", color="grey70")+
125 | labs(title="Simple Prediction of Vote-Shares", subtitle="February 9th, 2020", y="Predicted Yes-Share", x="", caption="politan.ch")+
126 | theme_bw()+
127 | theme(legend.position="none",
128 | axis.ticks.x = element_blank(),
129 | panel.grid.major.x = element_blank())
130 |
131 | #add the points to the previous plot
132 | prediction_data$yesshare[prediction_data$bfsnr==6290] <- prediction$fit[1]
133 | prediction_data$yesshare[prediction_data$bfsnr==6300] <- prediction$fit[2]
134 |
135 | prediction_data$lwr[prediction_data$bfsnr==6290] <- prediction$lwr[1]
136 | prediction_data$lwr[prediction_data$bfsnr==6300] <- prediction$lwr[2]
137 |
138 | prediction_data$upr[prediction_data$bfsnr==6290] <- prediction$upr[1]
139 | prediction_data$upr[prediction_data$bfsnr==6300] <- prediction$upr[2]
140 |
141 | plot1 + geom_point(data=prediction_data, aes(y=yesshare, x=nr_yesshare), color="#FF6B00", size=3)+
142 | geom_linerange(data=prediction_data,aes(ymin=lwr, ymax=upr), color="#FF6B00")
143 |
144 | ```
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
--------------------------------------------------------------------------------
/docs/authors.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Authors • swissdd
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
60 |
61 |
62 |
63 |
64 |
70 |
71 |
72 |
73 |
74 |