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 [email protected] or [email protected]
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.pyis where the Dash app, the layout and the callbacks are defined. -
countries.pydefines 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.pydefines the classElection, which serves as a parent class to classes that contain information about elections that were held in the past. -
electoral_systems.pydefines the classSystem, containing information about particular electoral systems. -
regions.pydefines the classesElectoral_RegionandElectoral_Result. AnElectoral_Regioncontains information about how many votes each party got in a particular region at a given election. Once anElectoral_Regionis defined, one can use its methodcompute_election_resultto 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_figuresanddisplay_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_diffandget_lost_votesto have an idea on how to implement your own method. -
Modify the
regions.Election_Resultmethodsplot_tooltip,get_map_plot,get_piechart_plotandget_bar_plotif 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, wherexrefers 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.pythat inherits fromCountryand add the map center, the map zoom and the geojson strings containing the borders of every region at every level. You can follow the classSpainas an example. -
Add the name of your country to the
COUNTRY_LISTdefined 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_NAMEwith 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 theElectioninternal method_parse_data. If you want to use a different format, feel free to modify the_parse_datamethod or add an alternative one.
-
Define a class in the file
elections.pythat inherits fromElection. This class will need to define the attributescountry,regions,parties,colors, andelectoral_system. They are all defined inElection's docstring. -
Each
Electoral_Regionobject also needs to have an attribute calledsubregions, which is a list ofElectoral_Regionsthat 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
ELECTIONSdictionary ofmain.pyin 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.
