我们程序员、设计人员,按需求辛辛苦苦开发出来的统计图形,往往达不到用户的要求,原因一般是表达不全面,也有内容过多而比较乱,真是众口难调。
其实,是我们表达方式与业务人员工作脱节。业务人员看了一张图或及临近几张图就知道是什么了。例如采油工程中的示功图(见《使用Matplotlib图像化分析数据构建训练集的方法及实践》),我稍加学习,简单的一眼就能识别工况状态。
同样道理,如下图所示的每日油罐液位变化情况,如果拿出单张图来看,就能看出油罐是出油还是进油,但是,看数据时,识别是静止还是震动或气温影响的波动往往很困难。我们会认为已经做的很好了。
如果换位思考,把图给AI进行智能识别呢。
这就意味着,我们设计开发时,再向前迈一步——人工智能如何识别,我上次做的就不到位(见《TensorFlow CNN卷积神经网络实现工况图分类识别(一)》),我当时训练数据集是单张图。
现如今,如果把多张图拼成一张图并标注出当前图块,或者,使用时序模型,那么效果将会更好。这是因为,我在做油罐液位数据分析时,看连续一个月的单日集成图,不懂业务的我都看出其运行周期及效率(进出斜率/余弦对比)情况。
以此,再更上一层楼,回归用户界面,为了更好的表达细节,可否转变为动画呢。我已经转化为动画,如何分析再学习。
本文先落实两个技术,一是绘制多子图拼成大图,二是绘制动画gif(MP4也可以)。
基于matplotlib库,通过plt.subplots(nrows=6, ncols=5)定义子图,返回一个包含figure和axes对象的元组,详解见代码(使用数组循环比较简洁)。
'''
Created on 2020年8月16日
@author: xiaoyw
'''
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.pyplot import MultipleLocator
#从pyplot导入MultipleLocator类,这个类用于设置刻度间隔
from matplotlib import animation
def get_DataFromExcel(file_name):
df = pd.read_excel(file_name)
return df
def feature_datatime(dat):
dat['MearsureTime'] = dat['MearsureTime'].astype('datetime64')
df_dt = pd.DataFrame(columns=('Year','Month','Day','Hour','Minute'))
df_dt['Hour'] = dat['MearsureTime'].dt.hour
df_dt['Year'] = dat['MearsureTime'].dt.year
df_dt['Month'] = dat['MearsureTime'].dt.month
df_dt['Day'] = dat['MearsureTime'].dt.day
df_dt['Minute'] = dat['MearsureTime'].dt.minute
return df_dt
def draw_OilCanLevel(df):
df0 = df[(df['OilCanID']==2) & (df['Year']==2020) & (df['Month']==6)].reset_index(drop=True)
df0 = df0.sort_values(by = ['Day','Hour','Minute'])
count = df0.shape[0]
y = []
x = []
for i in range(1,31):
df_tmp = df0[df0['Day']==i]
if df_tmp.shape[0]>0:
x0 = ((df_tmp['Hour']*60+df_tmp['Minute'])/10).tolist()
y0 = df_tmp['LiquidLevel'].values/100
x.append(x0)
y.append(y0)
fig1, ax1 = plt.subplots(nrows=6, ncols=5)
for i in range(6):
for j in range(5):
ax1[i, j].plot(x[i*5+j],y[i*5+j])
ax1[i, j].set_xlim(0,150)
ax1[i, j].set_ylim(0,13500/100)
x_major_locator=MultipleLocator(20)
#把x轴的刻度间隔设置为10,并存在变量里
y_major_locator=MultipleLocator(2000/100)
#把y轴的刻度间隔设置为20,并存在变量里
ax1[i, j].xaxis.set_major_locator(x_major_locator)
#把x轴的主刻度设置为1的倍数
ax1[i, j].yaxis.set_major_locator(y_major_locator)
#把y轴的主刻度设置为10的倍数
ax1[i, j].set_ylabel('Level')
ax1[i, j].set_xlabel('Time')
plt.show()
df0 = get_DataFromExcel('e:/drawCandata.xlsx') #('e:/drawCandata.xlsx') #('e:/CandataClean.xlsx')
df0 = pd.concat([df0,feature_datatime(df0)],axis=1)
draw_OilCanLevel(df0)
animation_gif(df0)
如果要让 matplotlib 实现动画功能的话,那么就要引入 animation 模块。
#实现每天按10分钟间隔画曲线
def animation_gif(df):
df0 = df[(df['OilCanID']==2) & (df['Year']==2020) & (df['Month']==6)].reset_index(drop=True)
df0 = df0.sort_values(by = ['Day','Hour','Minute'])
count = df0.shape[0]
y = []
x = []
for i in range(1,31):
df_tmp = df0[df0['Day']==i]
#取一天的数据长度
row = df_tmp.shape[0]
if row > 0:
for j in range(1,int(row/10)+1):
k = j * 10
if k > row: #一天24*6采集次数
k = row + 1
df_tmp0 = df_tmp[0:k]
print('Row is {0}'.format(df_tmp0.shape[0]))
x0 = ((df_tmp0['Hour']*60+df_tmp0['Minute'])/10).tolist()
y0 = df_tmp0['LiquidLevel'].values/100
x.append(x0)
y.append(y0)
#显示数据行数,也就是动画的帧数
frame = len(x)
fig, ax = plt.subplots()
ax.set_xlim(0,150)
ax.set_ylim(0,13500/100)
x_major_locator=MultipleLocator(20)
#把x轴的刻度间隔设置为10,并存在变量里
y_major_locator=MultipleLocator(2000/100)
#把y轴的刻度间隔设置为20,并存在变量里
ax.xaxis.set_major_locator(x_major_locator)
#把x轴的主刻度设置为1的倍数
ax.yaxis.set_major_locator(y_major_locator)
ax.set_ylabel('Level')
ax.set_xlabel('Time')
ln, = ax.plot([], [], 'r-', animated=False) #第三个参数表示画曲线的颜色和线型
def update(i):
label = 'timestep {0}'.format(i)
print(label)
#ax.cla()
# 更新线和坐标轴标签
ln.set_data(x[i],y[i])
# 返回要重绘的对象
return ln,
#frames为动画帧数
ani = animation.FuncAnimation(fig, update, frames=frame,interval=1000, blit=True)
ani.save('e:/animation.gif', fps=10, dpi=80, writer='imagemagick')
plt.show()
需要注意到的是,如果要保存 gif 图像,这要求开发者电脑已经安装了 ImageMagicK。
参考:
[1]《matplotlib.pyplot.subplots》
[2]《如何通过 Matplotlib 绘制动画及保存 GIF 图片?》 CSDN博客, frank909, 2018.12
[3]《使用Python科学计算包搭建CNN算法实践(1)》 CSDN博客,肖永威 ,2018.05