Matplotlib animation function: plots points but no animation [duplicate] - python

I'm trying to do an animation of a scatter plot where colors and size of the points changes at different stage of the animation. For data I have two numpy ndarray with an x value and y value:
data.shape = (ntime, npoint)
x.shape = (npoint)
y.shape = (npoint)
Now I want to plot a scatter plot of the type
pylab.scatter(x,y,c=data[i,:])
and create an animation over the index i. How do I do this?

Suppose you have a scatter plot, scat = ax.scatter(...), then you can
change the positions
scat.set_offsets(array)
where array is a N x 2 shaped array of x and y coordinates.
change the sizes
scat.set_sizes(array)
where array is a 1D array of sizes in points.
change the color
scat.set_array(array)
where array is a 1D array of values which will be colormapped.
Here's a quick example using the animation module.
It's slightly more complex than it has to be, but this should give you a framework to do fancier things.
(Code edited April 2019 to be compatible with current versions. For the older code see revision history)
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
class AnimatedScatter(object):
"""An animated scatter plot using matplotlib.animations.FuncAnimation."""
def __init__(self, numpoints=50):
self.numpoints = numpoints
self.stream = self.data_stream()
# Setup the figure and axes...
self.fig, self.ax = plt.subplots()
# Then setup FuncAnimation.
self.ani = animation.FuncAnimation(self.fig, self.update, interval=5,
init_func=self.setup_plot, blit=True)
def setup_plot(self):
"""Initial drawing of the scatter plot."""
x, y, s, c = next(self.stream).T
self.scat = self.ax.scatter(x, y, c=c, s=s, vmin=0, vmax=1,
cmap="jet", edgecolor="k")
self.ax.axis([-10, 10, -10, 10])
# For FuncAnimation's sake, we need to return the artist we'll be using
# Note that it expects a sequence of artists, thus the trailing comma.
return self.scat,
def data_stream(self):
"""Generate a random walk (brownian motion). Data is scaled to produce
a soft "flickering" effect."""
xy = (np.random.random((self.numpoints, 2))-0.5)*10
s, c = np.random.random((self.numpoints, 2)).T
while True:
xy += 0.03 * (np.random.random((self.numpoints, 2)) - 0.5)
s += 0.05 * (np.random.random(self.numpoints) - 0.5)
c += 0.02 * (np.random.random(self.numpoints) - 0.5)
yield np.c_[xy[:,0], xy[:,1], s, c]
def update(self, i):
"""Update the scatter plot."""
data = next(self.stream)
# Set x and y data...
self.scat.set_offsets(data[:, :2])
# Set sizes...
self.scat.set_sizes(300 * abs(data[:, 2])**1.5 + 100)
# Set colors..
self.scat.set_array(data[:, 3])
# We need to return the updated artist for FuncAnimation to draw..
# Note that it expects a sequence of artists, thus the trailing comma.
return self.scat,
if __name__ == '__main__':
a = AnimatedScatter()
plt.show()
If you're on OSX and using the OSX backend, you'll need to change blit=True to blit=False in the FuncAnimation initialization below. The OSX backend doesn't fully support blitting. The performance will suffer, but the example should run correctly on OSX with blitting disabled.
For a simpler example, which just updates the colors, have a look at the following:
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
def main():
numframes = 100
numpoints = 10
color_data = np.random.random((numframes, numpoints))
x, y, c = np.random.random((3, numpoints))
fig = plt.figure()
scat = plt.scatter(x, y, c=c, s=100)
ani = animation.FuncAnimation(fig, update_plot, frames=range(numframes),
fargs=(color_data, scat))
plt.show()
def update_plot(i, data, scat):
scat.set_array(data[i])
return scat,
main()

I wrote celluloid to make this easier. It's probably easiest to show by example:
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
from celluloid import Camera
numpoints = 10
points = np.random.random((2, numpoints))
colors = cm.rainbow(np.linspace(0, 1, numpoints))
camera = Camera(plt.figure())
for _ in range(100):
points += 0.1 * (np.random.random((2, numpoints)) - .5)
plt.scatter(*points, c=colors, s=100)
camera.snap()
anim = camera.animate(blit=True)
anim.save('scatter.mp4')
It uses ArtistAnimation under the hood. camera.snap captures the current state of the figure which is used to create the frames in the animation.
Edit: To quantify how much memory this uses I ran it through memory_profiler.
Line # Mem usage Increment Line Contents
================================================
11 65.2 MiB 65.2 MiB #profile
12 def main():
13 65.2 MiB 0.0 MiB numpoints = 10
14 65.2 MiB 0.0 MiB points = np.random.random((2, numpoints))
15 65.2 MiB 0.1 MiB colors = cm.rainbow(np.linspace(0, 1, numpoints))
16 65.9 MiB 0.6 MiB fig = plt.figure()
17 65.9 MiB 0.0 MiB camera = Camera(fig)
18 67.8 MiB 0.0 MiB for _ in range(100):
19 67.8 MiB 0.0 MiB points += 0.1 * (np.random.random((2, numpoints)) - .5)
20 67.8 MiB 1.9 MiB plt.scatter(*points, c=colors, s=100)
21 67.8 MiB 0.0 MiB camera.snap()
22 70.1 MiB 2.3 MiB anim = camera.animate(blit=True)
23 72.1 MiB 1.9 MiB anim.save('scatter.mp4')
To summarize this:
Creating 100 plots used 1.9 MiB.
Making the animation used 2.3 MiB.
This method of making animations used 4.2 MiB of memory in sum.

TL/DR: If you are having trouble with the ax.set_... methods for animating your scatter plot, you can try to just clear the plot each frame (i.e., ax.clear()) and re-plot things as desired. This is slower, but might be useful when you want to change a lot of things in a small animation.
Here is an example demonstrating this "clear" approach:
import itertools
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
# set parameters
frames = 10
points = 20
np.random.seed(42)
# create data
data = np.random.rand(points, 2)
# set how the graph will change each frame
sizes = itertools.cycle([10, 50, 150])
colors = np.random.rand(frames, points)
colormaps = itertools.cycle(['Purples', 'Blues', 'Greens', 'Oranges', 'Reds'])
markers = itertools.cycle(['o', 'v', '^', 's', 'p'])
# init the figure
fig, ax = plt.subplots(figsize=(5,5))
def update(i):
# clear the axis each frame
ax.clear()
# replot things
ax.scatter(data[:, 0], data[:, 1],
s=next(sizes),
c=colors[i, :],
cmap=next(colormaps),
marker=next(markers))
# reformat things
ax.set_xlabel('world')
ax.set_ylabel('hello')
ani = animation.FuncAnimation(fig, update, frames=frames, interval=500)
ani.save('scatter.gif', writer='pillow')
The tutorials I have seen from matplotlib and other sources do not seem to use this approach, but I have seen others (as well as myself) suggest it on this site. I see some pros & cons, but I would appreciate anyone else's thoughts:
Pros
You can avoid using the set_... methods for the scatter plot (i.e. .set_offsets(), .set_sizes(), ...), which have more obscure and less-detailed documentation (though the leading answer will get you very far here!). Plus, there are different methods for different plot types (e.g. you use set_data for lines, but not for scatter points). By clearing the axis, you determine the plotted elements each frame like any other plot in matplotlib.
Even more so, it is unclear if some properties are set-able, such as the marker type (as commented) or the colormap. I wouldn't know how to create the above plot using ax.set_..., for example, because of the marker and colormap changes. But this is pretty basic with ax.scatter().
Cons
It can be much slower; i.e. clearing and redrawing everything appears to be more expensive than the set... methods. So for large animations, this approach can be kind of painful.
Clearing the axis also clears things like the axis labels, axis limits, other text, etc. So, those sorts of formatting things need to be included in update (else they will be gone). This can be annoying if you want some things to change, but others to stay the same.
Of course, the speed is a big con. Here is an example showing the difference. Given this data:
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
np.random.seed(42)
frames = 40
x = np.arange(frames)
y = np.sin(x)
colors = itertools.cycle(['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'])
data = [(np.random.uniform(-1, 1, 10) + x[i],
np.random.uniform(-1, 1, 10) + y[i])
for i in range(frames)]
You can plot using the set... method:
fig, ax = plt.subplots()
s = ax.scatter([], [])
ax.set_xlim(-2, frames+2)
ax.set_ylim(min(y) - 1, max(y) + 1)
def update(i):
s.set_offsets(np.column_stack([data[i][0], data[i][1]]))
s.set_facecolor(next(colors))
ani = animation.FuncAnimation(fig, update, frames=frames, interval=100)
ani.save('set.gif', writer='pillow')
Or the "clear" method:
fig, ax = plt.subplots()
def update(i):
ax.clear()
ax.scatter(data[i][0], data[i][1], c=next(colors))
ax.set_xlim(-2, frames+2)
ax.set_ylim(min(y) - 1, max(y) + 1)
ani = animation.FuncAnimation(fig, update, frames=frames, interval=100)
ani.save('clear.gif', writer='pillow')
To get this figure:
Using %%time, we can see that clearing and replotting takes (more than) twice as long:
for set...: Wall time: 1.33 s
for clear: Wall time: 2.73 s
Play with the frames parameter to test this at different scales. For smaller animations (less frames/data), the time difference between the two methods is inconsequential (and for me, sometimes causes me to prefer the clearing method). But for larger cases, using set_... can save significant time.

Here is the thing. I used to a user of Qt and Matlab and I am not quite familiar with the animation system on the matplotlib.
But I do have find a way that can make any kind of animation you want just like it is in matlab. It is really powerful. No need to check the module references and you are good to plot anything you want. So I hope it can help.
The basic idea is to use the time event inside PyQt( I am sure other Gui system on the Python like wxPython and TraitUi has the same inner mechanism to make an event response. But I just don't know how). Every time a PyQt's Timer event is called I refresh the whole canvas and redraw the whole picture, I know the speed and performance may be slowly influenced but it is not that much.
Here is a little example of it:
import sys
from PyQt4 import QtGui
from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
import numpy as np
class Monitor(FigureCanvas):
def __init__(self):
self.fig = Figure()
self.ax = self.fig.add_subplot(111)
FigureCanvas.__init__(self, self.fig)
self.x = np.linspace(0,5*np.pi,400)
self.p = 0.0
self.y = np.sin(self.x+self.p)
self.line = self.ax.scatter(self.x,self.y)
self.fig.canvas.draw()
self.timer = self.startTimer(100)
def timerEvent(self, evt):
# update the height of the bars, one liner is easier
self.p += 0.1
self.y = np.sin(self.x+self.p)
self.ax.cla()
self.line = self.ax.scatter(self.x,self.y)
self.fig.canvas.draw()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
w = Monitor()
w.setWindowTitle("Convergence")
w.show()
sys.exit(app.exec_())
You can adjust the refresh speed in the
self.timer = self.startTimer(100)
I am just like you who want to use the Animated scatter plot to make a sorting animation. But I just cannot find a so called "set" function. So I refreshed the whole canva.
Hope it helps..

Why Not try this
import numpy as np
import matplotlib.pyplot as plt
x=np.random.random()
y=np.random.random()
fig, ax = plt.subplots()
ax.scatter(x,y,color='teal')
ax.scatter(y,x,color='crimson')
ax.set_xlim([0,1])
ax.set_ylim([0,1])
for i in np.arange(50):
x=np.random.random()
y=np.random.random()
bha=ax.scatter(x,y)
plt.draw()
plt.pause(0.5)
bha.remove()
plt.show()

Related

Cannot save animated gif with transparent background (matplotlib.animation)

I am trying to save an animation with a completely transparent background. Setting:
fig1 = (...,facecolor=(1,1,1,0))
Does not seem to work. Also, just as a side note, if you do that and view the plot then you get these weird transparency effects and lagging animation. Curious why that happens too, but mostly I just want the background to save as transparent.
If I then try:
line_ani.save('lines1.gif', writer='imagemagick',savefig_kwargs={"facecolor": (1,1,1,0)})
Then I get an output which does not have a transparent background and makes the lines thick. Same curiosity as above as why making the figure alpha to 0 would give this effect.
Another attempt:
fig1 = (...,facecolor=(1,1,1,0))
line_ani.save(...,savefig_kwargs={"transparent": None})
Also doesn't produce a transparent background.
If I just include the facecolor in the dictionary, then it gives the undesired line thickening bug.
line_ani.save(...,savefig_kwargs={"transparent": None,"facecolor":(1,1,1,0)})
The code is below.
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import time
from matplotlib.pyplot import figure
def update_line(num, data, line):
line.set_data(data[..., :num])
return line,
def plots():
plt.xlim(-1, 1)
plt.ylim(-1, 1)
plt.xticks([])
plt.yticks([])
plt.box()
# Since I'm calling things twice, it's convenient to define these
fs = (3,3)
inter = 100
frames = 219
lw = 0.25
alph = 0
fig1 = plt.figure(figsize=fs)
l, = plt.plot([], [],'r',linewidth =lw)
# Generate placeholder for data and set initial conditions
DAT =np.zeros((2,300))
DAT[0][0] = 0
DAT[1][0] = 1
theta=2*np.pi*(1/np.e +0.01)
# 2D Rotation Matrix
def R(x):
return [[np.cos(x),-np.sin(x)],[np.sin(x),np.cos(x)]]
# Generate the data
for i in range(len(DAT[0])):
if i < len(DAT[0])-1:
DAT[0][i+1]=DAT[0][i]*R(theta)[0][0] + DAT[1][i]*R(theta)[0][1]
DAT[1][i+1]=DAT[0][i]*R(theta)[1][0] + DAT[1][i]*R(theta)[1][1]
# Animate the data
plots()
line_ani = animation.FuncAnimation(fig1, update_line, frames, fargs=(DAT, l),
interval=inter, blit=True,repeat_delay = 2000)
plt.show()
# Save the animation
matplotlib.use("Agg")
fig1 = plt.figure(figsize=fs)
l, = plt.plot([], [],'r',linewidth = lw)
plots()
line_ani = animation.FuncAnimation(fig1, update_line, frames, fargs=(DAT, l),
interval=inter, blit=True,repeat_delay = 2000)
print("Saving animation...")
now=time.time()
line_ani.save('lines1.gif', writer='imagemagick',savefig_kwargs={"transparent": None})
later = time.time()
print("Saved in time: ", int(later-now),"seconds")
If you run the code it should show you the animation and then save it. It also will calculate the runtime.
Setting transparent = True does the trick...
line_ani.save('lines1.gif', writer='imagemagick',savefig_kwargs={"transparent": True})

matplotlib ArtistAnimation shows only one frame

I am trying to make an animation with matplotlib.animation, using the ArtistAnimation function. I have a large matrix and I want to show a different part of this matrix in a colormap using plt.imshow() in each frame. However, only one frame is shown in the animation and I don't know why. I tried to mimic the code in An animated image using a list of images. I have looked at this question and this question, but I could not find a solution there.
This is my code:
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
def make_animation(pwv_matrix, length_side):
pwv_shape = pwv_matrix.shape
fig = plt.figure()
num_frames = min(pwv_shape[0], pwv_shape[1])-length_side
y_min = int(np.round(pwv_shape[0]/2) - np.round(length_side/2))
y_max = int(np.round(pwv_shape[0]/2) + np.round(length_side/2))
x_min = 0
x_max = length_side
ims = []
for i in range(0, num_frames):
pwv_frame = pwv_matrix[x_min:x_max, y_min:y_max]
im = plt.imshow(pwv_frame, animated=True, cmap = 'viridis')
ims.append([im])
x_min += 1
x_max += 1
ani = animation.ArtistAnimation(fig, ims, interval=50, blit=True,
repeat_delay=1000)
plt.show()
x = np.linspace(0, 1, 200)
y = np.linspace(0, 1, 200)
pwv_matrix, yv = np.meshgrid(x, y)
length_side = 20 #m
make_animation(pwv_matrix, length_side)
where self.pwv_matrix is a large matrix obtained from a .dat file. Does anyone see the problem?
Thanks very much in advance!
Try adding a very small delay after the show() function with
matplotlib.pyplot.pause(interval)[source]
function or:
from time import sleep
sleep(0.05)
The actual drawing of the image occurs one you give it some processing time (in a very simplified way of explaining it)
If it does not work there are some other things you might try, let me know if it doesn't work

Problem animating matplotlib pcolor plot with polar projection using object

I am trying to create a plotting object that produces an animated matplotlib pcolor plot with a polar projection. Currently the object can either create a set of polar plots or try to create an animation of those plots.
When creating the set of polar plots (but not the animation) the object works as planned.
The animation portion of the object is based on this example, which works on my system. Unfortunately the animation as implemented in my object is not working. There is a figure and an MP4 file produced for the animation but both the figure and the too-short animation both show just some mis-shaped axes.
Does anyone have a suggestion of how to capture this figure series in an animation when embedded in an object?
I am using python 3.7, matplotlib 3.03 on a windows 10 machine
The code for the object and the code to run its instantiation are given below.
class Polar_smudge(object):
# object for creating polar contour plots
def __init__(self, azimuth_grid, range_grid):
import numpy as np
self.azimuth_grid = np.deg2rad(azimuth_grid)
self.range_grid = range_grid
self.fig = None
self.ax = None
self.images = []
#------------------------------------------------------------------
def add_data(self, value_grid):
import numpy as np
self.value_grid = value_grid
self.value_grid[self.value_grid<=0] = np.nan
#------------------------------------------------------------------
def add_figure(self, value_grid):
import matplotlib.pyplot as plt
# make and set-up figure
fig, ax = plt.subplots(subplot_kw=dict(projection='polar'))
ax.set_theta_zero_location("N")
ax.set_theta_direction(-1)
ax.set_rlim([0,10])
# make plot
cax = ax.pcolor(self.azimuth_grid, self.range_grid, value_grid, cmap=plt.cm.viridis_r)
ax.grid()
plt.show()
#------------------------------------------------------------------
def start_figure(self):
import matplotlib.pyplot as plt
# make and set-up figure
if self.fig is None :
self.fig, self.ax = plt.subplots(111, subplot_kw=dict(projection='polar'))
self.ax[0].set_theta_zero_location("N")
self.ax[0].set_theta_direction(-1)
def update_figure(self, value_grid):
import matplotlib.pyplot as plt
# make figure and add to image list
self.images.append((self.ax[0].pcolor(self.azimuth_grid, self.range_grid, value_grid, cmap=plt.cm.viridis_r),))
def end_figure(self):
import matplotlib.animation as animation
# animate the figure list
im_ani = animation.ArtistAnimation(self.fig, self.images, interval=50, repeat_delay=3000,blit=True)
im_ani.save('smudge.mp4')
#============This runs the object ====================================
import numpy as np
azimuth_bins = np.linspace(0, 360, 360)
range_bins = np.linspace(0, 10, 30)
# make plotting azim range grids
range_grid, azimuth_grid = np.meshgrid(range_bins, azimuth_bins)
# this works but isnt what I want
good_smudge = Polar_smudge(azimuth_grid,range_grid)
for ix in range(3):
val_grid = np.random.randn(360,30)
good_smudge.add_figure(val_grid)
# this doesnt work
bad_smudge = Polar_smudge(azimuth_grid,range_grid)
bad_smudge.start_figure()
for ix in range(3):
val_grid = np.random.randn(360,30)
bad_smudge.update_figure(val_grid)
bad_smudge.end_figure()
In response to the comment from Earnest, I did some further refinement and it appears that the problem is not linked to being embedded in an object, and also that increasing the number of frames (to eg. 30) does not solve the problem. The code snippet below provides a more concise demonstration of the problem (but lacks the correctly produced figure output option).
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
azimuth_bins = np.linspace(0, 360, 60)
range_bins = np.linspace(0, 10, 30)
images = []
# make plotting azim range grids
range_grid, azimuth_grid = np.meshgrid(range_bins, azimuth_bins)
fig,ax = plt.subplots(111, subplot_kw=dict(projection='polar'))
ax[0].set_theta_zero_location("N")
ax[0].set_theta_direction(-1)
for ix in range(30):
val_grid = np.random.randn(60,30)
images.append((ax[0].pcolor(azimuth_grid, range_grid, val_grid, cmap=plt.cm.viridis_r),))
# animate the figure list
im_ani = animation.ArtistAnimation(fig, images, interval=50, repeat_delay=3000,blit=False)
im_ani.save('smudge2.mp4')

Python: Animated 3D Scatterplot gets slow

My program plots the positions of particles in my file for every time step. Unfortunately it gets slower and slower although I used matplotlib.animation. Where is the bottleneck?
My data file for two particles looks like the following:
# x y z
# t1 1 2 4
# 4 1 3
# t2 4 0 4
# 3 2 9
# t3 ...
My script:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import mpl_toolkits.mplot3d.axes3d as p3
import matplotlib.animation as animation
# Number of particles
numP = 2
# Dimensions
DIM = 3
timesteps = 2000
with open('//home//data.dat', 'r') as fp:
particleData = []
for line in fp:
line = line.split()
particleData.append(line)
x = [float(item[0]) for item in particleData]
y = [float(item[1]) for item in particleData]
z = [float(item[2]) for item in particleData]
# Attaching 3D axis to the figure
fig = plt.figure()
ax = p3.Axes3D(fig)
# Setting the axes properties
border = 1
ax.set_xlim3d([-border, border])
ax.set_ylim3d([-border, border])
ax.set_zlim3d([-border, border])
def animate(i):
global x, y, z, numP
#ax.clear()
ax.set_xlim3d([-border, border])
ax.set_ylim3d([-border, border])
ax.set_zlim3d([-border, border])
idx0 = i*numP
idx1 = numP*(i+1)
ax.scatter(x[idx0:idx1],y[idx0:idx1],z[idx0:idx1])
ani = animation.FuncAnimation(fig, animate, frames=timesteps, interval=1, blit=False, repeat=False)
plt.show()
I would suggest to use pyqtgraph in this case. Citation from the docs:
Its primary goals are 1) to provide fast, interactive graphics for
displaying data (plots, video, etc.) and 2) to provide tools to aid in
rapid application development (for example, property trees such as
used in Qt Designer).
You can check out some examples after the installation:
import pyqtgraph.examples
pyqtgraph.examples.run()
This small code snippet generates 1000 random points and displays them in a 3D scatter plot by constantly updating the opacity, similar to the 3D scatter plot example in pyqtgraph.examples:
from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph.opengl as gl
import numpy as np
app = QtGui.QApplication([])
w = gl.GLViewWidget()
w.show()
g = gl.GLGridItem()
w.addItem(g)
#generate random points from -10 to 10, z-axis positive
pos = np.random.randint(-10,10,size=(1000,3))
pos[:,2] = np.abs(pos[:,2])
sp2 = gl.GLScatterPlotItem(pos=pos)
w.addItem(sp2)
#generate a color opacity gradient
color = np.zeros((pos.shape[0],4), dtype=np.float32)
color[:,0] = 1
color[:,1] = 0
color[:,2] = 0.5
color[0:100,3] = np.arange(0,100)/100.
def update():
## update volume colors
global color
color = np.roll(color,1, axis=0)
sp2.setData(color=color)
t = QtCore.QTimer()
t.timeout.connect(update)
t.start(50)
## Start Qt event loop unless running in interactive mode.
if __name__ == '__main__':
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
Small gif to give you an idea of the performance:
EDIT:
Displaying multiple points at every single time step is a little bit tricky since the gl.GLScatterPlotItem takes only (N,3)-arrays as point locations, see here. You could try to make a dictionary of ScatterPlotItems where each of them includes all time steps for a specific point. Then one would need to adapt the update function accordingly. You can find an example below where pos is an (100,10,3)-array representing 100 time steps for each point. I reduced the update time to 1000 ms for a slower animation.
from pyqtgraph.Qt import QtCore, QtGui
import pyqtgraph.opengl as gl
import numpy as np
app = QtGui.QApplication([])
w = gl.GLViewWidget()
w.show()
g = gl.GLGridItem()
w.addItem(g)
pos = np.random.randint(-10,10,size=(100,10,3))
pos[:,:,2] = np.abs(pos[:,:,2])
ScatterPlotItems = {}
for point in np.arange(10):
ScatterPlotItems[point] = gl.GLScatterPlotItem(pos=pos[:,point,:])
w.addItem(ScatterPlotItems[point])
color = np.zeros((pos.shape[0],10,4), dtype=np.float32)
color[:,:,0] = 1
color[:,:,1] = 0
color[:,:,2] = 0.5
color[0:5,:,3] = np.tile(np.arange(1,6)/5., (10,1)).T
def update():
## update volume colors
global color
for point in np.arange(10):
ScatterPlotItems[point].setData(color=color[:,point,:])
color = np.roll(color,1, axis=0)
t = QtCore.QTimer()
t.timeout.connect(update)
t.start(1000)
## Start Qt event loop unless running in interactive mode.
if __name__ == '__main__':
import sys
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
Keep in mind that in this examples, all points are shown in the scatter plot, however, the color opacity (4th dimension in the color array) is updated in every time step to get an animation. You could also try to update the points instead of the color to get better performance...
I would guess your bottleneck is calling ax.scatter and ax.set_xlim3d and similar in every frame in the animation.
Ideally, you should make a call to scatter once, then use the object returned by scatter and its set_... properties in the animate function (more details here).
I can't figure out how to do it with scatter, but if you use ax.plot(x, y, z, 'o') instead, you can then follow the demo method here.
Using some random data for x, y, z. It would work like this
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import mpl_toolkits.mplot3d.axes3d as p3
import matplotlib.animation as animation
from numpy.random import random
# Number of particles
numP = 2
# Dimensions
DIM = 3
timesteps = 2000
x, y, z = random(timesteps), random(timesteps), random(timesteps)
# Attaching 3D axis to the figure
fig = plt.figure()
ax = p3.Axes3D(fig)
# Setting the axes properties
border = 1
ax.set_xlim3d([-border, border])
ax.set_ylim3d([-border, border])
ax.set_zlim3d([-border, border])
line = ax.plot(x[:1], y[:1], z[:1], 'o')[0]
def animate(i):
global x, y, z, numP
idx1 = numP*(i+1)
# join x and y into single 2 x N array
xy_data = np.c_[x[:idx1], y[:idx1]].T
line.set_data(xy_data)
line.set_3d_properties(z[:idx1])
ani = animation.FuncAnimation(fig, animate, frames=timesteps, interval=1, blit=False, repeat=False)
plt.show()

Animate points with labels with matplotlib

I've got an animation with lines and now I want to label the points.
I tried plt.annotate() and I tried plt.text() but the labes don't move.
This is my example code:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
def update_line(num, data, line):
newData = np.array([[1+num,2+num/2,3,4-num/4,5+num],[7,4,9+num/3,2,3]])
line.set_data(newData)
plt.annotate('A0', xy=(newData[0][0],newData[1][0]))
return line,
fig1 = plt.figure()
data = np.array([[1,2,3,4,5],[7,4,9,2,3]])
l, = plt.plot([], [], 'r-')
plt.xlim(0, 20)
plt.ylim(0, 20)
plt.annotate('A0', xy=(data[0][0], data[1][0]))
# plt.text( data[0][0], data[1][0], 'A0')
line_ani = animation.FuncAnimation(fig1, update_line, 25, fargs=(data, l),
interval=200, blit=True)
plt.show()
Can you help me please?
My next step is:
I have vectors with origin in these Points. These vectors change their length and their direction in each animation step.
How can I animate these?
Without animation this works:
soa =np.array( [ [data[0][0],data[1][0],F_A0[i][0][0],F_A0[i][1][0]],
[data[0][1],data[1][1],F_B0[i][0][0],F_B0[i][1][0]],
[data[0][2],data[1][2],F_D[i][0][0],F_D[i][1][0]] ])
X,Y,U,V = zip(*soa)
ax = plt.gca()
ax.quiver(X,Y,U,V,angles='xy',scale_units='xy',scale=1)
First thanks a lot for your fast and very helpful answer!
My Vector animation problem I have solved with this:
annotation = ax.annotate("C0", xy=(data[0][2], data[1][2]), xycoords='data',
xytext=(data[0][2]+1, data[1][2]+1), textcoords='data',
arrowprops=dict(arrowstyle="->"))
and in the 'update-function' I write:
annotation.xytext = (newData[0][2], newData[1][2])
annotation.xy = (data[0][2]+num, data[1][2]+num)
to change the start and end position of the vectors (arrows).
But what is, wehn I have 100 vectors or more? It is not practicable to write:
annotation1 = ...
annotation2 = ...
.
:
annotation100 = ...
I tried with a list:
...
annotation = [annotation1, annotation2, ... , annotation100]
...
def update(num):
...
return line, annotation
and got this error:
AttributeError: 'list' object has no attribute 'axes'
What can I do? Have you any idea?
I'm coming here from this question, where an annotation should be updated that uses both xy and xytext. It appears that, in order to update the annotation correctly, one needs to set the attribute .xy of the annotation to set the position of the annotated point and to use the .set_position() method of the annotation to set the position of the annotation. Setting the .xytext attribute has no effect -- somewhat confusing in my opinion. Below a complete example:
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
fig, ax = plt.subplots()
ax.set_xlim([-1,1])
ax.set_ylim([-1,1])
L = 50
theta = np.linspace(0,2*np.pi,L)
r = np.ones_like(theta)
x = r*np.cos(theta)
y = r*np.sin(theta)
line, = ax.plot(1,0, 'ro')
annotation = ax.annotate(
'annotation', xy=(1,0), xytext=(-1,0),
arrowprops = {'arrowstyle': "->"}
)
def update(i):
new_x = x[i%L]
new_y = y[i%L]
line.set_data(new_x,new_y)
##annotation.xytext = (-new_x,-new_y) <-- does not work
annotation.set_position((-new_x,-new_y))
annotation.xy = (new_x,new_y)
return line, annotation
ani = animation.FuncAnimation(
fig, update, interval = 500, blit = False
)
plt.show()
The result looks something like this:
In case that versions matter, this code has been tested on Python 2.7 and 3.6 with matplotlib version 2.1.1, and in both cases setting .xytext had no effect, while .set_position() and .xy worked as expected.
You have the return all objects that changed from your update function. So since your annotation changed it's position you should return it also:
line.set_data(newData)
annotation = plt.annotate('A0', xy=(newData[0][0],newData[1][0]))
return line, annotation
You can read more about matplotlib animations in this tutorial
You should also specify the init function so that the FuncAnimation knows which elements to remove from the plot when redrawing on the first update. So the full example would be:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
# Create initial data
data = np.array([[1,2,3,4,5], [7,4,9,2,3]])
# Create figure and axes
fig = plt.figure()
ax = plt.axes(xlim=(0, 20), ylim=(0, 20))
# Create initial objects
line, = ax.plot([], [], 'r-')
annotation = ax.annotate('A0', xy=(data[0][0], data[1][0]))
annotation.set_animated(True)
# Create the init function that returns the objects
# that will change during the animation process
def init():
return line, annotation
# Create the update function that returns all the
# objects that have changed
def update(num):
newData = np.array([[1 + num, 2 + num / 2, 3, 4 - num / 4, 5 + num],
[7, 4, 9 + num / 3, 2, 3]])
line.set_data(newData)
# This is not working i 1.2.1
# annotation.set_position((newData[0][0], newData[1][0]))
annotation.xytext = (newData[0][0], newData[1][0])
return line, annotation
anim = animation.FuncAnimation(fig, update, frames=25, init_func=init,
interval=200, blit=True)
plt.show()
I think I figured out how to animate multiple annotations through a list. First you just create your annotations list:
for i in range(0,len(someMatrix)):
annotations.append(ax.annotate(str(i), xy=(someMatrix.item(0,i), someMatrix.item(1,i))))
Then in your "animate" function you do as you have already written:
for num, annot in enumerate(annotations):
annot.set_position((someMatrix.item((time,num)), someMatrix.item((time,num))))
(You can write it as a traditional for loop as well if you don't like the enumerate way). Don't forget to return the whole annotations list in your return statement.
Then the important thing is to set "blit=False" in your FuncAnimation:
animation.FuncAnimation(fig, animate, frames="yourframecount",
interval="yourpreferredinterval", blit=False, init_func=init)
It is good to point out that blit=False might slow things down. But its unfortunately the only way I could get animation of annotations in lists to work...

Categories

Resources