1

I tried to write a simple script which updates a scatter plot for every timestep t. I wanted to do it as simple as possible. But all it does is to open a window where I can see nothing. The window just freezes. It is maybe just an small error, but I can not find it.

The the data.dat has the format

                x      y
Timestep 1      1      2
                3      1
Timestep 2      6      3
                2      1

(the file contains just the numbers)

import numpy as np
import matplotlib.pyplot as plt
import time

# Load particle positioins
with open('//home//user//data.dat', 'r') as fp:
    particles = []
    for line in fp:
        line = line.split() 
        if line:
            line = [float(i) for i in line]
            particles.append(line)

T = 100
numbParticles = 2

x, y = np.array([]), np.array([])

plt.ion()
plt.figure()
plt.scatter(x,y)
for t in range(T):
    plt.clf()
    for k in range(numbP):
            x = np.append(x, particles[numbParticles*t+k][0])
            y = np.append(y, particles[numbParticles*t+k][1])
    plt.scatter(x,y)
    plt.draw()
    time.sleep(1)
    x, y = np.array([]), np.array([])
Gilfoyle
  • 3,282
  • 3
  • 47
  • 83

2 Answers2

5

The simplest, cleanest way to make an animation is to use the matplotlib.animation module.

Since a scatter plot returns a matplotlib.collections.PathCollection, the way to update it is to call its set_offsets method. You can pass it an array of shape (N, 2) or a list of N 2-tuples -- each 2-tuple being an (x,y) coordinate.

For example,

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

T = 100
numbParticles = 2
particles = np.random.random((T,numbParticles)).tolist()
x, y = np.array([]), np.array([])

def init():
    pathcol.set_offsets([[], []])
    return [pathcol]

def update(i, pathcol, particles):
    pathcol.set_offsets(particles[i])
    return [pathcol]

fig = plt.figure()
xs, ys = zip(*particles)
xmin, xmax = min(xs), max(xs)
ymin, ymax = min(ys), max(ys)
ax = plt.axes(xlim=(xmin, xmax), ylim=(ymin, ymax))
pathcol = plt.scatter([], [], s=100)

anim = animation.FuncAnimation(
    fig, update, init_func=init, fargs=(pathcol, particles), interval=1000, frames=T, 
    blit=True, repeat=True)
plt.show()
unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
  • 1
    Do I not need a loop? I do not see where I pass my list of numbers for every timestep. Can you please explain that? – Gilfoyle Nov 18 '16 at 22:43
  • 1
    The loop resides in the call to [`FuncAnimation()`](http://matplotlib.org/api/animation_api.html#matplotlib.animation.FuncAnimation). For every timestep (here every 1000 ms) the function `update` is called with an incremented value of `i`. – ImportanceOfBeingErnest Nov 19 '16 at 00:01
  • @ImportanceOfBeingErnest I do not see where `i` gets incremented. I would expect something like `i=i+1`. – Gilfoyle Nov 19 '16 at 10:10
  • @Samuel As I said, the loop is inside `FuncAnimation()` so you don't see it. You can look at the [source code](https://github.com/matplotlib/matplotlib/blob/master/lib/matplotlib/animation.py) if you like (actually there is no loop there, but an iterator), but you can also just accept that seen from the outside `FuncAnimation(fig, update, interval=1000, frames=100)` will just call the function `update` every 1000 ms and increment `i` by 1 up to the number given by `frames`. – ImportanceOfBeingErnest Nov 19 '16 at 10:46
  • 1
    `set_offsets` was the key for me. Why can't pyplot have a centralised interface for setting data of elements :( ... like `set_data()` – Ciprian Tomoiagă Aug 31 '18 at 14:54
0

I finally found a solution. You can do it simply by using this script. I tried to keep it simple:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

# Helps me to get the data from the file I want to plot
N = 0

# Load particle positioins
with open('//home//user//data.dat', 'r') as fp:
    particles = []
    for line in fp:
        line = line.split() 
        particles.append(line)

# Create new Figure and an Axes which fills it.
fig = plt.figure(figsize=(7, 7))
ax = fig.add_axes([0, 0, 1, 1], frameon=True)
border = 100
ax.set_xlim(-border, border), ax.set_xticks([])
ax.set_ylim(-border, border), ax.set_yticks([])

# particle data
p = 18 # number of particles
myPa = np.zeros(p, dtype=[('position', float, 2)])

# Construct the scatter which we will update during animation
scat = ax.scatter(myPa['position'][:, 0], myPa['position'][:, 1])

def update(frame_number):
    # New positions
    myPa['position'][:] = particles[N*p:N*p+p]

    # Update the scatter collection, with the new colors, sizes and positions.
    scat.set_offsets(myPa['position'])
    increment()

def increment():
    global N
    N = N+1

# Construct the animation, using the update function as the animation director.
animation = FuncAnimation(fig, update, interval=20)

plt.show()
Gilfoyle
  • 3,282
  • 3
  • 47
  • 83