Matplotlib玩转动态可视化

共 5627字,需浏览 12分钟

 ·

2020-09-16 01:13

最近看到很多盆友们用pyecharts、Bokeh和plotly等绘图库制作动态图,还有用pbi制作的,以及网页工具flourish等。其实matplotlib这个经典绘图库也是可以的,这不就来了嘛~

目录

  • 1.效果预览
  • 2.数据获取
  • 3.数据预处理
  • 4.matplotlib动态可视化

1.效果预览

我们从国家统计局 下载最近30年全国各地区生产总值(实际上是1993年-2019年),使用matplotlib绘制动态可视化图,效果如下:

2.数据获取

直接从国家统计局-「国家数据」(http://data.stats.gov.cn/)下载原始数据即可,数据长这样:

3.数据预处理

# 需要引入的库
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import matplotlib.animation as animation
from IPython.display import HTML

原始数据的行索引是地区,列索引是年份,我们后续作图需要的数据结构偏向于窄表(当然宽表其实也可以做到,这里我个人习惯用窄表处理)。

为了实现宽表变窄表,用到pandas里的melt方法。

import pandas as pd
# 读取下载后的数据
df = pd.read_excel(r'F:\微信公众号\matplotlib动态图\各地区生产总值.xlsx')
df.head()
# 使用melt方法进行处理
data = df.melt(id_vars='地区',value_vars=range(1993,2020),var_name='年份',value_name='生产总值(亿)')
data.head()

4.matplotlib动态可视化

matplotlib动态图用到的是animation.FuncAnimation方法,其实动态就是N张图一张一张按照一定频率刷新,我们也有其他方法实现,这里不展开。

在我们的效果展示中,可以看到 类型是条形图,数值高低排序,每个条形图颜色不一样,我们来一步一步看看如何做出最终效果~

4.1.朴实无华的条形图

barh是条形图,就是横着的柱状图,以下我们先取2019年的年度数据展示前10地区

import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
# 以下代码解决显示中文问题
plt.rcParams['font.sans-serif'] = ['SimHei']
# 绘制 2019年 各地区生产总值(前10)
currentYear = 2019
ddata = (data[data['年份']==currentYear]
        .sort_values(by='生产总值(亿)',ascending = True)
        .tail(10))
fix, ax = plt.subplots(figsize=(15,8))
ax.barh(ddata['地区'], ddata['生产总值(亿)'])

我们看到上面这张图平平无奇,朴实无华的配色,没有多一分的元素(标题、数据标签等等),接下来我们先把条形图美化一下

4.2.有点还行的条形图

通过自定义条形图配色,再附上一些text说明。

关于配色,直接从网上(https://www.beejson.com/word/rgb.html)找 16进制配色表,然后取31个即可(咱们一共有31个地区数据不含港澳台)。然后将31个地区与31个颜色进行组合成字典备用!

「构造地区-颜色字典」

# 我直接从网上批量复制了 30多个颜色值,然后随机抽取31个和31个地区配对
a = ['#FFFFCC  #FFCC00  #CC9909  #663300  #FF6600  #663333  #CC6666  #FF6666  #FF0000  #FFFF99  #FFCC66  #FF9900  #FF9966  #CC3300  #996666  #FFCCCC  #660000  #FF3300  #FF6666  #FFCC33  #CC6600  #FF6633  #996633  #CC9999  #FF3333  #990000  #CC9966  #FFFF33  #CC9933  #993300  #FF9933  #330000  #993333  #CC3333  #CC0000  #FFCC99  #FFFF00  #996600']
b =' '.join(a)
c = b.split(' \t')

import random
# 随机不放回抽取31个
color = random.sample(c,31)
# 获取原数据中地区列表
province = list(data['地区'].unique())
# 组合成 地区-颜色值 字典
colors = dict(zip(province,color))

「绘制有颜的条形图」

fig, ax = plt.subplots(figsize=(15,8))
ax.barh(ddata['地区'], ddata['生产总值(亿)'],color = [colors[x] for x in ddata['地区']])
# 在每个条形图末端显示 归属地区 及 生产总值(亿)
for i, (num, pro) in enumerate(zip(ddata['生产总值(亿)'], ddata['地区'])):
    ax.text(num,i, pro, ha = 'right')
    ax.text(num,i, f':{num}亿', ha = 'left')
# 在右侧中部偏下显示当前年份
ax.text(10.4, currentYear, transform= ax.transAxes, size=40, ha='right')
Text(10.4'2019')

有人就要说了,上面这个图也没啥好看的,除了增加了单独的配色以及数据显示外。。讲的太对了,字体还丑、颜色搭配也是难看,当然这些都是可以自己配置的 因为后续 我们会换个plt.xkcd()**「手绘卡通风格」**的形式,但是卡通风格的形式需要特别处理中文字体显示问题,这里先介绍下来自好朋友 「'小明哥'」 的帮助,如下代码(设置字体为我系统里的微软雅黑,字号16)

# 字体管理
from matplotlib import font_manager
my_font = font_manager.FontProperties(fname=r"C:\Windows\Fonts\msyh.ttc",size=14)
# 再加入其他一些元素(如标题、刻度线、刻度放在最上方等)
fig, ax = plt.subplots(figsize=(15,8))
ax.barh(ddata['地区'], ddata['生产总值(亿)'],color = [colors[x] for x in ddata['地区']])

for i, (num, pro) in enumerate(zip(ddata['生产总值(亿)'], ddata['地区'])):
    ax.text(num,i, pro, ha = 'right',fontproperties=my_font)
    ax.text(num,i, f' :{num}亿', ha = 'left',fontproperties=my_font)

ax.text(10.4, currentYear, transform= ax.transAxes,fontproperties=my_font, size=40, ha='right')

# x刻度设置在顶部
ax.xaxis.set_ticks_position('top')
# x刻度颜色设置为灰色,大小为12
ax.tick_params(axis= 'x',colors= '#777777',labelsize= 12)
# 去掉y刻度
ax.set_yticks([])
# 设置 xy轴内边距
ax.margins(00.01)
# 显示网格(x轴向虚线)
ax.grid(which= 'major', axis= 'x',linestyle= '--')
# 网格线至于底部
ax.set_axisbelow(True)
# 在左上角显示 标题(不是用的title方法)
ax.text(01.06,'全国各地区生产总值(1993-2019)',transform= ax.transAxes,weight =600,ha = 'left',fontproperties= my_font,size=24)
# 去掉边框
plt.box(False)

4.3.会动的条形图

既然动图是一张张图刷新而来,那我们把每年的数据都做一张图再定时刷新替换不就好了,这样当然是可以的。

所以,我们先看看「animation.FuncAnimation」(https://matplotlib.org/api/animation_api.html)吧

官网有个简单的例子:sin(x)的动态演示。这里不做介绍,源码清晰,我们直接现学现做~

先把上面作图代码封装成函数

这个函数只需要一个参数,year(年份)

fig, ax = plt.subplots(figsize=(12,16))
def drawBarh(year):
    ddata = (data[data['年份']==year]
        .sort_values(by='生产总值(亿)',ascending = True)
        .tail(31))
    ax.clear()
    
    ax.barh(ddata['地区'], ddata['生产总值(亿)'],color = [colors[x] for x in ddata['地区']])

    for i, (num, pro) in enumerate(zip(ddata['生产总值(亿)'], ddata['地区'])):
        ax.text(num,i, pro, ha = 'right',fontproperties=my_font)
        ax.text(num,i, f' :{num}亿', ha = 'left',fontproperties=my_font)

    ax.text(10.4, year, transform= ax.transAxes,fontproperties=my_font, size=40, ha='right')

    # x刻度设置在顶部
    ax.xaxis.set_ticks_position('top')
    # x刻度颜色设置为灰色,大小为12
    ax.tick_params(axis= 'x',colors= '#777777',labelsize= 12)
    # 去掉y刻度
    ax.set_yticks([])
    # 设置 xy轴内边距
    ax.margins(00.01)
    # 显示网格(x轴向虚线)
    ax.grid(which= 'major', axis= 'x',linestyle= '--')
    # 网格线至于底部
    ax.set_axisbelow(True)
    # 在左上角显示 标题(不是用的title方法)
    ax.text(01.06,'全国各地区生产总值(1993-2019)',transform= ax.transAxes,weight =600,ha = 'left',fontproperties= my_font,size=24)
    # 去掉边框
    plt.box(False)

# 演示2019年数据
drawBarh(2019)

animation动图制作

再调用animation.FuncAnimation方法进行动图制作,我们在输出的页面可以进行动画演示(快捷、后退、开始、暂停等等)。

fig ,ax =plt.subplots(figsize = (12,16))
# 手绘卡通风格
plt.xkcd()
animator = animation.FuncAnimation(fig,drawBarh,frames=range(1993,2020),interval=500)
HTML(animator.to_jshtml())
# 通过以下方式可以保存为动图(保存为视频方式我们单独介绍吧)
animator.save('生产总值动态图.gif',bitrate=1800,writer ='pillow')

「交流与思考」:我们在效果动图中发现其实没那么顺滑,这是因为我们是按照每一年的数据绘制一次导致的,那么如何让效果更加顺滑呢?

(一般来说,可以把每年的数据分为多份,比如我们认为每两年之间存在N组值,那么就是有N-2个缺失值,通过pandas的缺失值插值处理可以补充一些值作为绘图的辅助值,从而让效果更加顺滑,那么如何进行插值呢?pandas其实有现成的方式,这里也不展开说明了)

-END-



浏览 49
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报