Animating pyplot subplots - python

I have read widely on this topic and spent several hours looking for ways to implement an animation for my plot.
I have managed an "animation" in-loop using plt.ion(), however, I can't get anything in matplotlib.animation to produce me an animated clip.
Below is a basic setup of the data I have to animate.
import matplotlib.pyplot as plt
import numpy as np
class plot_output(object):
def __init__(self):
self.fig, self.ax = plt.subplots(2,10)
plt.ion()
plt.show()
def iterate_animation(self, i, weight_list):
X = weight_list[i]
for i in range(20):
self.ax[i/10, i%10].cla()
wi = X[:,i]
wi=wi.reshape((28,28))
self.ax[i/10, i%10].axis('off')
self.ax[i/10, i%10].imshow(wi,cmap=plt.cm.gist_yarg,
interpolation='gaussian', aspect='equal')
plt.draw()
w_list = [np.random.rand(784,20) for i in range(4)]
p = plot_output()
for i in range(4):
p.iterate_animation(i=i, weight_list=w_list)

Related

How to avoid multiple plots using matplotlib pause function

I am trying to create a crude animation, where each additional data point is plotted on the same graph. The problem is that the loop is generating a new graph for each data point.
Here's the code:
x = []
y = []
for i in range(3):
x.append(random.randint(0,10))
y.append(random.randint(0,10))
plt.scatter(x,y)
plt.pause(0.1)
This resulted in 3 separate plots stacked vertically. I would like all data points to update on the same graph, creating an animation. Thanks!
I've made an adaptation of your code (in particular, in each iteration I plot only another scatter point, because plotting each time ALL the points soon becomes unbearably slow).
If you will execute this file, as I invite you to do, $ python3 slow.py, it will print 0 50 100 150 200 and, initially fast, then slower and slower, it will produce a progressive display of the data points, all in the same Axes.
I have to confess that I don't understand your problem description because it's so different from what I've seen.
import matplotlib.pyplot as plt
from matplotlib.cm import ScalarMappable
import random
def point():
return (random.randint(0,10), random.randint(0,10))
plt.xlim((-1, 11))
plt.ylim((-1, 11))
random.seed(20221229)
N = 200
cmap = plt.get_cmap('plasma')
plt.colorbar(ScalarMappable(cmap=cmap)).set_ticks((0,1), labels=("1",str(N)))
for i in range(N):
if not(i%50) : print(i)
plt.scatter(*point(), color=cmap(i/N), ec='black', s=80)
plt.pause(0.001)
print(N)
plt.show()
To update matplotlib graph you should use the module animation like Matplotlib is not very compatible with threads.
Here is an example adding a new point every 2 seconds :
import matplotlib.pyplot as pl
from matplotlib.animation import FuncAnimation
import random
datas = [0, 50]
fig = pl.figure()
ax = fig.add_subplot(1,1,1)
ax.scatter(x=datas, y=datas, marker = '+', c='red')
def update(frame):
global datas
ax.clear()
ax.scatter(x=datas, y=datas, marker = '+', c='red')
datas.append(random.randint(0,50))
animation = FuncAnimation(fig, update, interval=2000, repeat = True)
pl.show()
Interval (in milliseconds) in FuncAnimation function plays the role of the pause function you are looking for.
James, I think plt.scatter can't make an animation. All the code will be executed and then a chart with 3 points will be the result.
To avoid the generation of multiple figures you can use plt.subplots.
fig, ax = plt.subplots()
for i in range(3):
ax.scatter(x=random.randint(0,10),y= random.randint(0,10))
If you want to create some animated figure use matplotlib.animation.FuncAnimation , as in the answer of the following topic. How can i make points of a python plot appear over time?
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from IPython.display import HTML
x = np.arange(10)
y = np.random.random(10)
size = np.random.randint(150, size=10)
colors = np.random.choice(["r", "g", "b"], size=10)
fig = plt.figure()
plt.xlim(0, 10)
plt.ylim(0, 1)
graph = plt.scatter([], [])
def animate(i):
graph.set_offsets(np.vstack((x[:i+1], y[:i+1])).T)
graph.set_sizes(size[:i+1])
graph.set_facecolors(colors[:i+1])
return graph
ani = FuncAnimation(fig, animate, repeat=False, interval=200)
HTML(ani.to_jshtml())
Mark the option loop, and click in the minus or plus sing to slow or speed up the animation
Alternative approach, using FuncAnimation
from matplotlib.pyplot import Normalize, get_cmap, subplots
from matplotlib.cm import ScalarMappable
from matplotlib.animation import FuncAnimation, PillowWriter
from numpy.random import rand, seed
def update(xyn):
x, y, n = xyn
ax.scatter(x, y, color=cmap(n/N), ec='grey')
def start():
ax.set_xlim((-0.1, 1.1)) ; ax.set_ylim((-0.1, 1.1))
cb = fig.colorbar(ScalarMappable(cmap=cmap, norm=Normalize(0, 1)))
cb.set_ticks((0, 1), labels=('First', 'Last'))
def points(n):
seed(20230102)
def _():
for n_ in range(n):
yield rand(), rand(), n_
return _
fig, ax = subplots()
cmap = get_cmap('Greys')
N = 80
FuncAnimation(fig, update, points(N), start, repeat=False).save(
'scatter.gif', writer=PillowWriter())

matplotlib is returning empty plot

Click Here for the image
trying to plot an animated line chart in python. Why is this code returning to a blank white plot ? a guidance would be appreciated. And also if there is a better way to draw an animated line chart in Python, please suggest.Thank you.
from matplotlib import pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np
x_data=[]
y_data =[]
fig,ax = plt.subplots()
ax.set_xlim(0,100)
ax.set_ylim(0,12)
line, = ax.plot(0,0)
def update(i):
x_data.append(i*10)
y_data.append(i)
line.set_xdata(x_data)
line.set_ydata(y_data)
return line,
animation = FuncAnimation(fig,func = update, frames = np.arange(0,10,0.01), interval =200)
plt.show()
The code works for me, but is very slow because you have added 1000 frames at 200ms intervals, so the full animation takes 200 seconds to complete.
You need the imports at the top (and the appropriate libraries installed)
from matplotlib import pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np
x_data = []
y_data = []
fig, ax = plt.subplots()
ax.set_xlim(0, 100)
ax.set_ylim(0, 12)
line, = ax.plot(0, 0)
def update(i):
x_data.append(i*10)
y_data.append(i)
line.set_xdata(x_data)
line.set_ydata(y_data)
return line,
animation = FuncAnimation(fig,func = update, frames = np.arange(0, 10, 0.01), interval = 2)
plt.show()
I have set the interval to 2ms in the above code to show a faster animation.

jupyter notebook prevent plot from showing up on class initialization

If I write following class:
from matplotlib import pyplot as plt
import numpy as np
class FigureShowingUp:
def __init__(self):
self.fig, self.ax = plt.subplots(ncols=1, figsize=(8,6))
def make_plot(self):
x = np.linspace(0, 1)
y = np.random.normal(loc=0, scale=1, size=len(x))
self.ax.scatter(x,y)
And import it in a notebook like:
from test_fig_class import FigureShowingUp
test = FigureShowingUp()
The plot always shows up upon initialization. How do I prevent that ?
I don't use notebooks very much, but presumably you have to turn off interactive plotting:
from matplotlib import pyplot as plt; plt.ioff()
Then show the figure after making the plot:
def make_plot(self):
x = np.linspace(0, 1)
y = np.random.normal(loc=0, scale=1, size=len(x))
self.ax.scatter(x,y)
plt.show()

Most efficient way to generate many figures with matplotlib

I need to generate and save thousands of scatter plots which are essentially the same except the only thing that changes is the "y" variable. What is the fastest way to do this?
I thought about creating the Figure and Axes instance and simply clearing then between plots like this:
import matplotlib.pyplot as plt
import numpy as np
data = np.random.random((100, 1000))
x = list(range(100))
fig = plt.figure()
ax = fig.add_subplot(111)
for i in range(data.shape[1]):
ax.scatter(x, data[:,i])
fig.savefig("%d.png" % i, dpi=100)
ax.cla()
This still takes an decent amount of time, so is a better/faster way to do this? Each image in this example is about 15kb so I'm assuming writing to disk is not limiting the speed too much.
One option is to use multiple processes.
import matplotlib.pyplot as plt
import numpy as np
import multiprocessing
data = np.random.random((100, 1000))
x = list(range(100))
fig = plt.figure()
ax = fig.add_subplot(111)
def save_plot(i):
ax.scatter(x, data[:,i])
fig.savefig("%d.png" % i, dpi=100)
ax.cla()
p = multiprocessing.Pool(4)
p.map(save_plot, range(data.shape[1]))
Try creating your figures without the GUI. I find that much faster when creating and saving many figures.
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
fig = plt.Figure()
ax = fig.add_subplot(111)
ax.plot(range(5))
canvas = FigureCanvas(fig)
canvas.print_figure('sample.png')
Something similar can be found at http://www.dalkescientific.com/writings/diary/archive/2005/04/23/matplotlib_without_gui.html

python matplotlib: fast updating of a single point in a scatter

I have a large scatter, say 100k points.
I want to animate it by enlarging a single point at each time-stamp so a trail will be seen.
FuncAnimation is great for updating scatter plots, however it updates the entire scatter, so once again slow at this scale.
Is there a way to get fast performance since i only wish to update a single point at each time?
Thanks!
import matplotlib.pyplot as plt
import numpy as np
num_points = 1000
plt.ion()
fig = plt.figure()
data = np.random.uniform(low=0, high=1, size=(num_points,2))
scat_hndlrs = []
for d in data:
scat_hndlrs.append(plt.scatter(*d, s=10))
for i in range(1,num_points):
scat_hndlrs[i]._sizes=[80]
scat_hndlrs[i-1]._sizes=[10]
fig.canvas.draw()
I'd suggest to only create one scatter plot, rather than a plot instance per point and then use the animation module to modify the sizes of the PathCollection returned by plt.scatter
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
num_points = 1000
fig, ax = plt.subplots()
data = np.random.uniform(low=0, high=1, size=(num_points,2))
data_t = data.T
sizes = np.ones(num_points)*10
scat_handler = plt.scatter(data_t[0], data_t[1], s=sizes)
def animate_blit(i):
sizes = scat_handler.get_sizes()
sizes[i] = 50
sizes[(i - 1) % num_points] = 10
scat_handler.set_sizes(sizes)
return scat_handler,
ani = animation.FuncAnimation(fig, animate_blit, num_points, blit=True, repeat=True)
plt.show()

Categories

Resources