How to create semi-sarcastic plots using Matplotlib's XKCD feature
5 min read

How to create semi-sarcastic plots using Matplotlib's XKCD feature

I love XKCD comics. I won't deny using a fair share of them in my presentations. My personal favorite:

Source: https://xkcd.com/1838

So you can imagine how delighted I was that Matplotlib added XKCD-style plotting to their library.

Granted, some presentations require a certain 'professional look' but often enough I sit together with other data scientists in knowledge shares or progress updates and plots could definitely use some pizzazz to get the message across and keep the audience entertained in the process.

I couldn't resist and created a few semi-sarcastic (and opinionated) plots to demonstrate how cool this feature is.

Importing dependencies

import matplotlib.pyplot as plt
from matplotlib.ticker import MaxNLocator
import numpy as np

Note, I used JupyterLab to create this tutorial. If you want to follow along, I'd recommend you use the same for the best results.

Also, this will only work if you have Matplotlib 1.3 or above installed, so make sure to verify that.

To get the typical XKCD font, you need to download and install Humor Sans. You can find the file here.

Creating your first XKCD plot

For our first plot, we're gonna create a plot visualising the 'expectation vs reality' of 'Modelling' in your data science job. Naturally, 'modelling' can be interpreted in many ways. For the purpose of this opinionated plot, let's just take it to mean 'fitting and predicting'. 🧙‍♀️

We data professionals all know it, yet the internet still keeps fooling budding data scientists that it's all .fit() / .predict() all the time.

Hate to break it to y'all. It's actually a whole lot of understanding the business problem and finding the right tool to solve it, even (might I argue, especially) if that's just a simple SQL query, and not your latest fancy model. Also, read Eugene Yan's post on this topic, it's awesome.

Understanding the problem and coming up with creative ways of working with the (limited|messy|biased) data available, I'd say, is also way more complex than your average .fit() / .predict() so don't feel discouraged. Data science is still very fun.

Anyway, I digress.

Let's create some data and get going:

x = np.arange(0, 50)
reality = np.random.randint(8, 11, size=50)
expectation = np.random.choice(np.arange(70, 95), size=50)

We just created some random data that fits our pattern. Feel free to tweak this.

Now, for the most important part. How do we enable the XKCD magic? All you need to do is:

plt.xkcd()

Btw, you can also use it as a context manager like:

with plt.xkcd():
	# create your plot

Then we create our plot, like so:

fig, ax = plt.subplots(figsize=(15,8))
ax.plot(x, expectation, label='expectation')
ax.plot(x, reality,label='reality')
ax.set_ylim(0, 100)
ax.legend()
ax.set_title("Time spent 'Modelling' as a DS in the real world", size=25)
ax.set_xlabel('Time', size=25)
ax.set_ylabel('% Time Spent Modelling', size=25);
Time spent fitting() and predicting() as a Data Scientist

Voila! Your first XKCD plot. Isn't it a beauty?

Customising your XKCD plots

So the previous plot was quite simple. But you can customise them just like you would with any regular Matplotlib plot. So I took the liberty of making another plot, customised with annotations.

So as you may know, I started blogging not too long ago. So I thought it'd be hilarious to make a plot of my first few viewers over time. Let's gooo.

First create some data

x = np.arange(0, 30)
y = [0, 2, 2, 0, 0, 2, 1, 0, 0, 2, 2, 2, 1, 1, 0, 0, 0, 1, 2, 1, 2, 1,
       2, 0, 1, 2, 1, 1, 2, 0] # I actually used np.random.choice(3,30) but for reproducibilities' sake

Let's plot this shit

fig, ax = plt.subplots(figsize=(15,7))
ax.plot(x, y)

ax.set_xlabel('Days', size=20)

ax.yaxis.set_major_locator(MaxNLocator(integer=True))
ax.set_ylabel('Visits', size=20)

ax.annotate('boyfriend & mom', xy=(1, 2), xytext=(1, 1.7), size=20,
            arrowprops=dict(facecolor='orange', shrink=0.05),
            )
ax.annotate('boyfriend & mom again', xy=(9, 2), xytext=(8, 1.8), size=20,
            arrowprops=dict(facecolor='orange', shrink=0.05),
            )
ax.annotate('finally someone clicked my Twitter post', xy=(19, 1), xytext=(20, .5), size=20,
            arrowprops=dict(facecolor='orange', shrink=0.05),
            )

ax.set_title('Average number of views on my blog in the first 30 days', pad=15, size=25);
Blog visits on my newly created blog over the first 30 days: XKCD style!

As you can see, the XKCD style really brings home the sarcasm and humor of this plot.

Note: these numbers do not reflect reality completely. But you get the point. ;)

Optional: Troubleshooting the XKCD font

So I had some difficulties getting the Humor Sans font set up initially.

If you got it working straight away, you can skip this section. If not, read along for some tips on how I got it to work.

First, make sure the font is installed. The exact location at which the font is stored might vary by operating system (just google it if you're unsure) but you could also verify by simply opening up a Word document and checking whether you can use the Humor Sans font there.

Once installed, restart the Jupyter kernel and check whether it shows up when running matplotlib.font_manager.findfont('Humor Sans') .

A possible bug you might encounter is the name of the font. When you download it, it might have a version number in the name (e.g. 1.0.0). It is best to remove this, so the font name becomes 'Humor-Sans.ttf'.

If it still doesn't show up, make sure to check whether it's found in your Matplotlib fontlist in your cache directory.

On my Mac, I found the fontlist at ~/.matplotlib/fontlist-v330.json. To find your cachedir you can run matplotlib.get_cachedir(). The file might also show up as fontList.cache.

The version number 'v330' might be different on your machine. If your newly installed font is not in the fontlist, the simplest thing to do is to remove the fontlist. Upon restarting and rerunning the kernel, Matplotlib will automatically regenerate the fontlist with your newly installed font.

The generated entry should look something like this:

{
  "fname": "/Users/{name}/Library/Fonts/Humor-Sans.ttf",
  "name": "Humor Sans",
  "style": "normal",
  "variant": "normal",
  "weight": 400,
  "stretch": "normal",
  "size": "scalable",
  "__class__": "FontEntry"
},

That's what got it to work for me (using Python 3 and Matplotlib 3.4.3 on a Macbook Pro).

Final thoughts

So that's how you make fun and sarcastic plots in Matplotlib using XKCD. Now go out and make some of these plots yourself.

They're really great for blog posts if you are a blogger, but you could also create memes out of them for your next presentation to your DS team. :)

Let's keep in touch! 📫

If you would like to be notified whenever I post a new article, you can sign up for my email newsletter here.

If you have any comments, questions or want to collaborate, please email me at lucy@lucytalksdata.com or drop me a message on Twitter.