Main contributor for this project is Alejandro de Miquel who is a fantastic coder and scientist with great expertise in everything data related. You can see some of his awesome work here: https://github.com/alejandrodemiquel
This repository contains a Dash app aimed at visualising and analysing the effects that using different electoral systems would have had in past parliamentary elections (house of representatives). The app is currently offline. But here is a screenshot of how it looks like:
The app itself is contained in the folder app
.
The folder data
contains information about the data sources that were used
to build the app, as well as the preprocessing scripts that were used to
generate all the files present in the folder app/data
.
Therefore, all the workflows are completely reproducible.
Anyone can contribute by making suggestions and reporting bugs. Feel free to open an issue or send us an email to alejandrodemiquel@gmail.com or atl64x@gmail.com
Everyone is welcome to make pull requests with improvements, be it bug fixes, performance improvements, adding analysis metrics, adding countries or modifying the layout/appearance of the app.
If you decide to contribute, please publish a pull request and we will review it as soon as we can.
All the relevant files that you need to understand are under the folder app
.
In particular,
-
main.py
is where the Dash app, the layout and the callbacks are defined. -
countries.py
defines the classCountry
, which serves as a parent class to classes that contain information about particular countries, such as the geojson with the region boundaries or the zoom to be used in the map. -
elections.py
defines the classElection
, which serves as a parent class to classes that contain information about elections that were held in the past. -
electoral_systems.py
defines the classSystem
, containing information about particular electoral systems. -
regions.py
defines the classesElectoral_Region
andElectoral_Result
. AnElectoral_Region
contains information about how many votes each party got in a particular region at a given election. Once anElectoral_Region
is defined, one can use its methodcompute_election_result
to obtain the result on that region given a particular electoral system. The result is returned as an object of the classElectoral_Result
. This object contains all sorts of methods to compute the metrics derived from that result, as well as to build the choropleth maps, the pie charts and the bar charts.
Currently we are supporting the metrics:
- Seat Difference
- Lost Votes
However, we are open to add more insightful metrics. If you want to add one yourself, all you need to do is:
-
Add your option in
main.py
'sdropdown_metrics
. -
Define what to do when your metric is selected in
main.py
's callbacksupdate_figures
anddisplay_tooltip
. You will need to think of what you plot in the upper figure, in the lower figure, in the choropleth maps and in the map tooltips. -
Add the relevant method in
regions.Election_Result
. You can use the methodsget_seat_diff
andget_lost_votes
to have an idea on how to implement your own method. -
Modify the
regions.Election_Result
methodsplot_tooltip
,get_map_plot
,get_piechart_plot
andget_bar_plot
if necessary, so that the figures show what you want them to show whenever your option is selected.
Adding a new country without any elections associated is easy to do and is the first step that needs to be done if you want to add an election of a country that is not supported yet. In order to do so, you need to follow these steps:
-
Download the borders of all the regions that you want to consider for this country, at every level (electoral district, province, state...) and store them under
data/YOUR_COUNTRY_NAME
. Also, add the data sources indata/Data_Sources.txt
. You might find the boundary of your country (level 0) in the fileapp/data/countries.geojson
. -
Preprocess them in order to have them in the correct format for the app: For every region level, build a geojson file making sure that the id of every region's polygon corresponds to the region name. Save these files under
app/data/YOUR_COUNTRY_NAME/level_x.geojson
, wherex
refers to the level of the regions contained in that file. If you use any python file to preprocess your data, store it underdata/YOUR_COUNTRY_NAME/preprocess_data.py
-
Add a class in the file
countries.py
that inherits fromCountry
and add the map center, the map zoom and the geojson strings containing the borders of every region at every level. You can follow the classSpain
as an example. -
Add the name of your country to the
COUNTRY_LIST
defined incountries.py
. -
Specify what happens when your country is selected in the dropdown menu: you will need to modify the callback
switch_country
.
In order to add a new election of an already supported country, follow these steps:
-
Download the election data from a reliable source. Store the data under
data/YOUR_COUNTRY_NAME
with a name that relates to the particular election. Specify the data source indata/Data_Sources.txt
. -
Preprocess the data in order to have it in the right format, and store the python file used for preprocessing the data under
data/YOUR_COUNTRY_NAME
. The current format is a pickle file whose object is a dictionary containing a list with the parties participating in the election and a dictionary of regions, divided by levels, and with each region having the following information:name
: The name of the region.level
: The level of the region.census
: The total number of votes registered in the region.n_seats
: The total number of parliament seats elected in the region.votes
: Keys are party names, values are the number of votes that they obtained in the region.nota
: Number of 'None of the Above' votes.spoilt_votes
: Number of spoilt votes You can use Spain as an example. The file with the preprocessed data will be read by theElection
internal method_parse_data
. If you want to use a different format, feel free to modify the_parse_data
method or add an alternative one.
-
Define a class in the file
elections.py
that inherits fromElection
. This class will need to define the attributescountry
,regions
,parties
,colors
, andelectoral_system
. They are all defined inElection
's docstring. -
Each
Electoral_Region
object also needs to have an attribute calledsubregions
, which is a list ofElectoral_Regions
that is exactly one lever lower and that are actually subregions of the given region. In the case of Spain and the USA, this is done in the method_build_region_tree
. -
Add the election object in the
ELECTIONS
dictionary ofmain.py
in order for it to be displayed as an option in the dashboard.
If you modify any of the source code, make sure you run the tests locally before submitting a pull request. In order to execute them, you need to run these two commands from the root directory:
flake8
pytest tests
pytest
is the framework that we are using for running our functional tests.
Flake8
checks that the python code follows all the python standards.
You will need to have both of them installed.
Just adding tests, without making any source code contribution, is another good way to contribute to this project. Currently, our tests are quite basic. They make sure that things are initialized correctly and do some other simple checks. We could make use of some contributions to test that each method gives expected results.