How to create a Choropleth Map Plot in Python with Geoviews
By: Christos Zeglis
Tags: Python, Education, Data Science, Data Visualization, Interactive Maps

To understand what we are looking at, the higher the number, the higher a country scores in terms of freedom. As you can see there is a score in each category which combined lead to the overall score . Thus, there is no need to get into the details of each category and so we will just use the overall score in this tutorial.

Let’s refer to that dataframe as ef_df from now on.

The dataframe is almost perfect. I will just add two lines of code for better understanding and to fix an issue with some strings I found out.

ef_df = ef_df.rename(columns={'name':'country'})

ef_df['country'] = ef_df['country'].apply(lambda x: x.strip())

Now we are ready to move on to the next element required in order to create a choropleth map plot. That is a is a shapefile (.shp) that contains the boundaries (the shapes actually) of the countries we are interested in.

I don’t really know if there are better shapefiles from the one I will use, but you are free to search and find them on the web. The one I will use can be download from the NaturalEarthData website.

We can read the shapefile using Geopandas and we will name that dataframe shapes_df .

shapes_df = gpd.read_file('C:/.../your_directory/ne_50m_admin_0_countries.shp', driver='ESRI Shapefile')

From shapes_df we need only 3 columns: ADMIN , TYPE , geometry .

The geometry column is of utmost importance as it contains the geoinformation, i.e. the country shapes. Therefore we will run the following line.

shapes_df = shapes_df[['ADMIN', 'TYPE', 'geometry']]

Now the shapes_df will look like this.

As I said, I don’t know if there is a better shapefile available on the web, but in this one, we will also have to run the following line to correct some country names in order to match the country names on the ef_df dataframe.

shapes_df = shapes_df.replace({'Czechia': 'Czech Republic',

'Republic of Serbia':'Serbia'})

Now we can finally merge these two dataframes into one. From the ef_df we will keep only the country , index year , and overall score columns and only for the the index year = 2019.

merged_df = gpd.GeoDataFrame(\

pd.merge(\

ef_df[ef_df['index year']==2019][['country','index

year', 'overall score']], shapes_df,

left_on='country', right_on='ADMIN'))

The merged_df will look like this.

Before we proceed, please make sure that the geometry data types are either Polygons or Multipolygons by running something like that.

merged_df['geometry'].geom_type.value_counts()

Just to make sure

Now, we are ready to plot our choropleth map using the geoviews.Polygons method. First, let’s assign to poly the gv.Polygons and to carto the basemap.

polys = gv.Polygons(merged_df, vdims=['country', 'overall score']).\

opts(color='overall score', cmap='reds_r', colorbar=True,\

width=1000, height=600, tools=['hover', 'tap'], alpha=0.7,\

hover_line_color='black',line_color='black',\

hover_fill_color=None, hover_fill_alpha=0.5,\

xaxis=None,yaxis=None) carto = gvts.CartoDark.opts(alpha=0.6)

Now if we run them together like:

polys * carto

we get our choropleth map:

We can clearly see which countries are considered to be more free in economic terms (light colors) and which ones are not (darker colors).

Bonus: Using Geoviews we can combine even more plots into one.

To show you that, I will use a dataset that contains the country capitals and their respective geo-locations, i.e. latitude and longitude. I found that dataset here. I will refer to that as capitals_df.

After some cleaning and merging, we get to that looking like this.

Now, we can use the gv.Points method we saw on the previous post and assign it to points.

points = gv.Points(capitals_df, ['longitude', 'latitude'],\

['capital']).opts(

opts.Points(width=1000, height=600, alpha=0.8,\

tools='hover'],\

xaxis=None, yaxis=None, color='white',\

size=7))

In the end, we will run the following command.

polys * carto * points

and we will get the combined plot.

That’s it for today. Hope you found it useful. Till next time!