用-Matplotlib-库生成动画图表

35次阅读

共计 5624 个字符,预计需要花费 15 分钟才能阅读完成。

翻译:疯狂的技术宅
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 np
from matplotlib import pyplot as plt
from matplotlib.animation import FuncAnimation
plt.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 libraries
import matplotlib.pyplot as plt
import matplotlib.animation as animation


fig = 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 的文件夹。所有图片文件都将存储在这里,然后将在动画中使用。

# library
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import pandas as pd
import 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 format
df=data.unstack().reset_index()
df.columns=["X","Y","Z"]

# And transform the old column name in something numeric
df['X']=pd.Categorical(df['X'])
df['X']=df['X'].cat.codes

# We are going to do 20 plots, for 20 different angles
for 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 plt
from celluloid import Camera

fig = 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 np
from matplotlib import pyplot as plt
from celluloid import Camera

fig, 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 matplotlib
from matplotlib import pyplot as plt
from celluloid import Camera

fig = 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')


总结

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

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

正文完
 0