This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import numpy as np | |
from matplotlib import pyplot as plt | |
#### From XKCD plot generator | |
""" | |
XKCD plot generator | |
------------------- | |
Author: Jake Vanderplas | |
This is a script that will take any matplotlib line diagram, and convert it | |
to an XKCD-style plot. It will work for plots with line & text elements, | |
including axes labels and titles (but not axes tick labels). | |
The idea for this comes from work by Damon McDougall | |
http://www.mail-archive.com/matplotlib-users@lists.sourceforge.net/msg25499.html | |
""" | |
import numpy as np | |
import pylab as pl | |
from scipy import interpolate, signal | |
import matplotlib.font_manager as fm | |
# We need a special font for the code below. It can be downloaded this way: | |
import os | |
import urllib2 | |
if not os.path.exists('Humor-Sans.ttf'): | |
fhandle = urllib2.urlopen('http://antiyawn.com/uploads/Humor-Sans.ttf') | |
open('Humor-Sans.ttf', 'w').write(fhandle.read()) | |
def xkcd_line(x, y, xlim=None, ylim=None, | |
mag=1.0, f1=30, f2=0.05, f3=15): | |
""" | |
Mimic a hand-drawn line from (x, y) data | |
Parameters | |
---------- | |
x, y : array_like | |
arrays to be modified | |
xlim, ylim : data range | |
the assumed plot range for the modification. If not specified, | |
they will be guessed from the data | |
mag : float | |
magnitude of distortions | |
f1, f2, f3 : int, float, int | |
filtering parameters. f1 gives the size of the window, f2 gives | |
the high-frequency cutoff, f3 gives the size of the filter | |
Returns | |
------- | |
x, y : ndarrays | |
The modified lines | |
""" | |
x = np.asarray(x) | |
y = np.asarray(y) | |
# get limits for rescaling | |
if xlim is None: | |
xlim = (x.min(), x.max()) | |
if ylim is None: | |
ylim = (y.min(), y.max()) | |
if xlim[1] == xlim[0]: | |
xlim = ylim | |
if ylim[1] == ylim[0]: | |
ylim = xlim | |
# scale the data | |
x_scaled = (x - xlim[0]) * 1. / (xlim[1] - xlim[0]) | |
y_scaled = (y - ylim[0]) * 1. / (ylim[1] - ylim[0]) | |
# compute the total distance along the path | |
dx = x_scaled[1:] - x_scaled[:-1] | |
dy = y_scaled[1:] - y_scaled[:-1] | |
dist_tot = np.sum(np.sqrt(dx * dx + dy * dy)) | |
# number of interpolated points is proportional to the distance | |
Nu = int(200 * dist_tot) | |
u = np.arange(-1, Nu + 1) * 1. / (Nu - 1) | |
# interpolate curve at sampled points | |
k = min(3, len(x) - 1) | |
res = interpolate.splprep([x_scaled, y_scaled], s=0, k=k) | |
x_int, y_int = interpolate.splev(u, res[0]) | |
# we'll perturb perpendicular to the drawn line | |
dx = x_int[2:] - x_int[:-2] | |
dy = y_int[2:] - y_int[:-2] | |
dist = np.sqrt(dx * dx + dy * dy) | |
# create a filtered perturbation | |
coeffs = mag * np.random.normal(0, 0.01, len(x_int) - 2) | |
b = signal.firwin(f1, f2 * dist_tot, window=('kaiser', f3)) | |
response = signal.lfilter(b, 1, coeffs) | |
x_int[1:-1] += response * dy / dist | |
y_int[1:-1] += response * dx / dist | |
# un-scale data | |
x_int = x_int[1:-1] * (xlim[1] - xlim[0]) + xlim[0] | |
y_int = y_int[1:-1] * (ylim[1] - ylim[0]) + ylim[0] | |
return x_int, y_int | |
#### End copy & paste | |
in2011 = np.array([ | |
539491, | |
537521, | |
579590, | |
688686, | |
790901, | |
814661, | |
761457, | |
770781, | |
728518, | |
686134, | |
642516, | |
571452, | |
550916, | |
538165, | |
453962, | |
701366, | |
], dtype=float) | |
brate = in2011[0]/in2011[4:8].sum() | |
drate = in2011[0]/in2011[-1:].sum() | |
population = [in2011.sum()] | |
rate = in2011[-3:].sum()/float(in2011[4:8].sum()) | |
rates = [rate] | |
steps = 20 | |
for i in xrange(steps): | |
old = in2011[-1] | |
in2011[1:] = in2011[:-1] | |
in2011[0] = brate * in2011[4:8].sum() | |
in2011[-1] += (1.-drate)*old | |
rate = in2011[-3:].sum()/float(in2011[4:8].sum()) | |
rates.append(rate) | |
population.append(in2011.sum()) | |
population = np.array(population) | |
rates = np.array(rates) | |
years = 2011.+np.arange(0,5*steps+1,5) | |
years_,population = xkcd_line(years, population/1000./1000.) | |
plt.plot(years_, population, 'r', lw=3) | |
plt.xlabel('Year') | |
plt.ylabel('Population (in millions)') | |
ax2 = plt.twinx() | |
years_,rates = xkcd_line(years, rates) | |
ax2.plot(years_, rates, 'b', lw=3) | |
ax2.set_ylabel('Dependency ratio') | |
plt.savefig('population.png') | |
brate = [ | |
24.1, 24.4, 24.5, 23.5, 24.0, 23.4, 23.2, 22.8, 22.1, 21.7, 20.8, 21.0, 20.2, | |
20.0, 19.6, 19.8, 20.0, 19.1, 17.5, 16.6, 16.2, 15.4, 15.2, 14.5, 14.3, 13.0, | |
12.6, 12.3, 12.2, 11.8, 11.7, 11.7, 11.5, 11.4, 10.9, 10.7, 11.0, 11.2, 11.2, | |
11.4, 11.7, 11.0, 11.0, 10.8, 10.4, 10.4, 10.0, 9.7, 9.8, 9.4, 9.6, 9.2] | |
plt.clf() | |
plt.xlabel('year') | |
plt.ylabel('fertility rate') | |
years = np.arange(1960,1960+len(brate)) | |
years_, brate = xkcd_line(years, brate) | |
plt.plot(years_, brate, 'r', lw=3) | |
plt.savefig('fertility.png') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment