Dynamically updated plots in python, how to save image at the end - python

Background
With the help of the following, I have created a dynamically updating plot
Dynamically updating plot in matplotlib
Updating a plot in python's matplotlib
How to update a plot in matplotlib?
Plot code
plt.ion()
fig_analysis = plt.figure()
for i in range(5):
ax = fig_analysis.add_subplot(211)
l1, = ax.plot(np.linspace(0, 200, simulation_time), variable_1)
ax.set_ylabel(r'$Variable\ 1$')
ax = fig_analysis.add_subplot(212)
l2, = ax.plot(np.linspace(0, 200, simulation_time), variable_2)
ax.set_ylabel(r'$Variable\ 2$')
ax.set_xlabel(r'$years$')
fig_analysis.canvas.draw()
plt.pause(0.5)
Behaviour
This code creates a plot and updates it. However, it closes the final plot after completion of the loop
Question
How should I modify the code to ensure that at the end of the loop, the program doesn't close the plot window, and I can save the image as I want.
Manual solution
One of the ways of achieving this is to manually pause the program. However, as runtime of my program is not fixed, it is difficult to implement this strategy.

Add
plt.ioff() # turn interactive mode off
plt.show()
at the end of the code.

Related

How to call plt.subplots() without opening GUI?

I want to run a deep learning training that uses matplotlib internally for creating graphs and then use the created figure and dump it to disk as image.
The dumping part is done for me using tensorboardX and that part works.
The problem:
plt.subplots(4, 1) opens a window, which slows down my program, especially when not all figures get closed upon request.
I want the same functionality without GUI:
Create the subplots.
Plot into them.
Have a figure object with the plots drawn (and not shown on screen) and do whatever I want with it.
Sample code (in pytorch-lightning) for context only, I don't expect anyone to have to reproduce this, as the question is clear.
tb = self.logger.experiment
fig, axs = plt.subplots(n_plots, 1)
for sample_idx in range(n_plots):
for feature_idx, (orig_feature, recon_feature) in enumerate(zip(orig_batch_view[sample_idx + first_sample_idx, :, :], recon_batch_view[sample_idx + first_sample_idx, :, :])):
i = feature_idx
if i > 0: continue # or scale issues don't allow informative plotting
axs[sample_idx].plot(orig_feature.detach().cpu().numpy(), label=f'orig{i}, sample{sample_idx}')
axs[sample_idx].plot(recon_feature.detach().cpu().numpy(), label=f'recon{i}, sample{sample_idx}')
axs[sample_idx].legend(loc='upper left')
tb.add_figure(f"{mode}recon_vs_orig", fig, global_step=self.current_epoch, close=True)
Can it be done?
Inspired by #Mr.T, I investigated some more and got a similar solution with different syntax.
matplotlib.use('Agg') # turn off gui
fig, axs = plt.subplots(n_plots, 1)
...
matplotlib.use('QT4Agg') # turn on gui

Efficient way to update matplotlib figure in gui?

My application is receiving data over the network at about 30fps, and needs to update a horizontal bar chart dynamically based on this new data.
I am using a matplotlib figure inside a tkinter window for this purpose. Profiling my code has shown that a major bottleneck in my code is the updating of this figure.
A simplified version of the code is given below:
def update_bars(self):
"""
Updates a horizontal bar chart
"""
for bar, new_d in zip(self.bars, self.latest_data):
bar.set_width(new_d)
self.figure.draw()
The lag I am experiencing is significant, and grows quickly over time. Is there a more efficient way to update the matplotlib figure? Any help would be great.
EDIT: I will be looking at this for possible speedup tips. I'll update if I get something working.
You can update the data of the plot objects. But to some extent, you can't change the shape of the plot, you can manually reset the x and y axis limits.
e.g.
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 6*np.pi, 100)
y = np.sin(x)
plt.ion()
fig = plt.figure()
ax = fig.add_subplot(111)
line1, = ax.plot(x, y)
for phase in np.linspace(0, 10*np.pi, 500):
line1.set_ydata(np.sin(x + phase))
# render the figure
# re-draw itself the next time
# some GUI backends add this to the GUI frameworks event loop.
fig.canvas.draw()
fig.canvas.flush_events() # flush the GUI events
flush_events
Flush the GUI events for the figure. Implemented only for backends
with GUIs.
flush_events make sure that the GUI framework has a chance to run its event loop and clear any GUI events.Sometimes this needs to be in a try/except block because the default implementation of this method is to raise NotImplementedError.
draw will render the figure,in the above code,maybe remove draw still work.But to some extent they're different.

Python matplotlib: how to plot realtime over plotted graph

Here I have plotted a line chart with two lists
import random as random
import matplotlib.pyplot as plt
lis1=random.sample(range(1, 100), 10)
lis2 = random.sample(range(1, 100), 10)
plt.plot(range(0,len(lis1), 1), lis1)
plt.plot(range(0,len(lis2), 1), lis2)
plt.show()
Now, I'm getting a third list from Arduino in realtime. My question is how to plot that third list/line over this plot without redrawing entire chart.
EDIT: Third list is something like this
import time
lis3 =[]
for i in range(10):
lis3.append(i)
time.sleep(1)
plt.show() will display the current chart that you're working on whereas plt.draw() will re-draw the figure. This essentially allows you to change the graph as your data changes
The plt.draw docs state:
This is used in interactive mode to update a figure that has been altered using one or more plot object method calls; it is not needed if figure modification is done entirely with pyplot functions, if a sequence of modifications ends with a pyplot function, or if matplotlib is in non-interactive mode and the sequence of modifications ends with show() or savefig().
have a look at the following post: When to use cla(), clf() or close() for clearing a plot in matplotlib?
i think you can clear the figure by using plt.clear() in an timer event. Re-drawing can be done by using the plt.draw() function. Because of the realtime data you have to have a function which is called after a certain delay. There i would call this plt.clear() or plt.draw() function.
Afterwards you have to re fill the lists or make a new list to draw the third line.
I don't know a better solution and maybe that's not what you want, because it's some keind of re-drawing but i hope that this is useful for you!
Also have a look at:
Dynamically updating plot in matplotlib
How to update a plot in matplotlib?

Multiple plot windows from plotting function in ipython?

I have a function that does my plotting. Something like:
def myplot(data):
fig, ax = plt.subplots(10,10, figsize=(18,12))
for i in range(10):
ax[i/10, i%10].imshow(data[i/10, i%10],cmap=plt.cm.gist_yarg,
interpolation='nearest', aspect='equal')
plt.show()
#print the fist plot
myplot(data1)
#print another
myplot(data2)
When I run the script using ipython myscript.py it will pause after myplot(data1) and won't resume until I close the plot window. How can I keep multiple windows open?
Sure, just remove your plt.show() from the loop and put it at the end of your script. Every time you create a new figure (as in the plt.subplots(...) function), a new window is created.

How can I show figures separately in matplotlib?

Say that I have two figures in matplotlib, with one plot per figure:
import matplotlib.pyplot as plt
f1 = plt.figure()
plt.plot(range(0,10))
f2 = plt.figure()
plt.plot(range(10,20))
Then I show both in one shot
plt.show()
Is there a way to show them separately, i.e. to show just f1?
Or better: how can I manage the figures separately like in the following 'wishful' code (that doesn't work):
f1 = plt.figure()
f1.plot(range(0,10))
f1.show()
Sure. Add an Axes using add_subplot. (Edited import.) (Edited show.)
import matplotlib.pyplot as plt
f1 = plt.figure()
f2 = plt.figure()
ax1 = f1.add_subplot(111)
ax1.plot(range(0,10))
ax2 = f2.add_subplot(111)
ax2.plot(range(10,20))
plt.show()
Alternatively, use add_axes.
ax1 = f1.add_axes([0.1,0.1,0.8,0.8])
ax1.plot(range(0,10))
ax2 = f2.add_axes([0.1,0.1,0.8,0.8])
ax2.plot(range(10,20))
With Matplotlib prior to version 1.0.1, show() should only be called once per program, even if it seems to work within certain environments (some backends, on some platforms, etc.).
The relevant drawing function is actually draw():
import matplotlib.pyplot as plt
plt.plot(range(10)) # Creates the plot. No need to save the current figure.
plt.draw() # Draws, but does not block
raw_input() # This shows the first figure "separately" (by waiting for "enter").
plt.figure() # New window, if needed. No need to save it, as pyplot uses the concept of current figure
plt.plot(range(10, 20))
plt.draw()
# raw_input() # If you need to wait here too...
# (...)
# Only at the end of your program:
plt.show() # blocks
It is important to recognize that show() is an infinite loop, designed to handle events in the various figures (resize, etc.). Note that in principle, the calls to draw() are optional if you call matplotlib.ion() at the beginning of your script (I have seen this fail on some platforms and backends, though).
I don't think that Matplotlib offers a mechanism for creating a figure and optionally displaying it; this means that all figures created with figure() will be displayed. If you only need to sequentially display separate figures (either in the same window or not), you can do like in the above code.
Now, the above solution might be sufficient in simple cases, and for some Matplotlib backends. Some backends are nice enough to let you interact with the first figure even though you have not called show(). But, as far as I understand, they do not have to be nice. The most robust approach would be to launch each figure drawing in a separate thread, with a final show() in each thread. I believe that this is essentially what IPython does.
The above code should be sufficient most of the time.
PS: now, with Matplotlib version 1.0.1+, show() can be called multiple times (with most backends).
I think I am a bit late to the party but...
In my opinion, what you need is the object oriented API of matplotlib. In matplotlib 1.4.2 and using IPython 2.4.1 with Qt4Agg backend, I can do the following:
import matplotlib.pyplot as plt
fig, ax = plt.subplots(1) # Creates figure fig and add an axes, ax.
fig2, ax2 = plt.subplots(1) # Another figure
ax.plot(range(20)) #Add a straight line to the axes of the first figure.
ax2.plot(range(100)) #Add a straight line to the axes of the first figure.
fig.show() #Only shows figure 1 and removes it from the "current" stack.
fig2.show() #Only shows figure 2 and removes it from the "current" stack.
plt.show() #Does not show anything, because there is nothing in the "current" stack.
fig.show() # Shows figure 1 again. You can show it as many times as you want.
In this case plt.show() shows anything in the "current" stack. You can specify figure.show() ONLY if you are using a GUI backend (e.g. Qt4Agg). Otherwise, I think you will need to really dig down into the guts of matplotlib to monkeypatch a solution.
Remember that most (all?) plt.* functions are just shortcuts and aliases for figure and axes methods. They are very useful for sequential programing, but you will find blocking walls very soon if you plan to use them in a more complex way.
Perhaps you need to read about interactive usage of Matplotlib. However, if you are going to build an app, you should be using the API and embedding the figures in the windows of your chosen GUI toolkit (see examples/embedding_in_tk.py, etc).
None of the above solutions seems to work in my case, with matplotlib 3.1.0 and Python 3.7.3. Either both the figures show up on calling show() or none show up in different answers posted above.
Building upon #Ivan's answer, and taking hint from here, the following seemed to work well for me:
import matplotlib.pyplot as plt
fig, ax = plt.subplots(1) # Creates figure fig and add an axes, ax.
fig2, ax2 = plt.subplots(1) # Another figure
ax.plot(range(20)) #Add a straight line to the axes of the first figure.
ax2.plot(range(100)) #Add a straight line to the axes of the first figure.
# plt.close(fig) # For not showing fig
plt.close(fig2) # For not showing fig2
plt.show()
As #arpanmangal, the solutions above do not work for me (matplotlib 3.0.3, python 3.5.2).
It seems that using .show() in a figure, e.g., figure.show(), is not recommended, because this method does not manage a GUI event loop and therefore the figure is just shown briefly. (See figure.show() documentation). However, I do not find any another way to show only a figure.
In my solution I get to prevent the figure for instantly closing by using click events. We do not have to close the figure — closing the figure deletes it.
I present two options:
- waitforbuttonpress(timeout=-1) will close the figure window when clicking on the figure, so we cannot use some window functions like zooming.
- ginput(n=-1,show_clicks=False) will wait until we close the window, but it releases an error :-.
Example:
import matplotlib.pyplot as plt
fig1, ax1 = plt.subplots(1) # Creates figure fig1 and add an axes, ax1
fig2, ax2 = plt.subplots(1) # Another figure fig2 and add an axes, ax2
ax1.plot(range(20),c='red') #Add a red straight line to the axes of fig1.
ax2.plot(range(100),c='blue') #Add a blue straight line to the axes of fig2.
#Option1: This command will hold the window of fig2 open until you click on the figure
fig2.waitforbuttonpress(timeout=-1) #Alternatively, use fig1
#Option2: This command will hold the window open until you close the window, but
#it releases an error.
#fig2.ginput(n=-1,show_clicks=False) #Alternatively, use fig1
#We show only fig2
fig2.show() #Alternatively, use fig1
As of November 2020, in order to show one figure at a time, the following works:
import matplotlib.pyplot as plt
f1, ax1 = plt.subplots()
ax1.plot(range(0,10))
f1.show()
input("Close the figure and press a key to continue")
f2, ax2 = plt.subplots()
ax2.plot(range(10,20))
f2.show()
input("Close the figure and press a key to continue")
The call to input() prevents the figure from opening and closing immediately.

Categories