Jupyter-lab pyplot cell re-evaluation breaks interactive widget display - matplotlib

This code works as expected on a cleanly started kernel, but on re-executing the second cell, instead of an interactive widget (ipypml per https://matplotlib.org/3.3.0/users/interactive.html ), I get just the text output as in the image.
How are jupyter, jupyter-lab, widgets, pyplot, and matplotlib interacting to cause this problem?
And how do I properly do a plot so that I can re-execute the cell without re-starting the kernel?
Cell 0:
%matplotlib widget
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
Cell 1:
fig,ax = plt.subplots(num=0)
ax.plot(np.arange(4))
Cell 1 output:
[<matplotlib.lines.Line2D at 0x161b913a0>]

I found that I need to make sure the figure gets cleanly opened (or closed and re-opened) in each update. I had used the plt.subplots(num=0) option to keep repeated updates from creating new, non-displayed figures. (note that the 2nd cell in the screenshot has a been re-executed 5 times and has a [6] prefix)
plt.close(0)
fig,ax = plt.subplots(num=0)
ax.plot(np.arange(4))

Related

Different behaviour of ipywidgets.interactive_output() on Windows and macOS

I wanted to learn how to use interactive plots in Jupyter Notebook and created a script that updates a figure based on the current values of two sliders. The idea is simply that when the value of one of the sliders changes the figure should be updated.
This worked fine when I first ran the code on my Windows PC (using Windows 10). However, when I then ran the same code on my MacBook (using macOS Monterey 12.6.1), the figure is not updated but a new figure is created every time I change the value of one of my sliders.
Please find a MWE below:
import numpy as np
import matplotlib.pyplot as plt
import ipywidgets as widgets
%matplotlib widget
# Create two sliders
slider1 = widgets.IntSlider(value=4, min=0, max=10, step=1,
description='Slider1', continuous_update = False)
slider2 = widgets.IntSlider(value=8, min=0, max=10, step=1,
description='Slider2', continuous_update = False)
# Some function that plots the result based on the current values of the sliders
def plotResult(a, b):
plt.close('all')
plt.figure()
plt.hlines(a+b, xmin=0, xmax=10)
# Create interactive plot
interactive_plot = widgets.interactive_output(plotResult, {"a":slider1, "b":slider2})
ui = widgets.HBox([slider1, slider2])
display(ui, interactive_plot)
When I run the above code in a cell in my Jupyter Notebook, I get the following output:
(https://i.stack.imgur.com/NX02T.png).
When I now change any of the sliders, on Windows my figure is "updated in-place", while on macOS a new figure is created everytime (every figure newly created figure is labeled as 'Figure 1'). I don't understand why this is happening as I explicitly close all currently opening figures at the beginning of plotResults(a,b) by using the command plt.close('all').
I thought that since every newly created figure on macOS is labeled 'Figure 1' the problem could be that on macOS the cells are not refreshed. Thus, I additionally imported the module IPython using import IPython and then added the command IPython.display.clear_output(wait=True) after the first line of plotResults(a,b). However, this did not work and the problem persisted.
I also tried to use a different Matplotlib backend such as %matplotlib ipympl or %matplotlib nbagg instead of %matplotlib widget but this also did not help anything.

Using multiple sliders to manipulate curves in a single graph

I created the following Jupyter Notebook. Here three functions are shifted using three sliders. In the future I would like to generalise it to an arbitrary number of curves (i.e. n-curves). However, right now, the graph updating procedure is very slow and the graph itself doesn't seem to be fixed in the corrispective cell . I didn't receive any error message but I'm pretty sure that there is a mistake in the update function.
Here is the the code
from ipywidgets import interact
import ipywidgets as widgets
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from IPython.display import display
x = np.linspace(0, 2*np.pi, 2000)
y1=np.exp(0.3*x)*np.sin(5*x)
y2=5*np.exp(-x**2)*np.sin(20*x)
y3=np.sin(2*x)
m=[y1,y2,y3]
num_curve=3
def shift(v_X):
v_T=v_X
vector=np.transpose(m)
print(' ')
print(v_T)
print(' ')
curve=vector+v_T
return curve
controls=[]
o='vertical'
for i in range(num_curve):
title="x%i" % (i%num_curve+1)
sl=widgets.FloatSlider(description=title,min=-2.0, max=2.0, step=0.1,orientation=o)
controls.append(sl)
Dict = {}
for c in controls:
Dict[c.description] = c
uif = widgets.HBox(tuple(controls))
def update_N(**xvalor):
xvalor=[]
for i in range(num_curve):
xvalor.append(controls[i].value)
curve=shift(xvalor)
new_curve=pd.DataFrame(curve)
new_curve.plot()
plt.show()
outf = widgets.interactive_output(update_N,Dict)
display(uif, outf)
Your function is running on every single value the slider moves through, which is probably giving you the long times to run you are seeing. You can change this by adding continuous_update=False into your FloatSlider call (line 32).
sl=widgets.FloatSlider(description=title,
min=-2.0,
max=2.0,
step=0.1,
orientation=o,
continuous_update=False)
This got me much better performance, and the chart doesn't flicker as much as there are vastly fewer redraws. Does this help?

Jupyter notebook matplotlib figures show up small until cell is completed

I'm trying to make a notebook where the data produced by a long for loop is put in a graph point by point. However, when using %matplotlib notebook and fig.canvas.draw() the graph is tiny up until the cell finishes running. (In fact, I've got many of those graphs and they are even tinyer when using more subplots.)
Here my code reproducing the behaviour in a jupyter notebook, at least on OS X with (latest) jupyter-core 4.3.0 and (latest) matplotlib 2.0.2.
%matplotlib notebook
import time
import matplotlib.pyplot as plt
fig, ax = plt.subplots(1, 1)
for _ in range(5):
ax.plot([1,2,3], [1,2,3])
fig.canvas.draw()
time.sleep(1)
During the cell execution I get this plot
And when the cell finishes execution (after 5 seconds) I get this
I would like to get the larger image even during cell execution. What am I doing wrong?
As proposed by ImportanceOfBeingErnest, one solution is to put the figure creation in its own cell.
[1] %matplotlib notebook
import time
import matplotlib.pyplot as plt
[2] fig, ax = plt.subplots(1, 1)
[3] for _ in range(5):
ax.plot([1,2,3], [1,2,3])
fig.canvas.draw()
time.sleep(1)
Edit: This solutions does not work if you run all cells at once.

ipython notebook pylab inline - matplotlib.pyplot - how to display plot with scrollbar?

I am using ipython notebook with pylab --inline
import matplotlib.pyplot as plt
import pandas as pd
......
plt.figsize(14,8)
ax1=plt.subplot(311)
#data is a pandas data frame with timeseries stock data
# this plots the data
data.plot(ax=ax1)
This shows a plot of the stock data but its all displayed at once.
I would like to display just subrange of dates and have a scrollbar to control what range
How do I do it such that it works with the inline display of plots.
You can't. Inline display is a static PNG.
If you want something like that you will have to use a Javascript plotting library for now.
None that I know of will work out of the box with panda.
You can't manipulate plots in inline-mode after they have been drawn. However, you can scale,zoom and resize plots in the interactive mode. Just start your notebook without the inline-option and all plots will be generated in an extra window with the functionality you asked for:
ipython notebook --pylab

FuncAnimation does not refresh

I am having problems with FuncAnimation, I am using a slightly modified example http://matplotlib.org/examples/animation/basic_example.html
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
def update_line(num, data, line):
data.pop(0)
data.append(np.random.random())
line.set_ydata(data)
return line,
fig1 = plt.figure()
data = [0.0 for i in xrange(100)]
l, = plt.plot(data, 'r-')
plt.ylim(-1, 1)
line_ani = animation.FuncAnimation(fig1, update_line, 25, fargs=(data, l), interval=50, blit=True)
plt.show()
The problem is that the first line (updated by update_line) stays in background.
If I resize the window (click on the corner of the window and moving the mouse). This first line disappears but now the first line after the resize stays in background.
Is this normal, or I am doing something wrong.
Thanks in advance
If you are not overly worried about speed, remove blit=True and it should work.
Blitting is a way to save time by only re-drawing the bits of the figure that have changed (instead of everything), but is easy to get messed up. If you do not include blit=True all the artists get re-drawn every time.
I recommend reading python matplotlib blit to axes or sides of the figure? and Animating Matplotlib panel - blit leaves old frames behind.