I usually use IPython notebook in my classes, they are a great tool for teaching code and science or even both at the same time.
However, when I am using the notebook to teach, I miss interaction with
matplotlib figures. One can drop the %matplotlib inline
magic to force the
figures to pop-up in the screen and use the interaction, but that's confusing, force
students to deal with multiple windows, and breaks the linear flow of the
notebook.
I've looked at some alternative to create interactive figures in HTML documents like:
Some of those, like Bokeh, even have some sort of integration with the IPython notebook. The problem with them is that they all require you to learn a new tool!
So here comes Jake to the rescue! (Like he already did before, and before, and before...) He is developing a D3 Renderings of Matplotlib Graphics. That means we can keep using the familiar matplotlib interface to make nice d3 interactive plots.
As an example I will re-write my tidal harmonic analysis using his mpld3
module so my student will be
able to explore the plot, zooming-in and -out at any point.
For that I will use sea level data from the GLOSS Station 194. Here is the data format:
%%bash
sed -n 71,83p ./data/gloss_hourly.fmt
To read that data let's define some handy functions and load the data directly from the zipfile into a pandas Series object.
import os
import numpy as np
import numpy.ma as ma
from zipfile import ZipFile
from datetime import datetime
from pandas import Series, date_range
def basename(fname):
return os.path.splitext(os.path.basename(fname))[0]
def make_date(data):
date = str(int(data))[:-1]
return datetime.strptime(date, '%Y%m%d')
def load_gloss(fname):
data = np.loadtxt(fname, skiprows=1, usecols=range(2, 15))
elev = ma.masked_equal(data[:, 1:].ravel(), 9999)
start = make_date(data[0, 0])
dates = date_range(start, periods=len(elev), freq='H')
return Series(elev, index=dates)
series = Series()
with ZipFile('./data/h281a.zip', 'r') as ziped:
for fname in sorted(ziped.namelist()):
if not fname.endswith('/'): # Skip directories.
if False:
with ziped.open(fname) as f:
print(f.readline())
name = basename(fname)
series = series.append(load_gloss(ziped.open(fname)))
Now we can convert the data from millimeters to meters, print a data description, and produce a quick plot to inspect the data.
series /= 1e3
print(series.describe())
ax = series.plot(figsize=(10, 3))
The data need some more pre-processing to fill-in gaps, and to remove outliers that passed the initial QA/QC. That is beyond of my scope here so I'll just slice a clean and continuous 2-month period from 2004, remove the mean, and move ahead.
obs = series.ix['2004'] - series.ix['2004'].mean()
obs = obs.ix['2004-07':'2004-08']
ax = obs.plot(figsize=(8, 3))
print(obs.describe())
Looks good to me. The next 3 cells are just a copy-and-paste from my previous post on calling t_tide via oct2py.
from matplotlib.dates import date2num
dates = date_range(datetime.now(), periods=72, freq='H')
tim = date2num(dates.to_pydatetime()) + 366 # Matlab Argh!!!
elev = obs.values
%load_ext oct2py.ipython
%%octave -i elev -i tim -o tidestruc -o pout -o yout
pkg load all
addpath(genpath('./t_tide_v1.3beta'))
[tidestruc, pout] = t_tide(elev, 'interval', 1, 'latitude', -25, 'starttime', [1998, 1, 1, 0]);
yout = t_predic(tim, tidestruc);
Now I'll use mpld3 to plot the observed data, tide prediction from the analysis, and the residue.
The main difference from my previous post is that, while the original plot was just a static png
figure, this one allow us to interact with the graph.
import mpld3
mpld3.enable_notebook()
import matplotlib.pyplot as plt
fig, (ax0, ax1, ax2) = plt.subplots(nrows=3, sharey=True, sharex=True, figsize=(8, 4))
ax0.plot(obs.index, obs)
ax1.plot(obs.index, pout.squeeze(), alpha=0.5)
ax2.plot(obs.index, obs-pout.squeeze(), alpha=0.5)
Now I can tell my students to zoom-in at the observed high water level event that occurred around July 14$^{\text{th}}$ and start quizzing them about what is happening there.
HTML(html)