【问题标题】:Animate scatter plot with colorbar using matplotlib使用 matplotlib 使用颜色条动画散点图
【发布时间】:2020-08-17 14:57:19
【问题描述】:

我花了很多时间尝试动画散点图,其中标记的颜色由数字定义。

以下是我的尝试,虽然效果不错,但并没有按计划进行:

  • 在每个动画步骤之后,应该删除旧点。相反,新点只是简单地添加到图上已有的点中。
  • 还应根据值在每一步更新颜色条(就像时间文本一样)。

但是,无论我做什么,都会生成一张空白图表。当涉及到动画散布时,这真的是我可以用 python 做的最好的吗?

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

time_steps = 50
N_nodes = 100

positions = []
solutions = []
for i in range(time_steps):
    positions.append(np.random.rand(2, N_nodes))
    solutions.append(np.random.random(N_nodes))

fig = plt.figure()
marker_size = 1
ax = fig.add_subplot(111, aspect='equal', autoscale_on=False, xlim=(0, 1), ylim=(0, 1))
time_text = ax.text(0.02, 0.95, '', transform=ax.transAxes)

def init():
    """ Initialize animation. """
    scat = ax.scatter(positions[0][0], positions[0][1], s = marker_size, c = solutions[0], cmap = "RdBu_r", marker = ".", edgecolor = None)
    fig.colorbar(scat)
    time_text.set_text('Time step = %d' % 0)

    return scat, time_text

def animate(i):
    """ Perform animation step. """
    scat = ax.scatter(positions[i][0], positions[i][1], s = marker_size, c = solutions[i], cmap = "RdBu_r", marker = ".", edgecolor = None)
    time_text.set_text('Time step = %d' % i)

    return scat, time_text

plt.xlabel('x [m]')
plt.ylabel('y [m]')
plt.grid(b=None)
plt.show()
ani = animation.FuncAnimation(fig, animate, interval=100, blit=True, repeat=True, init_func=init)

ani.save('animation.gif', writer='imagemagick', fps = 8)

【问题讨论】:

    标签: python matplotlib animation


    【解决方案1】:

    我不确定您是否在帖子中指出了这一点,但我无法让您的代码按原样运行。但是,我认为主要问题与您提到的第一点有关:“在每个动画步骤之后,应删除旧点。”在绘制动画时确实需要明确这一点。目前,您的代码正在为相同的Axes 重复创建scatter。就像您要在动画之外执行此操作一样,这将导致多组数据相互重叠。

    我见过人们这样做的两种主要方式:要么使用一些set_... 绘图方法来更新数据(见here for scatter plotshere in general或 strong> 每次迭代清除AxesFigure 以绘制新数据。我发现后者更容易/更普遍(如果更懒惰)。这是您的示例的一种方法(我已编辑此代码以删除对 plt.gridplt.label 的调用,因为它们不起作用):

    import matplotlib.pyplot as plt
    import matplotlib.animation as animation
    import numpy as np
    
    time_steps = 50
    N_nodes = 100
    
    positions = []
    solutions = []
    for i in range(time_steps):
        positions.append(np.random.rand(2, N_nodes))
        solutions.append(np.random.random(N_nodes))
    
    fig, ax = plt.subplots()
    marker_size = 5 #upped this to make points more visible
    
    def animate(i):
        """ Perform animation step. """
        #important - the figure is cleared and new axes are added
        fig.clear()
        ax = fig.add_subplot(111, aspect='equal', autoscale_on=False, xlim=(0, 1), ylim=(0, 1))
        #the new axes must be re-formatted
        ax.set_xlim(0,1)
        ax.set_ylim(0,1)
        ax.grid(b=None)
        ax.set_xlabel('x [m]')
        ax.set_ylabel('y [m]')
        # and the elements for this frame are added
        ax.text(0.02, 0.95, 'Time step = %d' % i, transform=ax.transAxes)
        s = ax.scatter(positions[i][0], positions[i][1], s = marker_size, c = solutions[i], cmap = "RdBu_r", marker = ".", edgecolor = None)
        fig.colorbar(s)
    
    ani = animation.FuncAnimation(fig, animate, interval=100, frames=range(time_steps))
    
    ani.save('animation.gif', writer='pillow')
    

    制作以下 GIF:

    这里,我使用fig.clear()清除每一帧的colorbar;否则,其中许多将被绘制。这意味着您每次都必须重新添加Axes 和格式。其他情况,使用ax.clear()就可以了,省去了add_subplot的步骤。

    不过,在here 之后,还有另一种方法可以做到这一点。如果您有颜色条Axes 的句柄,则可以清除它们(而不是清除整个Figure),类似于散点图轴:

    import matplotlib.pyplot as plt
    import matplotlib.animation as animation
    import numpy as np
    
    time_steps = 50
    N_nodes = 100
    
    positions = []
    solutions = []
    for i in range(time_steps):
        positions.append(np.random.rand(2, N_nodes))
        solutions.append(np.random.random(N_nodes))
    
    # init the figure, so the colorbar can be initially placed somewhere
    marker_size = 5
    fig = plt.figure()
    ax = fig.add_subplot(111, aspect='equal', autoscale_on=False, xlim=(0, 1), ylim=(0, 1))
    s = ax.scatter(positions[0][0], positions[0][1], s = marker_size, c = solutions[0], cmap = "RdBu_r", marker = ".", edgecolor = None)
    cb = fig.colorbar(s)
    
    # get the axis for the colobar
    cax = cb.ax
    
    def animate(i):
        """ Perform animation step. """
        # clear both plotting axis and colorbar axis
        ax.clear()
        cax.cla()
        #the new axes must be re-formatted
        ax.set_xlim(0,1)
        ax.set_ylim(0,1)
        ax.grid(b=None)
        ax.set_xlabel('x [m]')
        ax.set_ylabel('y [m]')
        # and the elements for this frame are added
        ax.text(0.02, 0.95, 'Time step = %d' % i, transform=ax.transAxes)
        s = ax.scatter(positions[i][0], positions[i][1], s = marker_size, c = solutions[i], cmap = "RdBu_r", marker = ".", edgecolor = None)
        fig.colorbar(s, cax=cax)
    
    ani = animation.FuncAnimation(fig, animate, interval=100, frames=range(time_steps))
    
    ani.save('animation2.gif', writer='pillow')
    

    生成相同的图形。

    【讨论】:

    • 我喜欢这个解决方案。干净且易于理解。谢谢!
    • 如果你不希望颜色条改变,你可以通过ax.scatter(...., vmin=0, vmax=1)强制它的限制。
    • 嗨,这是一个很好的答案。你能告诉我如何在动画中保留旧点(即没有 fig.clear())吗?我不明白你关于 ax.clear() 和保存 add_subplot 步骤的最后一点
    • @user42 你基本上不想打电话给ax.clear();您可能仍需要调用 cax.clear() 并重做颜色条(请参阅我更新的示例)。唯一的麻烦是如果您确实想要清除和重做其他元素(例如,本例中的文本)。然后,您可能需要使用一些set_... 方法(特别是这里,text.set_text('new_text'),其中text 是对Text 对象的引用)。
    猜你喜欢
    • 2014-09-10
    • 2021-07-28
    • 2011-08-29
    • 2017-05-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-24
    • 2013-01-22
    相关资源
    最近更新 更多