Creating a Dynamically-Colored Visual with Matplotlib

Creating an Dynamically-Colored Visual with Matplotlib

This project inspired by the following paper:

      Ferreira, N., Fisher, D., & Konig, A. C. (2014, April). Sample-oriented task-driven visualizations: allowing users to make better, more confident decisions.       In Proceedings of the SIGCHI Conference on Human Factors in Computing Systems (pp. 571-580). ACM. (video)

In the figures below, the color of the bar is based on the amount of data covered (e.g. a gradient ranging from dark blue for the distribution being certainly below this y-axis, to white if the value is certainly contained, to dark red if the value is certainly not contained as the distribution is above the axis). The 95% confidence interval for the data distribution of each bar is catured as the green error bars. The y-value is captured as the thin green line in each plot.

%matplotlib notebook

import pandas as pd
import matplotlib.pyplot as plt
import scipy.stats as st
import matplotlib as mpl
from matplotlib import cm
import numpy as np
mpl.rcParams['figure.dpi']= 150
np.random.seed(12345)

df = pd.DataFrame([np.random.normal(32000,5000,3650), 
                   np.random.normal(42000,2000,3650), 
                   np.random.normal(43500,2800,3650), 
                   np.random.normal(48000,1400,3650)], 
                  index=[1992,1993,1994,1995]).T
i = list(np.arange(4))
xs = [1992, 1993, 1994, 1995]
means = []
stds = []
for x in xs:
    means.append(df[x].mean())
    stds.append(df[x].std())

Create Visual

def create_visual(yval=30000):
    colors = []
    colormap = plt.get_cmap('seismic_r')
    
    for n, mean in enumerate(means):
        z_score = (yval - mean)/stds[n]
        p_value = st.norm.cdf(z_score)
        colors.append(colormap(p_value))
    
    cbar = plt.colorbar(cm.ScalarMappable(norm=cm.colors.Normalize(), 
                                          cmap=colormap), 
                        orientation='horizontal',
                        shrink=0.5,
                        pad=0.0625,
                        ax=plt.gca())
    for l in cbar.ax.xaxis.get_ticklabels():
        l.set_fontsize(8)
    cbar.ax.tick_params(length=0)
    cbar.outline.set_linewidth(0.25)
    
    barlist = plt.bar(x=i,
                      height=means,
                      yerr=[1.96*std for std in stds],
                      error_kw=dict(lw=1.5, 
                                    capsize=7.5, 
                                    capthick=1.5,
                                    ecolor='green'),
                      edgecolor='k',
                      lw=.25,
                      color=colors,
                      width=.5)
    
    plt.title('Dynamic Coloration Demonstration',
              fontsize=10,
              alpha=0.8)
    
    plt.xticks(i, 
               ('1992', '1993', '1994', '1995'))
    plt.xlim([-0.5,3.5])
    plt.gca().tick_params(length=0)
    
    plt.xticks(fontsize=8,
               alpha=0.8)
    plt.yticks(fontsize=8,
               alpha=0.8)
    
    for spine in plt.gca().spines.values():
        spine.set_visible(False)
    
    plt.gca().tick_params(length=0)
    
    plt.axhline(color='green',
                lw=0.5,
                y=yval)
    
#    fname = 'figures/color_demo_' + str(yval) + '.svg'
#    plt.savefig(fname)
plt.figure(figsize=(6,5))
create_visual(40000)
<IPython.core.display.Javascript object>

plt.figure(figsize=(6,5))
create_visual(35000)
<IPython.core.display.Javascript object>

plt.figure(figsize=(6,5))
create_visual(45000)
<IPython.core.display.Javascript object>

plt.figure(figsize=(6,5))
create_visual(42000)
<IPython.core.display.Javascript object>