利用Python的matplotlib库绘制动态图形

一、python 绘制动画图

python绘制动态图形是数据可视化更直观、更好看的一种方式,matplotlib工具包是常用的绘图工具,也可以用来绘制动态图形。本文介绍四种绘制动态图形的方法,包括生成图形的代码和动态图形演示示例。

用matplotlib工具包创建动画图有两种方法:

  • 使用 pause() 函数
  • 使用 FuncAnimation() 函数

动画柱状图,使用FuncAnimation() 函数

代码如下:

from matplotlib import pyplot as pltfrom matplotlib.animation import FuncAnimation, writersimport numpy as np  fig = plt.figure(figsize = (7,5))axes = fig.add_subplot(1,1,1)axes.set_ylim(0, 300)palette = ['blue', 'red', 'green',            'darkorange', 'maroon', 'black']  y1, y2, y3, y4, y5, y6 = [], [], [], [], [], []  def animation_function(i):    y1 = i    y2 = 5 * i    y3 = 3 * i    y4 = 2 * i    y5 = 6 * i    y6 = 3 * i      plt.xlabel("Country")    plt.ylabel("GDP of Country")          plt.bar(["India", "China", "Germany",              "USA", "Canada", "UK"],            [y1, y2, y3, y4, y5, y6],            color = palette)  plt.title("Bar Chart Animation")  animation = FuncAnimation(fig, animation_function,                           interval = 50)plt.show()

如下图:


横向柱状跑图 (Horizontal Bar Chart Race),使用FuncAnimation() 函数

以下代码是绘制世界1500年-2018年主要城市人口变化横向柱状跑图,需要数据集文件city_populations.csv评论区留言。

程序代码如下:

import pandas as pdimport matplotlib.pyplot as pltimport matplotlib.ticker as tickerfrom matplotlib.animation import FuncAnimationdf = pd.read_csv('city_populations.csv',usecols=['name', 'group', 'year', 'value'])colors = dict(zip(['India','Europe','Asia','Latin America','Middle East','North America','Africa'],['#adb0ff', '#ffb3ff', '#90d595','#e48381', '#aafbff', '#f7bb5f','#eafb50']))group_lk = df.set_index('name')['group'].to_dict()def draw_barchart(year):dff = df[df['year'].eq(year)].sort_values(by='value',ascending=True).tail(10)ax.clear()ax.barh(dff['name'], dff['value'],color=[colors[group_lk[x]] for x in dff['name']])dx = dff['value'].max() / 200for i, (value, name) in enumerate(zip(dff['value'],dff['name'])):ax.text(value-dx, i, name,size=14, weight=600,ha='right', va='bottom')ax.text(value-dx, i-.25, group_lk[name],size=10, color='#444444',ha='right', va='baseline')ax.text(value+dx, i, f'{value:,.0f}',size=14, ha='left', va='center')# polished stylesax.text(1, 0.4, year, transform=ax.transAxes,color='#777777', size=46, ha='right',weight=800)ax.text(0, 1.06, 'Population (thousands)',transform=ax.transAxes, size=12,color='#777777')ax.xaxis.set_major_formatter(ticker.StrMethodFormatter('{x:,.0f}'))ax.xaxis.set_ticks_position('top')ax.tick_params(axis='x', colors='#777777', labelsize=12)ax.set_yticks([])ax.margins(0, 0.01)ax.grid(which='major', axis='x', linestyle='-')ax.set_axisbelow(True)ax.text(0, 1.12, 'The most populous cities in the world from 1500 to 2018',transform=ax.transAxes, size=24, weight=600, ha='left')ax.text(1, 0, ' ',transform=ax.transAxes, ha='right', color='#777777',bbox=dict(facecolor='white', alpha=0.8, edgecolor='white'))plt.box(False)plt.show()fig, ax = plt.subplots(figsize=(15, 8))animator = FuncAnimation(fig, draw_barchart,frames = range(1990, 2019))plt.show()



散点图动画,使用FuncAnimation()函数

在本例中, 使用random 数据和自定义函数animation_func()

from matplotlib import pyplot as pltfrom matplotlib.animation import FuncAnimationimport randomimport numpy as npx = []y = []colors = []fig = plt.figure(figsize=(7,5))def animation_func(i):x.append(random.randint(0,100))y.append(random.randint(0,100))colors.append(np.random.rand(1))area = random.randint(0,30) * random.randint(0,30)plt.xlim(0,100)plt.ylim(0,100)plt.scatter(x, y, c = colors, s = area, alpha = 0.5)animation = FuncAnimation(fig, animation_func,interval = 100)plt.show()

如下图:


使用 pause() 函数绘制动态直线

matplotlib工具包的pyplot模块中有pause()函数,可用来设置时间间隔参数,达到绘制直线的动画效果。

代码如下:

from matplotlib import pyplot as pltx = []y = []for i in range(100):x.append(i)y.append(i)# Mention x and y limits to define their rangeplt.xlim(0, 100)plt.ylim(0, 100)# Ploting graphplt.plot(x, y, color = 'green')plt.pause(0.01)plt.show()

如下图:


使用 FuncAnimation() 绘制动态直线

FuncAnimation() 函数本身并不能创建动画效果,而是通过生成一系列不同参数的图片来实现动画效果.

Syntax: FuncAnimation(figure, animation_function, frames=None, init_func=None, fargs=None, save_count=None, *, cache_frame_data=True, **kwargs)

在这个实例代码中,使用FuncAnimation函数创建一条直线的简单动画效果,只需要调整参数即刻。

from matplotlib import pyplot as pltfrom matplotlib.animation import FuncAnimationimport numpy as np  x = []y = []  figure, ax = plt.subplots()  # Setting limits for x and y axisax.set_xlim(0, 100)ax.set_ylim(0, 12)  # Since plotting a single graphline,  = ax.plot(0, 0)   def animation_function(i):    x.append(i * 15)    y.append(i)      line.set_xdata(x)    line.set_ydata(y)    return line,  animation = FuncAnimation(figure,                          func = animation_function,                          frames = np.arange(0, 10, 0.1),                           interval = 10)plt.show()

如下图:

二、如何把python绘制的动态图形保存为gif文件或视频

使用Matplotlib中的matplotlib.animation 方法可以绘制更好看、更有吸引力的动画图形,那如何把这种动画图形保存为Gif动画文件或视频文件呢?本文简述与之相关的方法。把python程序中绘制的动画图形保存为视频格式或gif格式文件,以便播放或发送给其他人,或者插入找文档或网页中。本文分两部分,先介绍python程序绘制简单的动态图形或动画,然后再介绍如何把绘制的动态图形或动画保存为gif格式文件或视频文件。

先用 Matplotlib.animation 类中的函数 FuncAnimation 创建动态图和动画,如动态折线、柱状图动画,另外还需要使用 figure 函数和 animation 函数,这些都是python使用matplotlib绘制图形时常用的方法。

1. 创建动态折线图

1)绘制动态折线图,代码如下:

import randomimport matplotlibimport matplotlib.pyplot as pltfrom matplotlib.animation import FuncAnimationfig = plt.figure(figsize=(15,15))x,y = [], []index= count()def animate(i):    x.append(next(index))    y.append(random.randint(2,20))    plt.style.use("ggplot")        plt.plot(x,y)ani = FuncAnimation(fig, animate, interval=300)plt.show()

结果如图1:

主要代码行说明:

以上代码块中 “ani = FuncAnimation(fig, animate, interval=300)” 这一行中,FuncAnimation() 有三个参数:

1) fig,表示图形参数

指容纳plot图形的对象, 需要创建该对象,或者调用 matplotlib.pyplot.gcf() 函数表示获得当前图形;

2) animate 自定义函数

这是 FuncAnimation 中的动画自定义函数, 要想获得动画图形就要把该参数设定为“animate”,图形就会根据数据持续更新,注意创建图形和数据更新缺一不可。

3) 画面间隔参数 interval

该参数指定画面的更新速度,单位是毫秒。interval=1000 表示该函数运行动画函数,并且每秒钟更新一次。

以上代码块中 “plt.style.use("ggplot")” 这一行,指定动画图形的风格为 “ggplot”,要想了解更多的图形风格,可使用以下代码:

import matplotlib.pyplot as pltprint(plt.style.available)

输入结果如下:

bmhclassicdark_backgroundfastfivethirtyeightggplotgrayscaleseaborn-brightseaborn-colorblindseaborn-dark-paletteseaborn-darkseaborn-darkgridseaborn-deepseaborn-mutedseaborn-notebookseaborn-paperseaborn-pastelseaborn-posterseaborn-talkseaborn-ticksseaborn-whiteseaborn-whitegridseabornSolarize_Light2tableau-colorblind10_classic_test

上述代码块输出的图形结果中,两个数轴都是不固定的,这与 Matplotlib Axes Setting 有关,plot函数中也没有定义线的颜色。

2.创建动画图形

代码如下:

import matplotlib.pyplot as pltfrom matplotlib.animation import FuncAnimation%matplotlib qtfig = plt.figure(figsize=(6,4))axes = fig.add_subplot(1,1,1)plt.title("Dynamic Axes")y1 = [random.randint(-10,10)+(i**1.6)/(random.randint(9,12)) for i in range(0,280,2)]t = range(len(y1))x,y=[], []def animate(i):    x.append(t[i])    y.append((y1[i]))    plt.xlim(i-30,i+3)    axes.set_ylim(y1[i]-100, y1[i]+100)    plt.plot(x,y, scaley=True, scalex=True, color="red")anim = FuncAnimation(fig, animate, interval=100)

输出结果如图2:

图2 动态数轴动画


保存动态图形

保存Matplotlib绘制的动画可能会出现一些小故障。下面介绍一下常用的选项和参数,既可以节省保存动画所需的时间,又可以尽可能地减少故障;可以把python代码生成的动画保存为个人需要的格式,如gif、mp4、avi、mov等文件格式,这取决于保存时选择对应的参数。

  • 保存为GIF文件--图1,代码如下:
f = r"d:\animation.gif" writergif = animation.PillowWriter(fps=30) anim.save(f, writer=writergif)

这段代码中,常用的选项有 ImageMagick 和 PillowWriter。

对于windows 操作系统来说,使用 ImageMagick 方法一般要先安装相关程序包,并且在保存动画为 GIF 文件时,建议使用 Write Instance,还是比较复杂;如果是Unix操作系统,ImageMagick一般情况下都已经安装了,使用 ImageMagick 方法 就方便。因此,建议windows用户使用 PillowWriter 方法。

定义 gif 图形的帧数:

修改 FuncAnimation函数中的 save_count 参数的值,设定 GIF 文件的帧数,默认为 100 帧,代码如下:

anim = animation.FuncAnimation(figure, func=update_figure, fargs=(bar_rects, iteration), frames=generator, interval=100, repeat=True, save_count=1500)

示例代码中,把GIF文件的帧数修改为 1500 帧。

  • 保存为视频文件

把调用matplotlib的方法生成的动画保存为视频文件,需要 ffmpeg.exe 安装程序,可到官网下载,如下图:


特别注意,安装后要把该运行程序的路径 ffmpeg\bin\ffmpeg.exe 添加到环境变量中,此路径一定要正确 并且一定是指向可执行文件,而不仅仅是该文件所在的文件夹。在生成动画的python程序块中要加入以下代码行:

import matplotlib as mpl mpl.rcParams['animation.ffmpeg_path'] = r'C:\Users\xx\Desktop\ffmpeg\bin\ffmpeg.exe'

接下来,把 matplotlib 绘制的动画保存为 mp4 格式的视频文件,代码如下:

f = r"d:\animation.mp4" writervideo = animation.FFMpegWriter(fps=60) anim.save(f, writer=writervideo)

定义视频的尺寸:

视频的尺寸就是用 matplotlib 工具包绘制图形的窗口大小,调整对应窗口的大小即刻,示例代码如下:

plt.subplots(figsize=(12,8))

三、使用 matplotlib 绘制动画

在python编程中,用matplotlib绘图工具包绘制动画图形。动画是数据图形化的一种方法,视觉冲击力很强,给人留下了深刻的印象。动画图形或者可交互图形和静态图形相比,对人的诱惑力更强。像股票数据,气象数据,季节性和趋势这样的数据,用动画来表示会让人更容易明白。

matplotlib库的主要特征

  • 有很多后台渲图工具组成
  • 它可以重新生成任何形式的绘图(只需要略做修改)
  • 该工具包历史悠久,比较成熟
  • matplotlib和MATLAB绘图可以转换

然而,matplotlib库也有一些缺点:

  • matplotlib有一个必要的API,这类接口一般比较冗长
  • 有时一些默认的绘图风格比较粗陋
  • 对web图形和交互图形的支持不足
  • 绘制大规模数据和复杂数据的图形比较慢

matplotlib的animation基类和使用条件

matplotlib绘制动画图形就是使用animation基类的方法,它提供了构建动画功能的基本框架,使用的主要接口有两个:

  • FuncAnimation: 通过重复调用函数 func 绘制动画
  • ArtistAnimation: 使用一组固定的Artist对象实现动画效果

这两个接口相比之下,使用FuncAnimation最方便。

使用matplotlib绘制动画需要安装必要的工具包和程序:

  • 需要安装numpy和matplotlib工具包
  • 把程序生成的动画保存为mp4格式或gif格式文件,需要安装 ffmpegimagemagick。

更详细的内容可阅读文章--《如何把python绘制的动态图形保存为gif文件或视频》

实例1:移动的正弦曲线

代码如下:

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

输出的图形结果如下图所示:

实例2:螺旋线动画

代码如下:

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) #初始化函数 def init(): #创建空的 plot/frame line.set_data([], []) return line, # 存储x轴和y轴坐标值的列表xdata, ydata = [], [] # 定义动画函数 def animate(i): # 参数 t t = 0.1*i # 根据x和y的值绘制图形 x = t*np.sin(t) y = t*np.cos(t) # 把新的x轴和y轴的值追加到列表 xdata.append(x) ydata.append(y) line.set_data(xdata, ydata) return line, # 制定图形的标题plt.title('Creating a growing coil with matplotlib!') # 隐藏x轴上的数据标识 plt.axis('off') # 调用动画函数 anim = animation.FuncAnimation(fig, animate, init_func=init, frames=500, interval=20, blit=True) # 把动画保存为gif文件 anim.save('coil.gif',writer='imagemagick') 

输出结果如下图所示:

实例3:动画实时更新

绘制股票行情数据、传感器数据等实时图形就需要实时更新动画。

代码如下:

import matplotlib.pyplot as pltimport matplotlib.animation as animationfig = plt.figure()# 创建子图ax1 = fig.add_subplot(1,1,1)def animate(i):    data = open('stock.txt','r').read()    lines = data.split('
')    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()

输出结果如下图所示:

实例4:3D动画

代码如下:

from mpl_toolkits.mplot3d import Axes3Dimport matplotlib.pyplot as pltimport pandas as pdimport seaborn as sns# 获取数据url = 'https://python-graph-gallery.com/wp-content/uploads/volcano.csv'data = pd.read_csv(url)# 数据转换df=data.unstack().reset_index()df.columns=["X","Y","Z"]# 转换列名称df['X']=pd.Categorical(df['X'])df['X']=df['X'].cat.codes# 以20个不同的角度,绘制20个图形for angle in range(70,210,2):# 绘制图形    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()

以上代码创建多个PNG格式文件,要使用ImageMagick把这些图片转换为动画,即在命令行窗口输入下命令:

convert -delay 10 Volcano*.png animated_volcano.gif

输出结果如下图:

实例5:使用Celluloid工具包绘制动画

Celluloid工具包简化了用matplotlib绘制动画的过程,该工具包只创建一个matplotlib的figure对象和camera对象,然后创建的每一帧画面都重复使用这个figure和camera对象,就像是用照相机在抓拍图形。最后,用所有抓拍的画面帧就构成了动画。

安装Celluloid工具包:

pip install celluloid

使用celluloid工具包的绘图实例如下:

实例5-1,代码如下:

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

输出结果如下图:

实例5-2:子图,代码如下:

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

输出结果如下图:


实例5-3:图例,代码如下:

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

输出结果如下图:

四、Python - 2D/3D动画案例

本文以九个ETF基金行情数据为例,绘制出交易这九个基金的收益变化(行情数据有随机数生成)。如果想观察哪只基金的收益高于或低于沪深300ETF,就可以从2D/3D的动画图上看出来。下面分四部分来讲述。

导入必要的工具包

import numpy as npimport pandas as pd#import csv#from csv import writer# 绘图import matplotlib.pyplot as pltfrom matplotlib import animation, rcfrom matplotlib.cm import get_cmapfrom mpl_toolkits.mplot3d import Axes3Dfrom matplotlib.font_manager import FontPropertiesfrom matplotlib.collections import LineCollectionfrom matplotlib.colors import ListedColormapfrom mpl_toolkits.mplot3d.art3d import Line3DCollectionplt.rcParams['font.sans-serif'] = ['SimHei']plt.rcParams['axes.unicode_minus'] = Falseimport matplotlib as mpl mpl.rcParams['animation.ffmpeg_path'] = r'C:\ffmpeg\bin\ffmpeg.exe'# 设置动画类型rc('animation', html='html5')

导入数据

用随机数生成九只ETF基金的行情价格数据

index_returns = np.random.normal(loc=1e-4, scale=5e-3, size=(783, 9))index_returns = np.vstack((np.zeros(shape=(1, 9)) + 100, index_returns))# 累计收益index_prices = np.cumprod(1 + index_returns, axis=0)# 选择时间窗口window = 261indexes_rolling = np.zeros(shape=(index_prices.shape[0]-window, 9))# 生成滚动收益for i in range(window, index_prices.shape[0], 1):    indexes_rolling[i-window] = (index_prices[i]/index_prices[i-window]) - 1# 构成 dataframe 数据index = pd.date_range('2019-01-01', periods=index_prices.shape[0]-window, freq='B')columns = ['智能汽车|515250', '新能源车|515030 ', '半 导 体|512480',                  ' 银  行 |512800', ' 沪深300|510300',  '创 新 药|159992',                  ' 光  伏 |515790', '信息技术|159939', '食品饮料|515170']indexes_rolling = pd.DataFrame(indexes_rolling, index=index, columns=columns)

2D动画

  1. 创建2D动画的规格以及格式,即指定该动画图形的大小,颜色,图例等内容。

代码如下:

# 创建图形fig, axes = plt.subplots(1, 2, figsize=(18, 6), gridspec_kw={'width_ratios': [.9, .1]})fig.patch.set_alpha(1)# 设置:右边的图形不可见,只更新左边部分axes[1].axis('off')ax = axes[0]# 获取 cmap cmap = get_cmap('RdYlGn')# 数据切分current_slice = indexes_rolling.values[:261, :]index_names = indexes_rolling.columnsindex_dates = indexes_rolling.index# 保存各ETF基金数据的列表lines = []for i in range(current_slice.shape[1]):    # 获取坐标    x = np.array(np.arange(current_slice.shape[0]))    y = np.array(current_slice[:, i])    # 绘制不同颜色的点和线段    points = np.array([x, y]).T.reshape(-1, 1, 2)    segments = np.concatenate([points[:-1], points[1:]], axis=1)    # 指定连续值,映射数据点的颜色    norm = plt.Normalize(-0.19, 0.19)    lc = LineCollection(segments, cmap=cmap, norm=norm)    # 设置颜色值    lc.set_array(y)    lc.set_linewidth(2)    lc.set_color(cmap(y[-1] * 2.5 + 0.5))    lc.set_label(index_names[i])    lines.append(ax.add_collection(lc))# 添加背景的网格ax.legend(loc='center right', bbox_to_anchor=(1.2, 0.5), fancybox=True, facecolor=(.95,.95,.95,1), framealpha=1, shadow=False, frameon=True, ncol=1, columnspacing=0, prop={'family': 'SimHei'})ax.yaxis.grid(color='gray', linestyle='dashed')ax.xaxis.grid(color='gray', linestyle='dashed')ax.set_xlim(0, current_slice.shape[0]-1)ax.set_ylim(-0.39, 0.39)ax.set_yticklabels(['{:.0%}'.format(val) for val in ax.get_yticks()])ax.set_ylabel('滚动收益 - 1年')ax.set_xlabel('日 期')ax.set_xticklabels([index_dates[int(val)].strftime('%m/%y') for val in ax.get_xticks()])#ax.set_facecolor((0, 0, 0, 1.0)) # 背景色ax.set_facecolor((0.05, 0.05, 0.65, 1))# 演示图形plt.show()

演示图形如下:

2. 定义更新以上2D图形的函数

代码如下:

def update_lines_2D(num, data, columns, dates, cmap, lines, ax):            # 获得切分数据    current_slice = data[num:261+num, :]    current_dates = dates[num:261+num]        for i in range(current_slice.shape[1]):        # 获取坐标值        x = np.array(np.arange(current_slice.shape[0]))        y = np.array(current_slice[:, i])        # 绘制不同颜色的点和线段        points = np.array([x, y]).T.reshape(-1, 1, 2)        segments = np.concatenate([points[:-1], points[1:]], axis=1)        # 指定连续值,映射数据点的颜色        norm = plt.Normalize(-0.22, 0.22)                lines[i].set_segments(segments)        lines[i].set_array(y)        #lines[i].set_color(cmap(y[-1] * 2.5 + 0.5))        lines[i].set_color(cmap(y[-1] * 2.5 + 0.5))        # 动态更新数据和标识    ax.set_xticklabels([dates[int(val)+num].strftime('%m/%y') for val in ax.get_xticks()[:-1]] + [''])    ax.legend(loc='center right', bbox_to_anchor=(1.2, 0.5), fancybox=True, facecolor=(.95,.95,.95,1), framealpha=1, shadow=False, frameon=True, ncol=1, columnspacing=0, prop={'family': 'SimHei'})          return lines# 初始化图形的各行的数据def init_lines_2D():       for line in lines:        line.set_array([])    return lines

3.创建2D动画

代码如下:

line_ani = animation.FuncAnimation(fig=fig,                                    func=update_lines_2D,                                    # frames=30,                                   frames=indexes_rolling.shape[0]-261,                                    init_func=init_lines_2D,                                    fargs=(indexes_rolling.values, indexes_rolling.columns, indexes_rolling.index, cmap, lines, ax),                                   interval=75,                                    blit=True)# 演示2D动画line_ani

演示结果如下图:

4.保存动画为GIF格式

代码如下:

# 演示数据的变化progress_callback = lambda i, n: print('Saving frame {:.0%}'.format(i/n)) if int((i/n) * 100) % 10 == 0 else None# 保存动画line_ani.save('./2D_animation.gif', writer='imagemagick', fps=14, dpi=80, codec='h264', bitrate=2048, progress_callback=progress_callback)

3D动画

  1. 创建3D动画的规格以及格式,即指定该动画图形的大小,颜色,图例等内容。

代码如下:

# 创建图形fig = plt.figure(figsize=(14.4, 9))ax = fig.add_subplot(111, projection='3d')fig.patch.set_alpha(1)# 获得 cmap 的值cmap = get_cmap('RdYlGn')# 切分数据current_slice = indexes_rolling.values[:261, :]index_names = indexes_rolling.columnsindex_dates = indexes_rolling.index# 保存各ETF基金数据的线的列表lines = []for i in range(current_slice.shape[1]):    # 获取坐标值    x = np.array(np.arange(current_slice.shape[0]))    y = np.tile(i, current_slice.shape[0])    z = np.array(current_slice[:, i])    #  绘制不同颜色的点和线段    points = np.array([x, y, z]).T.reshape(-1, 1, 3)    segments = np.concatenate([points[:-1], points[1:]], axis=1)    # 指定连续值,映射数据点的颜色    norm = plt.Normalize(-0.19, 0.19)    lc = Line3DCollection(segments, cmap=cmap, norm=norm, zorder=current_slice.shape[1]-i)    # 动态更新数据和标识    lc.set_array(z)    lc.set_linewidth(2)    lc.set_color(cmap(z[-1] * 2.5 + 0.5))    lc.set_label(index_names[i])    lines.append(ax.add_collection(lc))# 添加动画背景的网格ax.legend(loc='center right', bbox_to_anchor=(1.1, 0.46), fancybox=True, facecolor=(.95,.95,.95,1), framealpha=1, shadow=False, frameon=True, ncol=1, columnspacing=0, prop={'family': 'SimHei'})ax.set_zlabel('滚动收益 1Y', labelpad=10)ax.set_zlim(-0.39, 0.39)ax.set_zticklabels([' '* 3 + '{:.0%}'.format(val) for val in ax.get_zticks()], fontdict={'verticalalignment': 'center', 'horizontalalignment': 'center'})ax.set_xlabel('Date', labelpad=30)ax.set_xlim(0, current_slice.shape[0]-1)ax.set_xticklabels([index_dates[int(val)].strftime('%m/%y') for val in ax.get_xticks()[:-1]] + [''], rotation=0, fontdict={'verticalalignment': 'top', 'horizontalalignment': 'center'})ax.set_yticks(np.arange(current_slice.shape[1]))ax.set_yticklabels([index_names[i] for i in range(current_slice.shape[1])], rotation=-15, fontdict={'verticalalignment': 'center', 'horizontalalignment': 'left'})# ax.w_xaxis.set_pane_color((0, 0, 0, 1.0))# ax.w_yaxis.set_pane_color((0, 0, 0, 1.0))# ax.w_zaxis.set_pane_color((0, 0, 0, 1.0))ax.w_xaxis.set_pane_color((0.05, 0.05, 0.65, 1)) #ax.set_facecolor((0.05, 0.05, 0.65, 1))ax.w_yaxis.set_pane_color((0.05, 0.05, 0.65, 1))ax.w_zaxis.set_pane_color((0.05, 0.05, 0.65, 1))ax.view_init(25, -60)# ------------------------------------------------------------------x_scale=1.8y_scale=1z_scale=1scale=np.diag([x_scale, y_scale, z_scale, 1.0])scale=scale*(1.0/scale.max())scale[3,3]=1.0def short_proj():    return np.dot(Axes3D.get_proj(ax), scale)ax.get_proj=short_projfig.subplots_adjust(left=0, right=1, bottom=0, top=1)# ------------------------------------------------------------------# 输出动画plt.show()

输出结果如下图所示:

2. 定义更新以上图形的函数

代码如下:

def update_lines_3D(num, data, columns, dates, cmap, lines, ax):        # 切分数据    current_slice = data[num:261+num, :]    current_dates = dates[num:261+num]         for i in range(current_slice.shape[1]):        # 获取坐标值        x = np.arange(current_slice.shape[0])        y = np.tile(i, current_slice.shape[0])        z = np.array(current_slice[:, i])        #  绘制不同颜色的点和线段        points = np.array([x, y, z]).T.reshape(-1, 1, 3)        segments = np.concatenate([points[:-1], points[1:]], axis=1)        # 指定连续值,映射数据点的颜色        norm = plt.Normalize(-0.19, 0.19)                lines[i].set_segments(segments)        lines[i].set_array(z)        lines[i].set_color(cmap(z[-1] * 2.5 + 0.5))    # 动态更新数据和标识    ax.set_xticklabels([dates[int(val)+num].strftime('%m/%y') for val in ax.get_xticks()[:-1]] + [''], rotation=0, fontdict={'verticalalignment': 'top', 'horizontalalignment': 'center'})    ax.legend(loc='center right', bbox_to_anchor=(1.1, 0.46), fancybox=True, facecolor=(.95,.95,.95,1), framealpha=1, shadow=False, frameon=True, ncol=1, columnspacing=0, prop={'family': 'SimHei'})        return linesdef init_lines_3D():    for line in lines:        line.set_array([])    return lines

3.创建3D动画

代码如下:

line_ani = animation.FuncAnimation(fig=fig,                                    func=update_lines_3D,                                    # frames=30,                                   frames=indexes_rolling.shape[0]-261,                                    init_func=init_lines_3D,                                    fargs=(indexes_rolling.values, indexes_rolling.columns, indexes_rolling.index, cmap, lines, ax),                                   interval=75,                                    blit=True)# 演示3D动画line_ani

演示结果如下图所示:

4.保存3D动画为GIF格式

progress_callback = lambda i, n: print('Saving frame {:.0%}'.format(i/n)) if int((i/n) * 100) % 10 == 0 else None# save the animationline_ani.save('./3D_animation.gif', writer='imagemagick', fps=14, dpi=80, codec='h264', bitrate=2048, progress_callback=progress_callback)

3D mesh 动画

  1. 创建3D mesh 动画的规格以及格式,即指定该动画图形的大小,颜色,图例等内容。

代码如下:

# 创建图形fig = plt.figure(figsize=(14.4, 9))ax = fig.add_subplot(111, projection='3d')fig.patch.set_alpha(1)# 获取 cmap 的值cmap = get_cmap('RdYlGn')# 切分数据# current_slice = indexes_rolling.values[:261, :]current_slice = indexes_rolling.values[:int(261/2), :]index_names = indexes_rolling.columnsindex_dates = indexes_rolling.index# 保存各ETF基金数据的线的列表lines = []for i in range(current_slice.shape[1]):    # 获取坐标值    x = np.array(np.arange(current_slice.shape[0]))    y = np.tile(i, current_slice.shape[0])    z = np.array(current_slice[:, i])    # 绘制不同颜色的点和线段    points = np.array([x, y, z]).T.reshape(-1, 1, 3)    segments = np.concatenate([points[:-1], points[1:]], axis=1)    # 指定连续值,映射数据点的颜色    norm = plt.Normalize(-0.19, 0.19)    lc = Line3DCollection(segments, cmap=cmap, norm=norm, zorder=current_slice.shape[1]-i)    # 设定颜色值    lc.set_array(z)    lc.set_linewidth(2)    lc.set_color(cmap(z[-1] * 2.5 + 0.5))    lc.set_label(index_names[i])    lines.append(ax.add_collection(lc))# 保存 mesh 线的列表mesh_lines = []for j in range(current_slice.shape[0]):    if j % 1 == 0:                # 获取坐标值        x = np.tile(j, current_slice.shape[1])        y = np.arange(current_slice.shape[1])        z = np.array(current_slice[j, :])        # 绘制不同颜色的点和线段        points = np.array([x, y, z]).T.reshape(-1, 1, 3)        segments = np.concatenate([points[:-1], points[1:]], axis=1)        # 指定连续值,映射数据点的颜色        norm = plt.Normalize(-0.19, 0.19)        lc = Line3DCollection(segments, cmap=cmap, norm=norm)        # 设定颜色值        lc.set_array(z)        lc.set_linewidth(2)        mesh_lines.append(ax.add_collection(lc))    # 添加 mesh 动画的背景网格ax.legend(loc='center right', bbox_to_anchor=(1.1, 0.46), fancybox=True, facecolor=(.95,.95,.95,1), framealpha=1, shadow=False, frameon=True, ncol=1, columnspacing=0, prop={'family': 'SimHei'})ax.set_zlabel('Rolling Equity 1Y', labelpad=10)ax.set_zlim(-0.39, 0.39)ax.set_zticklabels([' '* 3 + '{:.0%}'.format(val) for val in ax.get_zticks()], fontdict={'verticalalignment': 'center', 'horizontalalignment': 'center'})ax.set_xlabel('Date', labelpad=30)ax.set_xlim(0, current_slice.shape[0]-1)ax.set_xticklabels([index_dates[int(val)].strftime('%m/%y') for val in ax.get_xticks()[:-1]] + [''], rotation=0, fontdict={'verticalalignment': 'top', 'horizontalalignment': 'center'})ax.set_yticks(np.arange(current_slice.shape[1]))ax.set_yticklabels([index_names[i]for i in range(current_slice.shape[1])], rotation=-15, fontdict={'verticalalignment': 'center', 'horizontalalignment': 'left'})ax.w_xaxis.set_pane_color((0.05, 0.05, 0.65, 1))ax.w_yaxis.set_pane_color((0.05, 0.05, 0.65, 1))ax.w_zaxis.set_pane_color((0.05, 0.05, 0.65, 1)) # (0.05, 0.05, 0.65, 1)ax.view_init(25, -60)# ------------------------------------------------------------------x_scale=1.8y_scale=1z_scale=1scale=np.diag([x_scale, y_scale, z_scale, 1.0])scale=scale*(1.0/scale.max())scale[3,3]=1.0def short_proj():    return np.dot(Axes3D.get_proj(ax), scale)ax.get_proj=short_projfig.subplots_adjust(left=0, right=1, bottom=0, top=1)# ------------------------------------------------------------------# 输出图形plt.show()

输出结果如下图所示:

2.定义更新以上图形的函数

代码如下:

def update_mesh_lines_3D(num, data, columns, dates, cmap, lines, mesh_lines, ax):            # 切分数据    current_slice = data[num:int(261/2)+num, :]         for i in range(current_slice.shape[1]):        # 获取坐标值        x = np.arange(current_slice.shape[0])        y = np.tile(i, current_slice.shape[0])        z = np.array(current_slice[:, i])        # 绘制不同颜色的点和线段        points = np.array([x, y, z]).T.reshape(-1, 1, 3)        segments = np.concatenate([points[:-1], points[1:]], axis=1)        # 指定连续值,映射数据点的颜色        norm = plt.Normalize(-0.19, 0.19)                lines[i].set_segments(segments)        lines[i].set_array(z)        lines[i].set_color(cmap(z[-1] * 2.5 + 0.5))    # 通过计数检查当前的mesh线    counter = 0           for j in range(current_slice.shape[0]):        if j % 1 == 0:                                    # 获取坐标值            x = np.tile(j, current_slice.shape[1])            y = np.arange(current_slice.shape[1])            z = np.array(current_slice[j, :])            # 绘制不同颜色的点和线段            points = np.array([x, y, z]).T.reshape(-1, 1, 3)            segments = np.concatenate([points[:-1], points[1:]], axis=1)            # 设定 mesh 线的颜色值            norm = plt.Normalize(-0.22, 0.22)                    mesh_lines[counter].set_segments(segments)            mesh_lines[counter].set_array(z)            counter += 1            # 动态更新数据和标识    ax.set_xticklabels([dates[int(val)+num].strftime('%m/%y') for val in ax.get_xticks()[:-1]] + [''], rotation=0, fontdict={'verticalalignment': 'top', 'horizontalalignment': 'center'})    ax.legend(loc='center right', bbox_to_anchor=(1.1, 0.46), fancybox=True, facecolor=(.95,.95,.95,1), framealpha=1, shadow=False, frameon=True, ncol=1, columnspacing=0, prop={'family': 'SimHei'})        return linesdef init_mesh_lines_3D():    for line in lines:        line.set_array([])    return lines

3.创建3D mesh 动画

代码如下:

line_ani = animation.FuncAnimation(fig=fig,                                    func=update_mesh_lines_3D,                                    # frames=30,                                   frames=indexes_rolling.shape[0]-int(261/2),                                   init_func=init_mesh_lines_3D,                                    fargs=(indexes_rolling.values, indexes_rolling.columns, indexes_rolling.index, cmap, lines, mesh_lines, ax),                                   interval=100,                                    blit=True)# 演示动画line_ani

演示结果如下图所示:

发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章