python 如何设置散点图的动画

4xy9mtcn  于 2023-05-21  发布在  Python
关注(0)|答案(5)|浏览(134)

我试图做一个散点图的动画,其中的颜色和大小的点的变化,在不同阶段的动画。对于数据,我有两个numpy ndarray,分别有x值和y值:

data.shape = (ntime, npoint)
x.shape = (npoint)
y.shape = (npoint)

现在我想画一个散点图

pylab.scatter(x,y,c=data[i,:])

并在索引i上创建动画。我该怎么做?

vm0i2vca

vm0i2vca1#

假设你有一个散点图scat = ax.scatter(...),那么你可以

*改变位置

scat.set_offsets(array)

其中array是x和y坐标的N x 2形阵列。

*更改大小

scat.set_sizes(array)

其中array是以点为单位的大小的1D数组。

*更改颜色

scat.set_array(array)

其中array是将被色彩Map的值的1D数组。
下面是一个使用animation module的快速示例。
它比实际要稍微复杂一些,但这应该为您提供了一个框架来完成更复杂的任务。

  • (代码于2019年4月编辑,以与当前版本兼容。旧代码见revision history)*
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np

class AnimatedScatter(object):
    """An animated scatter plot using matplotlib.animations.FuncAnimation."""
    def __init__(self, numpoints=50):
        self.numpoints = numpoints
        self.stream = self.data_stream()

        # Setup the figure and axes...
        self.fig, self.ax = plt.subplots()
        # Then setup FuncAnimation.
        self.ani = animation.FuncAnimation(self.fig, self.update, interval=5, 
                                          init_func=self.setup_plot, blit=True)

    def setup_plot(self):
        """Initial drawing of the scatter plot."""
        x, y, s, c = next(self.stream).T
        self.scat = self.ax.scatter(x, y, c=c, s=s, vmin=0, vmax=1,
                                    cmap="jet", edgecolor="k")
        self.ax.axis([-10, 10, -10, 10])
        # For FuncAnimation's sake, we need to return the artist we'll be using
        # Note that it expects a sequence of artists, thus the trailing comma.
        return self.scat,

    def data_stream(self):
        """Generate a random walk (brownian motion). Data is scaled to produce
        a soft "flickering" effect."""
        xy = (np.random.random((self.numpoints, 2))-0.5)*10
        s, c = np.random.random((self.numpoints, 2)).T
        while True:
            xy += 0.03 * (np.random.random((self.numpoints, 2)) - 0.5)
            s += 0.05 * (np.random.random(self.numpoints) - 0.5)
            c += 0.02 * (np.random.random(self.numpoints) - 0.5)
            yield np.c_[xy[:,0], xy[:,1], s, c]

    def update(self, i):
        """Update the scatter plot."""
        data = next(self.stream)

        # Set x and y data...
        self.scat.set_offsets(data[:, :2])
        # Set sizes...
        self.scat.set_sizes(300 * abs(data[:, 2])**1.5 + 100)
        # Set colors..
        self.scat.set_array(data[:, 3])

        # We need to return the updated artist for FuncAnimation to draw..
        # Note that it expects a sequence of artists, thus the trailing comma.
        return self.scat,

if __name__ == '__main__':
    a = AnimatedScatter()
    plt.show()

如果您在OSX上并使用OSX后端,则需要在下面的FuncAnimation初始化中将blit=True更改为blit=False。OSX后端不完全支持位传输。性能将受到影响,但示例应该在禁用位传输的OSX上正确运行。
对于一个简单的例子,它只是更新颜色,看看下面的:

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

def main():
    numframes = 100
    numpoints = 10
    color_data = np.random.random((numframes, numpoints))
    x, y, c = np.random.random((3, numpoints))

    fig = plt.figure()
    scat = plt.scatter(x, y, c=c, s=100)

    ani = animation.FuncAnimation(fig, update_plot, frames=range(numframes),
                                  fargs=(color_data, scat))
    plt.show()

def update_plot(i, data, scat):
    scat.set_array(data[i])
    return scat,

main()
juud5qan

juud5qan2#

我写了celluloid来使它更容易。最简单的例子是:

import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
from celluloid import Camera

numpoints = 10
points = np.random.random((2, numpoints))
colors = cm.rainbow(np.linspace(0, 1, numpoints))
camera = Camera(plt.figure())
for _ in range(100):
    points += 0.1 * (np.random.random((2, numpoints)) - .5)
    plt.scatter(*points, c=colors, s=100)
    camera.snap()
anim = camera.animate(blit=True)
anim.save('scatter.mp4')

它在引擎盖下使用ArtistAnimationcamera.snap捕获用于创建动画帧的图形的当前状态。
编辑:为了量化这使用了多少内存,我通过memory_profiler运行了它。

Line #    Mem usage    Increment   Line Contents
================================================
    11     65.2 MiB     65.2 MiB   @profile
    12                             def main():
    13     65.2 MiB      0.0 MiB       numpoints = 10
    14     65.2 MiB      0.0 MiB       points = np.random.random((2, numpoints))
    15     65.2 MiB      0.1 MiB       colors = cm.rainbow(np.linspace(0, 1, numpoints))
    16     65.9 MiB      0.6 MiB       fig = plt.figure()
    17     65.9 MiB      0.0 MiB       camera = Camera(fig)
    18     67.8 MiB      0.0 MiB       for _ in range(100):
    19     67.8 MiB      0.0 MiB           points += 0.1 * (np.random.random((2, numpoints)) - .5)
    20     67.8 MiB      1.9 MiB           plt.scatter(*points, c=colors, s=100)
    21     67.8 MiB      0.0 MiB           camera.snap()
    22     70.1 MiB      2.3 MiB       anim = camera.animate(blit=True)
    23     72.1 MiB      1.9 MiB       anim.save('scatter.mp4')

总结一下:

  • 创建100个图使用了1.9 MiB。
  • 制作动画使用了2.3 MiB。
  • 这种制作动画的方法总共使用了4.2MiB的内存。
dxxyhpgq

dxxyhpgq3#

TL/DR:如果您在使用ax.set_...方法制作散点图动画时遇到问题,您可以尝试在每一帧(即ax.clear())清除图,然后根据需要重新绘制。这是,但可能是有用的,当你想改变很多东西在一个小的动画。

下面是一个演示这种“清晰”方法的示例:

import itertools

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

# set parameters
frames = 10
points = 20
np.random.seed(42)

# create data
data = np.random.rand(points, 2)

# set how the graph will change each frame
sizes = itertools.cycle([10, 50, 150])
colors = np.random.rand(frames, points)
colormaps = itertools.cycle(['Purples', 'Blues', 'Greens', 'Oranges', 'Reds'])
markers = itertools.cycle(['o', 'v', '^', 's', 'p'])

# init the figure
fig, ax = plt.subplots(figsize=(5,5))

def update(i):
    # clear the axis each frame
    ax.clear()

    # replot things
    ax.scatter(data[:, 0], data[:, 1],
               s=next(sizes),
               c=colors[i, :],
               cmap=next(colormaps),
               marker=next(markers))

    # reformat things
    ax.set_xlabel('world')
    ax.set_ylabel('hello')

ani = animation.FuncAnimation(fig, update, frames=frames, interval=500)
ani.save('scatter.gif', writer='pillow')

我从matplotlib和其他来源看到的教程似乎没有使用这种方法,但我在这个网站上看到其他人(以及我自己)建议使用这种方法。我看到了一些优点和缺点,但我会欣赏其他人的想法:

优点

  • 您可以避免对散点图使用set_...方法(即.set_offsets().set_sizes(),...),它们的文档比较晦涩,也不太详细(* 尽管主要的答案会让你在这里走得很远!* )。此外,对于不同的地块类型有不同的方法(例如:对于线使用set_data,但对于散点不使用)。通过清除轴,您可以像matplotlib中的任何其他绘图一样确定每个帧的绘制元素。
  • 更重要的是,不清楚某些属性是否支持set,例如标记类型(如注解所示)或色彩Map表。我不知道如何使用ax.set_...创建上面的图,例如,因为标记和色彩Map表发生了变化。但这对于ax.scatter()来说是非常基本的。

缺点

*可以慢很多;即清除和重绘所有内容似乎比set...方法更昂贵。所以对于大型动画,这种方法可能有点痛苦。

  • 清除轴也会清除轴标签、轴限制、其他文本等内容。因此,这些类型的格式化需要包含在update中(否则它们将消失)。如果你想改变一些事情,但其他事情保持不变,这可能会很烦人。

当然,速度是一个大骗局。这里有一个例子来说明两者的区别。鉴于此数据:

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

np.random.seed(42)
frames = 40

x = np.arange(frames)
y = np.sin(x)
colors = itertools.cycle(['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet'])
data = [(np.random.uniform(-1, 1, 10) + x[i],
         np.random.uniform(-1, 1, 10) + y[i])
        for i in range(frames)]

可以使用set...方法绘图:

fig, ax = plt.subplots()

s = ax.scatter([], [])

ax.set_xlim(-2, frames+2)
ax.set_ylim(min(y) - 1, max(y) + 1)

def update(i):
    s.set_offsets(np.column_stack([data[i][0], data[i][1]]))
    s.set_facecolor(next(colors))

ani = animation.FuncAnimation(fig, update, frames=frames, interval=100)
ani.save('set.gif', writer='pillow')

或“清除”方法:

fig, ax = plt.subplots()

def update(i):
    ax.clear()
    ax.scatter(data[i][0], data[i][1], c=next(colors))
    ax.set_xlim(-2, frames+2)
    ax.set_ylim(min(y) - 1, max(y) + 1)

ani = animation.FuncAnimation(fig, update, frames=frames, interval=100)
ani.save('clear.gif', writer='pillow')

要获得此图形:

使用%%time,我们可以看到清除和重新绘制需要(超过)两倍的时间:

  • 对于set...Wall time: 1.33 s
  • 透明:Wall time: 2.73 s

使用frames参数在不同比例下测试这一点。对于较小的动画(较少的帧/数据),两种方法之间的时间差是无关紧要的(对我来说,有时会导致我更喜欢清除方法)。但对于较大的情况,使用set_...可以保存大量时间。

6g8kf2rb

6g8kf2rb4#

事情是这样的我曾经是Qt和Matlab的用户,我不太熟悉matplotlib上的动画系统。
但我确实找到了一种方法,可以制作任何你想要的动画,就像在matlab中一样。它真的很强大。不需要检查模块引用,你可以很好地绘制任何你想要的。所以我希望它能帮上忙。
基本思想是在PyQt中使用时间事件(我相信Python上的其他GUI系统,如wxPython和TraitUi,也有相同的内部机制来响应事件。我只是不知道如何)。每次调用PyQt的Timer事件时,我都会刷新整个画布并重新绘制整个图片,我知道速度和性能可能会受到缓慢的影响,但并没有那么多。
这里有一个小例子:

import sys
from PyQt4 import QtGui

from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas

import numpy as np

class Monitor(FigureCanvas):
    def __init__(self):
        self.fig = Figure()
        self.ax = self.fig.add_subplot(111)

        FigureCanvas.__init__(self, self.fig)
        self.x = np.linspace(0,5*np.pi,400)
        self.p = 0.0
        self.y = np.sin(self.x+self.p)

        self.line = self.ax.scatter(self.x,self.y)

        self.fig.canvas.draw()

        self.timer = self.startTimer(100)

    def timerEvent(self, evt):
        # update the height of the bars, one liner is easier
        self.p += 0.1
        self.y = np.sin(self.x+self.p)
        self.ax.cla()
        self.line = self.ax.scatter(self.x,self.y)

        self.fig.canvas.draw()


if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    w = Monitor()
    w.setWindowTitle("Convergence")
    w.show()
    sys.exit(app.exec_())

您可以在

self.timer = self.startTimer(100)

我就像你谁想要使用动画散点图,使排序动画。但我就是找不到所谓的“设置”功能。所以我刷新了整个画布。
希望有帮助。

hl0ma9xz

hl0ma9xz5#

为什么不试试

import numpy as np
import matplotlib.pyplot as plt

x=np.random.random()
y=np.random.random()

fig, ax = plt.subplots()
ax.scatter(x,y,color='teal')
ax.scatter(y,x,color='crimson')
ax.set_xlim([0,1])
ax.set_ylim([0,1])

for i in np.arange(50):
    x=np.random.random()
    y=np.random.random()
    bha=ax.scatter(x,y)
    plt.draw()
    plt.pause(0.5)
    bha.remove()

plt.show()

相关问题