Visualizing World Happiness With A Geographic Map

Which countries are the happiest? This question is extremely difficult to answer. Yet, the World Happiness Report tries its best to report on this topic by ranking 156 countries on how happy their citizens perceive themselves to be. One interesting way to analyze the World Happiness Report data is to observe how the happiness scores of countries relate to those close to them on a geographic map.

By the end of this tutorial, you will have an interactive geographical map that you can use to investigate the happiness of different countries around the world – or, you can swap out the data and visualize some interesting geographical data of your own!

Installing packages

Here’s the prerequisites you’ll need to complete this tutorial that you may or may not have:

  • Geopandas. GeoPandas is a package that makes working with geospatial data in Python much easier. You can find instructions to install it here.
  • Shapefiles. We need a shapefile (a special file that is intended for storing geographic information) to render a map of the world, or a city. The shapefile we are using in this example is available on my Github, or you can find it here.
  • Bokeh. Bokeh helps quickly make interactive plots, dashboards, and data applications.
  • World Happiness Report Data. I found the data on Kaggle.
# basic modules
import geopandas as gpd
import pandas as pd
import json
import glob
import os

# visualizaztion modules
from bokeh.resources import INLINE
from bokeh.io import output_notebook, show, output_file
from bokeh.plotting import figure
from bokeh.models import GeoJSONDataSource, LinearColorMapper, ColorBar
from bokeh.palettes import brewer,mpl

# interactive visualization models
from bokeh.io import curdoc, output_notebook
from bokeh.models import Slider, HoverTool
from bokeh.layouts import widgetbox, row, column

Importing and cleaning the data

The shapefile contains all of the coordinates that will help draw the world map later. The columns can be renamed so that they make more sense for analysis.

shapefile = 'ne_110m_admin_0_countries/ne_110m_admin_0_countries.shp'

# Read shapefile using Geopandas
gdf = gpd.read_file(shapefile)[['ADMIN', 'ADM0_A3', 'geometry']]

# Rename columns.
gdf.columns = ['country', 'country_code', 'geometry']

The data that was provided by the World Happiness Report came in one file for each year between 2015 and 2019. Some of the files have different shapes and column names. We must ensure that all of the columns of interest have matching names to prevent issues later.

Also, before each of the files are merged together with the pd.concat() function, the year in the file name must be appended to the data frame in a new column called Year. Otherwise, we will have five rows in the dataframe for each country, without the slightest idea what year the data came from.

Finally, it was discovered that in the shapefile, US is labeled as United States of America. In the World Happiness Report data, US is labeled as United States. In order for the two to match up in later operations, we must update the countries data frame to have United States be replaced with United States of America.

datafiles_1 = ['2019.csv', '2018.csv']
datafiles_2 = ['2015.csv', '2016.csv']

df = pd.concat((pd.read_csv(f).assign(Year=os.path.basename(f[0:4])) for f in datafiles_1), sort=False)
df2 = pd.concat((pd.read_csv(f).assign(Year=os.path.basename(f[0:4])) for f in datafiles_2), sort=False)
df3 = pd.read_csv('2017.csv').assign(Year="2017")

df['Country'] = df['Country or region']
df2['Score'] = df2['Happiness Score']
df3['Score'] = df3['Happiness.Score']

countries = pd.concat([df[['Country', 'Score', 'Year']], df2[['Country', 'Score', 'Year']], df3[['Country', 'Score', 'Year']]], sort=False)

countries = countries.replace('United States', 'United States of America')

Creating functions for interactivity

We will need two functions to make our geographic map interactive and dynamic.

  1. A function to get the happiness data. This function will accept the year of choice as a parameter; the slider will allow you to select that year. The happiness data for that year will be returned in JSON form.
  2. A function to trigger whenever the slider is updated. The job of this function is to take whatever new value that the slider holds, and pass it along to the function from number 1. This function will keep the map data up to date with whatever the slider is set to.
# 1. Define function that returns json_data for the year selected by slider.
def json_data(selectedYear):
    yr = selectedYear
    df_yr = countries[countries['Year'] == yr]
    merged = gdf.merge(df_yr, left_on ='country', right_on ='Country', how ='left')
    merged['Score'] = merged.Score.fillna("N/A")
    merged_json = json.loads(merged.to_json())
    json_data = json.dumps(merged_json)
    return json_data

# 2. Define the callback function.
# This is what will be called when the slider value changes.
def update_plot(attr, old, new):
    yr = slider.value
    new_data = json_data(str(yr))
    geosource.geojson = new_data
    p.title.text = 'Happiness Score by Country (%d)' %yr

Rendering the geographic map

Finally, it’s time to see all of this data on a map!

Color can be make or break in a visualization. So, I did some research on Bokeh’s palettes and picked one that made sense with my data. Hovering tool tips were helpful in this scenario, because some people (like me) struggle with geography and probably couldn’t name half of the countries on the map.

When the slider object is created, its callback function is one of the functions created above. The lines between the countries were set to white to better match the color scheme.

# Input GeoJSON source that contains features for plotting.
# Select a default year to show on first display.
geosource = GeoJSONDataSource(geojson = json_data("2016"))

# Define a sequential multi-hue color palette.
# I chose the Plasma theme because 10 would map to yellow :)
palette = mpl['Plasma'][10]

# Instantiate LinearColorMapper that maps numbers to a sequence of colors.
color_mapper = LinearColorMapper(palette = palette, low = 0, high = 10)

# Add the hovering tooltips.
hover = HoverTool(tooltips = [ ('Country','@country'),('Score', '@Score')])

# Create the color bar. 
color_bar = ColorBar(color_mapper=color_mapper, label_standoff=8,width = 500, height = 20, border_line_color=None,location = (0,0), orientation = 'horizontal')

# Create the figure object.
p = figure(title = 'World Happiness by Country (2016)', plot_height = 600 , plot_width = 950, toolbar_location = None, tools = [hover])
# Remove the grid lines. 
p.xgrid.grid_line_color = None
p.ygrid.grid_line_color = None
# Increase the title font size.
p.title.text_font_size = '12pt'

# Add the patch renderer to the figure. 
# Notice that this is where you provide the Score column from the json_data and the color mapper.
p.patches('xs','ys', source = geosource,fill_color = {'field' :'Score', 'transform' : color_mapper}, line_color = 'white', line_width = 0.5, fill_alpha = 1)

# Place the color bar below the map.
p.add_layout(color_bar, 'below')
    
# Make a slider object.
slider = Slider(title = 'Year',start = 2015, end = 2019, step = 1, value = 2016)
slider.on_change('value', update_plot)

# Make a column layout of widgetbox (slider) and plot, and add it to the current document
layout = column(p,widgetbox(slider))
curdoc().add_root(layout)

# Show the figure.
output_notebook(INLINE)
show(layout)

Just one last step and you can see the beautiful map you created. Since we are utilizing the on_change() function, we need the map to be interactive, otherwise, the map will not update when you move the slider. To view this application in interactive mode you need to set up a local Bokeh server. Do not fret! You can do this in one line AND you get to do it in the command line and feel like a full-blown hacker for a day.

Open up your command line and type: bokeh serve –show YOURFILENAME.ipynb

Here’s what the final product will look like:

Boom! You just made an interactive map to analyze world happiness. I hope you enjoyed. If so, check out some of my other tutorials, such as my article on visualizing Kickstarter data with a word cloud. Maybe this article reminded you that you need to brush up on your data frame knowledge.. No worries, head over to my Data Frames 101.

30 thoughts on “Visualizing World Happiness With A Geographic Map

    1. The code assumes that the folder containing the shape file is in the same location on your computer as the Jupyter notebook. Is this the case for you currently?

    1. Hi!! I am using WordPress. I had never used WP before but I followed a couple YouTube tutorials and it wasn’t too bad!
      ! You got this!!

  1. you make running a blog glance easy. The full look of your site is fantastic, as neatly as the content material! this will help me to create my own blog. if you dont mind can you give me an advice? thanks alot!!!

    1. Hi friend! Thanks for the kind words. I watched some YouTube tutorials on WordPress and that’s what helped me the most. WP is great for beginners who want to start a vlog. Good luck! :))

    1. Hello there! Yes I am using WordPress. Glad to hear you’ve joined the blog world too – I’m sure you’re doing amazing!

  2. IM SO LUCKY I FOUND YOUR WEBSITE GOOD JOB!!!!
    THANKS A LOT!!!!KEEP IT UP!!!i just want to tell you that i am just beginner to weblog and honestly liked this web-site. likely i’m planning to bookmark your blog .

  3. Hi there, I enjoy reading all of your post. I like to write a little comment to support you. NICE ONE!!! GOOD JOB!!!KEEP IT UP!!!
    THANKS A LOT!!!i would like to thnkx for the efforts you have put in writing this site.

  4. IM SO LUCKY I FOUND YOUR WEBSITE GOOD JOB!!!!
    THANKS A LOT!!!!KEEP IT UP!!!i just want to tell you that i am just beginner to weblog and honestly liked this web-site. likely i’m planning to bookmark your blog .

  5. Excellent site you have here but I was wondering
    if you knew of any discussion boards that cover the same topics talked about in this article?
    I’d really like to be a part of group where
    I can get comments from other knowledgeable individuals that share the same interest.

    If you have any recommendations, please let me know.
    Cheers!

    1. I have found some great communities through Instagram and Twitter. You can use hashtags to find people who are posting about topics you are interested in.

  6. Hi there, I enjoy reading all of your post. I like to write a little comment to support you. NICE ONE!!! GOOD JOB!!!KEEP IT UP!!!
    THANKS A LOT!!!i would like to thnkx for the efforts you have put in writing this site.

  7. Hi there, I enjoy reading all of your post. I like to write a little comment to support you. NICE ONE!!! GOOD JOB!!!KEEP IT UP!!!
    THANKS A LOT!!!i would like to thnkx for the efforts you have put in writing this site.

  8. Normally I do not learn article on blogs, but I would like to say that this
    write-up very pressured me to try and do it!
    Your writing taste has been surprised me. Thank you, very great article.

    1. 🙂 That is so kind. Thank you for taking the time to comment that, and I’m so glad you actually tried the code out!! Makes me so happy

  9. Hello would you mind letting me know which webhost
    you’re using? I’ve loaded your blog in 3 different browsers and I
    must say this blog loads a lot faster then most.
    Can you recommend a good internet hosting provider at a honest price?
    Cheers, I appreciate it!

Leave a Reply

Your email address will not be published. Required fields are marked *