Trendlines With Matplotlib

on in matplotlib, plotting, python, sparkline, trendline

Okay, they look like sparklines, but as Tufte reminds us, sparklines are really meant to be super condensed and mostly used inline. But sometimes, a plot very much similar to sparklines can be a great way of showing trends.

Let’s call them trendlines.

I saw these sparklinesque plots used in the NYT Upshot article about the food trends.

Right now, I am working on blog post for OpenTable, and decided to quickly hand roll some code to plot these. Scroll to the bottom for the code, but here are how the results look:

1
trendline(xx, yy, xtick_pos_and_labels=[[0,6,12,18,24],['2000','2006','2012','2018','2024']])

Labels at both ends

1
trendline(xx, yy, xtick_pos_and_labels=[[0,6,12,18,24],['2000','2006','2012','2018','2024']], startlabel=True)

Add a title

1
trendline(xx, yy, xtick_pos_and_labels=[[0,6,12,18,24],['2000','2006','2012','2018','2024']], startlabel=True, title='TRENDING LINE')

Just the bare minimum

1
trendline(xx, yy, xtick_pos_and_labels=[[0,6,12,18,24],['2000','2006','2012','2018','2024']], fill=False)

Here is the code, enjoy, and customize away!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
def trendline(x,y,
              color='#5E3119',
              xtick_pos_and_labels = None,
              title = None,
              ymin = 0,
              fill=True,
              figsize=(10,3),
              startlabel=False):
    # create fig and axis elements
    fig, ax = plt.subplots(1,1,figsize=figsize)

    #extract beginning and ending values before rescaling
    label0 = y[0]
    label1 = y[-1]

    #rescale 
    ymax = y.max()*1.0
    y /= ymax
    ymin  /= ymax

    #fill 
    if fill:
        plt.fill_between(np.arange(len(x)),y, color=color, alpha=0.5)

    #plot the line 
    plt.plot(x,y, color=color,lw=2)


    #despine 
    for k,v in ax.spines.items():
        v.set_visible(False)
    ax.spines['top'].set_visible(False)

    #de-tick
    ax.set_yticks([])

    # add x ticks and labels 
    if xtick_pos_and_labels:
        ax.set_xticks(xtick_pos_and_labels[0])
        ax.set_xticklabels(xtick_pos_and_labels[1])
    else:
        ax.set_xticks(x)
    # let the ticks stick out 
    ax.xaxis.set_ticks_position('bottom')
    ax.xaxis.set_tick_params(direction='out')

    # give some room at both ends 

    plt.xlim(x[0]-0.5,x[-1]+0.5)
    plt.ylim(ymin,y.max()*1.15)

    #plot the ending  node
    plt.plot(x[-1],y[-1],'o',color=color)
    #put the label at the end node 
    plt.text(x[-1],y[-1]+0.05,'%2.1f'%label1)

    #optioally, plot the start node and its label 
    if startlabel:
        plt.plot(x[0],y[0],'o',color=color)
        plt.text(x[0]-0.05,y[0]+0.05,'%2.1f'%label0)

    #optionally put a title
    if title:
        plt.text(x[0],1.1,title,fontsize=15, color=color)

Comments