Stylised (Stylized) Economics Charts in Python/Matplotlib: Part 1

Charts can be your best friend in understanding the world. They can also be your worst enemy.

One of the Proprietor’s pet hates is ‘analysts’ putting a metaphorical magnifying glass over a chart and attempting to rationalize any little up or down that might occur. Or seeing relationships during part of the chart sample that would not hold if they calculated the simple correlation over the entire period (even if you thought correlation meant anything, but that’s a discussion for another time).

Now we’ve got that out of the way.

Charts based on data are easy. But what about stylised charts designed to illustrate concepts without being concerned about actual numbers?

For example, consider the standard average and marginal cost curve representation of a firm:

We’ve all seen this chart a thousand times before and know what it means.

You might be wondering why and when you would do such charts in the course of your work?

We might be old school, but we think you should be hand drawing such charts all the time to understand the problem you are analysing. Start simply and then complicate your model as required. We feel there’s too much making up models as you go nowadays.

There will be occasions where you need to illustrate economic concepts to consumers of your work – maybe not the charts here but stylised charts nonetheless.

Python’s Matplotlib package is very handy for a range of reasons – have we mentioned we love step charts. And don’t start us on trying to do stylised charts in Excel.

This isn’t a full tutorial on Matplotlib – there’s plenty of help available online. Our aim is to show you the stylized charting methods within some code.

If you do want a basic Matplotlib charting session, perhaps for data, contact us and we’ll see what we can do.

Having said that, this is our first post on Matplotlib, so we do owe a brief explanation of our technique.

There are two schools of thought on how to plot in Matplotlib once it is called into your Python environment – ‘plt’ (pyplot-style) and ‘fig/ax’ (object-orientated-style).

For both methods we need to import Matplotlib. We’ll also import pandas to generate a bit of data for our example.

import matplotlib.pyplot as plt
import pandas as pd

Let’s create some values by creating a dictionary and converting it to a DataFrame. We’ll use formulas to create the columns ‘yvalues’ and ‘secondyvalues’.

data={'xvalues':[1,2,3,4,5,6,7,8,9]}
df=pd.DataFrame(data)
df['yvalues']=df['xvalues']**2
df['secondyvalues']=df['xvalues']+10

The first method to chart our data is the ‘plt’ method. It’s as simple as typing the following lines.

plt.plot(df.xvalues,df.yvalues)
plt.plot(df.xvalues,df.secondyvalues)

Within ‘plt.plot’, your enter your chart x-values, followed by your y-values.

Note that if you’re running this code in Anaconda/Spyder’s IPython console, you’ll need to paste it all in at once. If you enter the first line and hit ‘return’, you’ll get a chart based on the first line. Some other consoles need you to enter ‘plt.show()’ before the chart appears, but not Anaconda/ Spyder. If you’re using the script window (i.e. running it from within a file), you’ll have everything scripted before you execute the code.

The ‘fig/ax’ method is below.

fig, ax = plt.subplots()
ax.plot(df.xvalues,df.yvalues)
ax.plot(df.xvalues,df.secondyvalues)

Either one gives the following chart.

No axis labels, titles or legends yet. But you get the idea.

The ‘fig/ax’ method does allow for a structured specification of subplots, but ‘plt.subplot()’ works fine. The subplots in our post on Australian Quarterly Gross State Product were done with ‘plt.subplot()’.

We have no strong feeling either way, but use ‘plt’ because that’s the way we first learned. The code presented below can use either.

There are three mains ways to do stylised charts:

  • point to point (straight lines);
  • with data from (say) DataFrames; and
  • directly from equations.

There might be others, but these three have covered everything we’ve needed to do so far.

In this post we’ll focus on the first one. We’ll detail the next two in a future post.

Straight lines (point to point) can be charted without any data input from (say) lists or DataFrames. It’s as simple as typing something like:

plt.plot([20,60],[180,140],color='tab:grey')

This code plots a grey straight line moving from 20 to 60 on the x-axis, and from 180 to 140 on the y-axis, as shown below. We’ve chosen a tab grey from Matplotlib’s colors.

Let’s say we want to draw a fairly standard stylised labour-market chart demonstrating how imposing a minimum wage higher than the market-clearing wage affects employment.

The code for this is below.

plt.title('Employment Impact of a Minimum Wage')

plt.xlim(10,70)
plt.ylim(130,200)
   
plt.plot([10,30],[150,150],color='b')
plt.text(10.5,140.0,"Marginal\nDisutility\nof Labour",size=8,color='b')
   
plt.plot([10,40],[160,160],color='lightgrey',linestyle='--')
plt.plot([40,40],[130,160],color='lightgrey',linestyle='--')
   
plt.annotate(s='', xy=(30,138),xytext=(40,138),arrowprops = dict(arrowstyle='<->'))
plt.text(31,140,"Employment\nLoss",size=8, color='k')

#plt.plot([30,40],[158,158],color='plum',linestyle='--')

#plt.annotate(s='', xy=(32,158),xytext=(40,175),arrowprops = dict(arrowstyle='->',color='purple'))
#plt.text(40,175,"Shadow Price of\nLabour",size=8, color='purple')

plt.axhspan(170,150,xmin=0.0,xmax=20/60,alpha=0.5,color='red')
plt.annotate(s='Additional Surplus to\nEmployed', xy=(20,162),xytext=(30,185),arrowprops = dict(arrowstyle='->'))


trianglex=[30,30,40,30]
triangley=[150,170,160,150]
plt.plot(trianglex,triangley, color='y')
plt.fill(trianglex,triangley,color='y')

plt.plot([10,30],[170,170],'tab:red')
plt.plot([30,30],[130,170],'tab:red')
#plt.plot([50,50],[130,170],'tab:red')
plt.text(11,171,"Wage Rate",size=8,color='r')

plt.annotate(s='Deadweight\nLoss', xy=(32,162),xytext=(38,175),arrowprops = dict(arrowstyle='->'))

#Labour Demand Curve
plt.plot([20,60],[180,140],color='tab:grey')
plt.text(61,135,"Marginal\nProduct\nof Labour\nDemand",size=8,color='k')

#Labour Supply Curve
plt.plot([20,60],[140,180],color='tab:grey')
plt.text(61,175,"Marginal\nDisutulity\nof Labour\nSupply",size=8,color='k')

There’s a bit of code here and our aim isn’t to detail all of it, but to show you the general method within some sort of meaningful example.

You might notice there are some lines commented out with hashtags that could turn the chart into some slightly different labour-market charts. You’ll need to comment out some of the existing code or you’ll have too many lines/text on your chart. As a test see if you can work the changes out for yourself.

We’ve used ‘plt.annotate’ in this code for arrows – the alternative is ‘plt.arrow’. We like ‘plt.annotate’ because you can pin text exactly to the end of the arrow, and we’ve just found the arrow styles a bit more flexible. But you can go either way.

You can see we’ve entered the ‘Employment Loss’ text (‘\n’ breaks the line) outside of the ‘plt.annotate’ command. We’ve done this because we wanted the text in a different location.

To colour areas we’ve used two different methods in our charts to fill areas:

  • ‘plt.axhspan’ to to the rectangle; and
  • defined specific triangles and ‘plt.fill’ for coloring triangles.

We did this to show a couple of different methods. The ‘plt.axhspan’ method only works for rectangles, but we could have specified ‘trianglex’ and ‘triangley’ as rectangles.

You’ll also notice that you have to be very exact when specifying locations of features of the chart, such as the ‘plt.annotate()’ commands for arrows and labels.

You might need to play a bit with locations (e.g. start and end points of the arrows), as it’s more pleasing to the eye if lines and arrows are parallel.

Entering exact coordinates is a bit tedious, but the advantage is that you can specify locations exactly.

So here’s the chart.

A final tip is to make sure you put the colour/line you want most prominent last so that it goes to the top in you chart. For example, if we put the code for the yellow triangle after the code for the vertical red line the chart looks like this.

Doesn’t look quite as good.

It’s also why we specify the labour demand and supply curves quite late in the code.

A disadvantage of the point to point method is that you’ll have to back-out the equations for each line should you wish to find the intersection of two curves (e.g. equilibrium employment at the market wage above, so we can draw our ‘plum’ lines).

But it’s not a big deal.

As with everything to do with Python, there are multiple ways to perform any task. Don’t get too hung up on the detail of your code. You want a chart that looks good, so whatever method you can come up with is fine.

What do you think? Do you ever use stylised charts in your work? Is there a better way? Is there anything else on charts you’d like us to cover?

Please contact us and let us know.