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

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()

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.

How to smoothly plot the moving dot

I want to plot a moving dot from left to right. Here's my code:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.animation import FuncAnimation
Acc_11 = [0,1,2,3,4,5,6,7,8]
Acc_12 = [4,4,4,4,4,4,4,4,4]
fig = plt.figure()
axes = fig.add_subplot(111, autoscale_on=False)
axes.set_xlim(min(Acc_11), max(Acc_11))
axes.set_ylim(0, 8)
point, = axes.plot([Acc_11[0]],[Acc_12[0]], 'go')
def ani(coords):
point.set_data([coords[0]],[coords[1]])
return point,
def frames():
for acc_11_pos, acc_12_pos in zip(Acc_11, Acc_12):
yield acc_11_pos, acc_12_pos
ani = FuncAnimation(fig, ani, frames=frames, interval=300)
plt.show()
However, the dot stops at each point then continue, but I want the dot moving smoothly in this speed without changing the interval. Can anyone please help?
"Smooth" would always require "more frames" in my opinion. So I do not see a way to make the movement smoother, i.e. increase the number of frames, without increasing the frames per second, i.e. changing the interval.
Here's a version with frames increased tenfold and interval reduced tenfold:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.animation import FuncAnimation
Acc_11 = np.linspace(0,8,90) # increased frames
Acc_12 = np.ones(len(Acc_11))*4
fig = plt.figure()
axes = fig.add_subplot(111, autoscale_on=False)
axes.set_xlim(min(Acc_11), max(Acc_11))
axes.set_ylim(0, 8)
point, = axes.plot([Acc_11[0]],[Acc_12[0]], 'go')
def ani(coords):
point.set_data([coords[0]],[coords[1]])
return point,
def frames():
for acc_11_pos, acc_12_pos in zip(Acc_11, Acc_12):
yield acc_11_pos, acc_12_pos
ani = FuncAnimation(fig, ani, frames=frames, interval=30) # decreased interval
plt.show()

How to create a video that plots the scatter plots one by one using python?

I'm plotting some data as scatter plots which is overlaid on an image. I would like to make an animation of this by plotting one scatter point at a time. This is the code I have right now using and editing the answer from here:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
x = random.sample(range(0, 287), 20)
y = random.sample(range(0, 380), 20)
size = [20 for x in range(20)]
colors = ["r" for x in range(20)]
cm = plt.get_cmap('jet')
fig = plt.figure(figsize=(18,9))
graph = plt.scatter([], [],marker='+')
url = 'https://raw.githubusercontent.com/kornelski/pngquant/master/test/img/test.png'
im = plt.imread(url)
def animate(i):
implot = plt.imshow(im)
graph.set_offsets(np.vstack((x[:i+1], y[:i+1])).T)
graph.set_sizes(size[:i])
graph.set_facecolors(colors[:i+1])
return graph
ani = FuncAnimation(fig, animate, repeat=False, interval=0.1)
plt.show()
There are two things I would like help with.
I would like the color of my scatterplot to change based on a third variable, i.e use a cmap. However, the set_facecolors does not accept such an argument.
When I try to save my animation using ani.save('files/animation.gif',writer='imagemagick', fps=60) my jupyter notebook crashes.
Can someone help me?
The background image of the graph is drawn by adding ax. The color map is also created according to the number of data, 20, and a list is created so that each color can be displayed. Since the coordinates of the image and the coordinate basis of the graph are different, the y-axis is set in the opposite direction.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import random
from PIL import Image
import urllib.request
random.seed(20210702)
N = 20
x = random.sample(range(0, 380), N)
y = random.sample(range(0, 287), N)
size = [20 for x in range(N)]
colors = []
cm = plt.get_cmap('jet', N)
fig,ax = plt.subplots(figsize=(9, 4.5))
plt.xlim(0, 380)
plt.ylim(287, 0)
graph = ax.scatter([], [], marker='+')#
url = 'https://raw.githubusercontent.com/kornelski/pngquant/master/test/img/test.png'
im = Image.open(urllib.request.urlopen(url))
print(im.size)
def animate(i):
ax.imshow(im)
graph.set_offsets(np.vstack((x[:i+1], y[:i+1])).T)
graph.set_sizes(size[:i+1])
colors.append(cm(i))
graph.set_facecolors(colors)
return graph
ani = FuncAnimation(fig, animate, frames=20, repeat=False, interval=200)
plt.show()

How to produce a gif/mp4 file from a list of matrices displayed using imshow?

I'm trying to see the evolution of a system in phase space. I have a list of matrices corresponding to each iteration (inside the array "results") and i'm trying to use matplotlib to animate a gif or mp4 file.
I'm omitting the code that leads to the matrices inside results fro brevity but I believe I include everything about the issue itself.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
a = results[0]
def init():
global im
im = plt.imshow(a, cmap=plt.cm.copper,
interpolation='sinc',
extent=[-1, 1, -1, 1])
im.set_data(a)
return [im]
def animate(i):
data = results[i]
im.set_array(data)
return [im]
fig = plt.figure()
ax = plt.axes()
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=N, interval=0.1, blit=True)
anim.save('test2.gif', writer='pillow')
plt.show()
Currently what I have does produce an output but it is a gif that the image simply fills up to a single color (https://i.imgur.com/yXRxpLr.gif), which I know for a fact doesn't correspond to the final state of the system because when I use imshow on individual matrices from 'results', I obtain correct results - a pattern in 2D).
Maybe I'm overlapping everything and that's why it turns to a single color? I don't know exactly where the problem lies.
For these type of tasks I like to use celluloid (disclaimer: I'm the author). It isn't memory efficient like FuncAnimation is since it uses ArtistAnimation under the hood. That being said here's an example of how you would use it to make an animation from a series of imshow calls. You essentially just call camera.snap after each imshow.
import matplotlib.pyplot as plt
from celluloid import Camera
import numpy as np
from numpy import random as rng
X = np.zeros((100, 100)) + 0.5
fig = plt.figure()
camera = Camera(fig)
for i in range(20):
plt.imshow(X, interpolation='none', vmin=0, vmax=1)
camera.snap()
X += 0.5 * (rng.rand(100, 100) - 0.5)
X = np.clip(X, 0, 1)
anim = camera.animate()
anim.save('im.mp4')

Categories

Resources