k-means¶
Focus: interpolate. By default the SVG jumps from one frame to the next. With interpolate=True shapes glide and colours fade between frames, which suits animations where each frame is a state, such as the iterations of Lloyd’s algorithm. The algorithm runs first and its states are stored; update then replays them, one frame per iteration, until the centroids stop moving.
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.animation import FuncAnimation
from svganim import ani_to_svg
rng = np.random.default_rng(0)
X = np.vstack([rng.normal(c, 0.8, (60, 2)) for c in ([0, 0], [4, 1], [1, 4])])
centers = X[rng.choice(len(X), 3, replace=False)]
states = []
for _ in range(20): # Lloyd's algorithm, keeping every iteration until it converges
labels = np.linalg.norm(X[:, None] - centers, axis=2).argmin(axis=1)
states.append((labels, centers))
new_centers = np.array([X[labels == j].mean(axis=0) for j in range(3)])
if np.allclose(new_centers, centers):
break
centers = new_centers
colors = np.array(["#4c72b0", "#dd8452", "#55a868"])
fig, ax = plt.subplots(figsize=(5, 3))
points = ax.scatter(*X.T, c=colors[states[0][0]], s=14)
stars = ax.scatter(
*states[0][1].T, c=colors, marker="X", s=160, edgecolors="black", zorder=3
)
ax.axis("off")
def update(i):
labels, centers = states[i]
points.set_facecolor(colors[labels])
stars.set_offsets(centers)
plt.close(fig) # otherwise the notebook also shows the static figure
ani = FuncAnimation(fig, update, frames=len(states))
svg = ani_to_svg(ani, fps=1.5, interpolate=True)
For comparison, the same figure without interpolate, which switches abruptly between states:
ani_to_svg(ani, fps=1.5)
ani_to_svg returns the SVG as a string that displays itself as an animated image:
svg
What changes in each frame¶
points.set_facecolor(colors[labels])recolours the points by cluster.stars.set_offsets(centers)moves the three centroids.
What svganim animates¶
The colour (fill, stroke) of the points that change cluster at some
point, and the position of the centroids (x, y).
With interpolate=True the centroids slide to their next position and the
points fade to their new colour. Numeric attributes (paths, positions, colours,
opacities) are interpolated linearly; the rest keep switching stepwise, such as
a path whose number of vertices changes, a none fill or a clip path.
Use it when each frame is a state you want to move between. Leave it off when the frames are already small steps of a continuous motion, where it adds nothing visible. It makes files slightly larger, because svganim turns off matplotlib’s path simplification: it changes the vertex count between frames, and such paths cannot be interpolated.
The loop stops when the algorithm converges: later iterations would be identical frames and only add dead time.