4

I have a question which is similar to this question, but instead of plotting a very large number of points, I have fewer points and edges (factor of at least 10), but want to animate them. To be more specific, it is the visualization of a Kohonen network with a 2D-map, which evolves and looks like a deformed square lattic, see this image from Wikipedia:

image

Taken the code from this answer modified a bit, a minimal example looks like this:

import numpy as np
import matplotlib.pyplot as plt

def getXY(points, edges):
   """Return x and y arrays to pass to the plot function"""
   return [points.T[i][edges.T] for i in np.arange(points.shape[1])]

points = numpy.array([[1,2],[4,5],[2,7],[3,9],[9,2]])
edges = numpy.array([[0,1],[3,4],[3,2],[2,4]])

lines = plt.plot(*getXY(points, edges), linestyle='-', color='y',
        markerfacecolor='red', marker='o')
plt.show()

Then, an update happens which changes the points' coordinates:

points += 1 # simplified version of a real update

I would like to avoid the "stupid" way to replot everything, because there is other data in the plot window which does not change, also this is really slow:

# repeat after each update all the calculation
plt.cla()
x, y = [points.T[i][edges.T] for i in np.arange(points.shape[1])]
lines = plt.plot(x, y, linestyle='-', color='y',
        markerfacecolor='red', marker='o')
plt.show()

As a first step, I saved the created Line2D data from the initial plot in the variable lines. The problem which I face now is that if I just want to update the lines data, the only solution which I could come up with requires me to iterate over all of the lines, which does not seem very elegant to me:

x, y = getXY(points, edges)
if len(lines) > 1: 
    for i, d in enumerate(zip(x.T, y.T)):
        lines[i].set_data(d)
else: # has to be treated seperately, since the format of x and y is different
    lines[0].set_data(x, y)
plt.show()

I am looking for suggestions how to...

  • Come up with a better solution than my for-loop
  • Ideas about how to solve the initial problem (plotting points with specified connections) in a more elegant way
Community
  • 1
  • 1
Markus
  • 494
  • 3
  • 12

1 Answers1

0

Here is one possible way. You can use the fact that if in the coordinates of the line there are NaNs or None's this is treated as essentially the end of the line segment. The next non-None point is treated as a beginning of a new segment.

import numpy as np, matplotlib.pyplot as plt
x,y = np.array([1, 4,2, 3, 9]), np.array([2, 5, 7, 9, 2])

edges = np.array([[0, 1],
   [3, 4],
   [3, 2],
   [2, 4],
   [2, 1]])

ys=np.vstack((y[edges[:,0]],y[edges[:,1]],y[edges[:,0]]+np.nan)).T.flatten()

xs=np.vstack((x[edges[:,0]],x[edges[:,1]],x[edges[:,0]]+np.nan)).T.flatten()

lines2d = plt.plot(xs,ys,marker='o')

Now in order to update the coordinates (e.g shift the x axis by .1 and y axis by .2 you can do just:

oldx,oldy=lines2d[0].get_data()
lines2d[0].set_data(oldx+.1,oldy+.2)
plt.draw()

PS I'm not entirely sure that my way of inserting Nans in the xs,ys arrays is the fastest, but it probably doesn't really matter.

sega_sai
  • 8,328
  • 1
  • 29
  • 38