Введение
В этой статье представлена очень мощная и простая в использовании библиотека рисования для языка Go —plot
.plot
Встроено множество часто используемых компонентов, которые в основном удовлетворяют повседневные потребности. В то же время он также предоставляет настраиваемый интерфейс, который может реализовать наши индивидуальные потребности.plot
Он в основном используется для визуализации данных, чтобы мы могли наблюдать и сравнивать.
быстрый в использовании
Сначала установите:
$ go get gonum.org/v1/plot/...
После использования:
package main
import (
"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 main
import (
"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)
}
}
Сначала создайте список значений, мы создали список 2D-координат в первом примере.plotter.XYs
, а на самом деле список 3D координат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.go
var DefaultColors = SoftColors
var 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:
return draw.GlyphStyle{Color: c, Radius: r, Shape: draw.SquareGlyph{}}
Можно нарисовать "квадратную схему" 😄:
практическое применение
Ниже мы применяем то, что было введено в предыдущей статьеgopsutil
и в этой статьеplot
Создайте веб-страницу для наблюдения за использованием ЦП и памяти машины в режиме реального времени:
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
Возвращаются методы построения графиков использования ЦП и памяти.Monitor
Структура будет представлена позже.index.html
Содержание следующее:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Monitor</title>
</head>
<body>
<img src="/image" alt="" id="img">
<script>
let img = document.querySelector("#img")
setInterval(function () {
img.src = "/image?s=" + Math.random()
}, 500)
</script>
</body>
</html>
Страница относительно проста, просто показывает изображение. Затем запустите таймер на 500 мс в JS и повторно запрашивайте изображение для замены существующего изображения каждые 500 мс. я устанавливаю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 записей. Каждые 500 мс данные об использовании ЦП и памяти будут собираться и записываться в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😄
Ссылаться на
- Сюжет Гитхаб:github.com/gonum/plot
- Example Plots: GitHub.com/go num/сюжет/…
- Перейти на ежедневный репозиторий GitHub:GitHub.com/Darenjun/go-of…
я
мой блог:darjun.github.io
Добро пожаловать, чтобы обратить внимание на мою общедоступную учетную запись WeChat [GoUpUp], учитесь вместе и добивайтесь прогресса вместе ~