2
votes

I am using IPython Notebook and Matplotlib to visualise some data, with inline display mode (i.e. I want to display and interact with my plots on the web interface of IPython Notebook).

I have a plot showing several different graphs and I would like to have an interactive interface (a set of checkboxes for example) that allow me to hide or show the graphs.

My plot looks like this:

Difficult to explore the 9 graphs together, better to hide some of them and show others

I another word: I have a plot where I display many different graphs, each graph is a line and it has its own legend. I would like to add a set of check-boxes to the plot, each one for a graph. When the checkpoint is checked the graph will be visible, when unchecked the graph will disappear.

1
yes, this is possible. You may want to have a look at ipywidgets. However your question is probably too broad to be answered. - cel
Thanks cel :) my question is: I have a plot where I plotted 9 different graphs (lines), each one has its own legend. I would like to add a set of checkboxes beside the plot (9 checkboxes), each one for one graph. When the checkpoint is checked the graph will be visible, when unchecked the graph will disappear. - Rami
%matplotlib notebook will give you an interactive figure you can pan/zoom. - tacaswell
@tcaswell that won't hide/display the individual lines that he's asking about. - Leb
@tcaswell exactly as Leb said, this will not allow me to show and hide the lines. - Rami

1 Answers

1
votes

To do this you need references to the artists created by your plotting routine. The plotting methods attached to the DataFrame objects return the Axes object they plotted to instead (this is useful for simple things, but makes complex things impossible) so, some plotting code:

%matplotlib notebook
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib

def pandas_plot(ax, df, style_cycle, **kwargs):
    """
    Plot a pandas DataFrame

    Parameters
    ----------
    ax : matplotlib.axes.Axes
        The axes to plot to

    df : pd.DataFrame
        The data to plot

    style_cycle : Cycler
        Something that when iterated over yields style dict

    Returns
    -------
    ret : dict
        Dictionary of line2d artists added 
    """
    ret = {}
    x = df.index
    for n, sty in zip(df.columns, style_cycle):
        sty.update(kwargs)
        ln, = ax.plot(x, df[n], label=n, **sty)
        ret[n] = ln
    ax.legend()
    return ret

Now some code to set up the widget interface (this is doing more than you asked for, but this is what I have pre-made from my scipy talk):

from IPython.html.widgets import *
from IPython.display import display

def widget_function_factory(arts):
    """
    Generate fulnction + args to pass to interactive
    Parameters
    ----------
    arts : dict
        dictionary of Line2D

    """

    name = Dropdown(options=list(arts.keys()))

    def set_all(_, old_line, new_line):
        ln = arts[new_line]
        lw.value = ln.get_lw()
        alph.value = ln.get_alpha() or 1
        visible.value = ln.get_visible()
        markevery.value = ln.get_markevery()
        marker.value = ln.get_marker()

    def set_lw(_, old_lw, new_lw):
        ln = arts[name.value]
        arts[name.value].set_lw(new_lw)
        arts[name.value].axes.legend()

    def set_alpha(_, old_value, new_value):
        ln = arts[name.value]
        ln.set_alpha(new_value)
        ln.axes.legend()

    def set_visible(_, old_value, new_value):
        ln = arts[name.value]
        ln.set_visible(new_value)
        ln.axes.legend()

    def set_markevery(_, old_value, new_value):
        ln = arts[name.value]
        ln.set_markevery(new_value)

    def set_marker(_, old_value, new_value):
        ln = arts[name.value]
        ln.set_marker(new_value)
        ln.axes.legend()

    lw = FloatSlider(min=1, max=5, description='lw: ')
    alph = FloatSlider(min=0, max=1, description='alpha: ')
    visible = Checkbox(description='visible: ')
    markevery = IntSlider(min=1, max=15, description='markevery: ')
    marker = Dropdown(options={v:k for k, v in matplotlib.markers.MarkerStyle.markers.items()},
                     description='marker: ')

    name.on_trait_change(set_all, 'value')
    lw.on_trait_change(set_lw, 'value')
    alph.on_trait_change(set_alpha, 'value')
    visible.on_trait_change(set_visible, 'value')
    markevery.on_trait_change(set_markevery, 'value')
    marker.on_trait_change(set_marker, 'value')
    display(name, lw, alph, marker, markevery, visible)
    set_all(None, None, name.value)

do the plotting:

th = np.linspace(0, 2*np.pi, 128)
df = pd.DataFrame({'sin': np.sin(th),
                   'shift +': np.sin(th + np.pi / 3), 
                   'shift -': np.sin(th - np.pi / 3)}, index=th)

fig, ax = plt.subplots()
from cycler import cycler
style_cycle = cycler('color',['r', 'black', 'pink']) + cycler('marker', 'sxo')
#style_cycle = [{'color': 'r', 'marker': 's'},
#               {'color': 'black', 'marker': 'x'},
#               {'color': 'pink', 'marker': 'o'}]
arts = pandas_plot(ax, df, style_cycle, markevery=10)
vlns = []
for x in np.arange(1, 7) * np.pi/3:
    vlns.append(plt.axvline(x, color='k', linestyle=':'))
plt.axhline(0, color='k', linestyle=':')

and create the controls

widget_function_factory(arts)

cycler is a side project that spun off of mpl (and will be a required dep for 1.5). It is currently pip installable.

See https://gist.github.com/tacaswell/7a0e5e76fb3cafa3b7cd#file-so_interactive_demo-ipynb for a demo notebook.

There is on-going work to make this easier (so that mpl Artists can auto-construct their UI elements). The infrastructure that will make that work is one of the major goals of mpl 2.1.