Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Interactive plots placement in ipython notebook widget

I've got two plots which I'd like to make interactive with ipython notebook widgets. The code below is a simplified sample of what I'm trying to do.

import matplotlib.pyplot as plt
import IPython.html.widgets as wdg

def displayPlot1(rngMax = 10):
    plt.figure(0)
    plt.plot([x for x in range(0, rngMax)])

wdg1 = wdg.interactive(displayPlot1, rngMax = wdg.IntSlider(20))

def displayPlot2(rngMax = 10):
    plt.figure(1)
    plt.plot([x**2 for x in range(0, rngMax)])

wdg2 = wdg.interactive(displayPlot2, rngMax = wdg.IntSlider(10))

wdg.ContainerWidget([wdg.HTML("""<h1>First Plot</h1>"""),
                     wdg1, 
                     wdg.HTML("""<h1>Second Plot</h1>"""), 
                     wdg2])

The first problem is that it displays all the widgets first, and two plots one after another at the end:

title1
widget1
title2
widget2
plot1
plot2

I'd like to have:

title1
widget1
plot1    
title2
widget2
plot2

Also it seems the whole output gets overwritten as soon as I touch any of the sliders, and displays one plot only (the one I'm changing).

How do I fix this problem? (I potentially can do it if I separate them into two different cells, however I'm planning to do something more complex and it needs to be in one cell eventually)

like image 910
sashkello Avatar asked Aug 10 '15 04:08

sashkello


People also ask

How do you display plots in Jupyter notebook?

Usually, displaying plots involves using the show() function from PyPlot. With Jupyter notebooks, this isn't necessary as the plots are displayed after running the cells containing the code that generates them. These plots are by default, displayed inline, which means, they're displayed in the notebook itself.

Can Jupyter notebooks be interactive?

Jupyter Notebook has support for many kinds of interactive outputs, including the ipywidgets ecosystem as well as many interactive visualization libraries.

Which is used to display plots on the Jupyter notebook?

IPython kernel of Jupyter notebook is able to display plots of code in input cells. It works seamlessly with matplotlib library. The inline option with the %matplotlib magic function renders the plot out cell even if show() function of plot object is not called.


1 Answers

IPython Notebook displays widgets before any output. One thing you can do is to place your plots inside an HTML widget. This can be placed in any position relative to other widgets.

If you do this however, you need to explicitly need to place your plot within the HTML widget. This can be a bit tricky, but a quick solution would be to save the byte string of the plot to a buffer and then put the byte string in an image tag.

Here's an example (Gist here):

import base64
import io
import matplotlib.pyplot as plt
import ipywidgets as widgets
from IPython.display import display

def handle_input1(slope):
    plt.figure()
    plt.plot([x * slope for x in range(0, 11)])
    plt.ylim((0,10))
    output1HTML.value = plot_to_html()
    plt.close()

def handle_input2(curvature):
    plt.figure()
    plt.plot([x * x * curvature for x in range(0, 11)])
    plt.ylim((0,100))
    output2HTML.value = plot_to_html()
    plt.close()

def plot_to_html():
    # write image data to a string buffer and get the PNG image bytes
    buf = io.BytesIO()
    plt.savefig(buf, format='png')
    buf.seek(0)
    return """<img src='data:image/png;base64,{}'/>""".format(base64.b64encode(buf.getvalue()).decode('ascii'))

plt.ioff()

heading1HTML = widgets.HTML("""<h1>Slope</h1>""")
input1Float = widgets.FloatSlider(value=0.5, min=0.0, max=1.0, step=0.01, description="slope: ")
widgets.interactive(handle_input1, slope=input1Float)
output1HTML = widgets.HTML()

heading2HTML = widgets.HTML("""<h1>Curvature</h1>""")
input2Float = widgets.FloatSlider(value=0.5, min=0.0, max=1.0, step=0.01, description="curvature: ")
widgets.interactive(handle_input2, curvature=input2Float)
output2HTML = widgets.HTML()

display(widgets.Box([heading1HTML, input1Float, output1HTML, heading2HTML, input2Float, output2HTML]))

handle_input1(input1Float.value)
handle_input2(input2Float.value)

EDIT 1: IPython widgets have been moved to IPython.html; updated the code accordingly.

EDIT 2: Using widgets.interactive as per the latest IPython widgets documentation; updating IPython widget location once more; adding ASCII encoding

EDIT 3: As of 2021, IPython widgets are probably no longer the tool of choice for interactive plots. Using Altair is probably a better way to produce interactive charts these days.

like image 141
Markus Schanta Avatar answered Oct 16 '22 22:10

Markus Schanta