0

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!

4 Answers4

0

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

Xavier
  • 66
  • 3
  • Thanks so much for your response Xavier! For some reason, the code block you shared (using FuncAnimation) does not work for me either. It generates an empty graph, but doesn't plot anything. It works well for you? – James Drury Dec 28 '22 at 19:48
  • You need to save the ini object as a .gif. ```ani.save("animation.gif", writer='Pillow', fps=30 )```. – Xavier Dec 28 '22 at 20:38
  • Is there a way to get the animation to display as output in Jupyter notebook? – James Drury Dec 29 '22 at 16:24
  • ```from IPython.display import HTML HTML(ani.to_jshtml())``` this is the only way i know. Mark the option loop, and click in the minus ou plus sing and then you will can slow or speed up the animation. I have edited the answer including this code. – Xavier Dec 29 '22 at 16:39
0

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.

Laurent B.
  • 1,653
  • 1
  • 7
  • 16
0

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

enter image description here

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()
gboffi
  • 22,939
  • 8
  • 54
  • 85
0

enter image description here

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())
gboffi
  • 22,939
  • 8
  • 54
  • 85