翻译:疯狂的技术宅
https://towardsdatascience.co...


更多文章请关注微信公众号:硬核智能


动画是一种展示现象的有趣方式。相对于静态图表,人类总是容易被动画和交互式图表所吸引。在描述多年来的股票价格、过去十年的气候变化、季节性和趋势等时间序列数据时,动画更有意义,因为我们可以看到特定的参数是怎样随时间变化的。

上面的图是雨滴的模拟并且已经使用 Matplotlib 库实现,该库是一个广为人知的祖父级别的 python 可视化包。 Matplotlib 通过对 50 个散点的比例和透明度进行设置来模拟雨滴。今天,Python 拥有大量强大的可视化工具,如 Plotly、Bokeh、Altair等等。这些库能够实现最先进的动画和交互特性。尽管如此,本文的目的是强调这个库的另一个方面,这个方面没有人进行过太多的探索,这就是动画


概述

Matplotlib 是一个广受欢迎的 Python 2D 绘图库。很多人都是从 Matplotlib 开始数据可视化之旅的。可以使用matplotlib轻松生成图表、直方图、功率谱,条形图,错误图表,散点图等。它还与 Pandas 和 Seaborn 等库无缝集成,创造出更加复杂的可视化效果。

matplotlib 的优点是:

  • 它的设计类似于 MATLAB,因此很容易在在两者之间切换。
  • 在后端进行渲染。
  • 可以重现任何图表(需要一点努力)。
  • 已经存在了十多年,拥有庞大的用户群。

然而,也有一些方面 Matplotlib 落后于同类的库。

  • Matplotlib 有一个过于冗长的规则 API。
  • 有时候风格很差。
  • 对 Web 和交互式图表的支持不佳。
  • 对于大型复杂数据而言通常很慢。

这份复习资料是来自 Datacamp 的 Matplotlib 小抄,你可以通过它来提高自己的基础知识。


动画

Matplotlib 的 animation 基类负责处理动画部分。它提供了一个构建动画功能的框架。使用下面两个接口来实现:

  • FuncAnimation 通过重复调用函数 func 来产生动画。
  • ArtistAnimation: 动画使用一组固定的 Artist 对象。

但是,在这两个接口中,FuncAnimation 是最方便使用的。你可以通过阅读文档 得到的更多信息,因为我们只关注 FuncAnimation 工具。

要求

  • 安装 numpymatplotlib
  • 要将动画保存为 mp4 或 gif,需要安装 ffmpegimagemagick

准备好之后,我们就可以在 Jupyter note 中开始创建第一个动画了。可以从 Github 得到本文的代码。

基本动画:移动的正弦波

我们先用 FuncAnimation 创建一个在屏幕上移动的正弦波的动画。动画的源代码来自 Matplotlib 动画教程。首先看一下输出,然后我们会分析代码以了解幕后的原理。

import numpy as npfrom matplotlib import pyplot as pltfrom matplotlib.animation import FuncAnimationplt.style.use('seaborn-pastel')fig = plt.figure()ax = plt.axes(xlim=(0, 4), ylim=(-2, 2))line, = ax.plot([], [], lw=3)def init():    line.set_data([], [])    return line,def animate(i):    x = np.linspace(0, 4, 1000)    y = np.sin(2 * np.pi * (x - 0.01 * i))    line.set_data(x, y)    return line,anim = FuncAnimation(fig, animate, init_func=init,                               frames=200, interval=20, blit=True)anim.save('sine_wave.gif', writer='imagemagick')

  • 在第(7-9)行中,我们只需在图中创建一个带有单个轴的图形窗口。然后创建一个空的行对象,它实际上是动画中要修改的对象。稍后将用数据对行对象进行填充。
  • 在第(11-13)行中,我们创建了 init 函数,它将使动画开始。 init 函数对数据进行初始化并设置轴限制。
  • 在第(14-18)行中,我们最终定义了动画函数,该函数将帧编号( i )作为参数并创建正弦波(或任何其他动画),这取决于 i 的值。此函数返回一个已修改的绘图对象的元组,它告诉动画框架哪些部分应该属于动画。
  • 在第 20 行中,我们创建了实际的动画对象。 blit 参数确保只重绘那些已经改变的图块。

这是在 Matplotlib 中创建动画的基本方法。通过对代码进行一些调整,可以创建有趣的可视化图表。接下来看看更多的可视化案例。


一个不断增长的线圈

同样,在 GeeksforGeeks 中有一个很好的例子。现在让我们在 matplotlib 的 animation 类的帮助下创建一个缓慢展开的动圈。该代码非常类似于正弦波图,只需稍作调整即可。

import matplotlib.pyplot as plt import matplotlib.animation as animation import numpy as np plt.style.use('dark_background')fig = plt.figure() ax = plt.axes(xlim=(-50, 50), ylim=(-50, 50)) line, = ax.plot([], [], lw=2) # initialization function def init():     # creating an empty plot/frame     line.set_data([], [])     return line, # lists to store x and y axis points xdata, ydata = [], [] # animation function def animate(i):     # t is a parameter     t = 0.1*i         # x, y values to be plotted     x = t*np.sin(t)     y = t*np.cos(t)         # appending new points to x, y axes points list     xdata.append(x)     ydata.append(y)     line.set_data(xdata, ydata)     return line,     # setting a title for the plot plt.title('Creating a growing coil with matplotlib!') # hiding the axis details plt.axis('off') # call the animator     anim = animation.FuncAnimation(fig, animate, init_func=init,                             frames=500, interval=20, blit=True) # save the animation as mp4 video file anim.save('coil.gif',writer='imagemagick') 


实时更新图表

在绘制动态数量(如库存数据,传感器数据或任何其他时间相关数据)时,实时更新的图表会派上用场。我们绘制了一个简单的图表,当有更多数据被输入系统时,该图表会自动更新。下面让我们绘制一家假想公司在一个月内的股票价格。

# importing librariesimport matplotlib.pyplot as pltimport matplotlib.animation as animationfig = plt.figure()# creating a subplot ax1 = fig.add_subplot(1,1,1)def animate(i):    data = open('stock.txt','r').read()    lines = data.split('\n')    xs = []    ys = []       for line in lines:        x, y = line.split(',') # Delimiter is comma            xs.append(float(x))        ys.append(float(y))        ax1.clear()    ax1.plot(xs, ys)    plt.xlabel('Date')    plt.ylabel('Price')    plt.title('Live graph with matplotlib')            ani = animation.FuncAnimation(fig, animate, interval=1000) plt.show()

现在,打开终端并运行 python 脚本。你将得到如下图所示的图表,该图表会自动更新:

这里的间隔是 1000 毫秒或一秒。


3D 图动画

创建 3D 图形是很常见的,但如果我们想要为这些图形的视角设置动画,该怎么办呢?我们的想法是更改摄像机视图,然后用每个生成的图像来创建动画。在 Python Graph Gallery 上有一个很好的例子。

在与 jupyter notebook 相同的目录中创建名为 volcano 的文件夹。所有图片文件都将存储在这里,然后将在动画中使用。

# libraryfrom mpl_toolkits.mplot3d import Axes3Dimport matplotlib.pyplot as pltimport pandas as pdimport seaborn as sns# Get the data (csv file is hosted on the web)url = 'https://python-graph-gallery.com/wp-content/uploads/volcano.csv'data = pd.read_csv(url)# Transform it to a long formatdf=data.unstack().reset_index()df.columns=["X","Y","Z"]# And transform the old column name in something numericdf['X']=pd.Categorical(df['X'])df['X']=df['X'].cat.codes# We are going to do 20 plots, for 20 different anglesfor angle in range(70,210,2):# Make the plot    fig = plt.figure()    ax = fig.gca(projection='3d')    ax.plot_trisurf(df['Y'], df['X'], df['Z'], cmap=plt.cm.viridis, linewidth=0.2)    ax.view_init(30,angle)    filename='Volcano/Volcano_step'+str(angle)+'.png'    plt.savefig(filename, dpi=96)    plt.gca()

这将会在 Volcano 文件夹中创建多个 PNG 文件。现在用 ImageMagick 将它们转换为动画。打开终端并切换到 Volcano 目录下输入以下命令:

convert -delay 10 Volcano*.png animated_volcano.gif


使用 Celluloid 模块创建的动画

Celluloid 是一个Python模块,它简化了在 matplotlib 中创建动画的过程。这个库创建一个 matplotlib 图,并从中再创建一个 Camera。然后重新处理数据,并在创建每个帧后,用 camera 拍摄快照。最后创建包含所有帧的动画。

安装

pip install celluloid

以下是使用 Celluloid 模块的一些示例。

Minimal

from matplotlib import pyplot as pltfrom celluloid import Camerafig = plt.figure()camera = Camera(fig)for i in range(10):    plt.plot([i] * 10)    camera.snap()animation = camera.animate()animation.save('celluloid_minimal.gif', writer = 'imagemagick')

Subplot

import numpy as npfrom matplotlib import pyplot as pltfrom celluloid import Camerafig, axes = plt.subplots(2)camera = Camera(fig)t = np.linspace(0, 2 * np.pi, 128, endpoint=False)for i in t:    axes[0].plot(t, np.sin(t + i), color='blue')    axes[1].plot(t, np.sin(t - i), color='blue')    camera.snap()    animation = camera.animate()  animation.save('celluloid_subplots.gif', writer = 'imagemagick')

Legend

import matplotlibfrom matplotlib import pyplot as pltfrom celluloid import Camerafig = plt.figure()camera = Camera(fig)for i in range(20):    t = plt.plot(range(i, i + 5))    plt.legend(t, [f'line {i}'])    camera.snap()animation = camera.animate()animation.save('celluloid_legends.gif', writer = 'imagemagick')


总结

动画有助于突出显示无法通过静态图表轻松传达的某些功能。尽管如此,不必要的过度使用有时会使事情复杂化,应该明智地使用数据可视化中的每个功能以产生最佳效果。

更多文章请关注微信公众号:硬核智能