This notebook has some XKCD-style plots which I’m making for a talk at the Genomic Epidemiology of Malaria (GEM) conference next week. I thought I’d share in case anyone else finds it useful, please feel free to repurpose any of the code below.

import numpy as np
from scipy import interpolate
import matplotlib.pyplot as plt
%matplotlib inline
import seaborn as sns
sns.set_style('white')
plt.xkcd();
rcParams = plt.rcParams
font_size = 14
rcParams['font.size'] = font_size
rcParams['axes.labelsize'] = font_size
rcParams['xtick.labelsize'] = font_size
rcParams['ytick.labelsize'] = font_size
rcParams['legend.fontsize'] = font_size
def draw_stick_figure(ax, x=.5, y=.5, radius=.03, quote=None, color='k', lw=2, xytext=(0, 20)):
    
    # draw the head
    head = plt.Circle((x, y), radius=radius, transform=ax.transAxes, 
                      edgecolor=color, lw=lw, facecolor='none', zorder=10)
    ax.add_patch(head)
    
    # draw the body
    body = plt.Line2D([x, x], [y-radius, y-(radius * 4)], 
                      color=color, lw=lw, transform=ax.transAxes)
    ax.add_line(body)
    
    # draw the arms
    arm1 = plt.Line2D([x, x+(radius)], [y-(radius * 1.5), y-(radius*5)], 
                      color=color, lw=lw, transform=ax.transAxes)
    ax.add_line(arm1)
    arm2 = plt.Line2D([x, x-(radius * .8)], [y-(radius * 1.5), y-(radius*5)], 
                      color=color, lw=lw, transform=ax.transAxes)
    ax.add_line(arm2)
    
    # draw the legs
    leg1 = plt.Line2D([x, x+(radius)], [y-(radius * 4), y-(radius*8)], 
                      color=color, lw=lw, transform=ax.transAxes)
    ax.add_line(leg1)
    leg2 = plt.Line2D([x, x-(radius*.5)], [y-(radius * 4), y-(radius*8)], 
                      color=color, lw=lw, transform=ax.transAxes)
    ax.add_line(leg2)
    
    # say something
    if quote:
        ax.annotate(quote, xy=(x+radius, y+radius), xytext=xytext,
                    xycoords='axes fraction', textcoords='offset points',
                    arrowprops=dict(arrowstyle='-', lw=1))
    
fig, ax = plt.subplots()
draw_stick_figure(ax, quote='Hello World!')
draw_stick_figure(ax, x=.8, y=.8, radius=.01, quote="I'm small!", lw=1)
draw_stick_figure(ax, x=.2, y=.7, radius=.05, quote="I'm big!", lw=2)

png

# two columns: date (year), optimism
data = np.array([
    [2013, 0],
    [2013 + 3/12, 0],
    [2013 + 6/12, .2],
    [2013 + 9/12, .3],
    [2014, .5],
    [2014 + 3/12, .5],
    [2014 + 8/12, .6],
    [2014 + 10/12, -.9],
    [2015, -.6],
    [2015 + 3/12, -.3],
    [2015 + 6/12, .1],
    [2015 + 9/12, -1.3],
    [2016, -.6],
    [2016 + 3/12, -.3],
    [2016 + 6/12, .1],
    [2016 + 7/12, .2],
    [2017, .2],
    [2018, .2],
])
x = data[:, 0]
y = data[:, 1]

# smooth the data
tck = interpolate.splrep(x, y, s=0)
xnew = np.linspace(min(x), max(x), 300)
ynew = interpolate.splev(xnew, tck, der=0)
def plot_optimism(ax, upto=None):
    
    # setup axes
    sns.despine(ax=ax, offset=15)
    ax.set_ylim(-1.5, 1.5)
    ax.set_yticks([-1, 0, 1])
    ax.set_yticklabels(['despair', '0', 'hope'])
    ax.set_xlim(2013 + 6/12, 2016 + 9/12)
    ax.set_xticks([2014, 2015, 2016])
    ax.set_xticklabels([2014, 2015, 2016])
    ax.set_ylabel('Optimism')
    ax.spines['bottom'].set_position('center')
    ax.set_title('An unofficial history of the Ag1000G project', fontsize=16)
    
    # plot GEM conference dates
    ax.axvline(2014 + 6/12, -1, 2, linestyle='--', color='k')
    ax.axvline(2016 + 6/12, -1, 2, linestyle='--', color='k')
    ax.annotate('GEM2014', xy=(2014+6/12, -1.5), xytext=(2014+6/12, -1.6), 
                xycoords='data', textcoords='data', ha='center', va='top')
    ax.annotate('GEM2016', xy=(2016+6/12, -1.5), xytext=(2016+6/12, -1.6), 
                xycoords='data', textcoords='data', ha='center', va='top')
    
    # plot optimism
    if upto:
        flt = xnew < upto
        x = xnew[flt]
        y = ynew[flt]
    else:
        x = xnew
        y = ynew
    ax.plot(x, y, zorder=20)
    
fig, ax = plt.subplots(figsize=(10, 6))
plot_optimism(ax, upto=2014+6.5/12)
ax.annotate('Phase 1 raw SNP data', xy=(2014+2/12, .5), xytext=(10, 40), 
            xycoords='data', textcoords='offset points', ha='right', va='bottom', 
            arrowprops=dict(arrowstyle='->', lw=2))
ax.set_yticks([0, 1])
ax.set_yticklabels(['0', 'hope'])
fig.tight_layout();

png

fig, ax = plt.subplots(figsize=(10, 6))
plot_optimism(ax, upto=2014+10/12)
ax.annotate('Phase 1 raw SNP data', xy=(2014+2/12, .5), xytext=(20, 40), 
            xycoords='data', textcoords='offset points', ha='right', va='bottom', 
            arrowprops=dict(arrowstyle='->', lw=2))
draw_stick_figure(ax, x=.4, y=.77, quote='Too ... much ... data ... ', radius=.02, xytext=(-20, 30))
fig.tight_layout();

png

fig, ax = plt.subplots(figsize=(10, 6))
plot_optimism(ax, upto=2015+5.8/12)
ax.annotate('Phase 1 raw SNP data', xy=(2014+2/12, .5), xytext=(20, 40), 
            xycoords='data', textcoords='offset points', ha='right', va='bottom', 
            arrowprops=dict(arrowstyle='->', lw=2))
ax.annotate('genome accessibility', xy=(2014+11/12, -1), xytext=(10, -40), 
            xycoords='data', textcoords='offset points', ha='left', va='top', 
            arrowprops=dict(arrowstyle='->', lw=2))
ax.annotate('SNP filtering', xy=(2014+12/12, -.8), xytext=(30, -30), 
            xycoords='data', textcoords='offset points', ha='left', va='top', 
            arrowprops=dict(arrowstyle='->', lw=2))
ax.annotate('haplotype phasing', xy=(2015+1/12, -.6), xytext=(30, -30), 
            xycoords='data', textcoords='offset points', ha='left', va='top', 
            arrowprops=dict(arrowstyle='->', lw=2))
ax.annotate('SNP validation', xy=(2015+2/12, -.4), xytext=(30, -30), 
            xycoords='data', textcoords='offset points', ha='left', va='top', 
            arrowprops=dict(arrowstyle='->', lw=2))
ax.annotate('haplotype validation', xy=(2015+4/12, -.2), xytext=(30, -30), 
            xycoords='data', textcoords='offset points', ha='left', va='top', 
            arrowprops=dict(arrowstyle='->', lw=2))
ax.annotate('Phase 1 AR3 public data release', xy=(2015+6/12, .2), xytext=(-20, 50), 
            xycoords='data', textcoords='offset points', ha='center', va='bottom', 
            arrowprops=dict(arrowstyle='->', lw=2))
fig.tight_layout();

png

fig, ax = plt.subplots(figsize=(10, 6))
plot_optimism(ax, upto=2015+9.5/12)
ax.annotate('Phase 1 raw SNP data', xy=(2014+2/12, .5), xytext=(20, 40), 
            xycoords='data', textcoords='offset points', ha='right', va='bottom', 
            arrowprops=dict(arrowstyle='->', lw=2))
ax.annotate('Phase 1 AR3 public data release', xy=(2015+6/12, .2), xytext=(-20, 50), 
            xycoords='data', textcoords='offset points', ha='center', va='bottom', 
            arrowprops=dict(arrowstyle='->', lw=2))
draw_stick_figure(ax, x=.6, y=.35, quote='Population genetics???', radius=.02, xytext=(25, -30))

fig.tight_layout();

png

fig, ax = plt.subplots(figsize=(10, 6))
plot_optimism(ax)
ax.annotate('Phase 1 raw SNP data', xy=(2014+2/12, .5), xytext=(10, 40), 
            xycoords='data', textcoords='offset points', ha='right', va='bottom', 
            arrowprops=dict(arrowstyle='->', lw=2))
ax.annotate('Phase 1 AR3 public data release', xy=(2015+6/12, .2), xytext=(-20, 50), 
            xycoords='data', textcoords='offset points', ha='center', va='bottom', 
            arrowprops=dict(arrowstyle='->', lw=2))
fig.tight_layout();

png