python可视化篇之流式数据监控的实现


Posted in Python onAugust 07, 2019

preface

流式数据的监控,以下主要是从算法的呈现出发,提供一种python的实现思路

其中:
1.python是2.X版本
2.提供两种实现思路,一是基于matplotlib的animation,一是基于matplotlib的ion

话不多说,先了解大概的效果,如下:

python可视化篇之流式数据监控的实现

一、一点构思

在做此流数据输出可视化前,一直在捣鼓nupic框架,其内部HTM算法主要是一种智能的异常检测算法,是目前AI框架中垂直领域下的一股清流,但由于其实现的例子对应的流数据展示并非我想要的,故此借鉴后自己重新写了一个,主要是达到三个目的,一是展示真实数据的波动,二是展示各波动的异常得分,三是罗列异常的点。 

上述的输出结构并非重点,重点是其实时更新的机制,了解后即可自行定义。另,js对于这种流数据展示应该不难,所以本文主要立足的是算法的呈现角度以及python的实现。

二、matplotlib animation实现思路

http://matplotlib.org/api/animation_api.html 链接是matplotlib animation的官方api文档

(一)、骨架与实时更新

animation翻译过来就是动画,其动画展示核心主要有三个:1是动画的骨架先搭好,就是图像的边边框框这些,2是更新的过程,即传入实时数据时图形的变化方法,3是FuncAnimation方法结尾。

下面以一个小例子做进一步说明: 

1.对于动画的骨架:

# initial the figure.
x = []
y = []
fig = plt.figure(figsize=(18, 8), facecolor="white")
ax1 = fig.add_subplot(111)
p1, = ax1.plot(x, y, linestyle="dashed", color="red")

以上分别对应初始化空数据,初始化图形大小和背景颜色,插入子图(三个数字分别表示几行几列第几个位置),初始化图形(数据为空)。

import numpy as np
x = np.arange(0, 1000, 1)
y = np.random.normal(100, 10, 1000)

随机生成一些作图数据,下面定义update过程。

2.对于更新过程:

def update(i):
  x.append(xs[i])
  y.append(ys[i])
  ax1.set_xlim(min(x),max(x)+1)
  ax1.set_ylim(min(y),max(y)+1)
  p1.set_data(x,y)
  ax1.figure.canvas.draw()
  return p1

上述定义更新函数,参数i为每轮迭代从FuncAnimation方法frames参数传进来的数值,frames参数的指定下文会进一步说,x/y通过相应更新之后,对图形的x/y轴大小做相应的重设,再把数据通过set_data传进图形,注意ax1和p1的区别,最后再把上述的变化通过draw()方法绘制到界面上,返回p1给FuncAnimation方法。

3.对于FuncAnimation方法:

ani = FuncAnimation(fig=fig,func=update,frames=len(xs),interval=1)
plt.show()

FuncAnimation方法主要是与update函数做交互,将frames参数对应的数据逐条传进update函数,再由update函数返回的图形覆盖FuncAnimation原先的图形,fig参数即为一开始对应的参数,interval为每次更新的时间间隔,还有其他一些参数如blit=True控制图形精细,当界面较多子图时,为True可以使得看起来不会太卡,关键是frames参数,下面是官方给出的注释:

python可视化篇之流式数据监控的实现

可为迭代数,可为函数,也可为空,上面我指定为数组的长度,其迭代则从0开始到最后该数值停止。

该例子最终呈现的效果如下:

python可视化篇之流式数据监控的实现

了解大概的实现,细节就不在这里多说了。

(二)、animation的优缺点

animation的绘制的结果相比于下文的ion会更加的细腻,主要体现在FuncAnimation方法的一些参数的控制上。但是缺点也是明显,就是必须先有指定的数据或者指定的数据大小,显然这样对于预先无法知道数据的情况没法处理。所以换一种思路,在matplotlib ion打开的模式下,每次往模板插入数据都会进行相应的更新,具体看第二部分。

三、matplotlib ion实现思路

(一)、实时更新

matplotlib ion的实现也主要是三个核心,1是打开ion,2是实时更新机制,3是呈现在界面上。

1.对于打开ion:

ion全称是 interactive on(交互打开),其意为打开一个图形的交互接口,之后每次绘图都在之前打开的面板上操作,举个例子:

import matplotlib.pyplot as plt
plt.ion()
fig = plt.figure()
ax1 = fig.add_subplot(111)
line, = ax1.plot(t, v, linestyle="-", color="r")

打开交互接口,初始化图形。

2.对于实时更新机制:

import numpy as np
ys = np.random.normal(100, 10, 1000)

def p(a, b):
  t.append(a)
  v.append(b)
  ax1.set_xlim(min(t), max(t) + 1)
  ax1.set_ylim(min(v), max(v) + 1)
  line.set_data(t, v)
  plt.pause(0.001)
  ax1.figure.canvas.draw()

for i in xrange(len(ys)):
  p(i, ys[i])

随机生成一组数据,定义作图函数p(包含pause表示暂定时延,最好有,防止界面卡死),传入数据实时更新。

3.对于界面最终呈现

plt.ioff()
plt.show()

ioff是关闭交互模式,就像open打开文件产生的句柄,最好也有个close关掉。

最终效果如下:

python可视化篇之流式数据监控的实现

(二)、ion的优缺点

animation可以在细节上控制比ion更加细腻,这也是ion没有的一点,但是单就无需预先指定数据这一点,ion也无疑是能把流数据做得更加好。

四、最后

贴一下两种方法在最开始那种图的做法,ion我定义成类,这样每次调用只需穿入参数就可以。

animation版本

# _*_ coding:utf-8 _*_

import os
import csv
import datetime
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from matplotlib.dates import DateFormatter
import matplotlib.ticker as ticker

# read the file
filePath = os.path.join(os.getcwd(), "data/anomalyDetect_output.csv")
file = open(filePath, "r")
allData = csv.reader(file)
# skip the first three columns
allData.next()
allData.next()
allData.next()
# cache the data
data = [line for line in allData]
# for i in data: print i

# take out the target value
timestamp = [line[0] for line in data]
value = [line[1:] for line in data]


# format the time style 2016-12-01 00:00:00
def timestampFormat(t):
  result = datetime.datetime.strptime(t, "%Y-%m-%d %H:%M:%S")
  return result


# take out the data
timestamp = map(timestampFormat, timestamp)
value_a = [float(x[0]) for x in value]
predict_a = [float(x[1]) for x in value]
anomalyScore_a = [float(x[2]) for x in value]

# initial the size of the figure
fig = plt.figure(figsize=(18, 8), facecolor="white")
fig.subplots_adjust(left=0.06, right=0.70)
ax1 = fig.add_subplot(2, 1, 1)
ax2 = fig.add_subplot(2, 1, 2)
ax3 = fig.add_axes([0.8, 0.1, 0.2, 0.8], frameon=False)

# initial plot
p1, = ax1.plot_date([], [], fmt="-", color="red", label="actual")
ax1.legend(loc="upper right", frameon=False)
ax1.grid(True)
p2, = ax2.plot_date([], [], fmt="-", color="red", label="anomaly score")
ax2.legend(loc="upper right", frameon=False)
ax2.axhline(0.8, color='black', lw=2)
# add the x/y label
ax2.set_xlabel("date time")
ax2.set_ylabel("anomaly score")
ax1.set_ylabel("value")
# add the table in ax3
col_labels = ["date time", 'actual value', 'predict value', 'anomaly score']
ax3.text(0.05, 0.99, "anomaly value table", size=12)
ax3.set_xticks([])
ax3.set_yticks([])

# axis format
dateFormat = DateFormatter("%m/%d %H:%M")
ax1.xaxis.set_major_formatter(ticker.FuncFormatter(dateFormat))
ax2.xaxis.set_major_formatter(ticker.FuncFormatter(dateFormat))


# define the initial function
def init():
  p1.set_data([], [])
  p2.set_data([], [])
  return p1, p2


# initial data for the update function
x1 = []
x2 = []
x1_2 = []
y1_2 = []
x1_3 = []
y1_3 = []
y1 = []
y2 = []
highlightList = []
turnOn = True
tableValue = [[0, 0, 0, 0]]


# update function
def stream(i):
  # update the main graph(contains actual value and predicted value)
  # add the data
  global turnOn, highlightList, ax3

  x1.append(timestamp[i])
  y1.append(value_a[i])
  # update the axis
  minAxis = max(x1) - datetime.timedelta(days=1)
  ax1.set_xlim(minAxis, max(x1))
  ax1.set_ylim(min(y1), max(y1))
  ax1.figure.canvas.draw()
  p1.set_data(x1, y1)

  # update the anomaly graph(contains anomaly score)
  x2.append(timestamp[i])
  y2.append(anomalyScore_a[i])
  ax2.set_xlim(minAxis, max(x2))
  ax2.set_ylim(min(y2), max(y2))

  # update the scatter
  if anomalyScore_a[i] >= 0.8:
    x1_3.append(timestamp[i])
    y1_3.append(value_a[i])
    ax1.scatter(x1_3, y1_3, s=50, color="black")

  # update the high light
  if anomalyScore_a[i] >= 0.8:
    highlightList.append(i)
    turnOn = True
  else:
    turnOn = False
  if len(highlightList) != 0 and turnOn is False:

    ax2.axvspan(timestamp[min(highlightList)] - datetime.timedelta(minutes=10),
          timestamp[max(highlightList)] + datetime.timedelta(minutes=10),
          color='r',
          edgecolor=None,
          alpha=0.2)
    highlightList = []
    turnOn = True
  p2.set_data(x2, y2)

  # add the table in ax3
  # update the anomaly tabel
  if anomalyScore_a[i] >= 0.8:
    ax3.remove()
    ax3 = fig.add_axes([0.8, 0.1, 0.2, 0.8], frameon=False)
    ax3.text(0.05, 0.99, "anomaly value table", size=12)
    ax3.set_xticks([])
    ax3.set_yticks([])
    tableValue.append([timestamp[i].strftime("%Y-%m-%d %H:%M:%S"), value_a[i], predict_a[i], anomalyScore_a[i]])
    if len(tableValue) >= 40: tableValue.pop(0)
    ax3.table(cellText=tableValue, colWidths=[0.35] * 4, colLabels=col_labels, loc=1, cellLoc="center")

  return p1, p2


# main animated function
anim = FuncAnimation(fig, stream, init_func=init, frames=len(timestamp), interval=0)

plt.show()
file.close()

ion版本

#! /usr/bin/python

import os
import csv
import datetime
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from matplotlib.dates import DateFormatter
import matplotlib.ticker as ticker


class streamDetectionPlot(object):
  """
  Anomaly plot output.
  """

  # initial the figure parameters.
  def __init__(self):
    # Turn matplotlib interactive mode on.
    plt.ion()
    # initial the plot variable.
    self.timestamp = []
    self.actualValue = []
    self.predictValue = []
    self.anomalyScore = []
    self.tableValue = [[0, 0, 0, 0]]
    self.highlightList = []
    self.highlightListTurnOn = True
    self.anomalyScoreRange = [0, 1]
    self.actualValueRange = [0, 1]
    self.predictValueRange = [0, 1]
    self.timestampRange = [0, 1]
    self.anomalyScatterX = []
    self.anomalyScatterY = []

    # initial the figure.
    global fig
    fig = plt.figure(figsize=(18, 8), facecolor="white")
    fig.subplots_adjust(left=0.06, right=0.70)
    self.actualPredictValueGraph = fig.add_subplot(2, 1, 1)
    self.anomalyScoreGraph = fig.add_subplot(2, 1, 2)
    self.anomalyValueTable = fig.add_axes([0.8, 0.1, 0.2, 0.8], frameon=False)

  # define the initial plot method.
  def initPlot(self):
    # initial two lines of the actualPredcitValueGraph.
    self.actualLine, = self.actualPredictValueGraph.plot_date(self.timestamp, self.actualValue, fmt="-",
                                 color="red", label="actual value")
    self.predictLine, = self.actualPredictValueGraph.plot_date(self.timestamp, self.predictValue, fmt="-",
                                  color="blue", label="predict value")
    self.actualPredictValueGraph.legend(loc="upper right", frameon=False)
    self.actualPredictValueGraph.grid(True)

    # initial two lines of the anomalyScoreGraph.
    self.anomalyScoreLine, = self.anomalyScoreGraph.plot_date(self.timestamp, self.anomalyScore, fmt="-",
                                 color="red", label="anomaly score")
    self.anomalyScoreGraph.legend(loc="upper right", frameon=False)
    self.baseline = self.anomalyScoreGraph.axhline(0.8, color='black', lw=2)

    # set the x/y label of the first two graph.
    self.anomalyScoreGraph.set_xlabel("datetime")
    self.anomalyScoreGraph.set_ylabel("anomaly score")
    self.actualPredictValueGraph.set_ylabel("value")

    # configure the anomaly value table.
    self.anomalyValueTableColumnsName = ["timestamp", "actual value", "expect value", "anomaly score"]
    self.anomalyValueTable.text(0.05, 0.99, "Anomaly Value Table", size=12)
    self.anomalyValueTable.set_xticks([])
    self.anomalyValueTable.set_yticks([])

    # axis format.
    self.dateFormat = DateFormatter("%m/%d %H:%M")
    self.actualPredictValueGraph.xaxis.set_major_formatter(ticker.FuncFormatter(self.dateFormat))
    self.anomalyScoreGraph.xaxis.set_major_formatter(ticker.FuncFormatter(self.dateFormat))


  # define the output method.
  def anomalyDetectionPlot(self, timestamp, actualValue, predictValue, anomalyScore):

    # update the plot value of the graph.
    self.timestamp.append(timestamp)
    self.actualValue.append(actualValue)
    self.predictValue.append(predictValue)
    self.anomalyScore.append(anomalyScore)

    # update the x/y range.
    self.timestampRange = [min(self.timestamp), max(self.timestamp)+datetime.timedelta(minutes=10)]
    self.actualValueRange = [min(self.actualValue), max(self.actualValue)+1]
    self.predictValueRange = [min(self.predictValue), max(self.predictValue)+1]

    # update the x/y axis limits
    self.actualPredictValueGraph.set_ylim(
      min(self.actualValueRange[0], self.predictValueRange[0]),
      max(self.actualValueRange[1], self.predictValueRange[1])
    )
    self.actualPredictValueGraph.set_xlim(
      self.timestampRange[1] - datetime.timedelta(days=1),
      self.timestampRange[1]
    )
    self.anomalyScoreGraph.set_xlim(
      self.timestampRange[1]- datetime.timedelta(days=1),
      self.timestampRange[1]
    )
    self.anomalyScoreGraph.set_ylim(
      self.anomalyScoreRange[0],
      self.anomalyScoreRange[1]
    )

    # update the two lines of the actualPredictValueGraph.
    self.actualLine.set_xdata(self.timestamp)
    self.actualLine.set_ydata(self.actualValue)
    self.predictLine.set_xdata(self.timestamp)
    self.predictLine.set_ydata(self.predictValue)

    # update the line of the anomalyScoreGraph.
    self.anomalyScoreLine.set_xdata(self.timestamp)
    self.anomalyScoreLine.set_ydata(self.anomalyScore)

    # update the scatter.
    if anomalyScore >= 0.8:
      self.anomalyScatterX.append(timestamp)
      self.anomalyScatterY.append(actualValue)
      self.actualPredictValueGraph.scatter(
        self.anomalyScatterX,
        self.anomalyScatterY,
        s=50,
        color="black"
      )

    # update the highlight of the anomalyScoreGraph.
    if anomalyScore >= 0.8:
      self.highlightList.append(timestamp)
      self.highlightListTurnOn = True
    else:
      self.highlightListTurnOn = False
    if len(self.highlightList) != 0 and self.highlightListTurnOn is False:
      self.anomalyScoreGraph.axvspan(
        self.highlightList[0] - datetime.timedelta(minutes=10),
        self.highlightList[-1] + datetime.timedelta(minutes=10),
        color="r",
        edgecolor=None,
        alpha=0.2
      )
      self.highlightList = []
      self.highlightListTurnOn = True

    # update the anomaly value table.
    if anomalyScore >= 0.8:
      # remove the table and then replot it
      self.anomalyValueTable.remove()
      self.anomalyValueTable = fig.add_axes([0.8, 0.1, 0.2, 0.8], frameon=False)
      self.anomalyValueTableColumnsName = ["timestamp", "actual value", "expect value", "anomaly score"]
      self.anomalyValueTable.text(0.05, 0.99, "Anomaly Value Table", size=12)
      self.anomalyValueTable.set_xticks([])
      self.anomalyValueTable.set_yticks([])
      self.tableValue.append([
        timestamp.strftime("%Y-%m-%d %H:%M:%S"),
        actualValue,
        predictValue,
        anomalyScore
      ])
      if len(self.tableValue) >= 40: self.tableValue.pop(0)
      self.anomalyValueTable.table(cellText=self.tableValue,
                     colWidths=[0.35] * 4,
                     colLabels=self.anomalyValueTableColumnsName,
                     loc=1,
                     cellLoc="center"
                     )

    # plot pause 0.0001 second and then plot the next one.
    plt.pause(0.0001)
    plt.draw()

  def close(self):
    plt.ioff()
    plt.show()

下面是ion版本的调用:

graph = stream_detection_plot.streamDetectionPlot()
graph.initPlot()

for i in xrange(len(timestamp)):
  graph.anomalyDetectionPlot(timestamp[i],value_a[i],predict_a[i],anomalyScore_a[i])

graph.close()

具体为实例化类,初始化图形,传入数据作图,关掉。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python通过定义一个类实例作为ftp回调方法
May 04 Python
Python基于QRCode实现生成二维码的方法【下载,安装,调用等】
Jul 11 Python
Python实现爬虫从网络上下载文档的实例代码
Jun 13 Python
Python Grid使用和布局详解
Jun 30 Python
python正则-re的用法详解
Jul 28 Python
弄懂这56个Python使用技巧(轻松掌握Python高效开发)
Sep 18 Python
python在不同条件下的输入与输出
Feb 13 Python
基于python实现数组格式参数加密计算
Apr 21 Python
Python的Django框架实现数据库查询(不返回QuerySet的方法)
May 19 Python
为什么python比较流行
Jun 19 Python
python遍历路径破解表单的示例
Nov 21 Python
python的netCDF4批量处理NC格式文件的操作方法
Mar 21 Python
Python+AutoIt实现界面工具开发过程详解
Aug 07 #Python
Django中的用户身份验证示例详解
Aug 07 #Python
浅谈Python中(&,|)和(and,or)之间的区别
Aug 07 #Python
Python操作远程服务器 paramiko模块详细介绍
Aug 07 #Python
使用Python快乐学数学Github万星神器Manim简介
Aug 07 #Python
python中的&&及||的实现示例
Aug 07 #Python
程序员的七夕用30行代码让Python化身表白神器
Aug 07 #Python
You might like
浅析PHP水印技术
2007/02/14 PHP
thinkphp浏览历史功能实现方法
2014/10/29 PHP
php中使用key,value,current,next和prev函数遍历数组的方法
2015/03/17 PHP
PHP cURL初始化和执行方法入门级代码
2015/05/28 PHP
JavaScript 原型继承
2011/12/26 Javascript
仿微博字符限制效果实现代码
2012/04/20 Javascript
jQuery学习笔记 操作jQuery对象 CSS处理
2012/09/19 Javascript
JS中获取数据库中的值的方法
2013/07/14 Javascript
javascript禁用Tab键脚本实例
2013/11/22 Javascript
js中对象的声明方式以及数组的一些用法示例
2013/12/11 Javascript
Bootstrap模态对话框的简单使用
2016/04/29 Javascript
简单封装js的dom查询实例代码
2016/07/08 Javascript
Bootstrap 3.x打印预览背景色与文字显示异常的解决
2016/11/06 Javascript
JavaScript获取中英文混合字符串长度的方法示例
2017/02/04 Javascript
将input框中输入内容显示在相应的div中【三种方法可选】
2017/05/08 Javascript
小程序绑定用户方案优化小结
2019/05/15 Javascript
JS实现纸牌发牌动画
2021/01/19 Javascript
[01:32]2016国际邀请赛中国区预选赛CDEC战队教练采访
2016/06/26 DOTA
详细探究Python中的字典容器
2015/04/14 Python
教你用Type Hint提高Python程序开发效率
2016/08/08 Python
Python利用BeautifulSoup解析Html的方法示例
2017/07/30 Python
python音频处理用到的操作的示例代码
2017/10/27 Python
python中学习K-Means和图片压缩
2017/11/20 Python
python3操作注册表的方法(Url protocol)
2020/02/05 Python
python isinstance函数用法详解
2020/02/13 Python
python 已知一个字符,在一个list中找出近似值或相似值实现模糊匹配
2020/02/29 Python
python 插入日期数据到Oracle实例
2020/03/02 Python
python 实现网易邮箱邮件阅读和删除的辅助小脚本
2021/03/01 Python
社区工作者思想汇报
2014/01/13 职场文书
物流专业自荐信
2014/05/23 职场文书
民族团结先进个人事迹材料
2014/06/02 职场文书
加强作风建设工作总结
2014/10/23 职场文书
商铺租房协议书范本
2014/12/04 职场文书
给客户的检讨书
2014/12/21 职场文书
大学开学典礼新闻稿
2015/07/17 职场文书
CentOS7 minimal 最小化安装网络设置过程
2022/12/24 Servers