How to use python matplot animation to draw the change of grid color

Viewed 432

I want to create a simple animation to show my data changes.

  1. create a 3 * 3 grid.
  2. I have an array which is 20 * 9. The data is read into the animation line by line.

    Color = [[0,0,0,0,0,0,0,0,0],
             [100,0,0,0,100,0,0,0,0,0],
             [80,0,80,0,80,100,0,0,0]
             ......]

I hope the list for the grid is read line by line and each line works for 100ms. If the number is more than 0, the color of the grid change into red. For example, in the first 100ms, all color is black, then, in the second 100ms, grid number 0 and grid number 4 change to red and last for 100ms. In the third 100ms, grid number 0,2,4,5 change to red and last for 100ms.

My current version of the code looks like this. I don't know how to draw like what I describe above.

%matplotlib notebook     ## show in jupyter

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


fig = plt.figure()
fig.set_dpi(100)
fig.set_size_inches(7, 6.5)

ax = plt.axes(xlim=(0, 10), ylim=(0, 10))
patch = plt.Circle((5, -5), 0.75, fc='y')

def init():
    patch.center = (5, 5)
    ax.add_patch(patch)
    return patch,

def animate(i):
    x, y = patch.center
    x = 5 + 3 * np.sin(np.radians(i))
    y = 5 + 3 * np.cos(np.radians(i))
    patch.center = (x, y)
    return patch,

anim = animation.FuncAnimation(fig, animate, 
                               init_func=init, 
                               frames=360, 
                               interval=20,
                               blit=True)

plt.show()

I would appreciate it a lot if anyone could help me!!!

2 Answers

Cool question! I have an answer that works but it changes the structure of your Colors list. Here's the answer with an explanation below.

%matplotlib notebook

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


fig = plt.figure()
fig.set_dpi(100)
fig.set_size_inches(7, 6.5)

ax = plt.axes(xlim=(0, 10), ylim=(0, 10))
patch = plt.Circle((5, -5), 0.75, fc='y')

colors = [
    [["black", None, None], ["black", None, "black"], [None, None, "red"]],
    [["black", None, None], ["black", None, "red"], ["black", None, "red"]],
    [["red", None, None], [None, None, None], [None, None, "red"]],
]

def get_coords(colors):
    y = 0
    x = 0
    coords = []
    for row in colors:
        x = 0
        for entry in row:
            if entry:
                coords.append([entry, x, y])
                x += 3.33
        y += 3.33
    return coords

def get_grids(coord):
    return [plt.Rectangle((x[1], x[2]), 3.33, 3.33, fc=x[0]) for x in coord]

coords = [get_coords(color) for color in colors]
grids = [get_grids(coord) for coord in coords]

def init():
    patch.center = (5, 5)
    ax.add_patch(patch)
    return patch,

def animate(i):
    patches = []
    if (i % 100 == 0):
        ax.patches = []
        next_grid = grids.pop(0)
        for rectangle in next_grid:
            patches.append(ax.add_patch(rectangle))

    x, y = patch.center
    x = 5 + 3 * np.sin(np.radians(i))
    y = 5 + 3 * np.cos(np.radians(i))
    patch.center = (x, y)
    patches.append(ax.add_patch(patch))
    return patches

anim = animation.FuncAnimation(fig, animate, 
                               init_func=init, 
                               frames=360, 
                               interval=20,
                               blit=True)

plt.show()

The key idea is adding and removing plt.Rectangles to give the appearance of a grid. Because your graph is 10x10, these rectangles are squares of side length 10/3 =~ 3.33.

I think it's easier to use 20 * 3 * 3 instead of a 20 * 9 list for the colors. I use the following:

colors = [
    [["black", None, None], ["black", None, "black"], [None, None, "red"]],
    [["black", None, None], ["black", None, "red"], ["black", None, "red"]],
    [["red", None, None], [None, None, None], [None, None, "red"]],
]

Each entry in this list, as yours, is a grid. Within these grids, however, are rows, each with a desired color entry. colors[0][0] == ["black", None, None] means at the first frame of the animation, the bottom left corner of the grid will be black and the rest of the bottom transparent. colors[0][1] == ["black", None, "black"] means the middle of the grid will have the left and right thirds black with the middle transparent.

The get_coords and get_grids functions are pretty hairy and clearly hard-coded to support a 3x3 grid with dimensions 10x10 - it'd be cool to parameterize that out down the line.

Only other important idea is that to change the animation every 100ms, we just check if i in the animate function is divisible by 100. If it is, we clear the existing patches (so we're not just adding rectangles ad nausem) and plot our new ones. When the grid list runs out, the grid will be transparent for the rest of the animation.

Hope this helps - happy plotting!

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

Color = [[0,0,0,0,0,0,0,0,0],
        [100,0,0,0,100,0,0,0,0,0],
        [80,0,80,0,80,100,0,0,0]]

fig = plt.figure()
fig.set_dpi(100)
fig.set_size_inches(7, 6.5)
fig.set_tight_layout(True)

ax = plt.axes(xlim=(0, 6), ylim=(0, 6))
patch = []

patch.append(plt.Rectangle(xy = (0,0),width = 2,height = 2,fill = True,color = 'k'))
patch.append(plt.Rectangle(xy = (2,0),width = 2,height = 2,fill = True,color = 'k'))
patch.append(plt.Rectangle(xy = (4,0),width = 2,height = 2,fill = True,color = 'k'))
patch.append(plt.Rectangle(xy = (0,2),width = 2,height = 2,fill = True,color = 'k'))
patch.append(plt.Rectangle(xy = (2,2),width = 2,height = 2,fill = True,color = 'k'))
patch.append(plt.Rectangle(xy = (4,2),width = 2,height = 2,fill = True,color = 'k'))
patch.append(plt.Rectangle(xy = (0,4),width = 2,height = 2,fill = True,color = 'k'))
patch.append(plt.Rectangle(xy = (2,4),width = 2,height = 2,fill = True,color = 'k'))
patch.append(plt.Rectangle(xy = (4,4),width = 2,height = 2,fill = True,color = 'k'))

def init():
    for i in range(9):
        patch[i].set_color('k')
        ax.add_patch(patch[i])
    return patch

def animate(i):
    value = (np.array(Color[i]) == 0)
    for j in range(9):
        patch[j].set_color('k' if value[j] else 'r')

    return patch

anim = animation.FuncAnimation(fig, animate,  
                                init_func=init, 
                                frames=3, 
                                interval=1000,
                                blit=True)

plt.show()
Related