go float64 比较_Go 每日一库之 plot

Go 每日一库之 plot

简介

本文介绍 Go 语言的一个非常强大、好用的绘图库——plot。plot内置了很多常用的组件,基本满足日常需求。同时,它也提供了定制化的接口,可以实现我们的个性化需求。plot主要用于将数据可视化,便于我们观察、比较。

快速使用

先安装:

$ go get gonum.org/v1/plot/...复制代码

后使用:

package mainimport (  "log"  "math/rand"  "gonum.org/v1/plot"  "gonum.org/v1/plot/plotter"  "gonum.org/v1/plot/plotutil"  "gonum.org/v1/plot/vg")func main() {  rand.Seed(int64(0))  p, err := plot.New()  if err != nil {    log.Fatal(err)  }  p.Title.Text = "Get Started"  p.X.Label.Text = "X"  p.Y.Label.Text = "Y"  err = plotutil.AddLinePoints(p,    "First", randomPoints(15),    "Second", randomPoints(15),    "Third", randomPoints(15))  if err != nil {    log.Fatal(err)  }  if err = p.Save(4*vg.Inch, 4*vg.Inch, "points.png"); err != nil {    log.Fatal(err)  }}func randomPoints(n int) plotter.XYs {  points := make(plotter.XYs, n)  for i := range points {    if i == 0 {      points[i].X = rand.Float64()    } else {      points[i].X = points[i-1].X + rand.Float64()    }    points[i].Y = points[i].X + 10 * rand.Float64()  }  return points}复制代码

程序运行输出points.png图片文件:

plot的使用比较直观。首先,调用plot.New()创建一个“画布”,画布结构如下:

// Plot is the basic type representing a plot.type Plot struct {  Title struct {    Text string    Padding vg.Length    draw.TextStyle  }  BackgroundColor color.Color  X, Y Axis  Legend Legend  plotters []Plotter}复制代码

然后,通过直接给画布结构字段赋值,设置图像的属性。例如p.Title.Text = "Get Started设置图像标题内容;p.X.Label.Text = "X",p.Y.Label.Text = "Y"设置图像的 X 和 Y 轴的标签名。

再然后,使用plotutil或者其他子包的方法在画布上绘制,上面代码中调用AddLinePoints()绘制了 3 条折线。

最后保存图像,上面代码中调用p.Save()方法将图像保存到文件中。

更多图形

gonum/plot将不同层次的接口封装到特定的子包中:

  • plot:提供了布局和绘图的简单接口;
  • plotter:使用plot提供的接口实现了一组标准的绘图器,例如散点图、条形图、箱状图等。可以使用plotter提供的接口实现自己的绘图器;
  • plotutil:为绘制常见图形提供简便的方法;
  • vg:封装各种后端,并提供了一个通用矢量图形 API。

条形图

条形图通过相同宽度条形的高度或长短来表示数据的大小关系。将相同类型的数据放在一起比较能非常直观地看出不同,我们经常在比较几个库的性能时使用条形图。下面我们采用json-iter/go的 GitHub 仓库中用来比较jsoniter、easyjson、std三个 JSON 库性能的数据来绘制条形图:

package mainimport (  "log"  "gonum.org/v1/plot"  "gonum.org/v1/plot/plotter"  "gonum.org/v1/plot/plotutil"  "gonum.org/v1/plot/vg")func main() {  std := plotter.Values{35510, 1960, 99}  easyjson := plotter.Values{8499, 160, 4}  jsoniter := plotter.Values{5623, 160, 3}  p, err := plot.New()  if err != nil {    log.Fatal(err)  }  p.Title.Text = "jsoniter vs easyjson vs std"  p.Y.Label.Text = ""  w := vg.Points(20)  stdBar, err := plotter.NewBarChart(std, w)  if err != nil {    log.Fatal(err)  }  stdBar.LineStyle.Width = vg.Length(0)  stdBar.Color = plotutil.Color(0)  stdBar.Offset = -w  easyjsonBar, err := plotter.NewBarChart(easyjson, w)  if err != nil {    log.Fatal(err)  }  easyjsonBar.LineStyle.Width = vg.Length(0)  easyjsonBar.Color = plotutil.Color(1)  jsoniterBar, err := plotter.NewBarChart(jsoniter, w)  if err != nil {    log.Fatal(err)  }  jsoniterBar.LineStyle.Width = vg.Length(0)  jsoniterBar.Color = plotutil.Color(2)  jsoniterBar.Offset = w  p.Add(stdBar, easyjsonBar, jsoniterBar)  p.Legend.Add("std", stdBar)  p.Legend.Add("easyjson", easyjsonBar)  p.Legend.Add("jsoniter", jsoniterBar)  p.Legend.Top = true  p.NominalX("ns/op", "allocation bytes", "allocation times")  if err = p.Save(5*vg.Inch, 5*vg.Inch, "barchart.png"); err != nil {    log.Fatal(err)  }}复制代码

首先生成值列表,我们在最开始的例子中生成了二维坐标列表plotter.XYs,实际上还有三维坐标列表plotter.XYZs。

然后,调用plotter.NewBarChart()分别为三组数据生成条形图。w = vg.Points(20)用来设置条形的宽度。LineStyle.Width设置线宽,这个实际上是边框的宽度。Color设置颜色。Offset设置偏移,因为每组对应位置的条形放在一起显示更好比较,将stdBar.Offset设置为-w会让其向左偏移一个条形的宽度;easyjson偏移不设置,默认为 0,不偏移;jsoniter偏移设置为w,向右偏移一个条形的宽度。最终它们紧挨着显示。

然后,将 3 个条形图添加到画布上。紧接着,设置它们的图例,并将其显示在顶部。

最后调用p.Save()保存图片。

程序运行生成下面的图片:

可以很直观地看到jsoniter的性能、内存占用、内存分配次数各方面都是顶尖的。可能用同一种维度的数据,数量级相差不大,图像会好看点(┬_┬)。

注意plotter.Color(2)这类用法。plot预定义了一组颜色值,如果我们想要使用它们,可以直接传入索引获取对应的颜色,更多的是为了区分不同的图形(例如上面的 3 个条形图用了 3 个不同的索引):

// src/gonum.org/v1/plot/plotutil/plotutil.govar DefaultColors = SoftColorsvar SoftColors = []color.Color{  rgb(241, 90, 96),  rgb(122, 195, 106),  rgb(90, 155, 212),  rgb(250, 167, 91),  rgb(158, 103, 171),  rgb(206, 112, 88),  rgb(215, 127, 180),}func Color(i int) color.Color {  n := len(DefaultColors)  if i < 0 {    return DefaultColors[i%n+n]  }  return DefaultColors[i%n]}复制代码

除了颜色,还有形状plotter.Shape(i)和划线模式plotter.Dashes(i)。

vg.Length(0)有所不同,这个只是将 0 转换为vg.Length类型!

函数图像

plot可以绘制函数图像!

func main() {  p, err := plot.New()  if err != nil {    log.Fatal(err)  }  p.Title.Text = "Functions"  p.X.Label.Text = "X"  p.Y.Label.Text = "Y"  square := plotter.NewFunction(func(x float64) float64 { return x * x })  square.Color = plotutil.Color(0)  sqrt := plotter.NewFunction(func(x float64) float64 { return 10 * math.Sqrt(x) })  sqrt.Dashes = []vg.Length{vg.Points(1), vg.Points(2)}  sqrt.Width = vg.Points(1)  sqrt.Color = plotutil.Color(1)  exp := plotter.NewFunction(func(x float64) float64 { return math.Pow(2, x) })  exp.Dashes = []vg.Length{vg.Points(2), vg.Points(3)}  exp.Width = vg.Points(2)  exp.Color = plotutil.Color(2)  sin := plotter.NewFunction(func(x float64) float64 { return 10*math.Sin(x) + 50 })  sin.Dashes = []vg.Length{vg.Points(3), vg.Points(4)}  sin.Width = vg.Points(3)  sin.Color = plotutil.Color(3)  p.Add(square, sqrt, exp, sin)  p.Legend.Add("x^2", square)  p.Legend.Add("10*sqrt(x)", sqrt)  p.Legend.Add("2^x", exp)  p.Legend.Add("10*sin(x)+50", sin)  p.Legend.ThumbnailWidth = 0.5 * vg.Inch  p.X.Min = 0  p.X.Max = 10  p.Y.Min = 0  p.Y.Max = 100  if err = p.Save(4*vg.Inch, 4*vg.Inch, "functions.png"); err != nil {    log.Fatal(err)  }}复制代码

首先调用plotter.NewFunction()创建一个函数图像。它接受一个函数,单输入参数float64,单输出参数float64,故只能画出单自变量的函数图像。接着为函数图像设置了三个属性Dashes(划线)、Width(线宽)和Color(颜色)。默认使用连续的线条来绘制函数,如图中的平方函数。可以通过设置Dashes让plot绘制不连续的线条,Dashes接受两个长度值,第一个长度表示间隔距离,第二个长度表示连续线的长度。这里也使用到了plotutil.Color(i)依次使用前 4 个预定义的颜色。

创建画布、设置图例这些都与前面的相同。这里还通过p.X和p.Y的Min/Max属性限制了图像绘制的坐标范围。

运行程序生成图像:

气泡图

使用plot可以画出非常好看的气泡图:

func main() {  n := 10  bubbleData := randomTriples(n)  minZ, maxZ := math.Inf(1), math.Inf(-1)  for _, xyz := range bubbleData {    if xyz.Z > maxZ {      maxZ = xyz.Z    }    if xyz.Z < minZ {      minZ = xyz.Z    }  }  p, err := plot.New()  if err != nil {    log.Fatal(err)  }  p.Title.Text = "Bubbles"  p.X.Label.Text = "X"  p.Y.Label.Text = "Y"  bs, err := plotter.NewScatter(bubbleData)  if err != nil {    log.Fatal(err)  }  bs.GlyphStyleFunc = func(i int) draw.GlyphStyle {    c := color.RGBA{R: 196, B: 128, A: 255}    var minRadius, maxRadius = vg.Points(1), vg.Points(20)    rng := maxRadius - minRadius    _, _, z := bubbleData.XYZ(i)    d := (z - minZ) / (maxZ - minZ)    r := vg.Length(d)*rng + minRadius    return draw.GlyphStyle{Color: c, Radius: r, Shape: draw.CircleGlyph{}}  }  p.Add(bs)  if err = p.Save(4*vg.Inch, 4*vg.Inch, "bubble.png"); err != nil {    log.Fatal(err)  }}func randomTriples(n int) plotter.XYZs {  data := make(plotter.XYZs, n)  for i := range data {    if i == 0 {      data[i].X = rand.Float64()    } else {      data[i].X = data[i-1].X + 2*rand.Float64()    }    data[i].Y = data[i].X + 10*rand.Float64()    data[i].Z = data[i].X  }  return data}复制代码

我们生成一组三维坐标点,调用plotter.NewScatter()生成散点图。我们设置了GlyphStyleFunc钩子函数,在绘制每个点之前都会调用它,它返回一个draw.GlyphStyle类型,plot会根据返回的这个对象来绘制。我们的例子中,每次我们都返回一个表示圆形的draw.GlyphStyle对象,通过Z坐标与最大、最小坐标的比例映射到[vg.Points(1),vg.Points(20)]区间中得到半径。

生成的图像:

同样地,我们可以返回正方形的draw.GlyphStyle的对象来绘制“方形图”,只需要把钩子函数GlyphStyleFunc的返回语句做些修改:

return draw.GlyphStyle{Color: c, Radius: r, Shape: draw.SquareGlyph{}}复制代码

即可绘制“方形图”:

实际应用

下面我们应用之前文章中介绍的gopsutil和本文中的plot搭建一个网页,可以实时观察机器的 CPU 和内存占用:

func index(w http.ResponseWriter, r *http.Request) {  t, err := template.ParseFiles("index.html")  if err != nil {    log.Fatal(err)  }  t.Execute(w, nil)}func image(w http.ResponseWriter, r *http.Request) {  monitor.WriteTo(w)}func main() {  mux := http.NewServeMux()  mux.HandleFunc("/", index)  mux.HandleFunc("/image", image)  go monitor.Run()  s := &http.Server{    Addr:    ":8080",    Handler: mux,  }  if err := s.ListenAndServe(); err != nil {    log.Fatal(err)  }}复制代码

首先,我们编写了一个 HTTP 服务器,监听在 8080 端口。设置两个路由,/显示主页,/image调用Monitor的方法生成 CPU 和内存占用图返回。Monitor结构稍后会介绍。index.html的内容如下:

  Monitor  复制代码

页面比较简单,就显示了一张图片。然后在 JS 中启动一个 500ms 的定时器,每隔 500ms 就重新请求一次图片替换现有的图片。我在设置img.src属性时在后面添加了一个随机数,这是为了防止缓存导致得到的可能不是最新的图片。

下面看看Monitor的结构:

type Monitor struct {  Mem       []float64  CPU       []float64  MaxRecord int  Lock      sync.Mutex}func NewMonitor(max int) *Monitor {  return &Monitor{    MaxRecord: max,  }}var monitor = NewMonitor(50)复制代码

这个结构中记录了最近的 50 条记录。每隔 500ms 会收集一次 CPU 和内存的占用情况,记录到CPU和Mem字段中:

func (m *Monitor) Collect() {  mem, err := mem.VirtualMemory()  if err != nil {    log.Fatal(err)  }  cpu, err := cpu.Percent(500*time.Millisecond, false)  if err != nil {    log.Fatal(err)  }  m.Lock.Lock()  defer m.Lock.Unlock()  m.Mem = append(m.Mem, mem.UsedPercent)  m.CPU = append(m.CPU, cpu[0])}func (m *Monitor) Run() {  for {    m.Collect()    time.Sleep(500 * time.Millisecond)  }}复制代码

当 HTTP 请求/image路由时,根据目前已经收集到的CPU和Mem数据生成图片返回:

func (m *Monitor) WriteTo(w io.Writer) {  m.Lock.Lock()  defer m.Lock.Unlock()  cpuData := make(plotter.XYs, len(m.CPU))  for i, p := range m.CPU {    cpuData[i].X = float64(i + 1)    cpuData[i].Y = p  }  memData := make(plotter.XYs, len(m.Mem))  for i, p := range m.Mem {    memData[i].X = float64(i + 1)    memData[i].Y = p  }  p, err := plot.New()  if err != nil {    log.Fatal(err)  }  cpuLine, err := plotter.NewLine(cpuData)  if err != nil {    log.Fatal(err)  }  cpuLine.Color = plotutil.Color(1)  memLine, err := plotter.NewLine(memData)  if err != nil {    log.Fatal(err)  }  memLine.Color = plotutil.Color(2)  p.Add(cpuLine, memLine)  p.Legend.Add("cpu", cpuLine)  p.Legend.Add("mem", memLine)  p.X.Min = 0  p.X.Max = float64(m.MaxRecord)  p.Y.Min = 0  p.Y.Max = 100  wc, err := p.WriterTo(4*vg.Inch, 4*vg.Inch, "png")  if err != nil {    log.Fatal(err)  }  wc.WriteTo(w)}复制代码

运行服务器:

$ go run main.go复制代码

打开浏览器,输入localhost:8080,观察图片变化:

总结

本文介绍了强大的绘图库plot,最后通过一个监控程序结尾。限于篇幅,plot提供的多种绘图类型未能一一介绍。plot还支持svg/pdf等多种格式的保存。感兴趣的童鞋可自行研究。

大家如果发现好玩、好用的 Go 语言库,欢迎到 Go 每日一库 GitHub 上提交 issue

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/297649.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

大前端快闪:package.json文件知多少?

最近在公司某项目参与了一些前端工作&#xff0c;作为后端抠脚大汉&#xff0c;改点前端细节磕磕绊绊&#xff0c;改点大前端、工程化、HTTP交互倒也还能做到柳暗花明。于是打算用后端程序猿的视角记录一些{大前端}的知识快闪&#xff0c;也算是帮助读者构建完整的全栈技能体系…

大牛C++编程开发学习建议50条

2019独角兽企业重金招聘Python工程师标准>>> 每个从事C开发的朋友相信都能给后来者一些建议&#xff0c;但是真正为此进行大致总结的很少。本文就给出了网上流传的对C编程开发学习的50条建议&#xff0c;总结的还是相当不错的&#xff0c;编程学习者&#xff08;不仅…

JQuery函数在IE浏览器中测试的注意事项

这几天刚学JQuery&#xff0c;在测试hide&#xff0c;show&#xff0c;slideToggle函数的时候&#xff0c;在firefox浏览器都很正常&#xff0c;而在IE浏览器里面却出现一些动画异常。这些过渡效果快结束的时候&#xff0c;都出现了一些跳动的现象&#xff0c;例如我对一个段落…

html2canvas改成同步,html2canvas转为图片异步转同步问题(记录)

描述&#xff1a;最近使用html2canvas插件截取页面上指定dom为图片&#xff0c;然后将生成的图片替换页面上的dom后&#xff0c;将替换后的页面内容保存至数据库&#xff0c;结果保存的是保存前的页面&#xff0c;初步判定是html2canvas的异步执行造成的。参考了两篇博文&#…

杨辉再发声明:没有及时交流工作进展,深表歉意

全世界只有3.14 % 的人关注了爆炸吧知识pixabay.com编者按加州大学付向东教授实名举报中科院上海神经所杨辉事件在过去一周成为学术界讨论的热点话题。付向东称自己2018年在神经所做学术报告后&#xff0c;杨辉重复其实验却未告知并抢发文章。杨辉7月3日发表声明&#xff0c;称…

fedora12下pppoe服务器的搭建

1. 查看系统上是否安装了pppoe服务器软件[rootlocalhost ~]# rpm -qa | grep pppoerp-pppoe-3.10-6.fc12.i686若显示以上信息则已经安装了pppoe软件&#xff0c;若无信息则首先安装pppoe软件[rootlocalhost ~]# yum install pppoe2. 配置系统支持IP转发[rootlocalhost ~]# vim …

z变换公式表_如何使用标准正态分布表?

正态分布这个概念在统计学中很常见&#xff0c;在做与正态分布有关计算的时候经常会用到标准正态分布表。如果知道一个数值的标准分数即z-score&#xff0c;就可以非常便捷地在标准正态分布表中查到该标准分数对应的概率值。任何数值&#xff0c;只要符合正态分布的规律&#x…

android 钢琴识别音阶对错_Sans钢琴音阶手机版|Sans钢琴音阶安卓版下载 v1.4 - 跑跑车安卓网...

Sans钢琴音阶游戏是一款有趣的音乐节奏类手机游戏&#xff0c;sans经典角色还原&#xff0c;融入闯关游戏元素&#xff0c;丰富的玩法&#xff0c;赶快来下载体验吧。游戏介绍Sans钢琴音阶游戏是一款拥有着大量不同类型游戏歌曲各种不一样游戏玩法的闯关类型音乐游戏&#xff0…

.NET 6 中的 ConfigurationManager

.NET 6 中的 ConfigurationManagerIntro.NET 6 为了 Minimal API 引入了一些新东西&#xff0c;其中就包含了一个全新的配置对象 ConfigurationManager这并不是 .NET Framework 里的静态类 ConfigurationManager&#xff0c;而是 .NET Core 里的配置 Microsoft.Extensions.Conf…

60个高质量的CSS、XHTML网页布局模板下载

无论您下载和解剖预建模板是为了学习最新的CSS布局技术&#xff0c;或者下载的目的是为了易于编辑制作现成的独立网站&#xff0c;您都不应该仅限于免费且可用的一个拥有众多克隆版本、陈旧的且往往是枯燥的模板。网页设计的流行趋势和技术总是在千变万化&#xff0c;因此&…

总有人会偷看你的朋友圈

全世界只有3.14 % 的人关注了爆炸吧知识生活中&#xff0c;总是会有人在默默关注着你。你的朋友圈&#xff0c;常常有人来“偷偷”光顾&#xff0c;而这些细枝末节常常被我们忽视。 你不想让关心你的人失望&#xff0c;于是在朋友圈分享优质文章&#xff0c;希望你的快乐…

关于tomcat的使用方法(配置及使用)

前一段时间要做一个音乐播放器&#xff0c;要实现在线下载歌曲功能&#xff0c;这里要用到服务器&#xff0c;我决定使用Tomcat。 1.下载tomcat http://tomcat.apache.org/ 推荐下载7.0 版本&#xff08;8.0刚出来&#xff0c;据说还有些问题&#xff1b;6.0反正我没搭建好&a…

HTML5中常见的列表标签包括,介绍几个常用的HTML5标签

一、Html的基本结构&#xff1a;网页的文本、图片等信息&#xff1b;二、Head部分&#xff1a;用于表示网页的元数据即描述网页的基本信息其常用标签及属性有&#xff1a;1、title标签&#xff1a;浏览器标签页显示的标题2、meta标签&#xff1a;其常用属性①charset:设置文档的…

正弦波 程序 角度传感器_激光位移传感器的原理及应用领域

激光位移传感器是利用激光技术进行测量的传感器&#xff0c;由激光器、激光检测器和测量电路组成。作为新型测量设备&#xff0c;激光位移传感器能够精确非接触测量被测物体的位置、位移等变化&#xff0c;还可测量位移、厚度、振动、距离、直径等精密的几何测量。目前&#xf…

Dapr牵手.NET学习笔记:状态管理之docker-compose发布

Dapr牵手.NET学习笔记&#xff1a;想入非非的服务调用Dapr牵手.NET学习笔记&#xff1a;跨物理机负载均衡服务调用Dapr牵手.NET学习笔记&#xff1a;用docker-compose部署服务说明&#xff1a;为了给出demo的全貌&#xff0c;这篇有点长&#xff0c;如果有上一篇的基础&#xf…

linux tomcat 启动权限不足解决办法

2019独角兽企业重金招聘Python工程师标准>>> 用命令 chmod 755 文件名 即可 转载于:https://my.oschina.net/pioneeer/blog/10374

c++求区间第k大数_寻找第K大的数的方法总结

今天看算法分析是&#xff0c;看到一个这样的问题&#xff0c;就是在一堆数据中查找到第k个大的值。名称是&#xff1a;设计一组N个数&#xff0c;确定其中第k个最大值&#xff0c;这是一个选择问题&#xff0c;当然&#xff0c;解决这个问题的方法很多&#xff0c;本人在网上搜…

她在哭,但我没资格安慰她......​

1 我没资格安慰她......▼2 妈妈的关心总是突如其来▼3 突然就热得离谱了▼4 当贾玲和沈腾开始演起韩剧▼5 有些菜一旦错过就不在▼6 这要不是德高望重的长老还真带不了这样的帽子▼7 连狗都比你会放风筝▼你点的每个赞&#xff0c;我都认真当成了喜欢

指定的文件不是虚拟磁盘 没有快照_vmware workstaiton 15 虚拟机克隆(4)

安装客户机操作系统和应用程序可能要耗费很多时间。通过使用克隆&#xff0c;您可以通过一次安装及配置过程制作很多虚拟机副本。克隆虚拟机比复制虚拟机更简单、更快速。当您需要将多个相同的虚拟机部署到一个组时&#xff0c;克隆功能会非常有用。例如&#xff0c;MIS 部门可…

MySQL集群配

一.下面假设这3台服务的情况&#xff1a; Server1: 172.18.3.205 Server2: 172.18.3.207 Server3: 172.18.3.208 Servers1和Server2作为实际配置MySQL集群的服务器。对于作为管理节点的Server3则要求较低&#xff0c;只需对Server3的系统进行很小的调整并且无需安装MySQL&#…