# 本文的目标读者
对用 Golang
代码生成折线图、扇形图等图表有兴趣的朋友。
# 本文摘要
主要介绍 Go 中用以绘图的开源库,分别是:
- GitHub - wcharczuk/go-chart: go chart is a basic charting library in go.
- GitHub - vicanso/go-charts: A charts library for Golang
- GitHub - vdobler/chart: Provide basic charts in go
- GitHub - gonum/plot: A repository for plotting and visualizing data
- GitHub - go-echarts/go-echarts: 🎨 The adorable charts library for Golang
我的需求是生成一个时间轴类型折线图的图片插入到我的报告中,前面两个库与我的需求比较符合,所以我会着重介绍;后面三个库不满足我的需求,在本文会大略带过。如果懒得看正文,这是我总结的表格:
go-chart | go-charts | chart | plot | go-echarts | |
---|---|---|---|---|---|
使用文档 / 示例完善 | ✅ | ✅ | ✅ | ✅ | ✅ |
学习成本 | 中 | 低 | 高 | 低 | 低 |
支持的图表种类 | 少 | 少 | 少 | 中 | 多 |
支持时间轴 | ✅ | ✅ | ✅ | ❌ | ✅ |
支持输出图片 | ✅ | ✅ | ✅ | ✅ | ❌ |
支持设置标签 | ✅ | ✅ | ✅ | ✅ | ✅ |
支持折线图 | ✅ | ✅ | ❌ | ✅ | ✅ |
支持自定义图表 | ❌ | ❌ | ❌ | ✅ | ❌ |
UI 美观 | ⭐️⭐️⭐️ | ⭐️⭐️⭐️⭐️ | ⭐️⭐️ | ⭐️⭐️ | ⭐️⭐️⭐️⭐️⭐️ |
还有一些库,例如 gg 和 Go
内部自带的 image/draw
包,在这里就不介绍了,因为它们俩都属于是绘制基础图形(圆形、正方形和矩形等)或者是对图像本身进行旋转、缩放、添加文字等处理的,与本文所讨论的绘制图表不太一样。
# go-chart
go-chart
是一个简单的 Golang
原生图表库,支持时间序列和连续折线图。因此, go-chart
其实对数据量特别多的情况无法很好地适应,以及如果要在图表中使用中文时,需要额外修改字体为支持中文的字体。
# 官方效果图
曲线图
单轴折线图
双轴折线图
饼状图
柱状图
# 安装
go get -u github.com/wcharczuk/go-chart
# 实际使用
我的需求是,从 influxdb
查询数据,再将数据渲染为折线图,代码如下:
/* | |
前面省略查询 influxdb 过程 | |
*/ | |
xValue := []string{} | |
yValue := []float64{} | |
// 处理查询到的结果数据 | |
for _, value := range allRequest[0].Series[0].Values { | |
if value[1] == nil { | |
yValue = append(yValue, 0) | |
t := value[0].(string) | |
xValue = append(xValue, t) | |
} else { | |
fmt.Println("value:", value[1]) | |
x, _ := value[1].(json.Number) | |
s, _ := x.Float64() | |
yValue = append(yValue, s) | |
fmt.Println(reflect.TypeOf(value[0])) | |
t := value[0].(string) | |
xValue = append(xValue, t) | |
} | |
} | |
// 时间轴的显示格式 | |
format := chart.TimeValueFormatterWithFormat("15:04") | |
lenX := len(xValue) | |
// X 轴内容 xValues 及 X 轴坐标 ticks | |
var xValues []time.Time | |
var ticks []chart.Tick | |
for i := 0; i < lenX; i++ { | |
t, _ := time.Parse( | |
time.RFC3339, | |
xValue[i]) | |
x := t.Local() | |
xValues = append(xValues, x) | |
ticks = append(ticks, chart.Tick{Value: getNsec(t), Label: format(t)}) | |
} | |
// 定义曲线 | |
var series []chart.Series | |
series = append(series, chart.TimeSeries{ | |
XValues: xValues, | |
YValues: yValue, | |
Style: chart.Style{ | |
StrokeColor: chart.GetDefaultColor(0).WithAlpha(64), | |
FillColor: drawing.ColorFromHex("9ADFEA"), | |
}, | |
}) | |
// 设置图表的样式 | |
lineChartStyle := chart.Style{ | |
Padding: chart.Box{ | |
Top: 30, | |
Left: 30, | |
Right: 30, | |
Bottom: 30, | |
}, | |
} | |
graph := chart.Chart{ | |
Title: "All Requests", | |
Background: lineChartStyle, | |
Width: 1280, | |
Height: 500, | |
XAxis: chart.XAxis{ | |
Name: "", | |
ValueFormatter: format, | |
Ticks: ticks, | |
}, | |
YAxis: chart.YAxis{ | |
Name: "", | |
}, | |
Series: series, | |
} | |
graph.Elements = []chart.Renderable{ | |
chart.LegendLeft(&graph), | |
} | |
// 生成图片 | |
var imgContent bytes.Buffer | |
err = graph.Render(chart.PNG, &imgContent) | |
if err != nil { | |
fmt.Println(err) | |
} | |
f, _ := os.Create("test.png") | |
_, _ = f.Write(imgContent.Bytes()) |
这里查询了 10 分钟的数据,生成的图片为
可以看到图表上的 x 轴已经看不清了,这是因为数据点非常多,而 go-chart 没有对此进行适配。
在数据点较少的情况下,比如只查询 1 分钟的数据,生成的图片为:
# 优点
- 图表的自定义程度高,例如可以选择给曲线填充颜色等
# 缺点
- 使用比较复杂
我想画的图表的 x 轴是时间轴类型,在这个库中绘制时间轴类型的 x 轴需要额外把数据进行处理为Time.time
类型。如使用float64
类型的 x 轴的代码会比较简单,示例如下:
graph := chart.Chart{ | |
Series: []chart.Series{ | |
chart.ContinuousSeries{ | |
XValues: []float64{1.0, 2.0, 3.0, 4.0}, | |
YValues: []float64{1.0, 2.0, 3.0, 4.0}, | |
}, | |
}, | |
} | |
buffer := bytes.NewBuffer([]byte{}) | |
err := graph.Render(chart.PNG, buffer) |
- 图表的样式不够精美
下面有go-charts
的 demo 图,可以对比一下,确实是不如它好看。
# go-charts
Go-charts
是国内的程序员在 go-chart
的基础上优化了图表的生成方式,同时还优化了图表的样式。目前支持 line
, bar
, horizontal bar
, pie
, radar
, funnel
以及 table
类型的图表。
# 官方效果图
主题为 light
与 grafana
。
# 安装
go get -u github.com/vicanso/go-charts/v2
# 实际使用
同样是从 influxdb
查询数据,再处理数据生成图表,代码如下:
xValue := []string{} | |
yValue := [][]float64{} | |
// 处理结果 | |
for _, value := range allRequest[0].Series[0].Values { | |
tempY := []float64{} | |
resultX := value[0].(string) | |
xValue = append(xValue, resultX) | |
number, _ := value[1].(json.Number) | |
resultY, _ := number.Float64() | |
tempY = append(tempY, resultY) | |
} | |
// 对 x 轴格式化 原:2022-07-29T09:24:10Z,新:09:24:10 | |
formatXValue := []string{} | |
for _, x := range xValue { | |
formatTime, err := time.Parse(time.RFC3339, x) | |
if err != nil { | |
} | |
formatX := formatTime.Local().Format("15:04:05") | |
formatXValue = append(formatXValue, formatX) | |
} | |
f := false // 设置 x 轴的样式 | |
// 字体文件需要自行下载 | |
buff, err := ioutil.ReadFile("./TencentSans-W7.ttf") | |
if err != nil { | |
panic(err) | |
} | |
err = charts.InstallFont("noto", buff) | |
if err != nil { | |
panic(err) | |
} | |
// 渲染图表 | |
p, err := charts.LineRender( | |
yValue, | |
charts.FontFamilyOptionFunc("noto"), | |
charts.TitleTextOptionFunc("全部请求"), | |
charts.XAxisDataOptionFunc(xValue), | |
func(opt *charts.ChartOption) { | |
opt.XAxis.BoundaryGap = &f | |
opt.Padding = charts.Box{Left: 20, Right: 50, Top: 20, Bottom: 20} | |
}, | |
charts.ThemeOptionFunc("grafana"), | |
charts.WidthOptionFunc(1000), | |
) |
查询 10 分钟的数据,生成的效果图为:
如果将主题换为 light
,效果图如下
如果只查询 1 分钟的数据,效果图如下
从上面的效果图中可以发现, go-charts
在数据量比较大的情况下,优化了 x 轴的展示,让数据不会挤在一起;以及样式也更好看一点。
# 优点
- 样式好看
看上面的图,一目了然 - 使用简单
从代码中也可以看出来,go-charts
对 x 轴和 y 轴的类型做了额外一层封装,x 轴的类型为string
,y 轴的类型为[][]float64
,只需要传相应类型的数据就可以直接渲染图表。
并且由于 y 轴的[]float64
就表示一条曲线,所以如果要在图表中增加渲染的曲线也比在go-chart
中要更简单 —— 直接给 y 轴数据 append 一个新的[]float64
数据即可。
# 缺点
自定义的自由度没有
go-chart
高
例如go-charts
中暂不支持用颜色填充曲线,以及不能自定义曲线的颜色等。当曲线超过 9 条后,曲线的颜色会开始重复
作者的回复是他自己的使用场景只需要用到 5 条曲线左右,建议如果涉及到太多的曲线,最好分开画图。
不过都不是什么大问题,是一个很好用的开源库。
# chart
前面这三个库的名字真是太像了,并且这三个库都提供基础图表的绘制功能。但是这个库更关注自动缩放、误差线和对数图等图表,并且对漂亮 UI 完全不在乎。
# 官方效果图
# 安装
go get -u github.com/vdobler/chart
# 详细说明
这个库支持的图表类型有
- 带状图
- 散点图 / 函数图
- 直方图
- 条形图和分类条形图
- 扇形图 / 环形图
- 箱形图
由上所述,这个介绍为 “Provide basic charts“
的开源库并不支持我需要的折线图……
以及在介绍里面,这个库有以下几个特点:
- 轴的值可以是线性、对数、分类或者时间 / 日期轴。
- 自动缩放具有很多选项。
- 抽动和标签的精细控制。
输出格式有 txtg
、 svgg
和 imgg
这三种格式
# plot
plot
的前身是 code.google.com/p/plotinum
,它提供了用于 Go 中构建和绘制图表的 API,主要用于将数据可视化。
# 官方效果图
默认样式
更细粒度的控制
自定义刻度线
带误差的点
条形图
函数
直方图
垂直箱形图
水平箱形图
四分位图
气泡图
# 安装
go get gonum.org/v1/plot/...
# 详细说明
plot
库其实包含以下四个部分:
plot
:提供了布局和绘图的简单接口;plotter
:使用 plot 提供的接口实现了一组标准的绘图器,例如散点图、条形图、箱状图等。可以使用 plotter 提供的接口实现自己的绘图器;plotutil
:为绘制常见图形提供简便的方法;vg
:封装各种后端,并提供了一个通用矢量图形 API。
引自《Go 每日一库之 plot》
由上所述,我在使用 plot 库时,发现里面自带的基本 API 不能满足我的需求 —— 我需要一个时间轴作为 x 轴,但是 plot
库默认的 API 中 x 轴只支持 float64
类型的数据。
但是在 plotter
中,你可以自定义一个绘图器实现一些特殊的需求,以及你还可以使用社区制作的绘图器,可以在这个网页中查看其中一部分社区绘图器:Community Plotters · gonum/plot Wiki · GitHub。
一些社区绘图器的示例图:
plotters/piechart at master · benoitmasson/plotters · GitHub
GitHub - pplcc/plotext: Extensions and custom plotters for the gonum plot packages
# go-echarts
go-echarts
是一个专注数据可视化的图表库,参考了 pyecharts
的一些设计思想;目前有 v1 和 v2 两个版本,其中 v1 已经不再维护。
# 官方效果图
# 安装
go get -u github.com/go-echarts/go-echarts/v2
# 详细介绍
go-echarts
是通过实现 Apache ECharts 的相关接口,来轻松生成全面又美观的图表。
因此,在编写完生成图表的代码后,你还需要将图表渲染成一个 HTML 文件,或者是用一个 HTTP
服务器去渲染图表,如图:
以及这个开源库渲染出来的图表也是这几个库里面最美观的(毕竟是用 HTML
代码渲染出来的)
# 总结
本文介绍了 Go
中最主要的几个绘制图表的绘图库,其中对最为接近的两个开源库 go-chart
和 go-charts
进行了比较详细的对比,其余三个开源库 chart
、 plot
和 go-echarts
则是简单介绍了一下。
这些开源库各有各的特点,没有绝对的优劣,希望能在大家需要挑选开源库时给予一些参考。