Plot one figure at a time without closing old figure (matplotlib) - python

Is there a way to plot a set of figures one at a time without closing the previous figure, maintaining control of the UI, and keeping the figures open at the end? Perhaps using a more appropriate backend, or writing it using the OO style instead of the pyplot/pylab style used below?
e.g. I know I can do
plt.ioff()
for i in range(10)
plt.figure()
arr = thisFunctionTakesTenSecondsToGenerateMyArray()
plt.plot(arr)
plt.show()
which will wait for me to close the figure at every iteration
I can also do
plt.ion()
for i in range(10)
plt.figure()
arr = thisFunctionTakesTenSecondsToGenerateMyArray()
plt.plot(arr)
which will plot them as fast as it can generate them (e.g. 10 secs) but will block my entire UI (e.g. I can't even move the windows around), won't show the axes (I only see the figure window), and also will automatically close all the figures at the end of the loop.
The third option that I'm not looking for is this
plt.ioff()
for i in range(10)
plt.figure()
arr = thisFunctionTakesTenSecondsToGenerateMyArray()
plt.plot(arr)
plt.show()
which involves waiting 100 seconds before seeing anything on my screen.
I'm looking for behavior similar to Matlab where I can do
for i = 1:10
figure()
arr = thisFunctionTakesTenSecondsToGenerateMyArray()
plot(arr)
drawnow
which will draw a figure every 10 seconds, and also allow me to move the windows around e.g. if fig 3 is on top and I want to go back to fig 1 while fig 4 is being generated.
Edit:
Using Python 2.7.13, Matplotlib 2.0.0. Running it using Spyder 3.1.3 on Windows 7 SP1 - I've tried running it in the built-in IPython console, the vanilla Python console, and from a script all with the same results.
Ideally, I would like to be able to run it both from a script and interactively, i.e. by executing a script or by copying and pasting into the console.

just add plt.pause(0.0001) inside the loop after plt.show(block=False), and a final plt.show() at the end in case you are executing the script from the operating system command line.

Related

Require Jupyter notebook to render matplotlib notebook figure before closing

Introduction and examples.
It is a widely known
best practice to
close matplotlib figures after opening them
to avoid consuming too much memory.
In a standalone Python script, for example,
I might do something like this:
fig, ax = plt.subplots()
ax.plot(x, y1);
plt.show() # stop here and wait for user to finish
plt.close(fig)
Similarly, for a Jupyter notebook
it's a common pattern to create a figure in one cell
and then close it in the next cell.
A minimal example might look something like this,
where each blank line is a new cell:
%matplotlib notebook
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
fig, ax = plt.subplots()
ax.plot(x, y1);
plt.close(fig)
However, this has a distinct disadvantage:
the figure is blank when run with "Kernel" -> "Restart & Run All"
or "Cell" -> "Run All".
That is, the figure doesn't display until all cells finish evaluating,
but the figure doesn't have time to render
since we are using plt.close right afterward.
Here's an example screenshot based on the example above:
This is different from running each cell individually:
as long as I run each cell slowly enough,
I can get each figure to display:
Full Jupyter notebook .ipynb files are available here:
https://github.com/nbeaver/jupyter-figure-rendering-tests
Inadequate workarounds.
The simplest workaround is start from the top
and manually step through each cell,
wait for it to render, and then move onto the next cell.
I don't consider this an acceptable workaround,
as it becomes impractically laborious for large notebooks
and is generally contrary to the purpose of an executable notebook environment.
Another workaround is to simply never call plt.close(fig).
This is not a good option for several reasons:
It results in excess memory usage,
as evidenced by warnings about opening too many figures.
On some machines or resource-constrained environments
this may prevent the notebook execution from completing at all.
Even in environments with abundant memory,
cursor tracking and interactive functionality like zoom or pan
becomes very slow when many figures are open at once.
As mentioned above, in general none of the figures will
display until the final cell has finished executing,
which is undesirable for notebooks
where execution time may be long for certain cells further down.
Another workaround is to use %matplotlib inline
instead of %matplotlib notebook.
This is unsatisfactory also:
The inline mode does not permit interactive inspection of the figure
such as cursor position values or pan and zoom.
This functionality may be desirable or essential for analysis.
The inline and notebook settings cannot in general be toggled on a per-cell basis,
so effectively this is an all-or-nothing setting.
Even if it were possible to toggle between inline and notebook,
I would prefer to only use notebook and then
close the figure in a subsequent cell,
so that I can return to the cell later
and re-run it to get the interactive controls
without needing to edit the cell.
An analogous workaround is to call savefig for each cell with a figure
and then browse the generated images with an external image viewing program.
While this allows limited zooming and panning,
it doesn't give cursor positions
and it's really not comparable to the interactive notebook plots.
Criteria and current workaround.
Here are my requirements:
The effect of "Restart & Run All" must render all figures eventually;
no figures can be left blank.
Use %matplotlib notebook for all cells
so that I can re-run the cell later and inspect the figure interactively.
Allow plt.close(fig) after each cell so that notebook
resources can be conserved.
Essentially, I would like a way to force the kernel
to render the current figure
before proceeding on to plt.close(fig).
My hope is that this is a well-known behavior
and that I've simply missed something.
Here's what I have tried so far that didn't help at all:
plt.show() at the end of a cell or between cells.
fig.show() at the end of a cell or between cells.
plt.ioff() at the end of a cell or between cells.
time.sleep(1) at the end of a cell or between cells.
plt.pause(1) at the end of a cell or between cells.
fig.canvas.draw_idle() at the end of a cell or between cells.
Doing from IPython.display import display and then display(fig)
at the end of a cell or between cells.
calling plt.close('all') at end of notebook instead of between each cell.
Currently, the best I've been able to do
is call fig.canvas.draw() in a separate cell
between the figure and the cell with plt.close(fig).
Using the example above, here's what this looks like:
%matplotlib notebook
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
fig, ax = plt.subplots()
ax.plot(x, y1);
fig.canvas.draw()
plt.close(fig)
This works reliably in smaller notebooks,
and for a while I thought this had solved the problem.
However, this doesn't always work;
particularly in large notebooks with many figures,
some of them still come out blank,
suggesting that fig.canvas.draw() adds some delay
but is not always sufficient,
perhaps due to a race condition.
Since fig.canvas.draw() is not a documented method
for making Jupyter notebooks render the current figure,
I would hesitate to describe this as a bug,
although it seems to be the closely related to this matplotlib issue,
which ultimately seems to be a Jupyter bug:
The simplest work around may be to put the input() call in the next cell or to add plt.gcf().canvas.draw() above the input call. This will still result in "dead" figures (which may not have caught up to the hi-dpi settings of your browser), but they will at least show.
I've observed this behavior in many combinations of matplotlib and Jupyter,
including matplotlib version 2.1.1 and 3.5.1
and Jupyter version 4.4.0 and 6.4.8.
I've also observed it in both Google Chrome 99.0.4844.51 and Firefox 97.0.2
and on both Windows 10 and Ubuntu 18.04.6.
Related questions (not duplicates):
Programmatically Stop Interaction for specific Figure in Jupyter notebook
Specify where in output matplotlib figures are rendered in jupyter using figure handles
Get Jupyter notebook to display matplotlib figures in real-time
Force matplotlib to fully render plots in an IPython/Jupyter notebook cell before advancing to next cell

Simple Python animation using pause command not working

I wanted to create a simple animation using plt.pause() command in python. So I tried the first example from this page. If you go there, it shows you a nice animation of a graph growing smoothly. I copy-paste it below
from matplotlib import pyplot as plt
x = []
y = []
for i in range(100):
x.append(i)
y.append(i)
# Mention x and y limits to define their range
plt.xlim(0, 100)
plt.ylim(0, 100)
# Ploting graph
plt.plot(x, y, color = 'green')
plt.pause(0.01)
plt.show()
However, it is giving me 100 different plots with an incremental increase until it completes the range. I think the problem is with figures appearing without waiting for the plt.show() command? Help me. I am using Jupiter 6.0.3 and sypder 4.1.3
Without the .show() command the plot will disappear at the completion of the code. however the animation should still be visible as it is created by the .plot() command.
I am not able to reproduce your problem using Python 3.9.7.
You can always try running the script outside of the IDE using python pathtoscript and see if that resolves the issue.
To build on #Regretful's answer, if you're running in a Jupyter notebook, use %matplotlib qt or some other non-inline backend to get the usual pop-up window, rather than static images.
No idea for Spyder, though!

Spyder plots not allowing enough user control

I am running python (3.8) via spyder (4.1.4) on a Windows laptop. I need to plot multiple series on a single graph, then do the usual things like adjusting axis limits, positioning the legend, etc. Spyder will not let me do all of this in one single plot.
For instance, the following produces two different plots:
plt.plot(seriesa,'o')
plt.plot(seriesb,'o')
and so does this:
fig = plt.figure()
ax = fig.add_subplot()
ax.plot(seriesa,'o')
ax.plot(seriesb,'o')
I can get both plots in one graph by doing the entire thing in one command:
fig = plt.figure() ; ax = fig.add_subplot() ; ax.plot(seriesa,'o') ; ax.plot(seriesb,'o')
(which seems like a hack to me, but I'll do whatever works). But then I need to adjust the y axis limits, and the command
ax.set_ylim((0,2000))
has no effect on the plot. And the command
plt.ylim((0,2000))
opens up an entirely new plot.
I tried inline plotting too (unchecking the "Mute inline plotting" menu item), with no improvement.
How do I get the control I need with my plots?
In case anyone is interested, the solution is, before doing any plotting, issue the IPython "magic command":
In [1]: %matplotlib auto
This puts spyder in a state where plots are by default placed in independent windows, as when python is run in a regular shell. Could not find this in spyder documentation. Found it in a similar stack overflow question here.

Matplotlib GUI showing even though I never called plt.show()

I am using the following functions in order...
- plt.figure()
- plt.plot()
- plt.ylim
- plt.xticks()
- figure = plt.gcf()
- figure.set_size_inches()
- plt.savefig()
I just want to save the figure as png, which I've been doing successfully. But the GUI keeps showing up and I am going to generate a bunch of graphs in one script, I don't want the GUI popping up every time one is created and slow my run time.
Does anyone know why it is showing up still?
If you are using a Jupyter Notebook, there are a number of potential solutions posted here.
To summarize, try this to disable inline output from Matplotlib:
import matplotlib as plt
plt.ioff()
Or put this at the start of a cell to prevent it from creating output:
%%capture

Matplotlib creates multiple figures after using subplots [duplicate]

In matplotlib.pyplot, what is the difference between plt.clf() and plt.close()? Will they function the same way?
I am running a loop where at the end of each iteration I am producing a figure and saving the plot. On first couple tries the plot was retaining the old figures in every subsequent plot. I'm looking for, individual plots for each iteration without the old figures, does it matter which one I use? The calculation I'm running takes a very long time and it would be very time consuming to test it out.
plt.close() will close the figure window entirely, where plt.clf() will just clear the figure - you can still paint another plot onto it.
It sounds like, for your needs, you should be preferring plt.clf(), or better yet keep a handle on the line objects themselves (they are returned in lists by plot calls) and use .set_data on those in subsequent iterations.
I think it is worth mentioning that plt.close() releases the memory, thus is preferred when generating and saving many figures in one run.
Using plt.clf() in such case will produce a warning after 20 plots (even if they are not going to be shown by plt.show()):
More than 20 figures have been opened. Figures created through the
pyplot interface (matplotlib.pyplot.figure) are retained until
explicitly closed and may consume too much memory.
plt.clf() clears the entire current figure with all its axes, but leaves the window opened, such that it may be reused for other plots.
plt.close() closes a window, which will be the current window, if not specified otherwise.
There is a slight difference between the two functions.
plt.close() - It altogether plots the graph in seperate windows,releasing
memory,retaining each window for view.
plt.clf() - We can say,it displays the graph in the same window one after other
For illustration, I have plotted two graphs with paramters year and views on X axis and Y axis each. Initially I have used closed function.it displayed the graphs in two seperate windows…
Afterwords, when I run the program with clf() it clears the graph and displays next one in same window i.e figure 1.
Here is the code snippet -
import matplotlib.pyplot as plt
year = [2001,2002,2003,2004]
Views= [12000,14000,16000,18000]
Views2 = [15000,1800,24000,84000]
plt.plot(year,Views)
plt.show()
plt.clf()
plt.plot(year,Views2)
plt.show()
plt.clf()

Categories

Resources