I'm using Jupyter Notebook and trying to create an interactive plot. I really like how simple the ipywidgets.interactive is to use and having the ability to lay things out in VBox or HBox. The problem I'm having is once I download as html the ipywidgets.interactive is not updating my plot.
Here is what I have:
from ipywidgets import interact, interactive, fixed, interact_manual
import ipywidgets as widgets
import plotly.graph_objs as go
import plotly.offline as py
import numpy as np
from IPython.display import display
py.init_notebook_mode()
xs = np.linspace(0,6,100)
ys = np.sin(xs)
scatter = go.Scatter(
x = xs,
y = ys
)
data = [scatter]
layout = go.Layout(title='test')
fig = go.FigureWidget(data=data, layout=layout)
slider = widgets.FloatRangeSlider(
min=1,
max=6,
step=.1,
description='desc'
)
def update_b(b):
fig.data[0].y = np.sin(xs+b)
vb = widgets.VBox((fig, interactive(update_b, b=(1, 6, .1))))
vb.layout.align_items='center'
# This displays it and allows it to be interactive, but only when I have it as .ipynb,
# not when I download as html
display(vb)
The way I am saving as html is:
1. Widgets > Save Notebook Widget State
2. From cmd: jupyter nbconvert --to html test_plot.ipynb
I have also done the following to enable the widget extension:
jupyter nbextension enable --py widgetsnbextension
Enabling notebook extension jupyter-js-widgets/extension...
- Validating: ok
After everything this is what I get:
The thing is the slider is movable but it does not update the graph. The graph is also able to be manipulated through zoom, etc. like normal with plotly. This leads me to believe there is something wrong with the way I've used interactive.
Any ideas?
Unfortunately this does not work this way, the function that links the slider with the plot is written in python and executes in the python kernel, so when you convert to a static html this function does not exist anymore.
I am not aware of some kind of python to javascript translator that allows these kind of functions to run without a python kernel, although plotly's Dash seems to be doing something in this line (see this issue). If you can put up a server you can use Voila or something similar to make the notebook look like a web page.
I'm not using plotly, however, try adding some line magic, like "widget" which makes the graph interactive...
%matplotlib widget
%matplotlib widget
from ipywidgets import *
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 2 * np.pi)
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
line, = ax.plot(x, np.sin(x))
def update(w = 1.0):
line.set_ydata(np.sin(w * x))
fig.canvas.draw()
interact(update);
screenshot of notebook
Related
Say, I have a code like this in a Jupyter notebook (like in Controlling order of display in ipywidgets Vbox (when matplotlib widget is used)?):
import IPython.display
from IPython.display import display
from ipywidgets import widgets, Layout
%matplotlib widget
import matplotlib as mpl
import matplotlib.pyplot as plt
widget_out = widgets.Output(layout=Layout(width='100%'))
with widget_out:
widget_out.clear_output(wait=True)
plt.ioff() # "turn off interactive mode so figure doesn't show"
fig = plt.figure(figsize=(10,1), dpi=90)
ax = fig.add_subplot(111)
ax.plot([0,1,2], [0,1,2])
plt.ion() # "figure still doesn't show"
display(fig.canvas) # "It's the canvas attribute that is the interactive widget, not the figure"
myvbox = widgets.VBox([
widget_out,
],)
display(myvbox)
It results with a rendering like this:
Now, when I click the "Download plot" button, I immediately get a download dialog for a "Figure 1.png" file:
..., and there is no obvious option I could change, so I could obtain a different format - and here I'd want .svg.
When I use the usual Matplotlib from Python on dekstop however, and I click the "Save the figure" button there, there is actually a picker for file formats:
... and .svg is there.
Is there a way to persuade %matplotlib widget inside Jupyter to "save as" .svg (or other vector format), instead of .png?
I am working on a project that involves generating a matplotlib animation using pyplot.imshow for the frames. I am doing this in a jupyter notebook. I have managed to get it working, but there is one annoying bug (or feature?) left. After the animation is created, Jupyter shows the last frame of the animation in the output cell. I would like the output to include the animation, captured as html, but not this final frame. Here is a simple example:
import numpy as np
from matplotlib import animation
from IPython.display import HTML
grid = np.zeros((10,10),dtype=int)
fig1 = plt.figure(figsize=(8,8))
ax1 = fig1.add_subplot(1,1,1)
def animate(i):
grid[i,i]=1
ax1.imshow(grid)
return
ani = animation.FuncAnimation(fig1, animate,frames=10);
html = HTML(ani.to_jshtml())
display(html)
I can use the capture magic, but that suppresses everything. This would be OK, but my final goal is to make this public, via binder, and make it as simple as possible for students to use.
I have seen matplotlib animations on the web that don't seem to have this problems, but those used plot, rather than imshow, which might be an issue.
Any suggestions would be greatly appreciated.
Thanks,
David
That's the answer I got from the same thing I was looking for in 'jupyter lab'. Just add plt.close().
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation
from IPython.display import HTML
grid = np.zeros((10,10),dtype=int)
fig1 = plt.figure(figsize=(8,8))
ax1 = fig1.add_subplot(1,1,1)
def animate(i):
grid[i,i]=1
ax1.imshow(grid)
return
ani = animation.FuncAnimation(fig1, animate,frames=10);
html = HTML(ani.to_jshtml())
display(html)
plt.close() # update
I have some plots that I want to interact with through widgets in Jupyterlab.
I installed the Widgets extension like described in this manual.
Anaconda shows me that ipywidgets and widgetsnbextension are installed in my environment.
In order to get the hang of the procedure, I followed a tutorial. When executing the code, I get the error message
'Javascript Error: IPython is not defined'.
I tried to change the magic command to %matplotlib inline, this builds an empty plot, that doesn't show any curve.
import ipywidgets as widgets
from IPython.display import display
import matplotlib.pyplot as plt
import numpy as np
%matplotlib nbagg
x=np.linspace(0,2,1000)
fig, ax=plt.subplots(1,figsize=(10,4))
plt.suptitle('Sine Wave')
def update_plot(amp, phase, freq):
'''
this function linked to the sliders and
it replots the sine waves when the sliders are changed
'''
ax.clear()
y = amp * np.sin(freq * 2 * np.pi * x + phase * 2 * np.pi)
ax.plot(x,y)
plt.show()
amp=widgets.FloatSlider(min=1,max=10,value=4,description='Amp')
phase=widgets.FloatSlider(min=0,max=5,value=0,description='Phase')
freq=widgets.FloatSlider(min=1,max=10,value=1,description='Freq')
widgets.interactive(update_plot, amp=amp, phase=phase, freq=freq)
This should deliver a plot of a sine curve, that is interactable via the sliders to change amplitue, phase and frequency.
I don't know what I could try to get it running.
Quick update:
I don't know what's wrong with the code I tried before, but I found another example that works and is more minimalistic as well:
from ipywidgets import interact, interactive, fixed, interact_manual
import ipywidgets as widgets
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np
def plot_func(freq):
x = np.linspace(0, 2*np.pi,1000)
y = np.sin(x * freq)
plt.plot(x, y)
interact(plot_func, freq = widgets.FloatSlider(value=2.5, min=1,
max=5.0,step=0.5))
I'll now just use this example as template to make my own codes.
I'm trying to control the display of a scatter plot with a checkbox. When I built it using the interact function it worked as expected. The plot was shown or hidden based on the value in the checkbox.
import matplotlib.pyplot as plt
from ipywidgets import interact, widgets
%matplotlib inline
def on_change(Display):
if Display == True:
plt.scatter(x,y)
plt.show()
return Display
interact(on_change, Display=False);
When I tried to do the same thing using the observe function every time I clicked on the checkbox I get an additional plot displayed below. What do I need to do to get it to redraw the same plot so it works like the example above?
I suppose something in the interact example is clearing the display but it's not clear how to do this manually.
import matplotlib.pyplot as plt
from ipywidgets import interact, widgets
%matplotlib inline
x = [1,2,3,4,5,6,7,8]
y = [5,2,4,2,1,4,5,2]
def on_change(change):
if change['new'] == True:
scat = plt.scatter(x,y)
plt.show()
cb = widgets.Checkbox(False, description = "Display")
cb.observe(on_change, names='value')
display(cb)
A couple of alterations I made to your example to hopefully demonstrate what you want. I have taken a more object-oriented route, not sure if you specifically wanted to avoid it but it helps achieve your desired outcome, it seems like you are moving towards a simple GUI here.
1) Include an Output widget (out) - basically a cell output which you can display like a normal widget. You can use a context manager block (with out:) when you want to print to that specific output widget. You can also clear the widget with out.clear_output()
2) Use the object oriented interface in matplotlib rather than using plt. I find this easier to control which plots are displayed and in which location at the right times.
temporarily suspend the interactive matplotlib with plt.ioff()
Create your figure and axis with fig, ax = plt.subplots(). NB figures can have multiple axes/subplots but we only need one.
'plot' the scatter data to your axis using ax.scatter(x,y), but this won't cause it to appear.
Explicitly display the figure with display(fig).
I'm assuming you want your figure to be replotted each time you check the box, so I have included it in the observe function. If your figure doesn't change, it would make sense to move it outside of the loop.
import matplotlib.pyplot as plt
from ipywidgets import interact, widgets
%matplotlib inline
out = widgets.Output()
x = [1,2,3,4,5,6,7,8]
y = [5,2,4,2,1,4,5,2]
def on_change(change):
if change['new'] == True:
with out:
plt.ioff()
fig,ax = plt.subplots()
ax.scatter(x,y)
display(fig)
else:
out.clear_output()
cb = widgets.Checkbox(False, description = "Display")
cb.observe(on_change, names='value')
display(cb)
display(out)
I am using Python 2.7.x with a Jupyter Notebook, matplotlib and %pylab backend with the inline flag
%pylab inline
to print images below active cells. I would like to be able to move my cursor over an image and know it's location and pixel value An example could be:
(x,y,val) = (123,285,230)
but I am not particular about any of the specifics of this example.
The %matplotlib inline backend displays the plot outputs as png images. It may be possible to write some JavaScript for the Jupyter notebook to obtain the color and pixel on mouse over an image in the cell output.
However it may be much easier to just use the %matplotlib notebook backend, which keeps the matplotlib figure alive when plotting it to the output and therefore the usual built-in mouseover functionality is readily available.
Note the picker in the lower right corner of the image, which displays x,y and the value of the current pixel.
To expand on ImportanceOfBeingErnest's answer, you can use mpl_connect to provide a callback on your clicks and ipywidgets to show an output of your callback. If needed, you can break up the code in different cells.
%matplotlib notebook
import matplotlib.pyplot as plt
import numpy as np
import ipywidgets as wdg # Using the ipython notebook widgets
# Create a random image
a = np.random.poisson(size=(12,15))
fig = plt.figure()
plt.imshow(a)
# Create and display textarea widget
txt = wdg.Textarea(
value='',
placeholder='',
description='event:',
disabled=False
)
display(txt)
# Define a callback function that will update the textarea
def onclick(event):
txt.value = str(event) # Dynamically update the text box above
# Create an hard reference to the callback not to be cleared by the garbage collector
ka = fig.canvas.mpl_connect('button_press_event', onclick)