Python Matplotlib: Keep shared x axis while updating figure in loop - python-3.x

I want to update a plot every x seconds and keep the shared x axis. The problem is that when using a cla() command the sharedx gets lost and when not using the cla(), the plot is not updated, but "overplotted", as in this minimal example:
import matplotlib.pyplot as plt
import pandas as pd
data = pd.DataFrame([[1,2,1],[3,1,3]], index = [1,2])
n_plots = data.shape[1]
fig, axs = plt.subplots(n_plots , 1, sharex = True)
axs = axs.ravel()
while True:
for i in range(n_plots):
#axs[i].cla()
axs[i].plot(data.iloc[:,i])
axs[i].grid()
plt.tight_layout()
plt.draw()
plt.pause(5)
data = pd.concat([data,data]).reset_index(drop = True)
The behaviour can be seen by uncommenting the axs[i].cla() line.
So the question is:
How can I update a plot (without predefined number of subplots) in a while loop (I want to update some data) and keep a shared x-axis?
Thanks in advance

First, to produce animations with matplotlib you should have a look at FuncAnimation. You'll find lots of posts on SO on this subject, for instance: Dynamically updating plot in matplotlib
The general guideline is to not repetitively call plt.plot() but instead use the set_data() function of the Line2D object returned by plot(). In other words, in a first part of your code you instantiate an object with an empty plot
l, = plt.plot([],[])
and then, whenever you need to update your plot, you keep the same object (do not clear the axes, do not make a new plot() call), and simply update its content:
l.set_data(X,Y)
# alternatively, if only Y-data changes
l.set_ydata(Y) # make sure that len(Y)==len(l.get_xdata())!
EDIT: Here is a minimal example showing 3 axes with shared x-axis, like you are trying to do
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
N_points_to_display = 20
N_lines = 3
line_colors = ['r','g','b']
fig, axs = plt.subplots(N_lines, 1, sharex=True)
my_lines = []
for ax,c in zip(axs,line_colors):
l, = ax.plot([], [], c, animated=True)
my_lines.append(l)
def init():
for ax in axs:
ax.set_xlim((0,N_points_to_display))
ax.set_ylim((-1.5,1.5))
return my_lines
def update(frame):
#generates a random number to simulate new incoming data
new_data = np.random.random(size=(N_lines,))
for l,datum in zip(my_lines,new_data):
xdata, ydata = l.get_data()
ydata = np.append(ydata, datum)
#keep only the last N_points_to_display
ydata = ydata[-N_points_to_display:]
xdata = range(0,len(ydata))
# update the data in the Line2D object
l.set_data(xdata,ydata)
return my_lines
anim = FuncAnimation(fig, update, interval=200,
init_func=init, blit=True)

Related

How to update scatter with plot?

I am updating the graph, but can't join to it the scatter, could someone help me, please? I don't understand, how to realize it.
from matplotlib import pyplot as plt
from matplotlib.animation import FuncAnimation
fig = plt.figure()
ax = fig.add_subplot()
line = ax.plot([],[])[0]
x = []
y = []
scat = ax.scatter(x,y,c='Red')
def animate(i):
x.append(i)
y.append((-1)**i)
line.set_data(x, y)
ax.relim()
ax.autoscale_view()
return [line]
anim = FuncAnimation(fig, animate, frames=200, interval=100, blit=True)
plt.show()
I want to add dotes and their coordinates change only in X, Y should be 0.
Several problems have to be addressed here. You have to update the scatter plot, which is a PathCollection that is updated via .set_offsets(). This is in turn requires the x-y data to be in an array of the form (N, 2). We could combine the two lists x, y in every animation loop to such an array but this would be time-consuming. Instead, we declare the numpy array in advance and update it in the loop.
As for axes labels, you might have noticed that they are not updated in your animation. The reason for this is that you use blitting, which suppresses redrawing all artists that are considered unchanged. So, if you don't want to take care manually of the axis limits, you have to turn off blitting.
from matplotlib import pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np
fig, ax = plt.subplots()
line, = ax.plot([],[])
scat = ax.scatter([], [], c='Red')
n=200
#prepare array for data storage
pos = np.zeros((n, 2))
def animate(i):
#calculate new x- and y-values
pos[i, 0] = i
pos[i, 1] = (-1)**i
#update line data
line.set_data(pos[:i, 0], pos[:i, 1])
#update scatter plot data
scat.set_offsets(pos[:i, :])
#update axis view - works only if blit is False
ax.relim()
ax.autoscale_view()
return scat, line
anim = FuncAnimation(fig, animate, frames=n, interval=100, blit=False)
plt.show()
Sample output:

Plotting a dot that moves along side a dispersive wave?

How would I go on about plotting a dot that moves along a wave pack/superposition. I saw this on the website and wanted to try for myself.https://blog.soton.ac.uk/soundwaves/further-concepts/2-dispersive-waves/. So I know how to animate a superpositon of two sine waves. But how would I plot a dot that moves along it? I won't post my entire code, but it looks somewhat like this
import matplotlib.pyplot as plt
import numpy as np
N = 1000
x = np.linspace(0,100,N)
wave1 = np.sin(2*x)
wave2 = np.sin(3*x)
sWave = wave1+wave2
plt.plot(x,sWave)
plt.ion()
for t in np.arange(0,400):
sWave.set_ydata(sWave)
plt.draw()
plt.pause(.1)
plt.ioff()
plt.show()
Note that this is just a quick draft of my original code.
You can add a scatter and update its data in a loop by using .set_offsets().
import matplotlib.pyplot as plt
import numpy as np
N = 1000
x = np.linspace(0, 100, N)
wave1 = np.sin(2*x)
wave2 = np.sin(3*x)
sWave = wave1 + wave2
fig, ax = plt.subplots()
ax.plot(x, sWave)
scatter = ax.scatter([], [], facecolor="red") # Initialize an empty scatter.
for t in range(N):
scatter.set_offsets((x[t], sWave[t])) # Modify that scatter's data.
fig.canvas.draw()
plt.pause(.001)

Matplotlib get all axes artist objects for ArtistAnimation?

I am trying to make an animation using ArtistAnimation like this:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig, ax = plt.subplots()
ims = []
for i in range(60):
x = np.linspace(0,i,1000)
y = np.sin(x)
im = ax.plot(x,y, color='black')
ims.append(im)
ani = animation.ArtistAnimation(fig, ims, interval=50, blit=True,
repeat_delay=1000)
plt.show()
This animates a sine wave growing across the figure. Currently I'm just adding the Lines2D object returned by ax.plot() to ims. However, I would like to potentially draw multiple overlapping plots on the Axes and adjust the title, legend and x-axis range for each frame. How do I get an object that I can add to ims after plotting and making all the changes I want for each frame?
The list you supply to ArtistAnimation should be a list of lists of artists, one list per frame.
artist_list = [[line1a, line1b, title1], [line2a, line2b, title2], ...]
where the first list is shown in the first frame, the second list in the second frame etc.
The reason your code works is that ax.plot returns a list of lines (in your case only a list of a single line).
In any case, the following might be a more understandable version of your code where an additional text is animated.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig, ax = plt.subplots()
artist_list = []
for i in range(60):
x = np.linspace(0,i,1000)
y = np.sin(x)
line, = ax.plot(x,y, color='black')
text = ax.text(i,0,i)
artist_list.append([line, text])
ani = animation.ArtistAnimation(fig, artist_list, interval=50, blit=True,
repeat_delay=1000)
plt.show()
In general, it will be hard to animate changing axes limits with ArtistAnimation, so if that is an ultimate goal consider using a FuncAnimation instead.

Line on animated matplotlib graph not showing up

I'm not sure why a graph line isn't showing on my widget.
The x-axis is moving with time as it should, and when I print the y-values that should be plotted (Temp), new values come up as expected. However, no line/points show up on the plot.
I've had a similar issue with a plot in the past, which was solved by changing the style of the points plotted (i.e. using 'r*' to make the points visible red stars). I'm not sure how to implement the same sort of thing in this code.
import matplotlib.pyplot as plt
import numpy
import datetime
import matplotlib.animation as animation
fig = plt.figure()
ax1 = fig.add_subplot(1,1,1)
def animate(i):
Time = []
Temp = []
x = datetime.datetime.now()
y = numpy.random.randint(48,52)
Time.append(x)
Temp.append(int(y))
ax1.plot(Time,Temp)
print(Temp)
ani = animation.FuncAnimation(fig,animate, interval=1000)
plt.show()
just put Time and Temp out of function animate
Time = []
Temp = []
def animate(i):
x = datetime.datetime.now()
y = numpy.random.randint(48,52)
Time.append(x)
Temp.append(int(y))
ax1.plot(Time,Temp)
print(Temp)
You can add the marker and it shows the points:
ax1.plot(Time,Temp, marker="s")

Python: Matplotlib: update graph by time in second

I have a series to plot at y-axis.
y = [3,4,5,1,4,7,4,7,1,9]
However, I want to plot it by recent time by second. I've done it like this,
import time
def xtime():
t = time.strftime("%H%M%S")
t = int(t)
xtime = [t]
while xtime:
t = time.strftime("%H%M%S")
t = int(t)
xtime.extend([t])
time.sleep(1)
I'm having problem when I want to plot each one of the number at y by each second. Please correct my code here,
import time
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig = plt.figure()
def animate(i):
x = xtime()
y = [3,4,5,1,4,7,4,7,1,9]
plt.plot(x,y)
ani = animation.FuncAnimation(fig, animate, interval=1000)
plt.show()
xtime function is referred as code at first.
Thanks!
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
# Y data
ydata = [3,4,5,1,4,7,4,7,1,9]
# how many points
N = len(ydata)
# make x data
xdata = np.arange(N)
def animate(i):
# update the date in our Line2D artist
# note that when run this will look at the global namespace for
# an object called `ln` which we will define later
ln.set_data(xdata[:i], ydata[:i])
# return the updated artist for the blitting
return ln,
# make our figure and axes
fig, ax = plt.subplots()
# make the artist we will be using. Note this was used in `animate`
ln, = ax.plot([], [], animated=True)
# set the axes limits
ax.set_xlim(0, N)
ax.set_ylim(0, 10)
# run the animation. Keeping a ref to the animation object is important
# as if it gets garbage collected it takes you timer and callbacks with it
ani = animation.FuncAnimation(fig, animate, frames=N, interval=1000, blit=True)

Resources