动态排名柱状图的两种做法

受B站拜年祭发射最多的弹幕是什么?视频启发,对日常维护工作中的故障硬件也做了一次盘点。这里介绍两种方法,第一种是Python + Matplotlib;第二种是利用GitHub上现成的“轮子”。如果需要快速出图,后者更简单(也更美观)。

方法一:Python

MATPLOTLIB的动画类

MATPLOTLIB包含了一个动画类——Animation,用这个类就可以实现图标的动画效果。先看下官方文档给出的例子:

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

fig, ax = plt.subplots()xdata, ydata = [], []ln, = plt.plot([], [], 'ro')

def init():
    ax.set_xlim(0, 2*np.pi)
    ax.set_ylim(-1, 1)
    return ln,

def update(frame):
    xdata.append(frame)
    ydata.append(np.sin(frame))
    ln.set_data(xdata, ydata)
    return ln,

ani = FuncAnimation(fig, update, frames=np.linspace(0, 2*np.pi, 128),
                    init_func=init, blit=True)plt.show()

上例中,init初始化图表,update函数则负责更新图表中的数据。最主要的是FuncAnimation,通过不断调用update,并将frames参数中的值依次传递给update,更新图表。这个例子的运行结果如下:


示例gif

数据整理

先将故障硬件的名称、故障数量和发生日期整理好。好在这些数据一直以来都有不定期梳理。梳理后的格式如下:


整理后的数据

保存成csv后由pandas读取到DataFrame中,同时解析日期:

with open('plugin_faults.csv', 'r') as f:
    data = pd.read_csv(f, parse_dates=['date'])

因为柱状图中每根柱的长度是个累计值,而表中的是当天故障的数量,因此要按故障硬件名称累积求和:

data = data.sort_values('date')
group = data.groupby('name')
data['value_cumsum'] = group.cumsum()
data.sort_values(['date','value_cumsum','name'], inplace=True)
data.drop_duplicates(['date','name'], keep='last', inplace=True)

填充所有日期,所有硬件类型

plugins = ['PCU', '电源板', 'AS7-D', 'ESB24-D', 'CPU', 'HWAT-B', 'SW256B', 'SWPRO-C',
           'ETP', 'ET16', '时钟板', 'DRAM', 'TR3T', '硬盘', 'HDSAM-A', 'DCAR1-A', '光模块', '熔丝', '网管交换机'] # 所有硬件名称
start = datetime(2017,1,1) # 开始日期
end = datetime(2019,12,31) # 结束日期
dates = [start + timedelta(days=i) for i in range((end - start).days + 1)]
index = pd.MultiIndex.from_product([dates, plugins], names = ["date", "name"]) # 3年内所有日期与硬件名称的笛卡尔积作为多重索引
df = pd.DataFrame(index=index)
df['value'] = data.set_index(['date', 'name'])['value_cumsum'] # 向DataFrame填入data中的数据
df = df.unstack(level=1).fillna(method='ffill') # 将硬件名称索引unstack,前向填充累计故障数(比如累计到昨天一共坏n件,而今天没有该硬件故障,则累计数量还是n件)
df = df.stack(dropna=False).reset_index().fillna(0) # 转换成无索引的DataFrame,并填充缺失值为0(从开始时间直至第一个同类型硬件发生故障的值均为缺失值)

至此数据整理部分结束,得到了2017至2019年共计3年内每天每种硬件故障的累计数的DataFrame。

作图

通过水平柱状图将故障硬件的数量进行排名可视化,每个柱一个颜色,由多到少自上往下排列。
水平柱状图用barh生成,动画需更新每日的排序,但同一种硬件的颜色不能改变,因此初始化了plugin_color字典。
update函数中的df_i为当前帧所使用的数据,以日期作为筛选标准。

fig, ax = plt.subplots()

# 分配颜色的字典
colormap = [i / len(plugins) * 0.7 + 0.15 for i, _ in enumerate(plugins)]
colormap = plt.get_cmap('Paired')(colormap)
plugin_color = {plugins[i]: colormap[i] for i in range(len(plugins))}

def update(i):

    date = datetime(2017, 1, 1) + timedelta(days=i)
    # 筛选当日的数据,按数量排序
    df_i = df[df['date'] == date]
    df_i = df_i.sort_values('value', ascending=True)
    names = df_i['name'] # 硬件名称作为Y轴标签
    colors = [plugin_color[name] for name in names]
    value = df_i['value'] # X轴的值

    ax.clear()  # 清理ax,否则排序不会重做
    x = max(value) * 0.85 # 图中日期标签的水平相对位置
    if i == 0:
        ax.set_xlim(0, 1.05)
        x = 0.85
    # 水平柱状图
    rects = ax.barh(y=names,
                    width=value,
                    align='center',
                    height=0.5,
                    color=colors)

    # 每个柱的数字标签
    for i, v in enumerate(value):
        ax.text(v + 0.1, i, int(v), color=colors[i], ha='left', va='center')

    # 日期标签
    ax.text(x, 0.5, date.strftime('%Y-%m-%d'),
            fontweight='bold', fontsize='25', color='red')

    return rects

生成动画对象,回调函数为update,frames传入3年的日期数,interval定义每一帧的时延(每秒8帧)。
blit我试了下没感觉有啥区别,按照官方文档的说法,是一种比较老的绘图技术,这里设成False。
repeat控制是否循环播放。

ani = animation.FuncAnimation(fig=fig,
                              func=update,
                              frames=int(len(df) / len(plugins)),
                              interval=1000/8,  # 每帧之间的时延,单位:ms
                              blit=False,
                              repeat=False)

保存为视频或GIF:

ani.save('test.gif', dpi=10, writer='pillow')
ani.save('test.mp4')

如果要在jupyter notebook中直接显示(中文显示问题参见“参考3”):

%matplotlib inline
from IPython.display import HTML
HTML(ani.to_html5_video())

生成的动态排名柱状图GIF(完整的GIF太大,只截部分):


生成gif

方法二:现成的“轮子”

从ID来看,应该就是B站视频的原作者在GitHub上的一个库(见“参考2”),使用的是d3.js的前端作图。只要将数据整理成规定的格式,直接从网页导入即可。也可以修改一些简单的配置,非常好用,在此推荐一下。
最后一帧截图:


最后一帧

参考

  1. Embedding Matplotlib Animations in Jupyter as Interactive JavaScript Widgets
  2. 手把手教你制作一个动态炫酷的可视化图表(历年中国大学学术排行榜)
  3. 用Matplotlib的animation.FuncAnimation画动画
  4. matplotlib图例中文乱码?
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 193,812评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,626评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,144评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,052评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,925评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,035评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,461评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,150评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,413评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,501评论 2 307
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,277评论 1 325
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,159评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,528评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,868评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,143评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,407评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,615评论 2 335

推荐阅读更多精彩内容

  • 这个点早睡的人已经进入梦乡了,晚睡的夜猫子还在加班熬夜。每当夜晚入眠时,脑海里总会无意间掠过这一天的镜头,...
    天涯浪石阅读 387评论 0 3
  • 作为一个妈妈,我要检讨,妈妈虽然心里爱你,嘴上也爱你,但妈妈确实没有做到时时刻刻关注你,给你爱的关怀,那可能是因为...
    啦啦啦_97ca阅读 449评论 0 1
  • 日式土豆可乐饼,解决了典型北方人老妈即将发芽的土豆,姥爷很爱吃呐, 下雪啦,很大的雪~ 我们的雪人️ 花千骨看完了...
    宅蕊茜茜阅读 164评论 0 0
  • 有三个迷途的青年敦野、翔太、幸平在逃跑途中路过一家废弃的杂货铺,刚好开的车又坏了,不得已躲进那家杂货铺避一晚上。就...
    木子张阅读 481评论 0 4