Jupyter Notebook: duplicated scatter plot using when using ipywidgets - python

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)

Related

Viewing Matplotlib funcAnimation live plots in browser

I am generating a live plot using matplotlib's funcAnimation function such as in the example below. To be clear I am plotting data that is dynamically updating in real-time and I want to view these updates in the browser rather than in its own local plotting window.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation
import mpld3
def animate(i):
plt.clf()
#generate random data
x = np.array([i for i in range(100)])
y = np.random.normal(loc=0,scale=1, size = 100)
plt.scatter(x,y)
def main():
fig = plt.figure()
ani = animation.FuncAnimation(fig, animate, interval=1000)
ani.save('animation.gif', fps=10)
plt.show()
#mpld3.show()
if __name__ == '__main__':
main()
It opens the plot in its own little window.
I was wondering what is the best/quickest way to open this in browser instead? I tried using mpld3 as recommended in some places (see the commented bits of code in my code snippet), but all I get is a static page that does not update. I would like the plot to be redrawn in the browser during each redraw done by the animate fuction as it does when the plot is generated in it's own window instead as in the first image.
Thanks in advance for any help anyone has to offer!

Suppress display of final frame in matplotlib animation in jupyter

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

How to exclude matplotlib figure from show() call?

I have a class that owns some matplotlib figures, axes, artists, etc. It has functions for manipulating and displaying these that all work fine when working with instances of the class.
However, if a user wants to make some separate matplotlib plots in, say, another cell of a notebook that contains instances of my class, all the class's figures get displayed when the user calls plt.show() to display their own figures.
I'm using ipympl, %matplotlib widget, and setting plt.ioff().
Is there a way for me to set my figures so that they are not displayed when plt.show() is called?
Example: Consider these two Jupyter Notebook cells (have to be split for first figure to display)
%matplotlib widget
import matplotlib.pyplot as plt
plt.ioff()
class interactiveplot(object):
def __init__(self, x, y):
plt.ioff()
self.fig,self.ax = plt.subplots(figsize=(2,2))
self.plot, = self.ax.plot(x,y)
def update(self,x,y):
self.plot.set_data(x,y)
self.fig.canvas.draw()
def show(self):
return self.fig.canvas
#create class instance
test = interactiveplot([1,2,3,4],[4,1,5,7])
#display figure
test.show()
#Change data in displayed figure
test.update([2,3],[6,5])
#display a plot of something totally unrelated
fig,ax = plt.subplots(figsize=(2,2))
ax.scatter([2.3,3.4],[34,23])
plt.show()
#Both test.fig and fig are displayed! I only want to see fig!

How can I display and update two matplotlib plots in the same window at the same time?

I've implemented a GUI that displays two dropdown menus in which you can choose two different set of graphs to be displayed. However when I create the graphs with the following code:
import matplotlib.pyplot as plt
from matplotlib.backends.backedn_qt4agg import FigureCanvasQTAgg as FigureCanvas
self.comparison_figure1 = plt.figure(figsize=(15,5))
self.comparison_canvas1 = FigureCanvas(self.comparison_figure1)
self.comparison_figure2 = plt.figure(figsize=15,5))
self.comparison_canvas2 = FigureCanvas(self.comparison_figure2)
And then I try to update the plots (plt.tight_layout() for example)
def on_resize(event):
plt.tight_layout()
self.comparison_canvas2.draw()
self.comparison_canvas1.draw() #this would do nothing
cid = self.comparison_canvas2.mpl_connect('resize_event', on_resize)
only the last plot called with "plt." is updated. How do I write my code so that I can reference both plots.
I've also tried to create one plot where I have both graphs being displayed side by side but because of the need to update the graphs independently I encountered more problems. If you are able to make it work that way instead, great! I'm just thinking that fixing the previous problem may be simpler.
If you need more code I can post it!
#
#
Solution (Thanks to ImportanceOfBeingErnest and Ash Sharma):
replace any "plt." with the specific figure
for example:
plt.tight_layout() #replace with self.comparison_figure1.tight_layout()
So this is some of the fixed code:
def on_resize(event):
self.comparison_figure1.tight_layout()
self.comparison_figure2.tight_layout()
self.comparison_canvas1.draw()
self.comparison_canvas2.draw()
cid = self.comparison_canvas2.mpl_connect('resize_event', on_resize)
#
#
Problem:
Previous solution hasn't fixed all plot updating issues. Though "plt" can be replaced with self.comparison_figure1 when using tight_layout(), the same cannot be done when using cla() to clear the plot.
Code where I'm using cla():
sns.set(style="whitegrid")
plt.cla()
ax = self.comparison_figure2.add_subplot(111)
.....
.....
I can post more code if you need it!
Solution (Thanks to ImportanceOfBeingErnest and Ash Sharma):
replace any plt with the specific figure
for example:
plt.tight_layout() #replace with self.comparison_figure1.tight_layout()
So this is some of the fixed code:
def on_resize(event):
self.comparison_figure1.tight_layout()
self.comparison_figure2.tight_layout()
self.comparison_canvas1.draw()
self.comparison_canvas2.draw()
cid = self.comparison_canvas2.mpl_connect('resize_event', on_resize)

Matplotlib figure not updating on data change

I'm implementing an image viewer using matplotlib. The idea is that changes being made to the image (such as filter application) will update automatically.
I create a Figure to show the inital image and have added a button using pyQt to update the data. The data does change, I have checked, but the Figure does not. However, if after I've pressed the filter application button, I move the image using matplotlib's standard tool bar, the image is then updated.
I assume I'm doing something wrong when updating the image, but since the fact of moving it actually forces the update, it then shows the data change. I would like for this to happen when I press the button, though.
Below is some of the code. This is the initial figure initialization, which shows the original image:
self.observableFig = Figure((4.0, 4.0), dpi=100)
self.canvas = FigureCanvas(self.observableFig)
self.canvas.setParent(self.observableWindow)
self.canvas.setFocusPolicy(Qt.StrongFocus)
self.canvas.setFocus()
self.canvas.mpl_connect('button_press_event', self.on_click)
# Showing initial data on Window
self.observableFig.clear()
self.observableAxes = self.observableFig.add_subplot(1, 1, 1)
min, max = self.min, self.max
self.observableAxes.imshow(
self.data,
vmin=min,
vmax=max,
origin='lower'
)
And this is the event for when the button that changes the data is pressed:
self.observableAxes.imshow(self.data/2, origin='lower')
# plt.clf()
# plt.draw()
# plt.show()
I have tried draw(), show(), basically anything I've found on pyplot about this. I have also tried both with and without plt.ion() at the beginning, but it hasn't made a difference in this.
Thanks in advance.
The reason that nothing is updating is that you're trying to use pyplot methods for a figure that's not a part of the pyplot state machine. plt.draw() won't draw this figure, as plt doesn't know the figure exists.
Use fig.canvas.draw() instead.
Regardless, it's better to use fig.canvas.draw() that plt.draw(), as it's clear which figure you're drawing (the former draws one, the latter draws all, but only if they're tracked by pyplot).
Try something along these lines:
import numpy as np
import matplotlib.pyplot as plt
data = np.random.random((10,10))
# To make a standalone example, I'm skipping initializing the
# `Figure` and `FigureCanvas` and using `plt.figure()` instead...
# `plt.draw()` would work for this figure, but the rest is identical.
fig, ax = plt.subplots()
ax.set(title='Click to update the data')
im = ax.imshow(data)
def update(event):
im.set_data(np.random.random((10,10)))
fig.canvas.draw()
fig.canvas.mpl_connect('button_press_event', update)
plt.show()

Categories