Live plotting in Jupyter Lab 3 using Matplotlib - matplotlib

I want to dynamically update the plot of a cell. I.e. the plot is initialized at the beginning of the cell, and updated in a (computationally heavy) for-loop, showing how the computation is progressing. In jupyter notebook, this can be done using pneumatics solution in What is the currently correct way to dynamically update plots in Jupyter/iPython?
%matplotlib notebook
import numpy as np
import matplotlib.pyplot as plt
import time
def pltsin(ax, colors=['b']):
x = np.linspace(0,1,100)
if ax.lines:
for line in ax.lines:
line.set_xdata(x)
y = np.random.random(size=(100,1))
line.set_ydata(y)
else:
for color in colors:
y = np.random.random(size=(100,1))
ax.plot(x, y, color)
fig.canvas.draw()
fig,ax = plt.subplots(1,1)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_xlim(0,1)
ax.set_ylim(0,1)
for f in range(5):
pltsin(ax, ['b', 'r'])
time.sleep(1)
I am looking for an equivalent way of doing it in jupyter lab. I tried replacing %matplotlib notebook with %matplotlib widget, using the ipympl library, but that didn't work: The figure only shows once the loop is finished.
What I do not want are solutions like the one proposed by Ziofil in or the one by Paidoo in jupyterlab interactive plot which clear the whole output, as I might print additional things such as e.g. a tqdm progress bar

This is a known for matplotlib for which there happily are workarounds.
The relevant issues are: https://github.com/matplotlib/matplotlib/issues/18596 and https://github.com/matplotlib/ipympl/issues/258
and probably the longest explanation is https://github.com/matplotlib/ipympl/issues/290#issuecomment-755377055
Both of these workarounds will work with ipympl.
Workaround 1
Use the async ipython event loop following this answer: https://stackoverflow.com/a/63517891/835607
Workaround 2
Split the plt.subplots and the updating plot code into two cells. If you wait for a second or two between executing the cells then the plot will have enough time to set itself up properly and it should all work. That looks like this:
Cell 1:
fig,ax = plt.subplots(1,1)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_xlim(0,1)
ax.set_ylim(0,1)
wait until the plot shows up then execute:
Cell 2:
for f in range(5):
pltsin(ax, ['b', 'r'])
time.sleep(1)

Related

Why am I getting two plots (instead of one) in a Jupyter notebook?

When using %matplotlib notebook I'm getting two plots instead of one from a pandas Series.
Code in cell is:
%matplotlib notebook
import matplotlib.pyplot as plt
fig=plt.figure()
ax1=fig.add_subplot(1,1,1)
cTitle='H-alpha plot '+galaxy[10:17]
cXAxisTitle='Galactocentric radius/pixels'
cYAxisTitle='Data counts'
ax1.set_title(cTitle)
ax1.set_xlabel(cXAxisTitle)
ax1.set_ylabel(cYAxisTitle)
ax1.grid()
ds.plot()
I'm getting Fig 1 and Fig 2:
Title, axis labels and grid lines in Fig 1 are what I want (expect) and plot in Fig 2 is also what I expect. But why am I getting two plots anyway?
Start from:
%matplotlib inline
import matplotlib.pyplot as plt
once, at the beginning of your notebook.
My suggestion is to use inline instead of notebook.
Then run:
cTitle = 'H-alpha plot '+ galaxy[10:17]
cXAxisTitle = 'Galactocentric radius/pixels'
cYAxisTitle = 'Data counts'
ax1 = ds.plot(title=cTitle, grid=True)
ax1.set(xlabel=cXAxisTitle, ylabel=cYAxisTitle);
Note the semicolon at the end of the last instruction.
Otherwise you will have additional "messages" superimposed
on your drawing.

Fast image sequences / animation in Jupyter Notebook with matplotlib

I can't seem to find a simple and fast way of plotting image sequences with plain matplotlib in a Jupyter Notebook. I've tried FuncAnimation, fig.canvas.draw(), blitting, as well as just the standard imshow-pause combo; without success or with very slow refresh rate. I don't need the images to be interactive - they just need to be shown sequentially and can't pop up a new figure window for each image. I've seen many solutions here, with none seeming to work the way I want.
My general pipeline does significant processing, with each image generated and plotted within a while or for loop. FuncAnimation is not desirable since it requires passing a function handle and my use case involves many arguments and state variables that make it difficult to use.
The best I've got is the working example below using fig.canvas.draw() - showing that drawing time increases linearly per iteration, where I need it to remain constant!
import numpy as np
import matplotlib.pyplot as plt
from timeit import default_timer as timer
%matplotlib notebook
num_iters = 50
im = np.arange(60).reshape((15,4))
fig, ax = plt.subplots(1,1)
fig.show()
fig.canvas.draw()
iter_times = np.zeros(num_iters)
for i in range(num_iters):
im = np.roll( a=im, shift=1, axis=0 )
t0 = timer()
ax.imshow(im.T, vmin=im.min(), vmax=im.max())
ax.set_title('Iter # {}/{}'.format(i+1, num_iters))
fig.canvas.draw()
iter_times[i] = timer()-t0
plt.figure(figsize=(6,3))
plt.plot(np.arange(num_iters)+1, iter_times)
plt.title('Imshow/drawing time per iteration')
plt.xlabel('Iteration number')
plt.ylabel('Time (seconds)')
plt.tight_layout()
plt.show()
I think the problem is that the plots are 'building up', so every one is being plotted every time. If you add ax.clear() right before the imshow(), you'll get linear plot times.

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.

Matplotlib: why are plots always closed shapes?

Using 1.5.1 in Python 2.7.
I'm creating a figure, adding an axes object to it, creating a canvas, and putting it into a window. To draw a simple graph, I set the X and Y limits in the axes object, and then call the plot member function with a numpy arange of values and an array of y values of the same length, along with a few formatting options.
What I get is a nice graph of my data, but it is drawn as a closed curve, meaning that there is a diagonal line leading from the end of my graph back to the beginning.
Why would it do this? I can see the occasional utility of an option that does this, when the X values aren't monotonically increasing (say, to draw a polygon), but it hardly seems like a reasonable default. I don't see any axes attribute that would affect this, or any plot parameter. Does anyone know how to make it not wrap around like this?
EDIT: here is some sample code. It assumes PyGTK as the GUI environment:
import numpy
import gtk
import matplotlib
from matplotlib.figure import Figure
from matplotlib.backends.backend_gtk import FigureCanvasGTK as FigureCanvas
class junk:
def __init__(self):
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.connect('destroy', self.destroy)
self.window.set_title('junk')
self.window.resize(400, 400)
self.figure = Figure()
self.axes = self.figure.add_axes((0, 0, 1, 1))
self.canvas = FigureCanvas(self.figure)
self.canvas.show()
self.window.add(self.canvas)
self.axes.set_xlim(-10, 12)
self.axes.set_ylim(-1, 122)
x = numpy.arange(-9, 12)
self.axes.plot(x, x * x, linestyle = 'solid')
self.canvas.draw()
self.window.show_all()
def destroy(self, widget, data = None):
gtk.main_quit()
def main(self):
gtk.main()
if __name__ == '__main__':
app = junk()
app.main()
This displays an off-center parabola, and the result looks like this:
Now change the lower Y limit from -1 to 1, so that it clips the bottom a little, and the result looks like this:
This shows that if more than one path is needed to draw the graph, each one has the spurious wraparound.
I'm doing this on Windows, but I had this same problem a couple years ago running on a Gumstix SOM running Linux.
I can not reproduce your issue with the given code
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
x = np.arange(-9, 12)
ax.plot(x, x*x)
plt.show()
A line is drawn between the points in the order you pass them in. This is the behavior so you can plot things with are not strict functions of x.
BrenBarn identified the solution in the comment to the original post: use the GTKAgg backend instead of the GTK backend. Thanks.

MatPlotlib from Spyder vs IPython

I'm working through a tutorial on MatPlotlib and I initially started using Spyder's IPython console. The very simple code:
import matplotlib.pyplot as plt
x = range(1, 5)
plt.plot(x, [xi*1.5 for xi in x])
plt.plot(x, [xi*3.0 for xi in x])
plt.plot(x, [xi/3.0 for xi in x])
plt.show()
in Ipython only produces a plot once plt.show() is executed, and that plot has 3 lines on it - as expected. But in Spyder's IPython console, I get a plot every time plt.plot() is executed, and plt.show() doesn't seem to have any effect at all. Each individual plot only has one line plotted on it rather than the 3 lines I see at the end of the code with IPython.
Is there a way to make Spyder behave like IPython?