Search code examples
pythonmatplotlibmatplotlib-animation

Change Marker dynamically based on if condition in animation


I have a list of points; after I convert it to an array, I want to display the point with the marker 'o', but if the distance between the point and a mobile point is more than 10, for example, the marker of the point should change from 'o' to '*'. I trid to do this, but all the marker points changed not just the markers that satisfy the if condition.

import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
def createList(r1, r2):
    return [item for item in range(r1, r2+1)]

r1, r2 = 1, 50
mialistax=createList(r1, r2)
mialistay=createList(r1, r2)

fig, ax = plt.subplots()

import numpy as np
x_l=np.array(mialistax)
y_l = np.array(mialistay)

def animate(i):

    max_x= max(mialistax)
    max_y=max(mialistay)
    ax.clear()

    if (x_l[i]>10):
        mark='*'
    else:
        mark='o'

    ax.plot(x_l[i],y_l[i],marker=mark)
    ax.set_xlim([0,max_x])
    ax.set_ylim([0,max_y])
    plt.plot(x_l,y_l,marker=mark)

ani = FuncAnimation(fig, animate, frames=200, interval=500, repeat=False)

plt.show()

In this code, the marker changes for all the points, but I want that the marker changed for only the points that satisfy the if condition.


Solution

  • If I understand the problem correctly, you want an animation that shows all the points, but the point at index i, which is the frame number, should be converted to a "*" marker if its x value is greater than 10.

    To start, you have three issues. The first is the order of your plots.

    ax.plot(x_l[i], y_l[i], marker=mark)
    ax.plot(x_l, y_l, marker=mark)
    

    This means that you first plot your individual point and then the rest of the points on top. So you won't see your "special" point because it is covered up by the other points.

    The second issue is that you're setting the same marker for both of the plots (the individual point and the rest of them). I would create a list of all the markers and update the marker at index i if it meets the condition. Then, loop through all the points and plot them individually (since you cannot pass a marker list). You probably want to have your "special" point stand out, so we can show it as a different color.

    markers = ["o"]*len(x_l)
    
    def animate(i):
        max_x = max(x_l)
        max_y = max(y_l)
        ax.clear()
    
        if x_l[i] > 10:
            markers[i] = '*'
    
        ax.set_xlim([0, max_x])
        ax.set_ylim([0, max_y])
        
        for indx, (x, y, marker) in enumerate(zip(x_l, y_l, markers)):
            color = "tab:orange" if indx == i else "tab:blue"
            ax.plot(x, y, marker=marker, color=color)
    

    Lastly, you only have 50 points, but have frames=200, so you need to either increase your points or decrease the number of frames to match.

    ani = FuncAnimation(fig, animate, frames=len(x_l), interval=5, repeat=False)
    


    I assume you're new to Python, so I'll also make other suggestions to improve your code.

    1. You can directly create a list from a range by using the list function: list(range(start, stop)). Since you are converting it to a numpy array, there is a numpy function for this: np.arange(start, stop).
    2. With numpy arrays you can use the max method: x_l.max(), though this change isn't as important.
    import matplotlib.pyplot as plt
    import numpy as np
    from matplotlib.animation import FuncAnimation
    
    plt.close("all")
    
    
    r1 = 1
    r2 = 50
    x_l = np.arange(r1, r2+1)
    y_l = np.arange(r1, r2+1)
    
    fig, ax = plt.subplots()
    
    markers = ["o"]*x_l.shape[0]
    
    def animate(i):
        max_x = x_l.max()
        max_y = y_l.max()
        ax.cla()
        
        if x_l[i] > 10:
            markers[i] = '*'
        
        for indx, (x, y, marker) in enumerate(zip(x_l, y_l, markers)):
            color = "tab:orange" if indx == i else "tab:blue"
            ax.plot(x, y, marker=marker, color=color)
            
        ax.set_xlim([0, max_x])
        ax.set_ylim([0, max_y])
    
    
    ani = FuncAnimation(fig, animate, frames=x_l.shape[0], interval=500, repeat=False)
    fig.show()