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?
Related
Matplotlib offers these functions:
cla() # Clear axis
clf() # Clear figure
close() # Close a figure window
When should I use each function and what exactly does it do?
They all do different things, since matplotlib uses a hierarchical order in which a figure window contains a figure which may consist of many axes. Additionally, there are functions from the pyplot interface and there are methods on the Figure class. I will discuss both cases below.
pyplot interface
pyplot is a module that collects a couple of functions that allow matplotlib to be used in a functional manner. I here assume that pyplot has been imported as import matplotlib.pyplot as plt.
In this case, there are three different commands that remove stuff:
See matplotlib.pyplot Functions:
plt.cla() clears an axis, i.e. the currently active axis in the current figure. It leaves the other axes untouched.
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.
Which functions suits you best depends thus on your use-case.
The close() function furthermore allows one to specify which window should be closed. The argument can either be a number or name given to a window when it was created using figure(number_or_name) or it can be a figure instance fig obtained, i.e., usingfig = figure(). If no argument is given to close(), the currently active window will be closed. Furthermore, there is the syntax close('all'), which closes all figures.
methods of the Figure class
Additionally, the Figure class provides methods for clearing figures.
I'll assume in the following that fig is an instance of a Figure:
fig.clf() clears the entire figure. This call is equivalent to plt.clf() only if fig is the current figure.
fig.clear() is a synonym for fig.clf()
Note that even del fig will not close the associated figure window. As far as I know the only way to close a figure window is using plt.close(fig) as described above.
There is just a caveat that I discovered today.
If you have a function that is calling a plot a lot of times you better use plt.close(fig) instead of fig.clf() somehow the first does not accumulate in memory. In short if memory is a concern use plt.close(fig) (Although it seems that there are better ways, go to the end of this comment for relevant links).
So the the following script will produce an empty list:
for i in range(5):
fig = plot_figure()
plt.close(fig)
# This returns a list with all figure numbers available
print(plt.get_fignums())
Whereas this one will produce a list with five figures on it.
for i in range(5):
fig = plot_figure()
fig.clf()
# This returns a list with all figure numbers available
print(plt.get_fignums())
From the documentation above is not clear to me what is the difference between closing a figure and closing a window. Maybe that will clarify.
If you want to try a complete script there you have:
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(1000)
y = np.sin(x)
for i in range(5):
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
ax.plot(x, y)
plt.close(fig)
print(plt.get_fignums())
for i in range(5):
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
ax.plot(x, y)
fig.clf()
print(plt.get_fignums())
If memory is a concern somebody already posted a work-around in SO see:
Create a figure that is reference counted
plt.cla() means clear current axis
plt.clf() means clear current figure
also, there's plt.gca() (get current axis) and plt.gcf() (get current figure)
Read more here: Matplotlib, Pyplot, Pylab etc: What's the difference between these and when to use each?
I am creating an interactive matplotlib figure. It is interactive in the sense that when I press a letter 'i' on the keyboard, an image is loaded into the figure. In a second step I would like to remove the image again, while I am still showing the plot. I really don't want to redraw the plot, as it takes too much time.
I am using plt.imshow(img) to display the image. So far I have not come across an equivalent that closes the image. I can only close the complete figure. Does anyone know of such a function?
PLT is tricky. In general, plt.COMMANDS apply to the most recently created object and don't offer much control over the figure, axis, plots, etc. If you label your global plt variables, it makes it more clear.
import matplotlib.pyplot as plt
X = [1,2,3,4]
Y = [1,1,3,3.5]
figure = plt.figure() #Creates the window.
axis = figure.add_subplot(1,1,1) #Creates a graphic inside the window.
axis.grid(True) #Change the axis.
plots = axis.plot(X,Y) #Put a plot in the axis.
figure.show() #Open the window.
Note, that plots is a list, since arrays, X and Y, could have generated many plots. Now, lets delete the plot while the window is open and watch it disappear, then insert the plot back into the axis.
plots[0].remove()
plots = axis.plot(X,Y)
In your case, you are working with axis.imshow() instead of axis.plot().
What I want to do is to dynamically change a plot so that I can see it update as Python is executing its code. Here is what I've come up with:
import matplotlib.pyplot as plt
import time
def plotResult(x,y):
plt.plot(x,y)
plt.figure()
for i in range(5):
x = [2,3,5*i]
y = [1,2,3]
plotResult(x,y)
time.sleep(1)
What I want is for each call of "plotResult" to erase the previous plot with the new plot in its place. What I end up with instead is each plot on top of each other. I'm using time.sleep here because I want some time to look at the newly plotted result before it gets erased and replaced with a new plot. I guess I'm essentially trying to create an animation here with each frame being a call to plotResult.
I'm going to do this for a code with a much longer execution time, so I don't want to have to wait until the code is done being executed to view the animation. Please let me know if you know of a way to do this.
Read about the FuncAnimation class. It repeatedly calls a function to update each frame of the animation.
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.
As a result of a full day of trial and error, I'm posting my findings as a help to anyone else who may come across this problem.
For the last couple days, I've been trying to simulate a real-time plot of some radar data from a netCDF file to work with a GUI I'm building for a school project. The first thing I tried was a simple redrawing of the data using the 'interactive mode' of matplotlib, as follows:
import matplotlib.pylab as plt
fig = plt.figure()
plt.ion() #Interactive mode on
for i in range(2,155): #Set to the number of rows in your quadmesh, start at 2 for overlap
plt.hold(True)
print i
#Please note: To use this example you must compute X, Y, and C previously.
#Here I take a slice of the data I'm plotting - if this were a real-time
#plot, you would insert the new data to be plotted here.
temp = plt.pcolormesh(X[i-2:i], Y[i-2:i], C[i-2:i])
plt.draw()
plt.pause(.001) #You must use plt.pause or the figure will freeze
plt.hold(False)
plt.ioff() #Interactive mode off
While this technically works, it also disables the zoom functions, as well as pan, and well, everything!
For a radar display plot, this was unacceptable. See my solution to this below.
So I started looking into the matplotlib animation API, hoping to find a solution. The animation did turn out to be exactly what I was looking for, although its use with a QuadMesh object in slices was not exactly documented. This is what I eventually came up with:
import matplotlib.pylab as plt
from matplotlib import animation
fig = plt.figure()
plt.hold(True)
#We need to prime the pump, so to speak and create a quadmesh for plt to work with
plt.pcolormesh(X[0:1], Y[0:1], C[0:1])
anim = animation.FuncAnimation(fig, animate, frames = range(2,155), blit = False)
plt.show()
plt.hold(False)
def animate( self, i):
plt.title('Ray: %.2f'%i)
#This is where new data is inserted into the plot.
plt.pcolormesh(X[i-2:i], Y[i-2:i], C[i-2:i])
Note that blit must be False! Otherwise it will yell at you about a QuadMesh object not being 'iterable'.
I don't have access to the radar yet, so I haven't been able to test this against live data streams, but for a static file, it has worked great thus far. While the data is being plotted, I can zoom and pan with the animation.
Good luck with your own animation/plotting ambitions!