US Election Choropleth with Python
By: Conor O'Sullivan
Tags: Data Visualization, Geospatial Data, Data Science, Towards Data Science, Python

You will learn how to create two interactive choropleths of US presidential election results from 1976 to 2016. The first map has a time slider. As you move the slider, the map will change to show the results in each state for a given year. For the second map, every state has been turned into a button. You can click the state to see voting trends through time. We’ll go over the code and you can find the full project on GitHub. You can also download the maps here. You should be able to open and explore them in your browser.

Python packages

We will be building the maps using folium. It is a very useful package for creating simple geospatial data visualisations. Along with folium, we will be using a handful of other Python packages. You can import them all with the code below. Make sure you have installed all the packages first.

Data sources

US Shapefile

The first dataset we need is a US shapefile. A shapefile is a file format used to store geospatial vector data. In our case, we have a set of coordinates that defines the border of each of the states in America. We read in the data as a GeoPandas dataframe. Each of the 51 rows of the dataframe gives the state’s name and coordinates (i.e. geometry).

Let’s use this shapefile to create our first folium map. In the code below, we initialise the map. By setting location=[50.77500, -100], the map will focus on the US when opened. We then add a choropleth to the map using the US shapefile and the GeoJson function. In Figure 1, you can see the map created by this code. The shapefile gives us a base to work from but we need another dataset for election results.

Figure 1: US shapefile choropleth

Election results dataset

For election results, we use the dataset provided by the MIT Election Data and Science Lab. It contains US presidential election results from 1976 to 2016. This dataset contains a row for every state, year and candidate who ran during that year. To make things a bit easier, we should first transform this dataset into a different format.

Using the code below, we transform the dataset into a nested dictionary. For each year, we have a dictionary with state names as keys. For each state, there is a dictionary giving the number of votes for the democratic and republican candidate. The dictionary has the form:

{

<year>: {

<state> : {'dem':<#votes>, 'rep':<#votes>},

<state> : {'dem':<#votes>, 'rep':<#votes>},

...},

...

}

Visualising the data

Before we move onto our interactive maps, let’s use these datasets to create a simple choropleth. We first need to define two functions. The state_style function returns a dictionary used to define the colour and border of a state. A state is made blue if it voted Democrat and red if it voted Republican in a given year. The function returns slightly different dictionaries depending on whether it is being used by a style_dictionary or style_function.

For this choropleth, we will use a style_function. This function is used by the GeoJson package to map GeoJson features to a style. In our case, a GeoJson feature would contain information on a state (i.e. name and geometry). These features are passed to style_function by the GeoJson package. By setting year=2016, we are using the results from the 2016 election to define the style for each state.

We can now use these functions to create our first choropleth. The code is very similar to the code we used to create our first map. The only difference is we now pass the style_function to the GeoJson function. As mentioned, this gives each of the states a colour based on the election results. The resulting map can be seen in Figure 2. Now, let's see how we can improve on this map and make it more interactive.

Figure 2: 2016 Election Results

Map 1: Choropleth slider

We start by creating a choropleth map with a time slider. The is done using the TimeSliderChoropleth function. This function assumes all dates are in Unix time format (i.e. a timestamp). So, we use the year_to_ts function to convert election years to timestamps. For example, the year 2016 would be converted to ‘1451606400’.

The second function we need to define, style_dictionary, returns a style dictionary. This is similar to the style_function except we are now dealing with time-series data. So, for every state, we need to define its style for every year from 1976 to 2016. The style_dictionary function returns a nested dictionary of the form:

{

<ID>: {

<timestamp> : {'opacity':1, 'color':<hex_color>},

<timestamp> : {'opacity':1, 'color':<hex_color>},

...},

...

}

The ID mentioned above is a unique ID given to a state. It is assigned automatically by the .to_json() function. These IDs are used by the TimeSliderChoropleth to map the states to their styles. So, to ensure we have the correct mapping, we first create a mapping from ID to state name. This is done in lines 7 to 13 below. The rest of the function creates the dictionary with the form seen above.

We are now ready to create our map. Again, the code is similar to before excepting we are using the TimeSliderChoropleth function and passing in a style dictionary. The results of the code can be seen in Figure 3. You will be able to slide the bar on top of the map to see the election results through time. For example, from 2012 to 2016 we can see several states turning red. This lead to a victory for the Republican candidate.

Figure 3: Choropleth slider

We should mention the code that saves the map on line 6 above. This line saves the map as an HTML file. You can open and explore it in any browser. If you are working with a jupyter notebook, the map will also be displayed below your code block. If the map is too complicated, the notebook may have issues rendering it. In this case, you have to save the map and open it in a browser before you can see it.

Map 2: Choropleth buttons

For the next map, we will turn every state into a button. You can click on the state to see the voting trends through time. Firstly, to create these trend charts, we use the code below. The getFigure function creates a standard matplotlib chart for a given state. In the last few lines, we convert the chart to HTML and add it to an IFrame. This is so that it can be embedded in our folium map. You can see an example of the chart made for California in Figure 5.

Figure 5: California voting trend

We have to define one last function before we create our button choropleth. The highlight_style function is used to define the style of a state when a mouse is hovering over it. The state will become slightly shaded when this happens. This allows us to see what state the mouse is on before clicking it.

Finally, to create the map we first create a choropleth using 2016 results. We do this using the exact same code as the map in Figure 2. Then, using the geometry of each state, we create a state marker and add a popup to each of these markers. Each popup contains one of the embedded charts discussed above. When clicked, the marker will display the popup and we will be able to see the voting trends.

You can see the result of this code in Figure 4. You can see how hovering over a state highlights it. Texas and California are also clicked to reveal their trends. You may have some difficulty viewing this map in a notebook. In this case, save it as an HTML file and open it in your browser.

Figure 4: Choropleth Buttons

Unlike this article, the US election is not quite over. At the time of writing this, there are still some votes left to be counted. When they are done, we will be able to update the visualisations with 2020 data. We’ll see some states change colour and in others voting trends will remain the same. The reasons for these changes are complicated. Visualisations like these are a good step in helping us understand them.