pythonmatplotlibstochastic-process

simple animation with matplotlib


I am trying to make a simple animation for a stochastic process (just black and white dots randomly changing their colors). To simulate that, I have basically plotted the dots over a grid. However, the important parameter for me being the rate of black dots, I'd like to draw under this grid a progressive bar showing the rate #blackdots/#totaldots looking approximately like this : [///////////////////////_____] 70% (simply just like a power charge bar).

I tried this but the bars are overlaid, and I don't think that Slider is meant to do such animations.

import numpy as np
import matplotlib.pyplot as plt
import random
from matplotlib.widgets import Slider

t=1500
d=5
n=10
raws = [i for i in range(n)]

config = [[2*random.randrange(2)-1 for i in range(n)] for i in range(n)]

def color(op):
    if op == 1:
        return 'white'
    return 'black'

nbblack = 0
for i in config:
  for j in i :
     nbblack += (j==-1)

blackrate = nbblack/(n**2)

plt.subplots_adjust(bottom=0.25)

for line in range(n):
    colors = [color(config[line][raw]) for raw in raws]
    plt.scatter([line]*n,raws,c=colors,edgecolors='black',s=50)

plt.title('t=0',fontdict={'size': 16},x=-0.20,y=25)

samp = Slider(axamp, 'Rate', 0, 1, valinit=blackrate,color='black')

for step in range(t):

    plt.pause(0.001)

    xpick = random.randrange(n)
    ypick = random.randrange(n)
    opinion_picked = config[xpick][ypick]
    for j in range(d) :
        neighboor = random.randrange(n),random.randrange(n)
        opinion_neig = config[neighboor[0]][neighboor[1]]
        if opinion_neig == opinion_picked :
            break

        elif j == d-1 :
            config[xpick][ypick]=-config[xpick][ypick]
            nbblack-=config[xpick][ypick]
            blackrate = nbblack/(n**2)
            plt.title('t={}'.format(step),fontdict={'size': 16},x=-0.20,y=25)
            for line in range(n):
               colors = [color(config[line][raw]) for raw in raws]
               plt.scatter([line]*n,raws,c=colors,edgecolors='black',s=50)

            axamp = plt.axes([0.28, 0.15, 0.48, 0.03])

            samp = Slider(axamp, 'Rate', 0, 1, valinit=blackrate,color='black')

plt.show()

I am not very familiar with maplot so please let me know if there is a better way to do things and thanks a lot for your help !


Solution

  • I don't think that Slider is meant to do such animations ... please let me know if there is a better way to do things ...

    Maybe using a custom colorbar would work. I adapted from the Discrete Intervals colorbar example.

    The following uses the percentage of black dots to decide which portion of the color bar should be black or white.

    Here is an example without animation: five successive plots drawn by a loop. I tried to keep it as close to your example as possible.

    import matplotlib as mpl
    from matplotlib import pyplot as plt
    import random
    
    t = 1500
    d = 5
    n = 10
    raws = [i for i in range(n)]
    
    
    def f(t=t, d=d, n=n, raws=raws):
    
        # try to get more skew in the data
        mode = random.random()
        config = [[random.triangular(mode=mode) > 0.5 for i in range(n)] for i in range(n)]
        config = [[int(item) or -1 for item in row] for row in config]
        # config = [[2*random.randrange(2)-1 for i in range(n)] for i in range(n)]
    
        def color(op):
            if op == 1:
                return "white"
            return "black"
    
        nbblack = 0
        for i in config:
            for j in i:
                nbblack += j == -1
    
        blackrate = nbblack / (n ** 2)
    
        fig, ax = plt.subplots()
        fig.subplots_adjust(bottom=0.25)
        # plt.subplots_adjust(bottom=0.25)
    
        for line in range(n):
            colors = [color(config[line][raw]) for raw in raws]
            plt.scatter([line] * n, raws, c=colors, edgecolors="black", s=50)
    
        plt.title("t=0", fontdict={"size": 16}, x=-0.20, y=25)
    
        cmap = mpl.colors.ListedColormap(["black", "white"])
        bounds = [0, int(blackrate * 100), 100]
        norm = mpl.colors.BoundaryNorm(bounds, cmap.N)
        fig.colorbar(
            mpl.cm.ScalarMappable(cmap=cmap, norm=norm),
            # cax=ax,
            # boundaries=[0] + bounds + [13],  # Adding values for extensions.
            # extend='both',
            ticks=bounds,
            spacing="proportional",
            orientation="horizontal",
            label="Percentage Black",
        )
    
        plt.show()
        plt.close()
    
    
    for _ in range(5):
        f()
    

    BoundaryNorm determines how the colors are distributed. The example uses two colors, black/white, and defines two bins between 0 and 100 using the percentage of black dots for the bin edge.

    The spacing="proportional" argument to Figure.colorbar ensures the black/white area is proportional to the bins.


    Example plot


    The Matplotlib Tutorials are worth investing time in.