+\end{verbatim}
+\end{codeoutput}
+\end{codecell}
+\subsubsection{Mathematics}
+
+And we also support the display of mathematical expressions typeset in
+LaTeX, which is rendered in the browser thanks to the
+\href{http://mathjax.org}{MathJax library}.
+
+Note that this is \emph{different} from the above examples. Above we
+were typing mathematical expressions in Markdown cells (along with
+normal text) and letting the browser render them; now we are displaying
+the output of a Python computation as a LaTeX expression wrapped by the
+\texttt{Math()} object so the browser renders it. The \texttt{Math}
+object will add the needed LaTeX delimiters (\texttt{\$\$}) if they are
+not provided:
+
+\begin{codecell}
+\begin{codeinput}
+\begin{lstlisting}
+from IPython.display import Math
+Math(r'F(k) = \int_{-\infty}^{\infty} f(x) e^{2\pi i k} dx')
+\end{lstlisting}
+\end{codeinput}
+\begin{codeoutput}
+\begin{equation*}
+F(k) = \int_{-\infty}^{\infty} f(x) e^{2\pi i k} dx
+\end{equation*}
+\end{codeoutput}
+\end{codecell}
+With the \texttt{Latex} class, you have to include the delimiters
+yourself. This allows you to use other LaTeX modes such as
+\texttt{eqnarray}:
+
+\begin{codecell}
+\begin{codeinput}
+\begin{lstlisting}
+from IPython.display import Latex
+Latex(r"""\begin{eqnarray}
+\nabla \times \vec{\mathbf{B}} -\, \frac1c\, \frac{\partial\vec{\mathbf{E}}}{\partial t} & = \frac{4\pi}{c}\vec{\mathbf{j}} \\
+\nabla \cdot \vec{\mathbf{E}} & = 4 \pi \rho \\
+\nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t} & = \vec{\mathbf{0}} \\
+\nabla \cdot \vec{\mathbf{B}} & = 0
+\end{eqnarray}""")
+\end{lstlisting}
+\end{codeinput}
+\begin{codeoutput}
+\begin{equation*}
+\begin{eqnarray}
+\nabla \times \vec{\mathbf{B}} -\, \frac1c\, \frac{\partial\vec{\mathbf{E}}}{\partial t} & = \frac{4\pi}{c}\vec{\mathbf{j}} \\
+\nabla \cdot \vec{\mathbf{E}} & = 4 \pi \rho \\
+\nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t} & = \vec{\mathbf{0}} \\
+\nabla \cdot \vec{\mathbf{B}} & = 0
+\end{eqnarray}
+\end{equation*}
+\end{codeoutput}
+\end{codecell}
+Or you can enter latex directly with the \texttt{\%\%latex} cell magic:
+
+\begin{codecell}
+\begin{codeinput}
+\begin{lstlisting}
+%%latex
+\begin{aligned}
+\nabla \times \vec{\mathbf{B}} -\, \frac1c\, \frac{\partial\vec{\mathbf{E}}}{\partial t} & = \frac{4\pi}{c}\vec{\mathbf{j}} \\
+\nabla \cdot \vec{\mathbf{E}} & = 4 \pi \rho \\
+\nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t} & = \vec{\mathbf{0}} \\
+\nabla \cdot \vec{\mathbf{B}} & = 0
+\end{aligned}
+\end{lstlisting}
+\end{codeinput}
+\begin{codeoutput}
+\begin{equation*}
+\begin{aligned}
+\nabla \times \vec{\mathbf{B}} -\, \frac1c\, \frac{\partial\vec{\mathbf{E}}}{\partial t} & = \frac{4\pi}{c}\vec{\mathbf{j}} \\
+\nabla \cdot \vec{\mathbf{E}} & = 4 \pi \rho \\
+\nabla \times \vec{\mathbf{E}}\, +\, \frac1c\, \frac{\partial\vec{\mathbf{B}}}{\partial t} & = \vec{\mathbf{0}} \\
+\nabla \cdot \vec{\mathbf{B}} & = 0
+\end{aligned}
+\end{equation*}
+\end{codeoutput}
+\end{codecell}
+There is also a \texttt{\%\%javascript} cell magic for running
+javascript directly, and \texttt{\%\%svg} for manually entering SVG
+content.
+
+\section{Loading external codes}
+
+\begin{itemize}
+\item
+ Drag and drop a \texttt{.py} in the dashboard
+\item
+ Use \texttt{\%load} with any local or remote url:
+ \href{http://matplotlib.sourceforge.net/gallery.html}{the Matplotlib
+ Gallery!}
+\end{itemize}
+In this notebook we've kept the output saved so you can see the result,
+but you should run the next cell yourself (with an active internet
+connection).
+
+Let's make sure we have pylab again, in case we have restarted the
+kernel due to the crash demo above
+
+\begin{codecell}
+\begin{codeinput}
+\begin{lstlisting}
+%pylab inline
+\end{lstlisting}
+\end{codeinput}
+\begin{codeoutput}
+\begin{verbatim}
+Welcome to pylab, a matplotlib-based Python environment [backend: module://IPython.zmq.pylab.backend_inline].
+For more information, type 'help(pylab)'.
+\end{verbatim}
+\end{codeoutput}
+\end{codecell}
+\begin{codecell}
+\begin{codeinput}
+\begin{lstlisting}
+%load http://matplotlib.sourceforge.net/mpl_examples/pylab_examples/integral_demo.py
+\end{lstlisting}
+\end{codeinput}
+\end{codecell}
+\begin{codecell}
+\begin{codeinput}
+\begin{lstlisting}
+#!/usr/bin/env python
+
+# implement the example graphs/integral from pyx
+from pylab import *
+from matplotlib.patches import Polygon
+
+def func(x):
+ return (x-3)*(x-5)*(x-7)+85
+
+ax = subplot(111)
+
+a, b = 2, 9 # integral area
+x = arange(0, 10, 0.01)
+y = func(x)
+plot(x, y, linewidth=1)
+
+# make the shaded region
+ix = arange(a, b, 0.01)
+iy = func(ix)
+verts = [(a,0)] + zip(ix,iy) + [(b,0)]
+poly = Polygon(verts, facecolor='0.8', edgecolor='k')
+ax.add_patch(poly)
+
+text(0.5 * (a + b), 30,
+ r"$\int_a^b f(x)\mathrm{d}x$", horizontalalignment='center',
+ fontsize=20)
+
+axis([0,10, 0, 180])
+figtext(0.9, 0.05, 'x')
+figtext(0.1, 0.9, 'y')
+ax.set_xticks((a,b))
+ax.set_xticklabels(('a','b'))
+ax.set_yticks([])
+show()
+
+\end{lstlisting}
+\end{codeinput}
+\begin{codeoutput}
+\begin{center}
+\includegraphics[width=0.7\textwidth]{00_notebook_tour_orig_files/00_notebook_tour_orig_fig_01.png}
+\par
+\end{center}
+\end{codeoutput}
+\end{codecell}
+\end{document}
diff --git a/tests/ipynbref/Gun_Data.orig.md b/tests/ipynbref/Gun_Data.orig.md
new file mode 100644
index 0000000..881be84
--- /dev/null
+++ b/tests/ipynbref/Gun_Data.orig.md
@@ -0,0 +1,322 @@
+# Some gun violence analysis with Wikipedia data
+
+As [requested by John Stokes](https://twitter.com/jonst0kes/status/282330530412888064),
+here are per-capita numbers for gun-related homicides,
+relating to GDP and total homicides,
+so the situation in the United States can be put in context relative to other nations.
+
+main data source is UNODC (via Wikipedia [here](http://en.wikipedia.org/wiki/List_of_countries_by_intentional_homicide_rate)
+and [here](http://en.wikipedia.org/wiki/List_of_countries_by_firearm-related_death_rate)).
+
+GDP data from World Bank, again [via Wikipedia](http://en.wikipedia.org/wiki/List_of_countries_by_GDP_(PPP)_per_capita).
+
+If the numbers on Wikipedia are inaccurate, or their relationship is not sound
+(e.g. numbers taken from different years, during which significant change occured)
+then obviously None of this analysis is valid.
+
+To summarize the data,
+every possible way you look at it the US is lousy at preventing gun violence.
+Even when compared to significantly more violent places,
+gun violence in the US is a serious problem,
+and when compared to similarly wealthy places,
+the US is an outstanding disaster.
+
+**UPDATE:** the relationship of the gun data and totals does not seem to be valid.
+[FBI data](http://www2.fbi.gov/ucr/cius2009/offenses/violent_crime/index.html) suggests that
+the relative contribution of guns to homicides in the US is 47%,
+but relating these two data sources gives 80%.
+Internal comparisons should still be fine, but 'fraction' analysis has been stricken.
+
+%load_ext retina
+%pylab inline
+
+
+
+
+ Welcome to pylab, a matplotlib-based Python environment [backend: module://IPython.zmq.pylab.backend_inline].
+ For more information, type 'help(pylab)'.
+
+
+from IPython.display import display
+import pandas
+pandas.set_option('display.notebook_repr_html', True)
+pandas.set_option('display.precision', 2)
+
+
+
+
+Some utility functions for display
+
+def plot_percent(df, limit=10):
+ df['Gun Percent'][:limit].plot()
+ plt.ylim(0,100)
+ plt.title("% Gun Homicide")
+ plt.show()
+
+
+
+
+def plot_percapita(df, limit=10):
+ df = df.ix[:,['Homicides', 'Gun Homicides']][:limit]
+ df['Total Homicides'] = df['Homicides'] - df['Gun Homicides']
+ del df['Homicides']
+ df.plot(kind='bar', stacked=True, sort_columns=True)
+ plt.ylabel("per 100k")
+ plt.show()
+
+
+
+
+def display_relevant(df, limit=10):
+ display(df.ix[:,['Homicides', 'Gun Homicides', 'Gun Data Source']][:limit])
+
+
+
+
+Load the data
+
+totals = pandas.read_csv('totals.csv', '\t', index_col=0)
+guns = pandas.read_csv('guns.csv', '\t', index_col=0)
+gdp = pandas.read_csv('gdp.csv', '\t', index_col=1)
+data = totals.join(guns).join(gdp)
+data['Gun Percent'] = 100 * data['Gun Homicides'] / data['Homicides']
+del data['Unintentional'],data['Undetermined'],data['Gun Suicides']
+data = data.dropna()
+
+
+
+
+Of all sampled countries (Found data for 68 countries),
+the US is in the top 15 in Gun Homicides per capita.
+
+Numbers are per 100k.
+
+data = data.sort("Gun Homicides", ascending=False)
+display_relevant(data, 15)
+
+
+
+ Homicides Gun Homicides Gun Data Source
+ Country
+ El Salvador 69.2 50.4 OAS 2011[1]
+ Jamaica 52.2 47.4 OAS 2011[1]
+ Honduras 91.6 46.7 OAS 2011[1]
+ Guatemala 38.5 38.5 OAS 2011[1]
+ Colombia 33.4 27.1 UNODC 2011 [2]
+ Brazil 21.0 18.1 UNODC 2011[3]
+ Panama 21.6 12.9 OAS 2011[1]
+ Mexico 16.9 10.0 UNODC 2011[4]
+ Paraguay 11.5 7.3 UNODC 2000[11]
+ Nicaragua 13.6 7.1 OAS 2011[1]
+ United States 4.2 3.7 OAS 2012[5][6]
+ Costa Rica 10.0 3.3 UNODC 2002[7]
+ Uruguay 5.9 3.2 UNODC 2002[7]
+ Argentina 3.4 3.0 UNODC 2011[12]
+ Barbados 11.3 3.0 UNODC 2000[11]
+
+Take top 30 Countries by GDP
+
+top = data.sort('GDP')[-30:]
+
+
+
+
+and rank them by Gun Homicides per capita:
+
+top_by_guns = top.sort("Gun Homicides", ascending=False)
+display_relevant(top_by_guns, 5)
+plot_percapita(top_by_guns, 10)
+
+
+
+ Homicides Gun Homicides Gun Data Source
+ Country
+ United States 4.2 3.7 OAS 2012[5][6]
+ Israel 2.1 0.9 WHO 2012[10]
+ Canada 1.6 0.8 Krug 1998[13]
+ Luxembourg 2.5 0.6 WHO 2012[10]
+ Greece 1.5 0.6 Krug 1998[13]
+
+![](tests/ipynbref/Gun_Data_orig_files/Gun_Data_orig_fig_00.png)
+
+
+**NOTE:** these bar graphs should not be interpreted as fractions of a total,
+as the two data sources do not appear to be comparable.
+But the red and blue bar graphs should still be internally comparable.
+
+The US is easily #1 of 30 wealthiest countries in Gun Homicides per capita,
+by a factor of 4:1
+
+Adding USA, Canada, and Mexico to all of Europe,
+USA is a strong #2 behind Mexico in total gun homicides per-capita
+
+index = (data['Region'] == 'Europe') + \
+ (data.index == 'United States') + \
+ (data.index == 'Canada') + \
+ (data.index == 'Mexico')
+selected = data[index]
+
+print "By Total Gun Homicides"
+sys.stdout.flush()
+
+by_guns = selected.sort("Gun Homicides", ascending=False)
+#by_guns['Gun Homicides'].plot(kind='bar')
+plot_percapita(by_guns, limit=25)
+display_relevant(selected, limit=None)
+
+
+
+ By Total Gun Homicides
+
+
+![](tests/ipynbref/Gun_Data_orig_files/Gun_Data_orig_fig_01.png)
+
+ Homicides Gun Homicides Gun Data Source
+ Country
+ Mexico 16.9 10.0 UNODC 2011[4]
+ United States 4.2 3.7 OAS 2012[5][6]
+ Montenegro 3.5 2.1 WHO 2012[10]
+ Moldova 7.5 1.0 WHO 2012[10]
+ Canada 1.6 0.8 Krug 1998[13]
+ Serbia 1.2 0.6 WHO 2012[10]
+ Luxembourg 2.5 0.6 WHO 2012[10]
+ Greece 1.5 0.6 Krug 1998[13]
+ Croatia 1.4 0.6 WHO 2012[10]
+ Switzerland 0.7 0.5 OAS 2011[1]
+ Malta 1.0 0.5 WHO 2012[10]
+ Portugal 1.2 0.5 WHO 2012[10]
+ Belarus 4.9 0.4 UNODC 2002[7]
+ Ireland 1.2 0.4 WHO 2012[10]
+ Italy 0.9 0.4 WHO 2012[10]
+ Ukraine 5.2 0.3 UNODC 2000[11]
+ Estonia 5.2 0.3 WHO 2012[10]
+ Belgium 1.7 0.3 WHO 2012[10]
+ Finland 2.2 0.3 WHO 2012[10]
+ Lithuania 6.6 0.2 WHO 2012[10]
+ Bulgaria 2.0 0.2 WHO 2012[10]
+ Georgia 4.3 0.2 WHO 2012[10]
+ Denmark 0.9 0.2 WHO 2012[10]
+ France 1.1 0.2 WHO 2012[10]
+ Netherlands 1.1 0.2 WHO 2012[10]
+ Sweden 1.0 0.2 WHO 2012[10]
+ Slovakia 1.5 0.2 WHO 2012[10]
+ Austria 0.6 0.2 WHO 2012[10]
+ Latvia 3.1 0.2 WHO 2012[10]
+ Spain 0.8 0.1 WHO 2012[10]
+ Hungary 1.3 0.1 WHO 2012[10]
+ Czech Republic 1.7 0.1 WHO 2012[10]
+ Germany 0.8 0.1 WHO 2012[10]
+ Slovenia 0.7 0.1 WHO 2012[10]
+ Romania 2.0 0.0 WHO 2012[10]
+ United Kingdom 1.2 0.0 WHO2012 [10]
+ Norway 0.6 0.0 WHO 2012[10]
+ Poland 1.1 0.0 WHO 2012[10]
+
+Let's just compare US, Canada, and UK:
+
+select = data.ix[['United States', 'Canada', 'United Kingdom']]
+plot_percapita(select)
+
+
+
+
+![](tests/ipynbref/Gun_Data_orig_files/Gun_Data_orig_fig_02.png)
+
+
+Normalize to the US numbers (inverse)
+
+select['Homicides'] = select['Homicides']['United States'] / select['Homicides']
+select['Gun Homicides'] = select['Gun Homicides']['United States'] / select['Gun Homicides']
+display_relevant(select)
+
+
+
+ Homicides Gun Homicides Gun Data Source
+ United States 1.0 1.0 OAS 2012[5][6]
+ Canada 2.6 4.9 Krug 1998[13]
+ United Kingdom 3.5 92.5 WHO2012 [10]
+
+So, you are 2.6 times more likely to be killed in the US than Canada,
+and 3.5 times more likely than in the UK.
+That's bad, but not extreme.
+
+However, you are 4.9 times more likely to be killed *with a gun* in the US than Canada,
+and almost 100 times more likely than in the UK. That is pretty extreme.
+
+
+Countries represented:
+
+for country in data.index:
+ print country
+
+
+
+ El Salvador
+ Jamaica
+ Honduras
+ Guatemala
+ Colombia
+ Brazil
+ Panama
+ Mexico
+ Paraguay
+ Nicaragua
+ United States
+ Costa Rica
+ Uruguay
+ Argentina
+ Barbados
+ Montenegro
+ Peru
+ Moldova
+ Israel
+ India
+ Canada
+ Serbia
+ Luxembourg
+ Greece
+ Uzbekistan
+ Croatia
+ Kyrgyzstan
+ Switzerland
+ Malta
+ Portugal
+ Belarus
+ Ireland
+ Italy
+ Kuwait
+ Ukraine
+ Estonia
+ Belgium
+ Finland
+ Lithuania
+ Cyprus
+ Bulgaria
+ Georgia
+ Denmark
+ France
+ Netherlands
+ Sweden
+ Slovakia
+ Qatar
+ Austria
+ Latvia
+ New Zealand
+ Spain
+ Hungary
+ Czech Republic
+ Hong Kong
+ Australia
+ Singapore
+ Chile
+ Germany
+ Slovenia
+ Romania
+ Azerbaijan
+ South Korea
+ United Kingdom
+ Norway
+ Japan
+ Poland
+ Mauritius
+
diff --git a/tests/ipynbref/Gun_Data.orig.py b/tests/ipynbref/Gun_Data.orig.py
new file mode 100644
index 0000000..08f0c0b
--- /dev/null
+++ b/tests/ipynbref/Gun_Data.orig.py
@@ -0,0 +1,304 @@
+## Some gun violence analysis with Wikipedia data
+
+# As [requested by John Stokes](https://twitter.com/jonst0kes/status/282330530412888064),
+# here are per-capita numbers for gun-related homicides,
+# relating to GDP and total homicides,
+# so the situation in the United States can be put in context relative to other nations.
+
+# main data source is UNODC (via Wikipedia [here](http://en.wikipedia.org/wiki/List_of_countries_by_intentional_homicide_rate)
+# and [here](http://en.wikipedia.org/wiki/List_of_countries_by_firearm-related_death_rate)).
+#
+# GDP data from World Bank, again [via Wikipedia](http://en.wikipedia.org/wiki/List_of_countries_by_GDP_(PPP)_per_capita).
+#
+# If the numbers on Wikipedia are inaccurate, or their relationship is not sound
+# (e.g. numbers taken from different years, during which significant change occured)
+# then obviously None of this analysis is valid.
+#
+# To summarize the data,
+# every possible way you look at it the US is lousy at preventing gun violence.
+# Even when compared to significantly more violent places,
+# gun violence in the US is a serious problem,
+# and when compared to similarly wealthy places,
+# the US is an outstanding disaster.
+
+# **UPDATE:** the relationship of the gun data and totals does not seem to be valid.
+# [FBI data](http://www2.fbi.gov/ucr/cius2009/offenses/violent_crime/index.html) suggests that
+# the relative contribution of guns to homicides in the US is 47%,
+# but relating these two data sources gives 80%.
+# Internal comparisons should still be fine, but 'fraction' analysis has been stricken.
+
+# In[1]:
+%load_ext retina
+%pylab inline
+
+# Out[1]:
+#
+# Welcome to pylab, a matplotlib-based Python environment [backend: module://IPython.zmq.pylab.backend_inline].
+# For more information, type 'help(pylab)'.
+#
+# In[2]:
+from IPython.display import display
+import pandas
+pandas.set_option('display.notebook_repr_html', True)
+pandas.set_option('display.precision', 2)
+
+# Some utility functions for display
+
+# In[3]:
+def plot_percent(df, limit=10):
+ df['Gun Percent'][:limit].plot()
+ plt.ylim(0,100)
+ plt.title("% Gun Homicide")
+ plt.show()
+
+
+# In[4]:
+def plot_percapita(df, limit=10):
+ df = df.ix[:,['Homicides', 'Gun Homicides']][:limit]
+ df['Total Homicides'] = df['Homicides'] - df['Gun Homicides']
+ del df['Homicides']
+ df.plot(kind='bar', stacked=True, sort_columns=True)
+ plt.ylabel("per 100k")
+ plt.show()
+
+
+# In[8]:
+def display_relevant(df, limit=10):
+ display(df.ix[:,['Homicides', 'Gun Homicides', 'Gun Data Source']][:limit])
+
+# Load the data
+
+# In[9]:
+totals = pandas.read_csv('totals.csv', '\t', index_col=0)
+guns = pandas.read_csv('guns.csv', '\t', index_col=0)
+gdp = pandas.read_csv('gdp.csv', '\t', index_col=1)
+data = totals.join(guns).join(gdp)
+data['Gun Percent'] = 100 * data['Gun Homicides'] / data['Homicides']
+del data['Unintentional'],data['Undetermined'],data['Gun Suicides']
+data = data.dropna()
+
+# Of all sampled countries (Found data for 68 countries),
+# the US is in the top 15 in Gun Homicides per capita.
+#
+# Numbers are per 100k.
+
+# In[10]:
+data = data.sort("Gun Homicides", ascending=False)
+display_relevant(data, 15)
+
+# Out[10]:
+# Homicides Gun Homicides Gun Data Source
+# Country
+# El Salvador 69.2 50.4 OAS 2011[1]
+# Jamaica 52.2 47.4 OAS 2011[1]
+# Honduras 91.6 46.7 OAS 2011[1]
+# Guatemala 38.5 38.5 OAS 2011[1]
+# Colombia 33.4 27.1 UNODC 2011 [2]
+# Brazil 21.0 18.1 UNODC 2011[3]
+# Panama 21.6 12.9 OAS 2011[1]
+# Mexico 16.9 10.0 UNODC 2011[4]
+# Paraguay 11.5 7.3 UNODC 2000[11]
+# Nicaragua 13.6 7.1 OAS 2011[1]
+# United States 4.2 3.7 OAS 2012[5][6]
+# Costa Rica 10.0 3.3 UNODC 2002[7]
+# Uruguay 5.9 3.2 UNODC 2002[7]
+# Argentina 3.4 3.0 UNODC 2011[12]
+# Barbados 11.3 3.0 UNODC 2000[11]
+# Take top 30 Countries by GDP
+
+# In[11]:
+top = data.sort('GDP')[-30:]
+
+# and rank them by Gun Homicides per capita:
+
+# In[12]:
+top_by_guns = top.sort("Gun Homicides", ascending=False)
+display_relevant(top_by_guns, 5)
+plot_percapita(top_by_guns, 10)
+
+# Out[12]:
+# Homicides Gun Homicides Gun Data Source
+# Country
+# United States 4.2 3.7 OAS 2012[5][6]
+# Israel 2.1 0.9 WHO 2012[10]
+# Canada 1.6 0.8 Krug 1998[13]
+# Luxembourg 2.5 0.6 WHO 2012[10]
+# Greece 1.5 0.6 Krug 1998[13]
+# image file: tests/ipynbref/Gun_Data_orig_files/Gun_Data_orig_fig_00.png
+
+# **NOTE:** these bar graphs should not be interpreted as fractions of a total,
+# as the two data sources do not appear to be comparable.
+# But the red and blue bar graphs should still be internally comparable.
+
+# The US is easily #1 of 30 wealthiest countries in Gun Homicides per capita,
+# by a factor of 4:1
+
+# Adding USA, Canada, and Mexico to all of Europe,
+# USA is a strong #2 behind Mexico in total gun homicides per-capita
+
+# In[13]:
+index = (data['Region'] == 'Europe') + \
+ (data.index == 'United States') + \
+ (data.index == 'Canada') + \
+ (data.index == 'Mexico')
+selected = data[index]
+
+print "By Total Gun Homicides"
+sys.stdout.flush()
+
+by_guns = selected.sort("Gun Homicides", ascending=False)
+#by_guns['Gun Homicides'].plot(kind='bar')
+plot_percapita(by_guns, limit=25)
+display_relevant(selected, limit=None)
+
+
+# Out[13]:
+# By Total Gun Homicides
+#
+# image file: tests/ipynbref/Gun_Data_orig_files/Gun_Data_orig_fig_01.png
+
+# Homicides Gun Homicides Gun Data Source
+# Country
+# Mexico 16.9 10.0 UNODC 2011[4]
+# United States 4.2 3.7 OAS 2012[5][6]
+# Montenegro 3.5 2.1 WHO 2012[10]
+# Moldova 7.5 1.0 WHO 2012[10]
+# Canada 1.6 0.8 Krug 1998[13]
+# Serbia 1.2 0.6 WHO 2012[10]
+# Luxembourg 2.5 0.6 WHO 2012[10]
+# Greece 1.5 0.6 Krug 1998[13]
+# Croatia 1.4 0.6 WHO 2012[10]
+# Switzerland 0.7 0.5 OAS 2011[1]
+# Malta 1.0 0.5 WHO 2012[10]
+# Portugal 1.2 0.5 WHO 2012[10]
+# Belarus 4.9 0.4 UNODC 2002[7]
+# Ireland 1.2 0.4 WHO 2012[10]
+# Italy 0.9 0.4 WHO 2012[10]
+# Ukraine 5.2 0.3 UNODC 2000[11]
+# Estonia 5.2 0.3 WHO 2012[10]
+# Belgium 1.7 0.3 WHO 2012[10]
+# Finland 2.2 0.3 WHO 2012[10]
+# Lithuania 6.6 0.2 WHO 2012[10]
+# Bulgaria 2.0 0.2 WHO 2012[10]
+# Georgia 4.3 0.2 WHO 2012[10]
+# Denmark 0.9 0.2 WHO 2012[10]
+# France 1.1 0.2 WHO 2012[10]
+# Netherlands 1.1 0.2 WHO 2012[10]
+# Sweden 1.0 0.2 WHO 2012[10]
+# Slovakia 1.5 0.2 WHO 2012[10]
+# Austria 0.6 0.2 WHO 2012[10]
+# Latvia 3.1 0.2 WHO 2012[10]
+# Spain 0.8 0.1 WHO 2012[10]
+# Hungary 1.3 0.1 WHO 2012[10]
+# Czech Republic 1.7 0.1 WHO 2012[10]
+# Germany 0.8 0.1 WHO 2012[10]
+# Slovenia 0.7 0.1 WHO 2012[10]
+# Romania 2.0 0.0 WHO 2012[10]
+# United Kingdom 1.2 0.0 WHO2012 [10]
+# Norway 0.6 0.0 WHO 2012[10]
+# Poland 1.1 0.0 WHO 2012[10]
+# Let's just compare US, Canada, and UK:
+
+# In[15]:
+select = data.ix[['United States', 'Canada', 'United Kingdom']]
+plot_percapita(select)
+
+# Out[15]:
+# image file: tests/ipynbref/Gun_Data_orig_files/Gun_Data_orig_fig_02.png
+
+# Normalize to the US numbers (inverse)
+
+# In[16]:
+select['Homicides'] = select['Homicides']['United States'] / select['Homicides']
+select['Gun Homicides'] = select['Gun Homicides']['United States'] / select['Gun Homicides']
+display_relevant(select)
+
+# Out[16]:
+# Homicides Gun Homicides Gun Data Source
+# United States 1.0 1.0 OAS 2012[5][6]
+# Canada 2.6 4.9 Krug 1998[13]
+# United Kingdom 3.5 92.5 WHO2012 [10]
+# So, you are 2.6 times more likely to be killed in the US than Canada,
+# and 3.5 times more likely than in the UK.
+# That's bad, but not extreme.
+#
+# However, you are 4.9 times more likely to be killed *with a gun* in the US than Canada,
+# and almost 100 times more likely than in the UK. That is pretty extreme.
+#
+
+# Countries represented:
+
+# In[14]:
+for country in data.index:
+ print country
+
+# Out[14]:
+# El Salvador
+# Jamaica
+# Honduras
+# Guatemala
+# Colombia
+# Brazil
+# Panama
+# Mexico
+# Paraguay
+# Nicaragua
+# United States
+# Costa Rica
+# Uruguay
+# Argentina
+# Barbados
+# Montenegro
+# Peru
+# Moldova
+# Israel
+# India
+# Canada
+# Serbia
+# Luxembourg
+# Greece
+# Uzbekistan
+# Croatia
+# Kyrgyzstan
+# Switzerland
+# Malta
+# Portugal
+# Belarus
+# Ireland
+# Italy
+# Kuwait
+# Ukraine
+# Estonia
+# Belgium
+# Finland
+# Lithuania
+# Cyprus
+# Bulgaria
+# Georgia
+# Denmark
+# France
+# Netherlands
+# Sweden
+# Slovakia
+# Qatar
+# Austria
+# Latvia
+# New Zealand
+# Spain
+# Hungary
+# Czech Republic
+# Hong Kong
+# Australia
+# Singapore
+# Chile
+# Germany
+# Slovenia
+# Romania
+# Azerbaijan
+# South Korea
+# United Kingdom
+# Norway
+# Japan
+# Poland
+# Mauritius
+#
\ No newline at end of file
diff --git a/tests/ipynbref/Gun_Data.orig.tex b/tests/ipynbref/Gun_Data.orig.tex
new file mode 100644
index 0000000..58ae153
--- /dev/null
+++ b/tests/ipynbref/Gun_Data.orig.tex
@@ -0,0 +1,505 @@
+%% This file was auto-generated by IPython.
+%% Conversion from the original notebook file:
+%% tests/ipynbref/Gun_Data.orig.ipynb
+%%
+\documentclass[11pt,english]{article}
+
+%% This is the automatic preamble used by IPython. Note that it does *not*
+%% include a documentclass declaration, that is added at runtime to the overall
+%% document.
+
+\usepackage{amsmath}
+\usepackage{amssymb}
+\usepackage{graphicx}
+\usepackage{ucs}
+\usepackage[utf8x]{inputenc}
+
+% needed for markdown enumerations to work
+\usepackage{enumerate}
+
+% Slightly bigger margins than the latex defaults
+\usepackage{geometry}
+\geometry{verbose,tmargin=3cm,bmargin=3cm,lmargin=2.5cm,rmargin=2.5cm}
+
+% Define a few colors for use in code, links and cell shading
+\usepackage{color}
+\definecolor{orange}{cmyk}{0,0.4,0.8,0.2}
+\definecolor{darkorange}{rgb}{.71,0.21,0.01}
+\definecolor{darkgreen}{rgb}{.12,.54,.11}
+\definecolor{myteal}{rgb}{.26, .44, .56}
+\definecolor{gray}{gray}{0.45}
+\definecolor{lightgray}{gray}{.95}
+\definecolor{mediumgray}{gray}{.8}
+\definecolor{inputbackground}{rgb}{.95, .95, .85}
+\definecolor{outputbackground}{rgb}{.95, .95, .95}
+\definecolor{traceback}{rgb}{1, .95, .95}
+
+% Framed environments for code cells (inputs, outputs, errors, ...). The
+% various uses of \unskip (or not) at the end were fine-tuned by hand, so don't
+% randomly change them unless you're sure of the effect it will have.
+\usepackage{framed}
+
+% remove extraneous vertical space in boxes
+\setlength\fboxsep{0pt}
+
+% codecell is the whole input+output set of blocks that a Code cell can
+% generate.
+
+% TODO: unfortunately, it seems that using a framed codecell environment breaks
+% the ability of the frames inside of it to be broken across pages. This
+% causes at least the problem of having lots of empty space at the bottom of
+% pages as new frames are moved to the next page, and if a single frame is too
+% long to fit on a page, will completely stop latex from compiling the
+% document. So unless we figure out a solution to this, we'll have to instead
+% leave the codecell env. as empty. I'm keeping the original codecell
+% definition here (a thin vertical bar) for reference, in case we find a
+% solution to the page break issue.
+
+%% \newenvironment{codecell}{%
+%% \def\FrameCommand{\color{mediumgray} \vrule width 1pt \hspace{5pt}}%
+%% \MakeFramed{\vspace{-0.5em}}}
+%% {\unskip\endMakeFramed}
+
+% For now, make this a no-op...
+\newenvironment{codecell}{}
+
+ \newenvironment{codeinput}{%
+ \def\FrameCommand{\colorbox{inputbackground}}%
+ \MakeFramed{\advance\hsize-\width \FrameRestore}}
+ {\unskip\endMakeFramed}
+
+\newenvironment{codeoutput}{%
+ \def\FrameCommand{\colorbox{outputbackground}}%
+ \vspace{-1.4em}
+ \MakeFramed{\advance\hsize-\width \FrameRestore}}
+ {\unskip\medskip\endMakeFramed}
+
+\newenvironment{traceback}{%
+ \def\FrameCommand{\colorbox{traceback}}%
+ \MakeFramed{\advance\hsize-\width \FrameRestore}}
+ {\endMakeFramed}
+
+% Use and configure listings package for nicely formatted code
+\usepackage{listingsutf8}
+\lstset{
+ language=python,
+ inputencoding=utf8x,
+ extendedchars=\true,
+ aboveskip=\smallskipamount,
+ belowskip=\smallskipamount,
+ xleftmargin=2mm,
+ breaklines=true,
+ basicstyle=\small \ttfamily,
+ showstringspaces=false,
+ keywordstyle=\color{blue}\bfseries,
+ commentstyle=\color{myteal},
+ stringstyle=\color{darkgreen},
+ identifierstyle=\color{darkorange},
+ columns=fullflexible, % tighter character kerning, like verb
+}
+
+% The hyperref package gives us a pdf with properly built
+% internal navigation ('pdf bookmarks' for the table of contents,
+% internal cross-reference links, web links for URLs, etc.)
+\usepackage{hyperref}
+\hypersetup{
+ breaklinks=true, % so long urls are correctly broken across lines
+ colorlinks=true,
+ urlcolor=blue,
+ linkcolor=darkorange,
+ citecolor=darkgreen,
+ }
+
+% hardcode size of all verbatim environments to be a bit smaller
+\makeatletter
+\g@addto@macro\@verbatim\small\topsep=0.5em\partopsep=0pt
+\makeatother
+
+% Prevent overflowing lines due to urls and other hard-to-break entities.
+\sloppy
+
+\begin{document}
+
+\section{Some gun violence analysis with Wikipedia data}
+As
+\href{https://twitter.com/jonst0kes/status/282330530412888064}{requested
+by John Stokes}, here are per-capita numbers for gun-related homicides,
+relating to GDP and total homicides, so the situation in the United
+States can be put in context relative to other nations.
+
+main data source is UNODC (via Wikipedia
+\href{http://en.wikipedia.org/wiki/List\_of\_countries\_by\_intentional\_homicide\_rate}{here}
+and
+\href{http://en.wikipedia.org/wiki/List\_of\_countries\_by\_firearm-related\_death\_rate}{here}).
+
+GDP data from World Bank, again
+\href{http://en.wikipedia.org/wiki/List\_of\_countries\_by\_GDP\_(PPP)\_per\_capita}{via
+Wikipedia}.
+
+If the numbers on Wikipedia are inaccurate, or their relationship is not
+sound (e.g.~numbers taken from different years, during which significant
+change occured) then obviously None of this analysis is valid.
+
+To summarize the data, every possible way you look at it the US is lousy
+at preventing gun violence. Even when compared to significantly more
+violent places, gun violence in the US is a serious problem, and when
+compared to similarly wealthy places, the US is an outstanding disaster.
+
+\textbf{UPDATE:} the relationship of the gun data and totals does not
+seem to be valid.
+\href{http://www2.fbi.gov/ucr/cius2009/offenses/violent\_crime/index.html}{FBI
+data} suggests that the relative contribution of guns to homicides in
+the US is 47\%, but relating these two data sources gives 80\%. Internal
+comparisons should still be fine, but `fraction' analysis has been
+stricken.
+
+\begin{codecell}
+\begin{codeinput}
+\begin{lstlisting}
+%load_ext retina
+%pylab inline
+\end{lstlisting}
+\end{codeinput}
+\begin{codeoutput}
+\begin{verbatim}
+Welcome to pylab, a matplotlib-based Python environment [backend: module://IPython.zmq.pylab.backend_inline].
+For more information, type 'help(pylab)'.
+\end{verbatim}
+\end{codeoutput}
+\end{codecell}
+\begin{codecell}
+\begin{codeinput}
+\begin{lstlisting}
+from IPython.display import display
+import pandas
+pandas.set_option('display.notebook_repr_html', True)
+pandas.set_option('display.precision', 2)
+\end{lstlisting}
+\end{codeinput}
+\end{codecell}
+Some utility functions for display
+
+\begin{codecell}
+\begin{codeinput}
+\begin{lstlisting}
+def plot_percent(df, limit=10):
+ df['Gun Percent'][:limit].plot()
+ plt.ylim(0,100)
+ plt.title("% Gun Homicide")
+ plt.show()
+
+\end{lstlisting}
+\end{codeinput}
+\end{codecell}
+\begin{codecell}
+\begin{codeinput}
+\begin{lstlisting}
+def plot_percapita(df, limit=10):
+ df = df.ix[:,['Homicides', 'Gun Homicides']][:limit]
+ df['Total Homicides'] = df['Homicides'] - df['Gun Homicides']
+ del df['Homicides']
+ df.plot(kind='bar', stacked=True, sort_columns=True)
+ plt.ylabel("per 100k")
+ plt.show()
+
+\end{lstlisting}
+\end{codeinput}
+\end{codecell}
+\begin{codecell}
+\begin{codeinput}
+\begin{lstlisting}
+def display_relevant(df, limit=10):
+ display(df.ix[:,['Homicides', 'Gun Homicides', 'Gun Data Source']][:limit])
+\end{lstlisting}
+\end{codeinput}
+\end{codecell}
+Load the data
+
+\begin{codecell}
+\begin{codeinput}
+\begin{lstlisting}
+totals = pandas.read_csv('totals.csv', '\t', index_col=0)
+guns = pandas.read_csv('guns.csv', '\t', index_col=0)
+gdp = pandas.read_csv('gdp.csv', '\t', index_col=1)
+data = totals.join(guns).join(gdp)
+data['Gun Percent'] = 100 * data['Gun Homicides'] / data['Homicides']
+del data['Unintentional'],data['Undetermined'],data['Gun Suicides']
+data = data.dropna()
+\end{lstlisting}
+\end{codeinput}
+\end{codecell}
+Of all sampled countries (Found data for 68 countries), the US is in the
+top 15 in Gun Homicides per capita.
+
+Numbers are per 100k.
+
+\begin{codecell}
+\begin{codeinput}
+\begin{lstlisting}
+data = data.sort("Gun Homicides", ascending=False)
+display_relevant(data, 15)
+\end{lstlisting}
+\end{codeinput}
+\begin{codeoutput}
+\begin{verbatim}
+Homicides Gun Homicides Gun Data Source
+Country
+El Salvador 69.2 50.4 OAS 2011[1]
+Jamaica 52.2 47.4 OAS 2011[1]
+Honduras 91.6 46.7 OAS 2011[1]
+Guatemala 38.5 38.5 OAS 2011[1]
+Colombia 33.4 27.1 UNODC 2011 [2]
+Brazil 21.0 18.1 UNODC 2011[3]
+Panama 21.6 12.9 OAS 2011[1]
+Mexico 16.9 10.0 UNODC 2011[4]
+Paraguay 11.5 7.3 UNODC 2000[11]
+Nicaragua 13.6 7.1 OAS 2011[1]
+United States 4.2 3.7 OAS 2012[5][6]
+Costa Rica 10.0 3.3 UNODC 2002[7]
+Uruguay 5.9 3.2 UNODC 2002[7]
+Argentina 3.4 3.0 UNODC 2011[12]
+Barbados 11.3 3.0 UNODC 2000[11]
+\end{verbatim}
+\end{codeoutput}
+\end{codecell}
+Take top 30 Countries by GDP
+
+\begin{codecell}
+\begin{codeinput}
+\begin{lstlisting}
+top = data.sort('GDP')[-30:]
+\end{lstlisting}
+\end{codeinput}
+\end{codecell}
+and rank them by Gun Homicides per capita:
+
+\begin{codecell}
+\begin{codeinput}
+\begin{lstlisting}
+top_by_guns = top.sort("Gun Homicides", ascending=False)
+display_relevant(top_by_guns, 5)
+plot_percapita(top_by_guns, 10)
+\end{lstlisting}
+\end{codeinput}
+\begin{codeoutput}
+\begin{verbatim}
+Homicides Gun Homicides Gun Data Source
+Country
+United States 4.2 3.7 OAS 2012[5][6]
+Israel 2.1 0.9 WHO 2012[10]
+Canada 1.6 0.8 Krug 1998[13]
+Luxembourg 2.5 0.6 WHO 2012[10]
+Greece 1.5 0.6 Krug 1998[13]
+\end{verbatim}
+\begin{center}
+\includegraphics[width=0.7\textwidth]{Gun_Data_orig_files/Gun_Data_orig_fig_00.png}
+\par
+\end{center}
+\end{codeoutput}
+\end{codecell}
+\textbf{NOTE:} these bar graphs should not be interpreted as fractions
+of a total, as the two data sources do not appear to be comparable. But
+the red and blue bar graphs should still be internally comparable.
+
+The US is easily \#1 of 30 wealthiest countries in Gun Homicides per
+capita, by a factor of 4:1
+
+Adding USA, Canada, and Mexico to all of Europe, USA is a strong \#2
+behind Mexico in total gun homicides per-capita
+
+\begin{codecell}
+\begin{codeinput}
+\begin{lstlisting}
+index = (data['Region'] == 'Europe') + \
+ (data.index == 'United States') + \
+ (data.index == 'Canada') + \
+ (data.index == 'Mexico')
+selected = data[index]
+
+print "By Total Gun Homicides"
+sys.stdout.flush()
+
+by_guns = selected.sort("Gun Homicides", ascending=False)
+#by_guns['Gun Homicides'].plot(kind='bar')
+plot_percapita(by_guns, limit=25)
+display_relevant(selected, limit=None)
+
+\end{lstlisting}
+\end{codeinput}
+\begin{codeoutput}
+\begin{verbatim}
+By Total Gun Homicides
+\end{verbatim}
+\begin{center}
+\includegraphics[width=0.7\textwidth]{Gun_Data_orig_files/Gun_Data_orig_fig_01.png}
+\par
+\end{center}
+\begin{verbatim}
+Homicides Gun Homicides Gun Data Source
+Country
+Mexico 16.9 10.0 UNODC 2011[4]
+United States 4.2 3.7 OAS 2012[5][6]
+Montenegro 3.5 2.1 WHO 2012[10]
+Moldova 7.5 1.0 WHO 2012[10]
+Canada 1.6 0.8 Krug 1998[13]
+Serbia 1.2 0.6 WHO 2012[10]
+Luxembourg 2.5 0.6 WHO 2012[10]
+Greece 1.5 0.6 Krug 1998[13]
+Croatia 1.4 0.6 WHO 2012[10]
+Switzerland 0.7 0.5 OAS 2011[1]
+Malta 1.0 0.5 WHO 2012[10]
+Portugal 1.2 0.5 WHO 2012[10]
+Belarus 4.9 0.4 UNODC 2002[7]
+Ireland 1.2 0.4 WHO 2012[10]
+Italy 0.9 0.4 WHO 2012[10]
+Ukraine 5.2 0.3 UNODC 2000[11]
+Estonia 5.2 0.3 WHO 2012[10]
+Belgium 1.7 0.3 WHO 2012[10]
+Finland 2.2 0.3 WHO 2012[10]
+Lithuania 6.6 0.2 WHO 2012[10]
+Bulgaria 2.0 0.2 WHO 2012[10]
+Georgia 4.3 0.2 WHO 2012[10]
+Denmark 0.9 0.2 WHO 2012[10]
+France 1.1 0.2 WHO 2012[10]
+Netherlands 1.1 0.2 WHO 2012[10]
+Sweden 1.0 0.2 WHO 2012[10]
+Slovakia 1.5 0.2 WHO 2012[10]
+Austria 0.6 0.2 WHO 2012[10]
+Latvia 3.1 0.2 WHO 2012[10]
+Spain 0.8 0.1 WHO 2012[10]
+Hungary 1.3 0.1 WHO 2012[10]
+Czech Republic 1.7 0.1 WHO 2012[10]
+Germany 0.8 0.1 WHO 2012[10]
+Slovenia 0.7 0.1 WHO 2012[10]
+Romania 2.0 0.0 WHO 2012[10]
+United Kingdom 1.2 0.0 WHO2012 [10]
+Norway 0.6 0.0 WHO 2012[10]
+Poland 1.1 0.0 WHO 2012[10]
+\end{verbatim}
+\end{codeoutput}
+\end{codecell}
+Let's just compare US, Canada, and UK:
+
+\begin{codecell}
+\begin{codeinput}
+\begin{lstlisting}
+select = data.ix[['United States', 'Canada', 'United Kingdom']]
+plot_percapita(select)
+\end{lstlisting}
+\end{codeinput}
+\begin{codeoutput}
+\begin{center}
+\includegraphics[width=0.7\textwidth]{Gun_Data_orig_files/Gun_Data_orig_fig_02.png}
+\par
+\end{center}
+\end{codeoutput}
+\end{codecell}
+Normalize to the US numbers (inverse)
+
+\begin{codecell}
+\begin{codeinput}
+\begin{lstlisting}
+select['Homicides'] = select['Homicides']['United States'] / select['Homicides']
+select['Gun Homicides'] = select['Gun Homicides']['United States'] / select['Gun Homicides']
+display_relevant(select)
+\end{lstlisting}
+\end{codeinput}
+\begin{codeoutput}
+\begin{verbatim}
+Homicides Gun Homicides Gun Data Source
+United States 1.0 1.0 OAS 2012[5][6]
+Canada 2.6 4.9 Krug 1998[13]
+United Kingdom 3.5 92.5 WHO2012 [10]
+\end{verbatim}
+\end{codeoutput}
+\end{codecell}
+So, you are 2.6 times more likely to be killed in the US than Canada,
+and 3.5 times more likely than in the UK. That's bad, but not extreme.
+
+However, you are 4.9 times more likely to be killed \emph{with a gun} in
+the US than Canada, and almost 100 times more likely than in the UK.
+That is pretty extreme.
+
+Countries represented:
+
+\begin{codecell}
+\begin{codeinput}
+\begin{lstlisting}
+for country in data.index:
+ print country
+\end{lstlisting}
+\end{codeinput}
+\begin{codeoutput}
+\begin{verbatim}
+El Salvador
+Jamaica
+Honduras
+Guatemala
+Colombia
+Brazil
+Panama
+Mexico
+Paraguay
+Nicaragua
+United States
+Costa Rica
+Uruguay
+Argentina
+Barbados
+Montenegro
+Peru
+Moldova
+Israel
+India
+Canada
+Serbia
+Luxembourg
+Greece
+Uzbekistan
+Croatia
+Kyrgyzstan
+Switzerland
+Malta
+Portugal
+Belarus
+Ireland
+Italy
+Kuwait
+Ukraine
+Estonia
+Belgium
+Finland
+Lithuania
+Cyprus
+Bulgaria
+Georgia
+Denmark
+France
+Netherlands
+Sweden
+Slovakia
+Qatar
+Austria
+Latvia
+New Zealand
+Spain
+Hungary
+Czech Republic
+Hong Kong
+Australia
+Singapore
+Chile
+Germany
+Slovenia
+Romania
+Azerbaijan
+South Korea
+United Kingdom
+Norway
+Japan
+Poland
+Mauritius
+\end{verbatim}
+\end{codeoutput}
+\end{codecell}
+\end{document}
diff --git a/tests/ipynbref/XKCD_plots.orig.md b/tests/ipynbref/XKCD_plots.orig.md
new file mode 100644
index 0000000..3adbffe
--- /dev/null
+++ b/tests/ipynbref/XKCD_plots.orig.md
@@ -0,0 +1,460 @@
+# XKCD plots in Matplotlib
+
+This notebook originally appeared as a blog post at [Pythonic Perambulations](http://jakevdp.github.com/blog/2012/10/07/xkcd-style-plots-in-matplotlib/) by Jake Vanderplas.
+
+One of the problems I've had with typical matplotlib figures is that everything in them is so precise, so perfect. For an example of what I mean, take a look at this figure:
+
+from IPython.display import Image
+Image('http://jakevdp.github.com/figures/xkcd_version.png')
+
+
+
+
+
+
+
+
+Sometimes when showing schematic plots, this is the type of figure I want to display. But drawing it by hand is a pain: I'd rather just use matplotlib. The problem is, matplotlib is a bit too precise. Attempting to duplicate this figure in matplotlib leads to something like this:
+
+Image('http://jakevdp.github.com/figures/mpl_version.png')
+
+
+
+
+
+
+
+
+It just doesn't have the same effect. Matplotlib is great for scientific plots, but sometimes you don't want to be so precise.
+
+This subject has recently come up on the matplotlib mailing list, and started some interesting discussions.
+As near as I can tell, this started with a thread on a
+[mathematica list](http://mathematica.stackexchange.com/questions/11350/xkcd-style-graphs)
+which prompted a thread on the [matplotlib list](http://matplotlib.1069221.n5.nabble.com/XKCD-style-graphs-td39226.html)
+wondering if the same could be done in matplotlib.
+
+Damon McDougall offered a quick
+[solution](http://www.mail-archive.com/matplotlib-users@lists.sourceforge.net/msg25499.html)
+which was improved by Fernando Perez in [this notebook](http://nbviewer.ipython.org/3835181/), and
+within a few days there was a [matplotlib pull request](https://github.com/matplotlib/matplotlib/pull/1329) offering a very general
+way to create sketch-style plots in matplotlib. Only a few days from a cool idea to a
+working implementation: this is one of the most incredible aspects of package development on github.
+
+The pull request looks really nice, but will likely not be included in a released version of
+matplotlib until at least version 1.3. In the mean-time, I wanted a way to play around with
+these types of plots in a way that is compatible with the current release of matplotlib. To do that,
+I created the following code:
+
+## The Code: XKCDify
+
+XKCDify will take a matplotlib ``Axes`` instance, and modify the plot elements in-place to make
+them look hand-drawn.
+First off, we'll need to make sure we have the Humor Sans font.
+It can be downloaded using the command below.
+
+Next we'll create a function ``xkcd_line`` to add jitter to lines. We want this to be very general, so
+we'll normalize the size of the lines, and use a low-pass filter to add correlated noise, perpendicular
+to the direction of the line. There are a few parameters for this filter that can be tweaked to
+customize the appearance of the jitter.
+
+Finally, we'll create a function which accepts a matplotlib axis, and calls ``xkcd_line`` on
+all lines in the axis. Additionally, we'll switch the font of all text in the axes, and add
+some background lines for a nice effect where lines cross. We'll also draw axes, and move the
+axes labels and titles to the appropriate location.
+
+"""
+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', 'wb').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
+
+
+def XKCDify(ax, mag=1.0,
+ f1=50, f2=0.01, f3=15,
+ bgcolor='w',
+ xaxis_loc=None,
+ yaxis_loc=None,
+ xaxis_arrow='+',
+ yaxis_arrow='+',
+ ax_extend=0.1,
+ expand_axes=False):
+ """Make axis look hand-drawn
+
+ This adjusts all lines, text, legends, and axes in the figure to look
+ like xkcd plots. Other plot elements are not modified.
+
+ Parameters
+ ----------
+ ax : Axes instance
+ the axes to be modified.
+ mag : float
+ the magnitude of the distortion
+ 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
+ xaxis_loc, yaxis_log : float
+ The locations to draw the x and y axes. If not specified, they
+ will be drawn from the bottom left of the plot
+ xaxis_arrow, yaxis_arrow : str
+ where to draw arrows on the x/y axes. Options are '+', '-', '+-', or ''
+ ax_extend : float
+ How far (fractionally) to extend the drawn axes beyond the original
+ axes limits
+ expand_axes : bool
+ if True, then expand axes to fill the figure (useful if there is only
+ a single axes in the figure)
+ """
+ # Get axes aspect
+ ext = ax.get_window_extent().extents
+ aspect = (ext[3] - ext[1]) / (ext[2] - ext[0])
+
+ xlim = ax.get_xlim()
+ ylim = ax.get_ylim()
+
+ xspan = xlim[1] - xlim[0]
+ yspan = ylim[1] - xlim[0]
+
+ xax_lim = (xlim[0] - ax_extend * xspan,
+ xlim[1] + ax_extend * xspan)
+ yax_lim = (ylim[0] - ax_extend * yspan,
+ ylim[1] + ax_extend * yspan)
+
+ if xaxis_loc is None:
+ xaxis_loc = ylim[0]
+
+ if yaxis_loc is None:
+ yaxis_loc = xlim[0]
+
+ # Draw axes
+ xaxis = pl.Line2D([xax_lim[0], xax_lim[1]], [xaxis_loc, xaxis_loc],
+ linestyle='-', color='k')
+ yaxis = pl.Line2D([yaxis_loc, yaxis_loc], [yax_lim[0], yax_lim[1]],
+ linestyle='-', color='k')
+
+ # Label axes3, 0.5, 'hello', fontsize=14)
+ ax.text(xax_lim[1], xaxis_loc - 0.02 * yspan, ax.get_xlabel(),
+ fontsize=14, ha='right', va='top', rotation=12)
+ ax.text(yaxis_loc - 0.02 * xspan, yax_lim[1], ax.get_ylabel(),
+ fontsize=14, ha='right', va='top', rotation=78)
+ ax.set_xlabel('')
+ ax.set_ylabel('')
+
+ # Add title
+ ax.text(0.5 * (xax_lim[1] + xax_lim[0]), yax_lim[1],
+ ax.get_title(),
+ ha='center', va='bottom', fontsize=16)
+ ax.set_title('')
+
+ Nlines = len(ax.lines)
+ lines = [xaxis, yaxis] + [ax.lines.pop(0) for i in range(Nlines)]
+
+ for line in lines:
+ x, y = line.get_data()
+
+ x_int, y_int = xkcd_line(x, y, xlim, ylim,
+ mag, f1, f2, f3)
+
+ # create foreground and background line
+ lw = line.get_linewidth()
+ line.set_linewidth(2 * lw)
+ line.set_data(x_int, y_int)
+
+ # don't add background line for axes
+ if (line is not xaxis) and (line is not yaxis):
+ line_bg = pl.Line2D(x_int, y_int, color=bgcolor,
+ linewidth=8 * lw)
+
+ ax.add_line(line_bg)
+ ax.add_line(line)
+
+ # Draw arrow-heads at the end of axes lines
+ arr1 = 0.03 * np.array([-1, 0, -1])
+ arr2 = 0.02 * np.array([-1, 0, 1])
+
+ arr1[::2] += np.random.normal(0, 0.005, 2)
+ arr2[::2] += np.random.normal(0, 0.005, 2)
+
+ x, y = xaxis.get_data()
+ if '+' in str(xaxis_arrow):
+ ax.plot(x[-1] + arr1 * xspan * aspect,
+ y[-1] + arr2 * yspan,
+ color='k', lw=2)
+ if '-' in str(xaxis_arrow):
+ ax.plot(x[0] - arr1 * xspan * aspect,
+ y[0] - arr2 * yspan,
+ color='k', lw=2)
+
+ x, y = yaxis.get_data()
+ if '+' in str(yaxis_arrow):
+ ax.plot(x[-1] + arr2 * xspan * aspect,
+ y[-1] + arr1 * yspan,
+ color='k', lw=2)
+ if '-' in str(yaxis_arrow):
+ ax.plot(x[0] - arr2 * xspan * aspect,
+ y[0] - arr1 * yspan,
+ color='k', lw=2)
+
+ # Change all the fonts to humor-sans.
+ prop = fm.FontProperties(fname='Humor-Sans.ttf', size=16)
+ for text in ax.texts:
+ text.set_fontproperties(prop)
+
+ # modify legend
+ leg = ax.get_legend()
+ if leg is not None:
+ leg.set_frame_on(False)
+
+ for child in leg.get_children():
+ if isinstance(child, pl.Line2D):
+ x, y = child.get_data()
+ child.set_data(xkcd_line(x, y, mag=10, f1=100, f2=0.001))
+ child.set_linewidth(2 * child.get_linewidth())
+ if isinstance(child, pl.Text):
+ child.set_fontproperties(prop)
+
+ # Set the axis limits
+ ax.set_xlim(xax_lim[0] - 0.1 * xspan,
+ xax_lim[1] + 0.1 * xspan)
+ ax.set_ylim(yax_lim[0] - 0.1 * yspan,
+ yax_lim[1] + 0.1 * yspan)
+
+ # adjust the axes
+ ax.set_xticks([])
+ ax.set_yticks([])
+
+ if expand_axes:
+ ax.figure.set_facecolor(bgcolor)
+ ax.set_axis_off()
+ ax.set_position([0, 0, 1, 1])
+
+ return ax
+
+
+
+
+## Testing it Out
+
+Let's test this out with a simple plot. We'll plot two curves, add some labels,
+and then call ``XKCDify`` on the axis. I think the results are pretty nice!
+
+
+
+
+
+ Welcome to pylab, a matplotlib-based Python environment [backend: module://IPython.zmq.pylab.backend_inline].
+ For more information, type 'help(pylab)'.
+
+
+np.random.seed(0)
+
+ax = pylab.axes()
+
+x = np.linspace(0, 10, 100)
+ax.plot(x, np.sin(x) * np.exp(-0.1 * (x - 5) ** 2), 'b', lw=1, label='damped sine')
+ax.plot(x, -np.cos(x) * np.exp(-0.1 * (x - 5) ** 2), 'r', lw=1, label='damped cosine')
+
+ax.set_title('check it out!')
+ax.set_xlabel('x label')
+ax.set_ylabel('y label')
+
+ax.legend(loc='lower right')
+
+ax.set_xlim(0, 10)
+ax.set_ylim(-1.0, 1.0)
+
+#XKCDify the axes -- this operates in-place
+XKCDify(ax, xaxis_loc=0.0, yaxis_loc=1.0,
+ xaxis_arrow='+-', yaxis_arrow='+-',
+ expand_axes=True)
+
+
+
+
+
+
+
+
+![](tests/ipynbref/XKCD_plots_orig_files/XKCD_plots_orig_fig_00.png)
+
+
+## Duplicating an XKCD Comic
+
+Now let's see if we can use this to replicated an XKCD comic in matplotlib.
+This is a good one:
+
+Image('http://imgs.xkcd.com/comics/front_door.png')
+
+
+
+
+
+
+
+
+With the new ``XKCDify`` function, this is relatively easy to replicate. The results
+are not exactly identical, but I think it definitely gets the point across!
+
+# Some helper functions
+def norm(x, x0, sigma):
+ return np.exp(-0.5 * (x - x0) ** 2 / sigma ** 2)
+
+def sigmoid(x, x0, alpha):
+ return 1. / (1. + np.exp(- (x - x0) / alpha))
+
+# define the curves
+x = np.linspace(0, 1, 100)
+y1 = np.sqrt(norm(x, 0.7, 0.05)) + 0.2 * (1.5 - sigmoid(x, 0.8, 0.05))
+
+y2 = 0.2 * norm(x, 0.5, 0.2) + np.sqrt(norm(x, 0.6, 0.05)) + 0.1 * (1 - sigmoid(x, 0.75, 0.05))
+
+y3 = 0.05 + 1.4 * norm(x, 0.85, 0.08)
+y3[x > 0.85] = 0.05 + 1.4 * norm(x[x > 0.85], 0.85, 0.3)
+
+# draw the curves
+ax = pl.axes()
+ax.plot(x, y1, c='gray')
+ax.plot(x, y2, c='blue')
+ax.plot(x, y3, c='red')
+
+ax.text(0.3, -0.1, "Yard")
+ax.text(0.5, -0.1, "Steps")
+ax.text(0.7, -0.1, "Door")
+ax.text(0.9, -0.1, "Inside")
+
+ax.text(0.05, 1.1, "fear that\nthere's\nsomething\nbehind me")
+ax.plot([0.15, 0.2], [1.0, 0.2], '-k', lw=0.5)
+
+ax.text(0.25, 0.8, "forward\nspeed")
+ax.plot([0.32, 0.35], [0.75, 0.35], '-k', lw=0.5)
+
+ax.text(0.9, 0.4, "embarrassment")
+ax.plot([1.0, 0.8], [0.55, 1.05], '-k', lw=0.5)
+
+ax.set_title("Walking back to my\nfront door at night:")
+
+ax.set_xlim(0, 1)
+ax.set_ylim(0, 1.5)
+
+# modify all the axes elements in-place
+XKCDify(ax, expand_axes=True)
+
+
+
+
+
+
+
+
+![](tests/ipynbref/XKCD_plots_orig_files/XKCD_plots_orig_fig_01.png)
+
+
+Pretty good for a couple hours's work!
+
+I think the possibilities here are pretty limitless: this is going to be a hugely
+useful and popular feature in matplotlib, especially when the sketch artist PR is mature
+and part of the main package. I imagine using this style of plot for schematic figures
+in presentations where the normal crisp matplotlib lines look a bit too "scientific".
+I'm giving a few talks at the end of the month... maybe I'll even use some of
+this code there.
+
+This post was written entirely in an IPython Notebook: the notebook file is available for
+download [here](http://jakevdp.github.com/downloads/notebooks/XKCD_plots.ipynb).
+For more information on blogging with notebooks in octopress, see my
+[previous post](http://jakevdp.github.com/blog/2012/10/04/blogging-with-ipython/)
+on the subject.
diff --git a/tests/ipynbref/XKCD_plots.orig.tex b/tests/ipynbref/XKCD_plots.orig.tex
new file mode 100644
index 0000000..e39b82f
--- /dev/null
+++ b/tests/ipynbref/XKCD_plots.orig.tex
@@ -0,0 +1,626 @@
+%% This file was auto-generated by IPython.
+%% Conversion from the original notebook file:
+%% tests/ipynbref/XKCD_plots.orig.ipynb
+%%
+\documentclass[11pt,english]{article}
+
+%% This is the automatic preamble used by IPython. Note that it does *not*
+%% include a documentclass declaration, that is added at runtime to the overall
+%% document.
+
+\usepackage{amsmath}
+\usepackage{amssymb}
+\usepackage{graphicx}
+\usepackage{ucs}
+\usepackage[utf8x]{inputenc}
+
+% needed for markdown enumerations to work
+\usepackage{enumerate}
+
+% Slightly bigger margins than the latex defaults
+\usepackage{geometry}
+\geometry{verbose,tmargin=3cm,bmargin=3cm,lmargin=2.5cm,rmargin=2.5cm}
+
+% Define a few colors for use in code, links and cell shading
+\usepackage{color}
+\definecolor{orange}{cmyk}{0,0.4,0.8,0.2}
+\definecolor{darkorange}{rgb}{.71,0.21,0.01}
+\definecolor{darkgreen}{rgb}{.12,.54,.11}
+\definecolor{myteal}{rgb}{.26, .44, .56}
+\definecolor{gray}{gray}{0.45}
+\definecolor{lightgray}{gray}{.95}
+\definecolor{mediumgray}{gray}{.8}
+\definecolor{inputbackground}{rgb}{.95, .95, .85}
+\definecolor{outputbackground}{rgb}{.95, .95, .95}
+\definecolor{traceback}{rgb}{1, .95, .95}
+
+% Framed environments for code cells (inputs, outputs, errors, ...). The
+% various uses of \unskip (or not) at the end were fine-tuned by hand, so don't
+% randomly change them unless you're sure of the effect it will have.
+\usepackage{framed}
+
+% remove extraneous vertical space in boxes
+\setlength\fboxsep{0pt}
+
+% codecell is the whole input+output set of blocks that a Code cell can
+% generate.
+
+% TODO: unfortunately, it seems that using a framed codecell environment breaks
+% the ability of the frames inside of it to be broken across pages. This
+% causes at least the problem of having lots of empty space at the bottom of
+% pages as new frames are moved to the next page, and if a single frame is too
+% long to fit on a page, will completely stop latex from compiling the
+% document. So unless we figure out a solution to this, we'll have to instead
+% leave the codecell env. as empty. I'm keeping the original codecell
+% definition here (a thin vertical bar) for reference, in case we find a
+% solution to the page break issue.
+
+%% \newenvironment{codecell}{%
+%% \def\FrameCommand{\color{mediumgray} \vrule width 1pt \hspace{5pt}}%
+%% \MakeFramed{\vspace{-0.5em}}}
+%% {\unskip\endMakeFramed}
+
+% For now, make this a no-op...
+\newenvironment{codecell}{}
+
+ \newenvironment{codeinput}{%
+ \def\FrameCommand{\colorbox{inputbackground}}%
+ \MakeFramed{\advance\hsize-\width \FrameRestore}}
+ {\unskip\endMakeFramed}
+
+\newenvironment{codeoutput}{%
+ \def\FrameCommand{\colorbox{outputbackground}}%
+ \vspace{-1.4em}
+ \MakeFramed{\advance\hsize-\width \FrameRestore}}
+ {\unskip\medskip\endMakeFramed}
+
+\newenvironment{traceback}{%
+ \def\FrameCommand{\colorbox{traceback}}%
+ \MakeFramed{\advance\hsize-\width \FrameRestore}}
+ {\endMakeFramed}
+
+% Use and configure listings package for nicely formatted code
+\usepackage{listingsutf8}
+\lstset{
+ language=python,
+ inputencoding=utf8x,
+ extendedchars=\true,
+ aboveskip=\smallskipamount,
+ belowskip=\smallskipamount,
+ xleftmargin=2mm,
+ breaklines=true,
+ basicstyle=\small \ttfamily,
+ showstringspaces=false,
+ keywordstyle=\color{blue}\bfseries,
+ commentstyle=\color{myteal},
+ stringstyle=\color{darkgreen},
+ identifierstyle=\color{darkorange},
+ columns=fullflexible, % tighter character kerning, like verb
+}
+
+% The hyperref package gives us a pdf with properly built
+% internal navigation ('pdf bookmarks' for the table of contents,
+% internal cross-reference links, web links for URLs, etc.)
+\usepackage{hyperref}
+\hypersetup{
+ breaklinks=true, % so long urls are correctly broken across lines
+ colorlinks=true,
+ urlcolor=blue,
+ linkcolor=darkorange,
+ citecolor=darkgreen,
+ }
+
+% hardcode size of all verbatim environments to be a bit smaller
+\makeatletter
+\g@addto@macro\@verbatim\small\topsep=0.5em\partopsep=0pt
+\makeatother
+
+% Prevent overflowing lines due to urls and other hard-to-break entities.
+\sloppy
+
+\begin{document}
+
+\section{XKCD plots in Matplotlib}
+This notebook originally appeared as a blog post at
+\href{http://jakevdp.github.com/blog/2012/10/07/xkcd-style-plots-in-matplotlib/}{Pythonic
+Perambulations} by Jake Vanderplas.
+
+One of the problems I've had with typical matplotlib figures is that
+everything in them is so precise, so perfect. For an example of what I
+mean, take a look at this figure:
+
+\begin{codecell}
+\begin{codeinput}
+\begin{lstlisting}
+from IPython.display import Image
+Image('http://jakevdp.github.com/figures/xkcd_version.png')
+\end{lstlisting}
+\end{codeinput}
+\begin{codeoutput}
+\begin{verbatim}
+
+\end{verbatim}
+\end{codeoutput}
+\end{codecell}
+Sometimes when showing schematic plots, this is the type of figure I
+want to display. But drawing it by hand is a pain: I'd rather just use
+matplotlib. The problem is, matplotlib is a bit too precise. Attempting
+to duplicate this figure in matplotlib leads to something like this:
+
+\begin{codecell}
+\begin{codeinput}
+\begin{lstlisting}
+Image('http://jakevdp.github.com/figures/mpl_version.png')
+\end{lstlisting}
+\end{codeinput}
+\begin{codeoutput}
+\begin{verbatim}
+
+\end{verbatim}
+\end{codeoutput}
+\end{codecell}
+It just doesn't have the same effect. Matplotlib is great for scientific
+plots, but sometimes you don't want to be so precise.
+
+This subject has recently come up on the matplotlib mailing list, and
+started some interesting discussions. As near as I can tell, this
+started with a thread on a
+\href{http://mathematica.stackexchange.com/questions/11350/xkcd-style-graphs}{mathematica
+list} which prompted a thread on the
+\href{http://matplotlib.1069221.n5.nabble.com/XKCD-style-graphs-td39226.html}{matplotlib
+list} wondering if the same could be done in matplotlib.
+
+Damon McDougall offered a quick
+\href{http://www.mail-archive.com/matplotlib-users@lists.sourceforge.net/msg25499.html}{solution}
+which was improved by Fernando Perez in
+\href{http://nbviewer.ipython.org/3835181/}{this notebook}, and within a
+few days there was a
+\href{https://github.com/matplotlib/matplotlib/pull/1329}{matplotlib
+pull request} offering a very general way to create sketch-style plots
+in matplotlib. Only a few days from a cool idea to a working
+implementation: this is one of the most incredible aspects of package
+development on github.
+
+The pull request looks really nice, but will likely not be included in a
+released version of matplotlib until at least version 1.3. In the
+mean-time, I wanted a way to play around with these types of plots in a
+way that is compatible with the current release of matplotlib. To do
+that, I created the following code:
+
+\subsection{The Code: XKCDify}
+XKCDify will take a matplotlib \texttt{Axes} instance, and modify the
+plot elements in-place to make them look hand-drawn. First off, we'll
+need to make sure we have the Humor Sans font. It can be downloaded
+using the command below.
+
+Next we'll create a function \texttt{xkcd\_line} to add jitter to lines.
+We want this to be very general, so we'll normalize the size of the
+lines, and use a low-pass filter to add correlated noise, perpendicular
+to the direction of the line. There are a few parameters for this filter
+that can be tweaked to customize the appearance of the jitter.
+
+Finally, we'll create a function which accepts a matplotlib axis, and
+calls \texttt{xkcd\_line} on all lines in the axis. Additionally, we'll
+switch the font of all text in the axes, and add some background lines
+for a nice effect where lines cross. We'll also draw axes, and move the
+axes labels and titles to the appropriate location.
+
+\begin{codecell}
+\begin{codeinput}
+\begin{lstlisting}
+"""
+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', 'wb').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
+
+
+def XKCDify(ax, mag=1.0,
+ f1=50, f2=0.01, f3=15,
+ bgcolor='w',
+ xaxis_loc=None,
+ yaxis_loc=None,
+ xaxis_arrow='+',
+ yaxis_arrow='+',
+ ax_extend=0.1,
+ expand_axes=False):
+ """Make axis look hand-drawn
+
+ This adjusts all lines, text, legends, and axes in the figure to look
+ like xkcd plots. Other plot elements are not modified.
+
+ Parameters
+ ----------
+ ax : Axes instance
+ the axes to be modified.
+ mag : float
+ the magnitude of the distortion
+ 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
+ xaxis_loc, yaxis_log : float
+ The locations to draw the x and y axes. If not specified, they
+ will be drawn from the bottom left of the plot
+ xaxis_arrow, yaxis_arrow : str
+ where to draw arrows on the x/y axes. Options are '+', '-', '+-', or ''
+ ax_extend : float
+ How far (fractionally) to extend the drawn axes beyond the original
+ axes limits
+ expand_axes : bool
+ if True, then expand axes to fill the figure (useful if there is only
+ a single axes in the figure)
+ """
+ # Get axes aspect
+ ext = ax.get_window_extent().extents
+ aspect = (ext[3] - ext[1]) / (ext[2] - ext[0])
+
+ xlim = ax.get_xlim()
+ ylim = ax.get_ylim()
+
+ xspan = xlim[1] - xlim[0]
+ yspan = ylim[1] - xlim[0]
+
+ xax_lim = (xlim[0] - ax_extend * xspan,
+ xlim[1] + ax_extend * xspan)
+ yax_lim = (ylim[0] - ax_extend * yspan,
+ ylim[1] + ax_extend * yspan)
+
+ if xaxis_loc is None:
+ xaxis_loc = ylim[0]
+
+ if yaxis_loc is None:
+ yaxis_loc = xlim[0]
+
+ # Draw axes
+ xaxis = pl.Line2D([xax_lim[0], xax_lim[1]], [xaxis_loc, xaxis_loc],
+ linestyle='-', color='k')
+ yaxis = pl.Line2D([yaxis_loc, yaxis_loc], [yax_lim[0], yax_lim[1]],
+ linestyle='-', color='k')
+
+ # Label axes3, 0.5, 'hello', fontsize=14)
+ ax.text(xax_lim[1], xaxis_loc - 0.02 * yspan, ax.get_xlabel(),
+ fontsize=14, ha='right', va='top', rotation=12)
+ ax.text(yaxis_loc - 0.02 * xspan, yax_lim[1], ax.get_ylabel(),
+ fontsize=14, ha='right', va='top', rotation=78)
+ ax.set_xlabel('')
+ ax.set_ylabel('')
+
+ # Add title
+ ax.text(0.5 * (xax_lim[1] + xax_lim[0]), yax_lim[1],
+ ax.get_title(),
+ ha='center', va='bottom', fontsize=16)
+ ax.set_title('')
+
+ Nlines = len(ax.lines)
+ lines = [xaxis, yaxis] + [ax.lines.pop(0) for i in range(Nlines)]
+
+ for line in lines:
+ x, y = line.get_data()
+
+ x_int, y_int = xkcd_line(x, y, xlim, ylim,
+ mag, f1, f2, f3)
+
+ # create foreground and background line
+ lw = line.get_linewidth()
+ line.set_linewidth(2 * lw)
+ line.set_data(x_int, y_int)
+
+ # don't add background line for axes
+ if (line is not xaxis) and (line is not yaxis):
+ line_bg = pl.Line2D(x_int, y_int, color=bgcolor,
+ linewidth=8 * lw)
+
+ ax.add_line(line_bg)
+ ax.add_line(line)
+
+ # Draw arrow-heads at the end of axes lines
+ arr1 = 0.03 * np.array([-1, 0, -1])
+ arr2 = 0.02 * np.array([-1, 0, 1])
+
+ arr1[::2] += np.random.normal(0, 0.005, 2)
+ arr2[::2] += np.random.normal(0, 0.005, 2)
+
+ x, y = xaxis.get_data()
+ if '+' in str(xaxis_arrow):
+ ax.plot(x[-1] + arr1 * xspan * aspect,
+ y[-1] + arr2 * yspan,
+ color='k', lw=2)
+ if '-' in str(xaxis_arrow):
+ ax.plot(x[0] - arr1 * xspan * aspect,
+ y[0] - arr2 * yspan,
+ color='k', lw=2)
+
+ x, y = yaxis.get_data()
+ if '+' in str(yaxis_arrow):
+ ax.plot(x[-1] + arr2 * xspan * aspect,
+ y[-1] + arr1 * yspan,
+ color='k', lw=2)
+ if '-' in str(yaxis_arrow):
+ ax.plot(x[0] - arr2 * xspan * aspect,
+ y[0] - arr1 * yspan,
+ color='k', lw=2)
+
+ # Change all the fonts to humor-sans.
+ prop = fm.FontProperties(fname='Humor-Sans.ttf', size=16)
+ for text in ax.texts:
+ text.set_fontproperties(prop)
+
+ # modify legend
+ leg = ax.get_legend()
+ if leg is not None:
+ leg.set_frame_on(False)
+
+ for child in leg.get_children():
+ if isinstance(child, pl.Line2D):
+ x, y = child.get_data()
+ child.set_data(xkcd_line(x, y, mag=10, f1=100, f2=0.001))
+ child.set_linewidth(2 * child.get_linewidth())
+ if isinstance(child, pl.Text):
+ child.set_fontproperties(prop)
+
+ # Set the axis limits
+ ax.set_xlim(xax_lim[0] - 0.1 * xspan,
+ xax_lim[1] + 0.1 * xspan)
+ ax.set_ylim(yax_lim[0] - 0.1 * yspan,
+ yax_lim[1] + 0.1 * yspan)
+
+ # adjust the axes
+ ax.set_xticks([])
+ ax.set_yticks([])
+
+ if expand_axes:
+ ax.figure.set_facecolor(bgcolor)
+ ax.set_axis_off()
+ ax.set_position([0, 0, 1, 1])
+
+ return ax
+\end{lstlisting}
+\end{codeinput}
+\end{codecell}
+\subsection{Testing it Out}
+Let's test this out with a simple plot. We'll plot two curves, add some
+labels, and then call \texttt{XKCDify} on the axis. I think the results
+are pretty nice!
+
+\begin{codecell}
+\begin{codeinput}
+\begin{lstlisting}
+%pylab inline
+\end{lstlisting}
+\end{codeinput}
+\begin{codeoutput}
+\begin{verbatim}
+Welcome to pylab, a matplotlib-based Python environment [backend: module://IPython.zmq.pylab.backend_inline].
+For more information, type 'help(pylab)'.
+\end{verbatim}
+\end{codeoutput}
+\end{codecell}
+\begin{codecell}
+\begin{codeinput}
+\begin{lstlisting}
+np.random.seed(0)
+
+ax = pylab.axes()
+
+x = np.linspace(0, 10, 100)
+ax.plot(x, np.sin(x) * np.exp(-0.1 * (x - 5) ** 2), 'b', lw=1, label='damped sine')
+ax.plot(x, -np.cos(x) * np.exp(-0.1 * (x - 5) ** 2), 'r', lw=1, label='damped cosine')
+
+ax.set_title('check it out!')
+ax.set_xlabel('x label')
+ax.set_ylabel('y label')
+
+ax.legend(loc='lower right')
+
+ax.set_xlim(0, 10)
+ax.set_ylim(-1.0, 1.0)
+
+#XKCDify the axes -- this operates in-place
+XKCDify(ax, xaxis_loc=0.0, yaxis_loc=1.0,
+ xaxis_arrow='+-', yaxis_arrow='+-',
+ expand_axes=True)
+
+\end{lstlisting}
+\end{codeinput}
+\begin{codeoutput}
+\begin{verbatim}
+
+\end{verbatim}
+\begin{center}
+\includegraphics[width=0.7\textwidth]{XKCD_plots_orig_files/XKCD_plots_orig_fig_00.png}
+\par
+\end{center}
+\end{codeoutput}
+\end{codecell}
+\subsection{Duplicating an XKCD Comic}
+Now let's see if we can use this to replicated an XKCD comic in
+matplotlib. This is a good one:
+
+\begin{codecell}
+\begin{codeinput}
+\begin{lstlisting}
+Image('http://imgs.xkcd.com/comics/front_door.png')
+\end{lstlisting}
+\end{codeinput}
+\begin{codeoutput}
+\begin{verbatim}
+
+\end{verbatim}
+\end{codeoutput}
+\end{codecell}
+With the new \texttt{XKCDify} function, this is relatively easy to
+replicate. The results are not exactly identical, but I think it
+definitely gets the point across!
+
+\begin{codecell}
+\begin{codeinput}
+\begin{lstlisting}
+# Some helper functions
+def norm(x, x0, sigma):
+ return np.exp(-0.5 * (x - x0) ** 2 / sigma ** 2)
+
+def sigmoid(x, x0, alpha):
+ return 1. / (1. + np.exp(- (x - x0) / alpha))
+
+# define the curves
+x = np.linspace(0, 1, 100)
+y1 = np.sqrt(norm(x, 0.7, 0.05)) + 0.2 * (1.5 - sigmoid(x, 0.8, 0.05))
+
+y2 = 0.2 * norm(x, 0.5, 0.2) + np.sqrt(norm(x, 0.6, 0.05)) + 0.1 * (1 - sigmoid(x, 0.75, 0.05))
+
+y3 = 0.05 + 1.4 * norm(x, 0.85, 0.08)
+y3[x > 0.85] = 0.05 + 1.4 * norm(x[x > 0.85], 0.85, 0.3)
+
+# draw the curves
+ax = pl.axes()
+ax.plot(x, y1, c='gray')
+ax.plot(x, y2, c='blue')
+ax.plot(x, y3, c='red')
+
+ax.text(0.3, -0.1, "Yard")
+ax.text(0.5, -0.1, "Steps")
+ax.text(0.7, -0.1, "Door")
+ax.text(0.9, -0.1, "Inside")
+
+ax.text(0.05, 1.1, "fear that\nthere's\nsomething\nbehind me")
+ax.plot([0.15, 0.2], [1.0, 0.2], '-k', lw=0.5)
+
+ax.text(0.25, 0.8, "forward\nspeed")
+ax.plot([0.32, 0.35], [0.75, 0.35], '-k', lw=0.5)
+
+ax.text(0.9, 0.4, "embarrassment")
+ax.plot([1.0, 0.8], [0.55, 1.05], '-k', lw=0.5)
+
+ax.set_title("Walking back to my\nfront door at night:")
+
+ax.set_xlim(0, 1)
+ax.set_ylim(0, 1.5)
+
+# modify all the axes elements in-place
+XKCDify(ax, expand_axes=True)
+
+\end{lstlisting}
+\end{codeinput}
+\begin{codeoutput}
+\begin{verbatim}
+
+\end{verbatim}
+\begin{center}
+\includegraphics[width=0.7\textwidth]{XKCD_plots_orig_files/XKCD_plots_orig_fig_01.png}
+\par
+\end{center}
+\end{codeoutput}
+\end{codecell}
+Pretty good for a couple hours's work!
+
+I think the possibilities here are pretty limitless: this is going to be
+a hugely useful and popular feature in matplotlib, especially when the
+sketch artist PR is mature and part of the main package. I imagine using
+this style of plot for schematic figures in presentations where the
+normal crisp matplotlib lines look a bit too ``scientific''. I'm giving
+a few talks at the end of the month\ldots{} maybe I'll even use some of
+this code there.
+
+This post was written entirely in an IPython Notebook: the notebook file
+is available for download
+\href{http://jakevdp.github.com/downloads/notebooks/XKCD\_plots.ipynb}{here}.
+For more information on blogging with notebooks in octopress, see my
+\href{http://jakevdp.github.com/blog/2012/10/04/blogging-with-ipython/}{previous
+post} on the subject.
+
+\end{document}
diff --git a/tests/ipynbref/data_geeks_team_calendar.orig.md b/tests/ipynbref/data_geeks_team_calendar.orig.md
new file mode 100644
index 0000000..d0cbdd1
--- /dev/null
+++ b/tests/ipynbref/data_geeks_team_calendar.orig.md
@@ -0,0 +1,69 @@
+#! /usr/bin/env python
+'''
+github_team_calendar.py
+Python program to scrape friends github to build team calendar for github
+'''
+
+
+import json
+import requests
+import pandas as pd
+
+
+def line_draw_target(target):
+ github_url = 'https://github.com/users/%s/contributions_calendar_data'
+ r = requests.get(github_url % target)
+ data = json.loads(r.text)
+ dates, contributions = zip(*data)
+ ts = pd.Series(contributions, index=dates)
+ plt.plot(ts)
+
+
+
+
+target = "mikedewar"
+line_draw_target(target)
+
+
+
+
+
+![](tests/ipynbref/data_geeks_team_calendar_orig_files/data_geeks_team_calendar_orig_fig_00.png)
+
+
+target = "drewconway"
+line_draw_target(target)
+
+
+
+
+![](tests/ipynbref/data_geeks_team_calendar_orig_files/data_geeks_team_calendar_orig_fig_01.png)
+
+
+target = "hmason"
+line_draw_target(target)
+
+
+
+
+![](tests/ipynbref/data_geeks_team_calendar_orig_files/data_geeks_team_calendar_orig_fig_02.png)
+
+
+target = "mbostock"
+line_draw_target(target)
+
+
+
+
+![](tests/ipynbref/data_geeks_team_calendar_orig_files/data_geeks_team_calendar_orig_fig_03.png)
+
+
+target = "amueller"
+line_draw_target(target)
+
+
+
+
+![](tests/ipynbref/data_geeks_team_calendar_orig_files/data_geeks_team_calendar_orig_fig_04.png)
+
+
diff --git a/tests/ipynbref/data_geeks_team_calendar.orig.tex b/tests/ipynbref/data_geeks_team_calendar.orig.tex
new file mode 100644
index 0000000..601db14
--- /dev/null
+++ b/tests/ipynbref/data_geeks_team_calendar.orig.tex
@@ -0,0 +1,220 @@
+%% This file was auto-generated by IPython.
+%% Conversion from the original notebook file:
+%% tests/ipynbref/data_geeks_team_calendar.orig.ipynb
+%%
+\documentclass[11pt,english]{article}
+
+%% This is the automatic preamble used by IPython. Note that it does *not*
+%% include a documentclass declaration, that is added at runtime to the overall
+%% document.
+
+\usepackage{amsmath}
+\usepackage{amssymb}
+\usepackage{graphicx}
+\usepackage{ucs}
+\usepackage[utf8x]{inputenc}
+
+% needed for markdown enumerations to work
+\usepackage{enumerate}
+
+% Slightly bigger margins than the latex defaults
+\usepackage{geometry}
+\geometry{verbose,tmargin=3cm,bmargin=3cm,lmargin=2.5cm,rmargin=2.5cm}
+
+% Define a few colors for use in code, links and cell shading
+\usepackage{color}
+\definecolor{orange}{cmyk}{0,0.4,0.8,0.2}
+\definecolor{darkorange}{rgb}{.71,0.21,0.01}
+\definecolor{darkgreen}{rgb}{.12,.54,.11}
+\definecolor{myteal}{rgb}{.26, .44, .56}
+\definecolor{gray}{gray}{0.45}
+\definecolor{lightgray}{gray}{.95}
+\definecolor{mediumgray}{gray}{.8}
+\definecolor{inputbackground}{rgb}{.95, .95, .85}
+\definecolor{outputbackground}{rgb}{.95, .95, .95}
+\definecolor{traceback}{rgb}{1, .95, .95}
+
+% Framed environments for code cells (inputs, outputs, errors, ...). The
+% various uses of \unskip (or not) at the end were fine-tuned by hand, so don't
+% randomly change them unless you're sure of the effect it will have.
+\usepackage{framed}
+
+% remove extraneous vertical space in boxes
+\setlength\fboxsep{0pt}
+
+% codecell is the whole input+output set of blocks that a Code cell can
+% generate.
+
+% TODO: unfortunately, it seems that using a framed codecell environment breaks
+% the ability of the frames inside of it to be broken across pages. This
+% causes at least the problem of having lots of empty space at the bottom of
+% pages as new frames are moved to the next page, and if a single frame is too
+% long to fit on a page, will completely stop latex from compiling the
+% document. So unless we figure out a solution to this, we'll have to instead
+% leave the codecell env. as empty. I'm keeping the original codecell
+% definition here (a thin vertical bar) for reference, in case we find a
+% solution to the page break issue.
+
+%% \newenvironment{codecell}{%
+%% \def\FrameCommand{\color{mediumgray} \vrule width 1pt \hspace{5pt}}%
+%% \MakeFramed{\vspace{-0.5em}}}
+%% {\unskip\endMakeFramed}
+
+% For now, make this a no-op...
+\newenvironment{codecell}{}
+
+ \newenvironment{codeinput}{%
+ \def\FrameCommand{\colorbox{inputbackground}}%
+ \MakeFramed{\advance\hsize-\width \FrameRestore}}
+ {\unskip\endMakeFramed}
+
+\newenvironment{codeoutput}{%
+ \def\FrameCommand{\colorbox{outputbackground}}%
+ \vspace{-1.4em}
+ \MakeFramed{\advance\hsize-\width \FrameRestore}}
+ {\unskip\medskip\endMakeFramed}
+
+\newenvironment{traceback}{%
+ \def\FrameCommand{\colorbox{traceback}}%
+ \MakeFramed{\advance\hsize-\width \FrameRestore}}
+ {\endMakeFramed}
+
+% Use and configure listings package for nicely formatted code
+\usepackage{listingsutf8}
+\lstset{
+ language=python,
+ inputencoding=utf8x,
+ extendedchars=\true,
+ aboveskip=\smallskipamount,
+ belowskip=\smallskipamount,
+ xleftmargin=2mm,
+ breaklines=true,
+ basicstyle=\small \ttfamily,
+ showstringspaces=false,
+ keywordstyle=\color{blue}\bfseries,
+ commentstyle=\color{myteal},
+ stringstyle=\color{darkgreen},
+ identifierstyle=\color{darkorange},
+ columns=fullflexible, % tighter character kerning, like verb
+}
+
+% The hyperref package gives us a pdf with properly built
+% internal navigation ('pdf bookmarks' for the table of contents,
+% internal cross-reference links, web links for URLs, etc.)
+\usepackage{hyperref}
+\hypersetup{
+ breaklinks=true, % so long urls are correctly broken across lines
+ colorlinks=true,
+ urlcolor=blue,
+ linkcolor=darkorange,
+ citecolor=darkgreen,
+ }
+
+% hardcode size of all verbatim environments to be a bit smaller
+\makeatletter
+\g@addto@macro\@verbatim\small\topsep=0.5em\partopsep=0pt
+\makeatother
+
+% Prevent overflowing lines due to urls and other hard-to-break entities.
+\sloppy
+
+\begin{document}
+
+\begin{codecell}
+\begin{codeinput}
+\begin{lstlisting}
+#! /usr/bin/env python
+'''
+github_team_calendar.py
+Python program to scrape friends github to build team calendar for github
+'''
+
+
+import json
+import requests
+import pandas as pd
+
+
+def line_draw_target(target):
+ github_url = 'https://github.com/users/%s/contributions_calendar_data'
+ r = requests.get(github_url % target)
+ data = json.loads(r.text)
+ dates, contributions = zip(*data)
+ ts = pd.Series(contributions, index=dates)
+ plt.plot(ts)
+\end{lstlisting}
+\end{codeinput}
+\end{codecell}
+\begin{codecell}
+\begin{codeinput}
+\begin{lstlisting}
+target = "mikedewar"
+line_draw_target(target)
+
+\end{lstlisting}
+\end{codeinput}
+\begin{codeoutput}
+\begin{center}
+\includegraphics[width=0.7\textwidth]{data_geeks_team_calendar_orig_files/data_geeks_team_calendar_orig_fig_00.png}
+\par
+\end{center}
+\end{codeoutput}
+\end{codecell}
+\begin{codecell}
+\begin{codeinput}
+\begin{lstlisting}
+target = "drewconway"
+line_draw_target(target)
+\end{lstlisting}
+\end{codeinput}
+\begin{codeoutput}
+\begin{center}
+\includegraphics[width=0.7\textwidth]{data_geeks_team_calendar_orig_files/data_geeks_team_calendar_orig_fig_01.png}
+\par
+\end{center}
+\end{codeoutput}
+\end{codecell}
+\begin{codecell}
+\begin{codeinput}
+\begin{lstlisting}
+target = "hmason"
+line_draw_target(target)
+\end{lstlisting}
+\end{codeinput}
+\begin{codeoutput}
+\begin{center}
+\includegraphics[width=0.7\textwidth]{data_geeks_team_calendar_orig_files/data_geeks_team_calendar_orig_fig_02.png}
+\par
+\end{center}
+\end{codeoutput}
+\end{codecell}
+\begin{codecell}
+\begin{codeinput}
+\begin{lstlisting}
+target = "mbostock"
+line_draw_target(target)
+\end{lstlisting}
+\end{codeinput}
+\begin{codeoutput}
+\begin{center}
+\includegraphics[width=0.7\textwidth]{data_geeks_team_calendar_orig_files/data_geeks_team_calendar_orig_fig_03.png}
+\par
+\end{center}
+\end{codeoutput}
+\end{codecell}
+\begin{codecell}
+\begin{codeinput}
+\begin{lstlisting}
+target = "amueller"
+line_draw_target(target)
+\end{lstlisting}
+\end{codeinput}
+\begin{codeoutput}
+\begin{center}
+\includegraphics[width=0.7\textwidth]{data_geeks_team_calendar_orig_files/data_geeks_team_calendar_orig_fig_04.png}
+\par
+\end{center}
+\end{codeoutput}
+\end{codecell}
+
+\end{document}