Europe Happiness

In this notebook we visualize Europe countries happiness scores. Data are retrieved from World Happiness Report 2016.

In [1]:
import numpy as np
import pandas as pd
import matplotlib.cm as cm
In [2]:
df = pd.read_excel('Data/Europe-Happiness.xlsx')
df.head()
Out[2]:
Country World-Rank Score
0 Denmark 1 7.526
1 Switzerland 2 7.509
2 Iceland 3 7.501
3 Norway 4 7.498
4 Finland 5 7.413
In [3]:
N = len(df)
In [4]:
score = df['Score'].values
country = list(df.Country)
country[13] = 'U Kingdom'
country[14] = 'Czech Rep'
country[38] = 'Bosn Herzeg'
world_rk = list(df['World-Rank'])
In [5]:
import plotly.plotly as py
import plotly.graph_objs as go

Function that maps a normalized happiness score to a color in a given matplotlib colormap:

In [6]:
def map_z2color(val, colormap, vmin, vmax):
    #map the normalized value val to a corresponding color in the mpl colormap
    
    if vmin >= vmax:
        raise ValueError('incorrect relation between vmin and vmax')
    t = (val-vmin) / (vmax-vmin) #normalize val
    C = (np.array(colormap(t)[:3])*255).astype(np.uint8)
    #convert to a Plotly color code:
    return f'rgb{tuple(C)}'
In [7]:
def set_layout(title, plot_size):# set plot layout

    return go.Layout(title=title,
                     font=dict(size=12), 
                     xaxis=dict(visible=False, range=[-3.5, 4]),
                     yaxis=dict(visible=False, range=[-4.5, 3.5]),
                     showlegend=False,
                     width=plot_size,
                     height=plot_size,
                     hovermode='closest')  
In [8]:
def set_annotation(x, y, anno_text,  angle, fontsize=11): # annotations
    return dict(x= x,  
                y= y,       
                text= anno_text,      
                textangle=angle, # angle in degrees 
                font=dict(size=fontsize),  
                showarrow=False     
                     ) 

We visualize countries happiness through a circular barchart. The height of each bar is proportional to the corresponding happiness score:

In [9]:
bar_height = [score[k]/4.75 for k in range(N)]

The bars are inserted along the unit circle, starting with the angular position $\pi/2$, in the anti-clockwise direction:

In [10]:
theta = [np.pi/2+(2*k+1)*np.pi/72  for k in range(N)] # angular position of base bar centers
In [11]:
xi = [np.cos(theta[k]) for k in range(N)]# starting bar position
yi = [np.sin(theta[k]) for k in range(N)]


xf = [(bar_height[k]+1)*np.cos(theta[k]) for k in range(N)]#end bar position
yf = [(bar_height[k]+1)*np.sin(theta[k]) for k in range(N)]

xm = [(xi[k]+xf[k])*0.5   for k in range(N)]#mid bar position for inserting hovering text
ym = [(yi[k]+yf[k])*0.5   for k in range(N)]

xpos_t = [(bar_height[k]+1.32)*np.cos(theta[k]) for k in range(N)]#text position
ypos_t = [(bar_height[k]+1.32)*np.sin(theta[k]) for k in range(N)]

The decreased level of happiness is illustrated through the Viridis colormap:

In [12]:
import matplotlib.cm as cm
cmap = cm.viridis
In [13]:
vmin = score[-1]
vmax = score[0]
bar_colors = [map_z2color(score[k], cmap, score[-1], score[0]) for k in range(N)]

Define the hover text:

In [14]:
text = [f'{country[k]}<br>Score: {round(score[k],3)}<br>World rank: {world_rk[k]}'  for  k in range(len(score))]

Set position of the hover text inside bars:

In [15]:
trace = go.Scatter(x=xm,
                   y=ym,
                   name='',
                   mode='markers' ,
                   marker=dict(size=0.05, color=bar_colors[15]),
                   text=text,
                   hoverinfo='text')
tracet = go.Scatter(x=xf,
                    y=yf,
                    name='',
                    mode='markers' ,
                    marker=dict(size=0.05, color=bar_colors[15]), 
                    text=text,
                    hoverinfo='text')
In [16]:
traceB = [go.Scatter(x=[xi[k], xf[k], None], # Circular bars are drawn as lines of width 9
                     y=[yi[k], yf[k], None], 
                     mode='lines', 
                     line=dict(color=bar_colors[k], width=9.6),
                     hoverinfo='none')  for k in range(N)]
In [17]:
title = "Europe Happiness Score and Global Ranking"+\
"<br>Data: World Happiness Report, 2016"+\
"<a href='http://worldhappiness.report/wp-content/uploads/sites/2/2016/03/HR-V1_web.pdf'> [1]</a>"
layout = set_layout('', 900)
In [18]:
annotext_angle = [(-(180*np.arctan(yi[k]/xi[k])/np.pi)) for k in range(N)]# angles in degrees, computed following 
                                                                          #Plotly reversed trigonometric rule

Define and insert annotations in plot layout:

In [19]:
annot = []
for k in range(N):
    annot += [set_annotation(xpos_t[k], ypos_t[k], country[k],  annotext_angle[k],  fontsize=10)]
annot += [set_annotation(0.5, 0.1, title, 0 ) ]    
In [20]:
fig = go.FigureWidget(data=traceB+[trace, tracet], layout=layout)
In [21]:
fig.layout.update(annotations=annot,
                  plot_bgcolor='rgb(245,245,245)');
fig
In [ ]: