Package plotter
Package plotter defines a variety of standard Plotters for the
plot package.
Plotters use the primitives provided by the plot package to draw to
the data area of a plot. This package provides some standard data
styles such as lines, scatter plots, box plots, labels, and more.
New* functions return an error if the data contains Inf, NaN, or is
empty. Some of the New* functions return other plotter-specific errors
too.
▾ Example
Draw the plot logo.
Code:
p := plot.New()
plotter.DefaultLineStyle.Width = vg.Points(1)
plotter.DefaultGlyphStyle.Radius = vg.Points(3)
p.Y.Tick.Marker = plot.ConstantTicks([]plot.Tick{
{Value: 0, Label: "0"}, {Value: 0.25, Label: ""}, {Value: 0.5, Label: "0.5"}, {Value: 0.75, Label: ""}, {Value: 1, Label: "1"},
})
p.X.Tick.Marker = plot.ConstantTicks([]plot.Tick{
{Value: 0, Label: "0"}, {Value: 0.25, Label: ""}, {Value: 0.5, Label: "0.5"}, {Value: 0.75, Label: ""}, {Value: 1, Label: "1"},
})
pts := plotter.XYs{{X: 0, Y: 0}, {X: 0, Y: 1}, {X: 0.5, Y: 1}, {X: 0.5, Y: 0.6}, {X: 0, Y: 0.6}}
line, err := plotter.NewLine(pts)
if err != nil {
log.Panic(err)
}
scatter, err := plotter.NewScatter(pts)
if err != nil {
log.Panic(err)
}
p.Add(line, scatter)
pts = plotter.XYs{{X: 1, Y: 0}, {X: 0.75, Y: 0}, {X: 0.75, Y: 0.75}}
line, err = plotter.NewLine(pts)
if err != nil {
log.Panic(err)
}
scatter, err = plotter.NewScatter(pts)
if err != nil {
log.Panic(err)
}
p.Add(line, scatter)
pts = plotter.XYs{{X: 0.5, Y: 0.5}, {X: 1, Y: 0.5}}
line, err = plotter.NewLine(pts)
if err != nil {
log.Panic(err)
}
scatter, err = plotter.NewScatter(pts)
if err != nil {
log.Panic(err)
}
p.Add(line, scatter)
err = p.Save(100, 100, "testdata/plotLogo.png")
if err != nil {
log.Panic(err)
}
▹ Example (InvertedScale)
▾ Example (InvertedScale)
Code:
p := plot.New()
p.Title.Text = "Example of inverted axes"
p.Y.Scale = plot.InvertedScale{Normalizer: plot.LogScale{}}
p.X.Scale = plot.InvertedScale{Normalizer: plot.LinearScale{}}
p.Y.Tick.Marker = plot.LogTicks{Prec: -1}
p.X.Label.Text = "x"
p.Y.Label.Text = "f(x)"
f := plotter.NewFunction(math.Exp)
f.XMin = 0.2
f.XMax = 10
f.Color = color.RGBA{R: 255, A: 255}
p.Add(f, plotter.NewGrid())
p.Legend.Add("exp(x)", f)
p.X.Min = f.XMin
p.X.Max = f.XMax
p.Y.Min = math.Exp(f.XMin)
p.Y.Max = math.Exp(f.XMax)
err := p.Save(10*vg.Centimeter, 10*vg.Centimeter, "testdata/invertedlogscale.png")
if err != nil {
log.Panic(err)
}
▾ Example (LogScale)
Example_logScale shows how to create a plot with a log-scale on the Y-axis.
Code:
p := plot.New()
p.Title.Text = "My Plot"
p.Y.Scale = plot.LogScale{}
p.Y.Tick.Marker = plot.LogTicks{Prec: -1}
p.X.Label.Text = "x"
p.Y.Label.Text = "f(x)"
f := plotter.NewFunction(math.Exp)
f.XMin = 0.2
f.XMax = 10
f.Color = color.RGBA{R: 255, A: 255}
p.Add(f, plotter.NewGrid())
p.Legend.Add("exp(x)", f)
p.X.Min = f.XMin
p.X.Max = f.XMax
p.Y.Min = math.Exp(f.XMin)
p.Y.Max = math.Exp(f.XMax)
err := p.Save(10*vg.Centimeter, 10*vg.Centimeter, "testdata/logscale.png")
if err != nil {
log.Panic(err)
}
▹ Example (LogScaleWithAutoRescale)
▾ Example (LogScaleWithAutoRescale)
Example_logScaleWithAutoRescale shows how to create a plot with a log-scale on the Y-axis.
The Y-axis is instructed to automatically adapt its range according to
the underlying Y-ticker.
Code:
p := plot.New()
p.Title.Text = "My Plot"
p.Y.Scale = plot.LogScale{}
p.Y.Tick.Marker = plot.LogTicks{Prec: -1}
p.Y.AutoRescale = true
p.X.Label.Text = "x"
p.Y.Label.Text = "f(x)"
f := plotter.NewFunction(math.Exp)
f.XMin = 0.2
f.XMax = 10
f.Color = color.RGBA{R: 255, A: 255}
p.Add(f, plotter.NewGrid())
p.Legend.Add("exp(x)", f)
p.X.Min = f.XMin
p.X.Max = f.XMax
p.Y.Min = math.Exp(f.XMin)
p.Y.Max = math.Exp(f.XMax)
err := p.Save(10*vg.Centimeter, 10*vg.Centimeter, "testdata/logscale_autorescale.png")
if err != nil {
log.Panic(err)
}
▾ Example (Rotation)
Example_rotation gives some examples of rotating text.
Code:
n := 100
xmax := 2 * math.Pi
sin := func(n int, xmax float64) plotter.XYs {
xy := make(plotter.XYs, n)
for i := 0; i < n; i++ {
xy[i].X = xmax / float64(n) * float64(i)
xy[i].Y = math.Sin(xy[i].X) * 100
}
return xy
}
linePoints := sin(n, xmax)
labelPoints := sin(8, xmax)
p := plot.New()
p.Title.Text = "Rotation Example"
p.X.Label.Text = "X"
p.Y.Label.Text = "100 × Sine X"
l, err := plotter.NewLine(linePoints)
if err != nil {
log.Panic(err)
}
l.LineStyle.Width = vg.Points(1)
l.LineStyle.Color = color.RGBA{B: 255, A: 255}
labelData := plotter.XYLabels{
XYs: labelPoints,
Labels: []string{"0", "pi/4", "pi/2", "3pi/4", "pi", "5pi/4", "3pi/2", "7pi/4", "2pi"},
}
labels, err := plotter.NewLabels(labelData)
if err != nil {
log.Panic(err)
}
for i := range labels.TextStyle {
x := labels.XYs[i].X
labels.TextStyle[i].Rotation = math.Atan(math.Cos(x))
labels.TextStyle[i].XAlign = draw.XCenter
labels.TextStyle[i].YAlign = draw.YCenter
if x >= math.Pi {
labels.TextStyle[i].YAlign = draw.YTop
} else {
labels.TextStyle[i].YAlign = draw.YBottom
}
}
p.Add(l, labels)
p.Add(plotter.NewGlyphBoxes())
p.NominalX("0", "The number 1", "Number 2", "The number 3", "Number 4",
"The number 5", "Number 6")
p.X.Tick.Label.Rotation = math.Pi / 5
p.X.Tick.Label.YAlign = draw.YCenter
p.X.Tick.Label.XAlign = draw.XRight
p.Y.Tick.Label.Rotation = math.Pi / 2
p.Y.Tick.Label.XAlign = draw.XCenter
p.Y.Tick.Label.YAlign = draw.YBottom
err = p.Save(200, 150, "testdata/rotation.png")
if err != nil {
log.Panic(err)
}
▾ Example (TimeSeries)
Example_timeSeries draws a time series.
Code:
rnd := rand.New(rand.NewSource(1))
xticks := plot.TimeTicks{Format: "2006-01-02\n15:04"}
randomPoints := func(n int) plotter.XYs {
const (
month = 1
day = 1
hour = 1
min = 1
sec = 1
nsec = 1
)
pts := make(plotter.XYs, n)
for i := range pts {
date := time.Date(2007+i, month, day, hour, min, sec, nsec, time.UTC).Unix()
pts[i].X = float64(date)
pts[i].Y = float64(pts[i].X+10*rnd.Float64()) * 1e-9
}
return pts
}
n := 10
data := randomPoints(n)
p := plot.New()
p.Title.Text = "Time Series"
p.X.Tick.Marker = xticks
p.Y.Label.Text = "Number of Gophers\n(Billions)"
p.Add(plotter.NewGrid())
line, points, err := plotter.NewLinePoints(data)
if err != nil {
log.Panic(err)
}
line.Color = color.RGBA{G: 255, A: 255}
points.Shape = draw.CircleGlyph{}
points.Color = color.RGBA{R: 255, A: 255}
p.Add(line, points)
err = p.Save(10*vg.Centimeter, 5*vg.Centimeter, "testdata/timeseries.png")
if err != nil {
log.Panic(err)
}
▾ Example (Volcano)
Code:
package plotter_test
import (
"image/color"
"gonum.org/v1/gonum/mat"
"gonum.org/v1/plot"
"gonum.org/v1/plot/palette"
"gonum.org/v1/plot/plotter"
"gonum.org/v1/plot/vg"
"gonum.org/v1/plot/vg/draw"
)
type deciGrid struct{ mat.Matrix }
func (g deciGrid) Dims() (c, r int) { r, c = g.Matrix.Dims(); return c, r }
func (g deciGrid) Z(c, r int) float64 { return g.Matrix.At(r, c) }
func (g deciGrid) X(c int) float64 {
_, n := g.Matrix.Dims()
if c < 0 || c >= n {
panic("index out of range")
}
return 10 * float64(c)
}
func (g deciGrid) Y(r int) float64 {
m, _ := g.Matrix.Dims()
if r < 0 || r >= m {
panic("index out of range")
}
return 10 * float64(r)
}
func Example_volcano() {
var levels []float64
for l := 100.5; l < mat.Max(volcano.Matrix.(*mat.Dense)); l += 5 {
levels = append(levels, l)
}
c := plotter.NewContour(volcano, levels, palette.Rainbow(len(levels), (palette.Yellow+palette.Red)/2, palette.Blue, 1, 1, 1))
quarterStyle := draw.LineStyle{
Color: color.Black,
Width: vg.Points(0.5),
Dashes: []vg.Length{0.2, 0.4},
}
halfStyle := draw.LineStyle{
Color: color.Black,
Width: vg.Points(0.5),
Dashes: []vg.Length{5, 2, 1, 2},
}
c.LineStyles = append(c.LineStyles, quarterStyle, halfStyle, quarterStyle)
h := plotter.NewHeatMap(volcano, palette.Heat(len(levels)*2, 1))
p := plot.New()
p.Title.Text = "Maunga Whau Volcano"
p.Add(h)
p.Add(c)
p.X.Padding = 0
p.Y.Padding = 0
_, p.X.Max, _, p.Y.Max = h.DataRange()
if err := p.Save(10*vg.Centimeter, 10*vg.Centimeter, "testdata/volcano.png"); err != nil {
panic(err)
}
}
- Variables
- func CheckFloats(fs ...float64) error
- func NewLinePoints(xys XYer) (*Line, *Scatter, error)
- func PaletteThumbnailers(p palette.Palette) []plot.Thumbnailer
- func Range(vs Valuer) (min, max float64)
- func XYRange(xys XYer) (xmin, xmax, ymin, ymax float64)
- type BarChart
- func NewBarChart(vs Valuer, width vg.Length) (*BarChart, error)
- func (b *BarChart) BarHeight(i int) float64
- func (b *BarChart) DataRange() (xmin, xmax, ymin, ymax float64)
- func (b *BarChart) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox
- func (b *BarChart) Plot(c draw.Canvas, plt *plot.Plot)
- func (b *BarChart) StackOn(on *BarChart)
- func (b *BarChart) Thumbnail(c *draw.Canvas)
- type BoxPlot
- func NewBoxPlot(w vg.Length, loc float64, values Valuer) (*BoxPlot, error)
- func (b *BoxPlot) DataRange() (float64, float64, float64, float64)
- func (b *BoxPlot) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox
- func (b *BoxPlot) OutsideLabels(labels Labeller) (*Labels, error)
- func (b *BoxPlot) Plot(c draw.Canvas, plt *plot.Plot)
- type ColorBar
- func (l *ColorBar) DataRange() (xmin, xmax, ymin, ymax float64)
- func (l *ColorBar) Plot(c draw.Canvas, p *plot.Plot)
- type Contour
- func NewContour(g GridXYZ, levels []float64, p palette.Palette) *Contour
- func (h *Contour) DataRange() (xmin, xmax, ymin, ymax float64)
- func (h *Contour) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox
- func (h *Contour) Plot(c draw.Canvas, plt *plot.Plot)
- type Errors
- type Field
- func NewField(f FieldXY) *Field
- func (f *Field) DataRange() (xmin, xmax, ymin, ymax float64)
- func (f *Field) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox
- func (f *Field) Plot(c draw.Canvas, plt *plot.Plot)
- type FieldXY
- type Flow
- type Function
- func NewFunction(f func(float64) float64) *Function
- func (f *Function) Plot(c draw.Canvas, p *plot.Plot)
- func (f Function) Thumbnail(c *draw.Canvas)
- type GlyphBoxes
- func NewGlyphBoxes() *GlyphBoxes
- func (g GlyphBoxes) Plot(c draw.Canvas, plt *plot.Plot)
- type Grid
- func NewGrid() *Grid
- func (g *Grid) Plot(c draw.Canvas, plt *plot.Plot)
- type GridXYZ
- type HeatMap
- func NewHeatMap(g GridXYZ, p palette.Palette) *HeatMap
- func (h *HeatMap) DataRange() (xmin, xmax, ymin, ymax float64)
- func (h *HeatMap) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox
- func (h *HeatMap) Plot(c draw.Canvas, plt *plot.Plot)
- type Histogram
- func NewHist(vs Valuer, n int) (*Histogram, error)
- func NewHistogram(xy XYer, n int) (*Histogram, error)
- func (h *Histogram) DataRange() (xmin, xmax, ymin, ymax float64)
- func (h *Histogram) Normalize(sum float64)
- func (h *Histogram) Plot(c draw.Canvas, p *plot.Plot)
- func (h *Histogram) Thumbnail(c *draw.Canvas)
- type HistogramBin
- type Image
- func NewImage(img image.Image, xmin, ymin, xmax, ymax float64) *Image
- func (img *Image) DataRange() (xmin, xmax, ymin, ymax float64)
- func (img *Image) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox
- func (img *Image) Plot(c draw.Canvas, p *plot.Plot)
- type Labeller
- type Labels
- func NewLabels(d XYLabeller) (*Labels, error)
- func (l *Labels) DataRange() (xmin, xmax, ymin, ymax float64)
- func (l *Labels) GlyphBoxes(p *plot.Plot) []plot.GlyphBox
- func (l *Labels) Plot(c draw.Canvas, p *plot.Plot)
- type Line
- func NewLine(xys XYer) (*Line, error)
- func (pts *Line) DataRange() (xmin, xmax, ymin, ymax float64)
- func (pts *Line) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox
- func (pts *Line) Plot(c draw.Canvas, plt *plot.Plot)
- func (pts *Line) Thumbnail(c *draw.Canvas)
- type Polygon
- func NewPolygon(xys ...XYer) (*Polygon, error)
- func (pts *Polygon) DataRange() (xmin, xmax, ymin, ymax float64)
- func (pts *Polygon) Plot(c draw.Canvas, plt *plot.Plot)
- func (pts *Polygon) Thumbnail(c *draw.Canvas)
- type QuartPlot
- func NewQuartPlot(loc float64, values Valuer) (*QuartPlot, error)
- func (b *QuartPlot) DataRange() (float64, float64, float64, float64)
- func (b *QuartPlot) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox
- func (b *QuartPlot) OutsideLabels(labels Labeller) (*Labels, error)
- func (b *QuartPlot) Plot(c draw.Canvas, plt *plot.Plot)
- type Sankey
- func NewSankey(flows ...Flow) (*Sankey, error)
- func (s *Sankey) DataRange() (xmin, xmax, ymin, ymax float64)
- func (s *Sankey) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox
- func (s *Sankey) Plot(c draw.Canvas, plt *plot.Plot)
- func (s *Sankey) StockRange(label string, category int) (min, max float64, err error)
- func (s *Sankey) Thumbnailers() (legendLabels []string, thumbnailers []plot.Thumbnailer)
- type Scatter
- func NewScatter(xys XYer) (*Scatter, error)
- func (pts *Scatter) DataRange() (xmin, xmax, ymin, ymax float64)
- func (pts *Scatter) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox
- func (pts *Scatter) Plot(c draw.Canvas, plt *plot.Plot)
- func (pts *Scatter) Thumbnail(c *draw.Canvas)
- type StepKind
- type ValueLabels
- func (vs ValueLabels) Label(i int) string
- func (vs ValueLabels) Len() int
- func (vs ValueLabels) Value(i int) float64
- type Valuer
- type Values
- func CopyValues(vs Valuer) (Values, error)
- func (vs Values) Len() int
- func (vs Values) Value(i int) float64
- type XErrorBars
- func NewXErrorBars(xerrs interface {XYer XErrorer}) (*XErrorBars, error)
- func (e *XErrorBars) DataRange() (xmin, xmax, ymin, ymax float64)
- func (e *XErrorBars) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox
- func (e *XErrorBars) Plot(c draw.Canvas, p *plot.Plot)
- type XErrorer
- type XErrors
- func (xe XErrors) XError(i int) (float64, float64)
- type XValues
- func (xs XValues) Value(i int) float64
- type XY
- type XYLabeller
- type XYLabels
- func (l XYLabels) Label(i int) string
- type XYValues
- func (xy XYValues) XY(i int) (float64, float64)
- type XYZ
- type XYZer
- type XYZs
- func CopyXYZs(data XYZer) (XYZs, error)
- func (xyz XYZs) Len() int
- func (xyz XYZs) XY(i int) (float64, float64)
- func (xyz XYZs) XYZ(i int) (float64, float64, float64)
- type XYer
- type XYs
- func CopyXYs(data XYer) (XYs, error)
- func (xys XYs) Len() int
- func (xys XYs) XY(i int) (float64, float64)
- type YErrorBars
- func NewYErrorBars(yerrs interface {XYer YErrorer}) (*YErrorBars, error)
- func (e *YErrorBars) DataRange() (xmin, xmax, ymin, ymax float64)
- func (e *YErrorBars) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox
- func (e *YErrorBars) Plot(c draw.Canvas, p *plot.Plot)
- type YErrorer
- type YErrors
- func (ye YErrors) YError(i int) (float64, float64)
- type YValues
- func (ys YValues) Value(i int) float64
Package files
barchart.go
boxplot.go
colorbar.go
conrec.go
contour.go
errbars.go
field.go
functions.go
glyphbox.go
grid.go
heat.go
histogram.go
image.go
johnson.go
labels.go
line.go
palettethumbnailer.go
plotter.go
polygon.go
quartile.go
sankey.go
scatter.go
Variables
var (
DefaultFont = plot.DefaultFont
DefaultFontSize = vg.Points(10)
)
var (
DefaultLineStyle = draw.LineStyle{
Color: color.Black,
Width: vg.Points(1),
Dashes: []vg.Length{},
DashOffs: 0,
}
DefaultGlyphStyle = draw.GlyphStyle{
Color: color.Black,
Radius: vg.Points(2.5),
Shape: draw.RingGlyph{},
}
)
var (
ErrInfinity = errors.New("plotter: infinite data point")
ErrNaN = errors.New("plotter: NaN data point")
ErrNoData = errors.New("plotter: no data points")
)
var (
DefaultQuartMedianStyle = draw.GlyphStyle{
Color: color.Black,
Radius: vg.Points(1.5),
Shape: draw.CircleGlyph{},
}
DefaultQuartWhiskerStyle = draw.LineStyle{
Color: color.Black,
Width: vg.Points(0.5),
Dashes: []vg.Length{},
DashOffs: 0,
}
)
DefaultCapWidth is the default width of error bar caps.
var DefaultCapWidth = vg.Points(5)
var (
DefaultGridLineStyle = draw.LineStyle{
Color: color.Gray{128},
Width: vg.Points(0.25),
}
)
func CheckFloats(fs ...float64) error
CheckFloats returns an error if any of the arguments are NaN or Infinity.
func NewLinePoints(xys XYer) (*Line, *Scatter, error)
NewLinePoints returns both a Line and a
Points for the given point data.
func PaletteThumbnailers(p palette.Palette) []plot.Thumbnailer
PaletteThumbnailers creates a slice of plot.Thumbnailers that can be used to
add legend entries for the colors in a color palette.
func Range(vs Valuer) (min, max float64)
Range returns the minimum and maximum values.
func XYRange(xys XYer) (xmin, xmax, ymin, ymax float64)
XYRange returns the minimum and maximum
x and y values.
A BarChart presents grouped data with rectangular bars
with lengths proportional to the data values.
type BarChart struct {
Values
Width vg.Length
Color color.Color
draw.LineStyle
Offset vg.Length
XMin float64
Horizontal bool
}
▾ Example
Code:
values := plotter.Values{0.5, 10, 20, 30}
verticalLabels := []string{"A", "B", "C", "D"}
horizontalLabels := []string{"Label A", "Label B", "Label C", "Label D"}
p1 := plot.New()
verticalBarChart, err := plotter.NewBarChart(values, 0.5*vg.Centimeter)
if err != nil {
log.Panic(err)
}
p1.Add(verticalBarChart)
p1.NominalX(verticalLabels...)
err = p1.Save(100, 100, "testdata/verticalBarChart.png")
if err != nil {
log.Panic(err)
}
p2 := plot.New()
horizontalBarChart, err := plotter.NewBarChart(values, 0.5*vg.Centimeter)
horizontalBarChart.Horizontal = true
if err != nil {
log.Panic(err)
}
p2.Add(horizontalBarChart)
p2.NominalY(horizontalLabels...)
err = p2.Save(100, 100, "testdata/horizontalBarChart.png")
if err != nil {
log.Panic(err)
}
groupA := plotter.Values{20, 35, 30, 35, 27}
groupB := plotter.Values{25, 32, 34, 20, 25}
groupC := plotter.Values{12, 28, 15, 21, 8}
groupD := plotter.Values{30, 42, 6, 9, 12}
p := plot.New()
p.Title.Text = "Bar chart"
p.Y.Label.Text = "Heights"
w := vg.Points(8)
barsA, err := plotter.NewBarChart(groupA, w)
if err != nil {
log.Panic(err)
}
barsA.Color = color.RGBA{R: 255, A: 255}
barsA.Offset = -w / 2
barsB, err := plotter.NewBarChart(groupB, w)
if err != nil {
log.Panic(err)
}
barsB.Color = color.RGBA{R: 196, G: 196, A: 255}
barsB.Offset = w / 2
barsC, err := plotter.NewBarChart(groupC, w)
if err != nil {
log.Panic(err)
}
barsC.XMin = 6
barsC.Color = color.RGBA{B: 255, A: 255}
barsC.Offset = -w / 2
barsD, err := plotter.NewBarChart(groupD, w)
if err != nil {
log.Panic(err)
}
barsD.Color = color.RGBA{B: 255, R: 255, A: 255}
barsD.XMin = 6
barsD.Offset = w / 2
p.Add(barsA, barsB, barsC, barsD)
p.Legend.Add("A", barsA)
p.Legend.Add("B", barsB)
p.Legend.Add("C", barsC)
p.Legend.Add("D", barsD)
p.Legend.Top = true
p.NominalX("Zero", "One", "Two", "Three", "Four", "",
"Six", "Seven", "Eight", "Nine", "Ten")
p.Add(plotter.NewGlyphBoxes())
err = p.Save(300, 250, "testdata/barChart2.png")
if err != nil {
log.Panic(err)
}
p = plot.New()
p.Title.Text = "Bar chart"
p.Y.Label.Text = "Heights"
w = vg.Points(15)
barsA, err = plotter.NewBarChart(groupA, w)
if err != nil {
log.Panic(err)
}
barsA.Color = color.RGBA{R: 255, A: 255}
barsA.Offset = -w / 2
barsB, err = plotter.NewBarChart(groupB, w)
if err != nil {
log.Panic(err)
}
barsB.Color = color.RGBA{R: 196, G: 196, A: 255}
barsB.StackOn(barsA)
barsC, err = plotter.NewBarChart(groupC, w)
if err != nil {
log.Panic(err)
}
barsC.Offset = w / 2
barsC.Color = color.RGBA{B: 255, A: 255}
barsD, err = plotter.NewBarChart(groupD, w)
if err != nil {
log.Panic(err)
}
barsD.StackOn(barsC)
barsD.Color = color.RGBA{B: 255, R: 255, A: 255}
p.Add(barsA, barsB, barsC, barsD)
p.Legend.Add("A", barsA)
p.Legend.Add("B", barsB)
p.Legend.Add("C", barsC)
p.Legend.Add("D", barsD)
p.Legend.Top = true
p.NominalX("Zero", "One", "Two", "Three", "Four")
p.Add(plotter.NewGlyphBoxes())
err = p.Save(250, 250, "testdata/stackedBarChart.png")
if err != nil {
log.Panic(err)
}
▹ Example (PositiveNegative)
▾ Example (PositiveNegative)
This example shows a bar chart with both positive and negative values.
Code:
rnd := rand.New(rand.NewSource(1))
const n = 6
data1 := make(plotter.Values, n)
data2 := make(plotter.Values, n)
net := make(plotter.XYs, n)
for i := 0; i < n; i++ {
data1[i] = rnd.Float64()*2 - 1
data2[i] = rnd.Float64()*2 - 1
net[i].X = data1[i] + data2[i]
net[i].Y = float64(i)
}
splitBySign := func(d plotter.Values) (pos, neg plotter.Values) {
pos = make(plotter.Values, len(d))
neg = make(plotter.Values, len(d))
for i, v := range d {
if v > 0 {
pos[i] = v
} else {
neg[i] = v
}
}
return
}
data1Pos, data1Neg := splitBySign(data1)
data2Pos, data2Neg := splitBySign(data2)
const barWidth = 0.3 * vg.Centimeter
pos1, err := plotter.NewBarChart(data1Pos, barWidth)
if err != nil {
log.Panic(err)
}
pos2, err := plotter.NewBarChart(data2Pos, barWidth)
if err != nil {
log.Panic(err)
}
neg1, err := plotter.NewBarChart(data1Neg, barWidth)
if err != nil {
log.Panic(err)
}
neg2, err := plotter.NewBarChart(data2Neg, barWidth)
if err != nil {
log.Panic(err)
}
netDots, err := plotter.NewScatter(net)
if err != nil {
log.Panic(err)
}
netDots.Radius = vg.Points(1.25)
pos2.StackOn(pos1)
neg2.StackOn(neg1)
color1 := color.NRGBA{R: 112, G: 22, B: 0, A: 255}
color2 := color.NRGBA{R: 91, G: 194, B: 54, A: 100}
pos1.Color, neg1.Color = color1, color1
pos2.Color, neg2.Color = color2, color2
pos1.Horizontal, pos2.Horizontal, neg1.Horizontal, neg2.Horizontal = true, true, true, true
zero, err := plotter.NewLine(plotter.XYs{{0, 0}, {0, 5}})
if err != nil {
log.Panic(err)
}
p := plot.New()
p.Add(zero, pos1, pos2, neg1, neg2, netDots)
p.NominalY("Alpha", "Bravo", "Charlie", "Echo", "Foxtrot", "Golf")
p.Legend.Add("1", pos1)
p.Legend.Add("2", pos2)
p.Legend.Add("Sum", netDots)
p.Legend.Top = true
p.Legend.ThumbnailWidth = 2 * vg.Millimeter
p.Legend.TextStyle.Font.Size = 2 * vg.Millimeter
err = p.Save(100, 100, "testdata/barChart_positiveNegative.png")
if err != nil {
log.Panic(err)
}
func NewBarChart(vs Valuer, width vg.Length) (*BarChart, error)
NewBarChart returns a new bar chart with a single bar for each value.
The bars heights correspond to the values and their x locations correspond
to the index of their value in the Valuer.
func (b *BarChart) BarHeight(i int) float64
BarHeight returns the maximum y value of the
ith bar, taking into account any bars upon
which it is stacked.
func (b *BarChart) DataRange() (xmin, xmax, ymin, ymax float64)
DataRange implements the plot.DataRanger interface.
func (b *BarChart) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox
GlyphBoxes implements the GlyphBoxer interface.
func (*BarChart) Plot
¶
func (b *BarChart) Plot(c draw.Canvas, plt *plot.Plot)
Plot implements the plot.Plotter interface.
func (*BarChart) StackOn
¶
func (b *BarChart) StackOn(on *BarChart)
StackOn stacks a bar chart on top of another,
and sets the XMin and Offset to that of the
chart upon which it is being stacked.
func (b *BarChart) Thumbnail(c *draw.Canvas)
Thumbnail fulfills the plot.Thumbnailer interface.
BoxPlot implements the Plotter interface, drawing
a boxplot to represent the distribution of values.
type BoxPlot struct {
Offset vg.Length
Width vg.Length
CapWidth vg.Length
GlyphStyle draw.GlyphStyle
FillColor color.Color
BoxStyle draw.LineStyle
MedianStyle draw.LineStyle
WhiskerStyle draw.LineStyle
Horizontal bool
}
▾ Example
Code:
rnd := rand.New(rand.NewSource(1))
const n = 100
uniform := make(plotter.ValueLabels, n)
normal := make(plotter.ValueLabels, n)
expon := make(plotter.ValueLabels, n)
for i := 0; i < n; i++ {
uniform[i].Value = rnd.Float64()
uniform[i].Label = fmt.Sprintf("%4.4f", uniform[i].Value)
normal[i].Value = rnd.NormFloat64()
normal[i].Label = fmt.Sprintf("%4.4f", normal[i].Value)
expon[i].Value = rnd.ExpFloat64()
expon[i].Label = fmt.Sprintf("%4.4f", expon[i].Value)
}
uniBox, err := plotter.NewBoxPlot(vg.Points(20), 0, uniform)
if err != nil {
log.Panic(err)
}
uniBox.FillColor = color.RGBA{127, 188, 165, 1}
normBox, err := plotter.NewBoxPlot(vg.Points(20), 1, normal)
if err != nil {
log.Panic(err)
}
normBox.FillColor = color.RGBA{127, 188, 165, 1}
expBox, err := plotter.NewBoxPlot(vg.Points(20), 2, expon)
if err != nil {
log.Panic(err)
}
expBox.FillColor = color.RGBA{127, 188, 165, 1}
uniLabels, err := uniBox.OutsideLabels(uniform)
if err != nil {
log.Panic(err)
}
normLabels, err := normBox.OutsideLabels(normal)
if err != nil {
log.Panic(err)
}
expLabels, err := expBox.OutsideLabels(expon)
if err != nil {
log.Panic(err)
}
p1 := plot.New()
p1.Title.Text = "Vertical Box Plot"
p1.Y.Label.Text = "plotter.Values"
p1.Y.Max = 6
p1.Add(uniBox, uniLabels, normBox, normLabels, expBox, expLabels)
p1.NominalX("Uniform\nDistribution", "Normal\nDistribution",
"Exponential\nDistribution")
err = p1.Save(200, 200, "testdata/verticalBoxPlot.png")
if err != nil {
log.Panic(err)
}
normBox.Horizontal = true
expBox.Horizontal = true
uniBox.Horizontal = true
uniLabels, err = uniBox.OutsideLabels(uniform)
if err != nil {
log.Panic(err)
}
normLabels, err = normBox.OutsideLabels(normal)
if err != nil {
log.Panic(err)
}
expLabels, err = expBox.OutsideLabels(expon)
if err != nil {
log.Panic(err)
}
p2 := plot.New()
p2.Title.Text = "Horizontal Box Plot"
p2.X.Label.Text = "plotter.Values"
p2.Add(uniBox, uniLabels, normBox, normLabels, expBox, expLabels)
p2.NominalY("Uniform\nDistribution", "Normal\nDistribution",
"Exponential\nDistribution")
err = p2.Save(200, 200, "testdata/horizontalBoxPlot.png")
if err != nil {
log.Panic(err)
}
p3 := plot.New()
p3.Title.Text = "Box Plot"
p3.Y.Label.Text = "plotter.Values"
w := vg.Points(20)
for x := 0.0; x < 3.0; x++ {
b0, err := plotter.NewBoxPlot(w, x, uniform)
if err != nil {
log.Panic(err)
}
b0.Offset = -w - vg.Points(3)
b1, err := plotter.NewBoxPlot(w, x, normal)
if err != nil {
log.Panic(err)
}
b2, err := plotter.NewBoxPlot(w, x, expon)
if err != nil {
log.Panic(err)
}
b2.Offset = w + vg.Points(3)
p3.Add(b0, b1, b2)
}
p3.Add(plotter.NewGlyphBoxes())
p3.NominalX("Group 0", "Group 1", "Group 2")
err = p3.Save(300, 300, "testdata/groupedBoxPlot.png")
if err != nil {
log.Panic(err)
}
func NewBoxPlot(w vg.Length, loc float64, values Valuer) (*BoxPlot, error)
NewBoxPlot returns a new BoxPlot that represents
the distribution of the given values. The style of
the box plot is that used for Tukey's schematic
plots in “Exploratory Data Analysis.”
An error is returned if the boxplot is created with
no values.
The fence values are 1.5x the interquartile before
the first quartile and after the third quartile. Any
value that is outside of the fences are drawn as
Outside points. The adjacent values (to which the
whiskers stretch) are the minimum and maximum
values that are not outside the fences.
func (b *BoxPlot) DataRange() (float64, float64, float64, float64)
DataRange returns the minimum and maximum x
and y values, implementing the plot.DataRanger
interface.
func (b *BoxPlot) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox
GlyphBoxes returns a slice of GlyphBoxes for the
points and for the median line of the boxplot,
implementing the plot.GlyphBoxer interface
func (b *BoxPlot) OutsideLabels(labels Labeller) (*Labels, error)
OutsideLabels returns a *Labels that will plot
a label for each of the outside points. The
labels are assumed to correspond to the
points used to create the box plot.
func (*BoxPlot) Plot
¶
func (b *BoxPlot) Plot(c draw.Canvas, plt *plot.Plot)
Plot draws the BoxPlot on Canvas c and Plot plt.
ColorBar is a plot.Plotter that draws a color bar legend for a ColorMap.
type ColorBar struct {
ColorMap palette.ColorMap
Vertical bool
Colors int
}
▾ Example (Horizontal)
Code:
p := plot.New()
p.HideY()
p.X.Padding = 0
p.Title.Text = "Title"
l := &plotter.ColorBar{ColorMap: moreland.ExtendedBlackBody()}
l.ColorMap.SetMin(0.5)
l.ColorMap.SetMax(1.5)
p.Add(l)
if err := p.Save(300, 48, "testdata/colorBarHorizontal.png"); err != nil {
log.Panic(err)
}
▾ Example (Vertical)
Code:
p := plot.New()
l := &plotter.ColorBar{ColorMap: moreland.ExtendedBlackBody()}
l.ColorMap.SetMin(0.5)
l.ColorMap.SetMax(1.5)
l.Vertical = true
p.Add(l)
p.HideX()
p.Y.Padding = 0
p.Title.Text = "Title"
if err := p.Save(40, 300, "testdata/colorBarVertical.png"); err != nil {
log.Panic(err)
}
func (l *ColorBar) DataRange() (xmin, xmax, ymin, ymax float64)
DataRange implements the DataRange method
of the plot.DataRanger interface.
func (*ColorBar) Plot
¶
func (l *ColorBar) Plot(c draw.Canvas, p *plot.Plot)
Plot implements the Plot method of the plot.Plotter interface.
Contour implements the Plotter interface, drawing
a contour plot of the values in the GridXYZ field.
type Contour struct {
GridXYZ GridXYZ
Levels []float64
LineStyles []draw.LineStyle
Palette palette.Palette
Underflow color.Color
Overflow color.Color
Min, Max float64
}
▾ Example
Code:
rnd := rand.New(rand.NewSource(1234))
const stddev = 2
data := make([]float64, 6400)
for i := range data {
r := float64(i/80) - 40
c := float64(i%80) - 40
data[i] = rnd.NormFloat64()*stddev + math.Hypot(r, c)
}
var (
grid = unitGrid{mat.NewDense(80, 80, data)}
levels = []float64{-1, 3, 7, 9, 13, 15, 19, 23, 27, 31}
c = plotter.NewContour(
grid,
levels,
palette.Rainbow(10, palette.Blue, palette.Red, 1, 1, 1),
)
)
p := plot.New()
p.Title.Text = "Contour"
p.X.Padding = 0
p.Y.Padding = 0
p.X.Max = 79.5
p.Y.Max = 79.5
p.Add(c)
err := p.Save(10*vg.Centimeter, 10*vg.Centimeter, "testdata/contour.png")
if err != nil {
log.Fatalf("could not save plot: %+v", err)
}
func NewContour(g GridXYZ, levels []float64, p palette.Palette) *Contour
NewContour creates as new contour plotter for the given data, using
the provided palette. If levels is nil, contours are generated for
the 0.01, 0.05, 0.25, 0.5, 0.75, 0.95 and 0.99 quantiles.
If g has Min and Max methods that return a float, those returned
values are used to set the respective Contour fields.
If the returned Contour is used when Min is greater than Max, the
Plot method will panic.
func (h *Contour) DataRange() (xmin, xmax, ymin, ymax float64)
DataRange implements the DataRange method
of the plot.DataRanger interface.
func (h *Contour) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox
GlyphBoxes implements the GlyphBoxes method
of the plot.GlyphBoxer interface.
func (*Contour) Plot
¶
func (h *Contour) Plot(c draw.Canvas, plt *plot.Plot)
Plot implements the Plot method of the plot.Plotter interface.
Errors is a slice of low and high error values.
type Errors []struct{ Low, High float64 }
▾ Example
ExampleErrors draws points and error bars.
Code:
rnd := rand.New(rand.NewSource(1))
randomError := func(n int) plotter.Errors {
err := make(plotter.Errors, n)
for i := range err {
err[i].Low = rnd.Float64()
err[i].High = rnd.Float64()
}
return err
}
randomPoints := func(n int) plotter.XYs {
pts := make(plotter.XYs, n)
for i := range pts {
if i == 0 {
pts[i].X = rnd.Float64()
} else {
pts[i].X = pts[i-1].X + rnd.Float64()
}
pts[i].Y = pts[i].X + 10*rnd.Float64()
}
return pts
}
type errPoints struct {
plotter.XYs
plotter.YErrors
plotter.XErrors
}
n := 15
data := errPoints{
XYs: randomPoints(n),
YErrors: plotter.YErrors(randomError(n)),
XErrors: plotter.XErrors(randomError(n)),
}
p := plot.New()
scatter, err := plotter.NewScatter(data)
if err != nil {
log.Panic(err)
}
scatter.Shape = draw.CrossGlyph{}
xerrs, err := plotter.NewXErrorBars(data)
if err != nil {
log.Panic(err)
}
yerrs, err := plotter.NewYErrorBars(data)
if err != nil {
log.Panic(err)
}
p.Add(scatter, xerrs, yerrs)
err = p.Save(200, 200, "testdata/errorBars.png")
if err != nil {
log.Panic(err)
}
Field implements the Plotter interface, drawing
a vector field of the values in the FieldXY field.
type Field struct {
FieldXY FieldXY
DrawGlyph func(c vg.Canvas, sty draw.LineStyle, v XY)
LineStyle draw.LineStyle
}
▾ Example
Code:
f := plotter.NewField(field{
r: 17, c: 19,
fn: func(x, y float64) plotter.XY {
return plotter.XY{
X: y,
Y: -x,
}
},
})
f.LineStyle.Width = 0.2
p := plot.New()
p.Title.Text = "Vector field"
p.X.Tick.Marker = integerTicks{}
p.Y.Tick.Marker = integerTicks{}
p.Add(f)
img := vgimg.New(250, 175)
dc := draw.New(img)
p.Draw(dc)
w, err := os.Create("testdata/field.png")
if err != nil {
log.Panic(err)
}
png := vgimg.PngCanvas{Canvas: img}
if _, err = png.WriteTo(w); err != nil {
log.Panic(err)
}
▾ Example (Colors)
Code:
f := plotter.NewField(field{
r: 5, c: 9,
fn: func(x, y float64) plotter.XY {
return plotter.XY{
X: -0.75*x + y,
Y: -0.75*y - x,
}
},
})
pal := moreland.ExtendedBlackBody()
pal.SetMin(0)
pal.SetMax(1.1)
f.DrawGlyph = func(c vg.Canvas, sty draw.LineStyle, v plotter.XY) {
c.Push()
defer c.Pop()
mag := math.Hypot(v.X, v.Y)
var pa vg.Path
if mag == 0 {
c.SetColor(color.Black)
pa.Move(vg.Point{X: sty.Width})
pa.Arc(vg.Point{}, sty.Width, 0, 2*math.Pi)
pa.Close()
c.Fill(pa)
return
}
col, err := pal.At(mag)
if err != nil {
panic(err)
}
c.SetColor(col)
pa.Move(vg.Point{})
pa.Line(vg.Point{X: 1, Y: 0})
pa.Close()
c.Stroke(pa)
}
p := plot.New()
p.Title.Text = "Vortex"
p.X.Tick.Marker = integerTicks{}
p.Y.Tick.Marker = integerTicks{}
p.Add(f)
img := vgimg.New(250, 175)
dc := draw.New(img)
p.Draw(dc)
w, err := os.Create("testdata/color_field.png")
if err != nil {
log.Panic(err)
}
defer w.Close()
png := vgimg.PngCanvas{Canvas: img}
if _, err = png.WriteTo(w); err != nil {
log.Panic(err)
}
▾ Example (Gophers)
Code:
file, err := os.Open("testdata/gopher_running.png")
if err != nil {
log.Panic(err)
}
defer file.Close()
gopher, err := png.Decode(file)
if err != nil {
log.Panic(err)
}
f := plotter.NewField(field{
r: 5, c: 9,
fn: func(x, y float64) plotter.XY {
return plotter.XY{
X: -0.75*x + y,
Y: -0.75*y - x,
}
},
})
f.DrawGlyph = func(c vg.Canvas, _ draw.LineStyle, v plotter.XY) {
if math.Hypot(v.X, v.Y) == 0 {
return
}
c.Translate(vg.Point{X: -1, Y: -1})
c.DrawImage(vg.Rectangle{Max: vg.Point{X: 2, Y: 2}}, gopher)
}
p := plot.New()
p.Title.Text = "Gopher vortex"
p.X.Tick.Marker = integerTicks{}
p.Y.Tick.Marker = integerTicks{}
p.Add(f)
img := vgimg.New(250, 175)
dc := draw.New(img)
p.Draw(dc)
w, err := os.Create("testdata/gopher_field.png")
if err != nil {
log.Panic(err)
}
png := vgimg.PngCanvas{Canvas: img}
if _, err = png.WriteTo(w); err != nil {
log.Panic(err)
}
func NewField(f FieldXY) *Field
NewField creates a new vector field plotter.
func (f *Field) DataRange() (xmin, xmax, ymin, ymax float64)
DataRange implements the DataRange method
of the plot.DataRanger interface.
func (f *Field) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox
GlyphBoxes implements the GlyphBoxes method
of the plot.GlyphBoxer interface.
func (*Field) Plot
¶
func (f *Field) Plot(c draw.Canvas, plt *plot.Plot)
Plot implements the Plot method of the plot.Plotter interface.
FieldXY describes a two dimensional vector field where the
X and Y coordinates are arranged on a rectangular grid.
type FieldXY interface {
Dims() (c, r int)
Vector(c, r int) XY
X(c int) float64
Y(r int) float64
}
A Flow represents the amount of an entity flowing between two stocks.
type Flow struct {
SourceLabel, ReceptorLabel string
SourceCategory, ReceptorCategory int
Value float64
Group string
}
Function implements the Plotter interface,
drawing a line for the given function.
type Function struct {
F func(x float64) (y float64)
XMin, XMax float64
Samples int
draw.LineStyle
}
▾ Example
ExampleFunction draws some functions.
Code:
quad := plotter.NewFunction(func(x float64) float64 { return x * x })
quad.Color = color.RGBA{B: 255, A: 255}
exp := plotter.NewFunction(func(x float64) float64 { return math.Pow(2, x) })
exp.Dashes = []vg.Length{vg.Points(2), vg.Points(2)}
exp.Width = vg.Points(2)
exp.Color = color.RGBA{G: 255, A: 255}
sin := plotter.NewFunction(func(x float64) float64 { return 10*math.Sin(x) + 50 })
sin.Dashes = []vg.Length{vg.Points(4), vg.Points(5)}
sin.Width = vg.Points(4)
sin.Color = color.RGBA{R: 255, A: 255}
p := plot.New()
p.Title.Text = "Functions"
p.X.Label.Text = "X"
p.Y.Label.Text = "Y"
p.Add(quad, exp, sin)
p.Legend.Add("x^2", quad)
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
err := p.Save(200, 200, "testdata/functions.png")
if err != nil {
log.Panic(err)
}
func NewFunction(f func(float64) float64) *Function
NewFunction returns a Function that plots F using
the default line style with 50 samples.
func (*Function) Plot
¶
func (f *Function) Plot(c draw.Canvas, p *plot.Plot)
Plot implements the Plotter interface, drawing a line
that connects each point in the Line.
func (f Function) Thumbnail(c *draw.Canvas)
Thumbnail draws a line in the given style down the
center of a DrawArea as a thumbnail representation
of the LineStyle of the function.
GlyphBoxes implements the Plotter interface, drawing
all of the glyph boxes of the plot. This is intended for
debugging.
type GlyphBoxes struct {
draw.LineStyle
}
func NewGlyphBoxes() *GlyphBoxes
func (GlyphBoxes) Plot
¶
func (g GlyphBoxes) Plot(c draw.Canvas, plt *plot.Plot)
Grid implements the plot.Plotter interface, drawing
a set of grid lines at the major tick marks.
type Grid struct {
Vertical draw.LineStyle
Horizontal draw.LineStyle
}
func NewGrid() *Grid
NewGrid returns a new grid with both vertical and
horizontal lines using the default grid line style.
func (*Grid) Plot
¶
func (g *Grid) Plot(c draw.Canvas, plt *plot.Plot)
Plot implements the plot.Plotter interface.
GridXYZ describes three dimensional data where the X and Y
coordinates are arranged on a rectangular grid.
type GridXYZ interface {
Dims() (c, r int)
Z(c, r int) float64
X(c int) float64
Y(r int) float64
}
HeatMap implements the Plotter interface, drawing
a heat map of the values in the GridXYZ field.
type HeatMap struct {
GridXYZ GridXYZ
Palette palette.Palette
Underflow color.Color
Overflow color.Color
NaN color.Color
Min, Max float64
Rasterized bool
}
▾ Example
Code:
m := offsetUnitGrid{
XOffset: -2,
YOffset: -1,
Data: mat.NewDense(3, 4, []float64{
1, 2, 3, 4,
5, 6, 7, 8,
9, 10, 11, 12,
})}
pal := palette.Heat(12, 1)
h := plotter.NewHeatMap(m, pal)
p := plot.New()
p.Title.Text = "Heat map"
p.X.Tick.Marker = integerTicks{}
p.Y.Tick.Marker = integerTicks{}
p.Add(h)
l := plot.NewLegend()
thumbs := plotter.PaletteThumbnailers(pal)
for i := len(thumbs) - 1; i >= 0; i-- {
t := thumbs[i]
if i != 0 && i != len(thumbs)-1 {
l.Add("", t)
continue
}
var val float64
switch i {
case 0:
val = h.Min
case len(thumbs) - 1:
val = h.Max
}
l.Add(fmt.Sprintf("%.2g", val), t)
}
p.X.Padding = 0
p.Y.Padding = 0
p.X.Max = 1.5
p.Y.Max = 1.5
img := vgimg.New(250, 175)
dc := draw.New(img)
l.Top = true
r := l.Rectangle(dc)
legendWidth := r.Max.X - r.Min.X
l.YOffs = -p.Title.TextStyle.FontExtents().Height
l.Draw(dc)
dc = draw.Crop(dc, 0, -legendWidth-vg.Millimeter, 0, 0)
p.Draw(dc)
w, err := os.Create("testdata/heatMap.png")
if err != nil {
log.Panic(err)
}
png := vgimg.PngCanvas{Canvas: img}
if _, err = png.WriteTo(w); err != nil {
log.Panic(err)
}
▾ Example (Rasterized)
Code:
m := offsetUnitGrid{
XOffset: -2,
YOffset: -1,
Data: mat.NewDense(3, 4, []float64{
1, 2, 3, 4,
5, 6, 7, 8,
9, 10, 11, 12,
})}
pal := palette.Heat(12, 1)
plt := plot.New()
raster := plotter.NewHeatMap(&m, pal)
raster.Rasterized = true
plt.Add(raster)
err := plt.Save(250, 175, "testdata/rasterHeatMap.png")
if err != nil {
log.Panic(err)
}
func NewHeatMap(g GridXYZ, p palette.Palette) *HeatMap
NewHeatMap creates as new heat map plotter for the given data,
using the provided palette. If g has Min and Max methods that return
a float, those returned values are used to set the respective HeatMap
fields. If the returned HeatMap is used when Min is greater than Max,
the Plot method will panic.
func (h *HeatMap) DataRange() (xmin, xmax, ymin, ymax float64)
DataRange implements the DataRange method
of the plot.DataRanger interface.
func (h *HeatMap) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox
GlyphBoxes implements the GlyphBoxes method
of the plot.GlyphBoxer interface.
func (*HeatMap) Plot
¶
func (h *HeatMap) Plot(c draw.Canvas, plt *plot.Plot)
Plot implements the Plot method of the plot.Plotter interface.
Histogram implements the Plotter interface,
drawing a histogram of the data.
type Histogram struct {
Bins []HistogramBin
Width float64
FillColor color.Color
draw.LineStyle
LogY bool
}
▾ Example
An example of making a histogram.
Code:
rnd := rand.New(rand.NewSource(1))
stdNorm := func(x float64) float64 {
const sigma = 1.0
const mu = 0.0
const root2π = 2.50662827459517818309
return 1.0 / (sigma * root2π) * math.Exp(-((x-mu)*(x-mu))/(2*sigma*sigma))
}
n := 10000
vals := make(plotter.Values, n)
for i := 0; i < n; i++ {
vals[i] = rnd.NormFloat64()
}
p := plot.New()
p.Title.Text = "Histogram"
h, err := plotter.NewHist(vals, 16)
if err != nil {
log.Panic(err)
}
h.Normalize(1)
p.Add(h)
norm := plotter.NewFunction(stdNorm)
norm.Color = color.RGBA{R: 255, A: 255}
norm.Width = vg.Points(2)
p.Add(norm)
err = p.Save(200, 200, "testdata/histogram.png")
if err != nil {
log.Panic(err)
}
▾ Example (LogScaleY)
Code:
p := plot.New()
p.Title.Text = "Histogram in log-y"
p.Y.Scale = plot.LogScale{}
p.Y.Tick.Marker = plot.LogTicks{Prec: -1}
p.Y.Label.Text = "Y"
p.X.Label.Text = "X"
h1, err := plotter.NewHist(plotter.Values{
-2, -2,
-1,
+3, +3, +3, +3,
+1, +1, +1, +1, +1, +1, +1, +1, +1, +1,
+1, +1, +1, +1, +1, +1, +1, +1, +1, +1,
}, 16)
if err != nil {
log.Fatal(err)
}
h1.LogY = true
h1.FillColor = color.RGBA{255, 0, 0, 255}
h2, err := plotter.NewHist(plotter.Values{
-3, -3, -3,
+2, +2, +2, +2, +2,
}, 16)
if err != nil {
log.Fatal(err)
}
h2.LogY = true
h2.FillColor = color.RGBA{0, 0, 255, 255}
p.Add(h1, h2, plotter.NewGrid())
err = p.Save(200, 200, "testdata/histogram_logy.png")
if err != nil {
log.Fatal(err)
}
func NewHist(vs Valuer, n int) (*Histogram, error)
NewHist returns a new histogram, as in
NewHistogram, except that it accepts a Valuer
instead of an XYer.
func NewHistogram(xy XYer, n int) (*Histogram, error)
NewHistogram returns a new histogram
that represents the distribution of values
using the given number of bins.
Each y value is assumed to be the frequency
count for the corresponding x.
If the number of bins is non-positive than
a reasonable default is used.
func (h *Histogram) DataRange() (xmin, xmax, ymin, ymax float64)
DataRange returns the minimum and maximum X and Y values
func (h *Histogram) Normalize(sum float64)
Normalize normalizes the histogram so that the
total area beneath it sums to a given value.
func (*Histogram) Plot
¶
func (h *Histogram) Plot(c draw.Canvas, p *plot.Plot)
Plot implements the Plotter interface, drawing a line
that connects each point in the Line.
func (h *Histogram) Thumbnail(c *draw.Canvas)
Thumbnail draws a rectangle in the given style of the histogram.
A HistogramBin approximates the number of values
within a range by a single number (the weight).
type HistogramBin struct {
Min, Max float64
Weight float64
}
Image is a plotter that draws a scaled, raster image.
type Image struct {
}
▾ Example
An example of embedding an image in a plot.
Code:
p := plot.New()
p.Title.Text = "A Logo"
f, err := os.Open("testdata/image_plot_input.png")
if err != nil {
log.Fatalf("error opening image file: %v\n", err)
}
defer f.Close()
img, err := png.Decode(f)
if err != nil {
log.Fatalf("error decoding image file: %v\n", err)
}
p.Add(plotter.NewImage(img, 100, 100, 200, 200))
const (
w = 5 * vg.Centimeter
h = 5 * vg.Centimeter
)
err = p.Save(w, h, "testdata/image_plot.png")
if err != nil {
log.Fatalf("error saving image plot: %v\n", err)
}
▾ Example (Log)
An example of embedding an image in a plot with non-linear axes.
Code:
p := plot.New()
p.Title.Text = "A Logo"
f, err := os.Open("testdata/gopher.png")
if err != nil {
log.Fatalf("error opening image file: %v\n", err)
}
defer f.Close()
img, err := png.Decode(f)
if err != nil {
log.Fatalf("error decoding image file: %v\n", err)
}
p.Add(plotter.NewImage(img, 100, 100, 10000, 10000))
p.X.Scale = plot.LogScale{}
p.Y.Scale = plot.LogScale{}
p.X.Tick.Marker = plot.LogTicks{Prec: -1}
p.Y.Tick.Marker = plot.LogTicks{Prec: 3}
const (
w = 5 * vg.Centimeter
h = 5 * vg.Centimeter
)
err = p.Save(w, h, "testdata/image_plot_log.png")
if err != nil {
log.Fatalf("error saving image plot: %v\n", err)
}
func NewImage(img image.Image, xmin, ymin, xmax, ymax float64) *Image
NewImage creates a new image plotter.
Image will plot img inside the rectangle defined by the
(xmin, ymin) and (xmax, ymax) points given in the data space.
The img will be scaled to fit inside the rectangle.
func (img *Image) DataRange() (xmin, xmax, ymin, ymax float64)
DataRange implements the DataRange method
of the plot.DataRanger interface.
func (img *Image) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox
GlyphBoxes implements the GlyphBoxes method
of the plot.GlyphBoxer interface.
func (*Image) Plot
¶
func (img *Image) Plot(c draw.Canvas, p *plot.Plot)
Plot implements the Plot method of the plot.Plotter interface.
Labeller wraps the Label methods.
type Labeller interface {
Label(int) string
}
Labels implements the Plotter interface,
drawing a set of labels at specified points.
type Labels struct {
XYs
Labels []string
TextStyle []text.Style
Offset vg.Point
}
▾ Example
Code:
p := plot.New()
p.Title.Text = "Labels"
p.X.Min = -10
p.X.Max = +10
p.Y.Min = 0
p.Y.Max = +20
labels, err := plotter.NewLabels(plotter.XYLabels{
XYs: []plotter.XY{
{X: -5, Y: 5},
{X: +5, Y: 5},
{X: +5, Y: 15},
{X: -5, Y: 15},
},
Labels: []string{"A", "B", "C", "D"},
})
if err != nil {
log.Fatalf("could not creates labels plotter: %+v", err)
}
p.Add(labels)
err = p.Save(10*vg.Centimeter, 10*vg.Centimeter, "testdata/labels.png")
if err != nil {
log.Fatalf("could save plot: %+v", err)
}
▹ Example (InCanvasCoordinates)
▾ Example (InCanvasCoordinates)
ExampleLabels_inCanvasCoordinates shows how to write a label
in a plot using the canvas coordinates (instead of the data coordinates.)
It can be useful in the situation where one wants to draw some label
always in the same location of a plot, irrespective of the minute
details of a particular plot data range.
Code:
p := plot.New()
p.Title.Text = "Labels - X"
p.X.Min = -10
p.X.Max = +10
p.Y.Min = 0
p.Y.Max = +20
labels, err := plotter.NewLabels(plotter.XYLabels{
XYs: []plotter.XY{
{X: -5, Y: 5},
{X: +5, Y: 5},
{X: +5, Y: 15},
{X: -5, Y: 15},
},
Labels: []string{"A", "B", "C", "D"},
},
)
if err != nil {
log.Fatalf("could not creates labels plotter: %+v", err)
}
p.Add(labels)
f, err := os.Create("testdata/labels_cnv_coords.png")
if err != nil {
log.Fatalf("could not create output plot file: %+v", err)
}
defer f.Close()
cnv := vgimg.PngCanvas{
Canvas: vgimg.New(10*vg.Centimeter, 10*vg.Centimeter),
}
dc := draw.New(cnv)
p.Draw(dc)
{
fnt := p.TextHandler.Cache().Lookup(plotter.DefaultFont, vg.Points(12))
da := p.DataCanvas(dc)
da.FillString(fnt, vg.Point{X: da.X(0.5), Y: da.Y(0.5)}, "X")
}
_, err = cnv.WriteTo(f)
if err != nil {
log.Fatalf("could not write to output plot file: %+v", err)
}
err = f.Close()
if err != nil {
log.Fatalf("could save plot: %+v", err)
}
func NewLabels(d XYLabeller) (*Labels, error)
NewLabels returns a new Labels using the DefaultFont and
the DefaultFontSize.
func (l *Labels) DataRange() (xmin, xmax, ymin, ymax float64)
DataRange returns the minimum and maximum X and Y values
func (l *Labels) GlyphBoxes(p *plot.Plot) []plot.GlyphBox
GlyphBoxes returns a slice of GlyphBoxes,
one for each of the labels, implementing the
plot.GlyphBoxer interface.
func (*Labels) Plot
¶
func (l *Labels) Plot(c draw.Canvas, p *plot.Plot)
Plot implements the Plotter interface, drawing labels.
Line implements the Plotter interface, drawing a line.
type Line struct {
XYs
StepStyle StepKind
draw.LineStyle
FillColor color.Color
}
▾ Example (FilledLine)
Code:
rnd := rand.New(rand.NewSource(1))
randomPoints := func(n int, x float64) plotter.XYs {
pts := make(plotter.XYs, n)
for i := range pts {
if i == 0 {
pts[i].X = x + rnd.Float64()
} else {
pts[i].X = pts[i-1].X + 0.5 + rnd.Float64()
}
pts[i].Y = -5. + 10*rnd.Float64()
}
return pts
}
p := plot.New()
p.Title.Text = "Filled Line Example"
p.X.Label.Text = "X"
p.Y.Label.Text = "Y"
p.Add(plotter.NewGrid())
filled, err := plotter.NewLine(randomPoints(4, 0))
if err != nil {
log.Panic(err)
}
filled.FillColor = color.RGBA{R: 196, G: 255, B: 196, A: 255}
p.Add(filled)
err = p.Save(200, 200, "testdata/filledLine.png")
if err != nil {
log.Panic(err)
}
▾ Example (StepLine)
Code:
rnd := rand.New(rand.NewSource(1))
randomPoints := func(n int, x float64) plotter.XYs {
pts := make(plotter.XYs, n)
for i := range pts {
pts[i].X = float64(i) + x
pts[i].Y = 5. + 10*rnd.Float64()
}
return pts
}
n := 4
p := plot.New()
p.Title.Text = "Step Example"
p.X.Label.Text = "X"
p.Y.Label.Text = "Y"
p.Add(plotter.NewGrid())
stepPre, err := plotter.NewLine(randomPoints(n, 0))
if err != nil {
log.Panic(err)
}
stepPre.StepStyle = plotter.PreStep
stepPre.FillColor = color.RGBA{R: 196, G: 255, B: 196, A: 255}
stepMid, err := plotter.NewLine(randomPoints(n, 3.5))
if err != nil {
log.Panic(err)
}
stepMid.StepStyle = plotter.MidStep
stepMid.LineStyle = draw.LineStyle{Color: color.RGBA{R: 196, B: 128, A: 255}, Width: vg.Points(1)}
stepMidFilled, err := plotter.NewLine(randomPoints(n, 7))
if err != nil {
log.Panic(err)
}
stepMidFilled.StepStyle = plotter.MidStep
stepMidFilled.LineStyle = draw.LineStyle{Color: color.RGBA{R: 196, B: 128, A: 255}, Width: vg.Points(1)}
stepMidFilled.FillColor = color.RGBA{R: 255, G: 196, B: 196, A: 255}
stepPost, err := plotter.NewLine(randomPoints(n, 10.5))
if err != nil {
log.Panic(err)
}
stepPost.StepStyle = plotter.PostStep
stepPost.LineStyle.Width = 0
stepPost.FillColor = color.RGBA{R: 196, G: 196, B: 255, A: 255}
p.Add(stepPre, stepMid, stepMidFilled, stepPost)
p.Legend.Add("pre", stepPre)
p.Legend.Add("mid", stepMid)
p.Legend.Add("midFilled", stepMidFilled)
p.Legend.Add("post", stepPost)
p.Legend.Top = true
p.Y.Max = 20
p.Y.Min = 0
err = p.Save(200, 200, "testdata/step.png")
if err != nil {
log.Panic(err)
}
func NewLine(xys XYer) (*Line, error)
NewLine returns a Line that uses the default line style and
does not draw glyphs.
func (pts *Line) DataRange() (xmin, xmax, ymin, ymax float64)
DataRange returns the minimum and maximum
x and y values, implementing the plot.DataRanger interface.
func (pts *Line) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox
GlyphBoxes implements the plot.GlyphBoxer interface.
func (*Line) Plot
¶
func (pts *Line) Plot(c draw.Canvas, plt *plot.Plot)
Plot draws the Line, implementing the plot.Plotter interface.
func (pts *Line) Thumbnail(c *draw.Canvas)
Thumbnail returns the thumbnail for the Line, implementing the plot.Thumbnailer interface.
Polygon implements the Plotter interface, drawing a polygon.
type Polygon struct {
XYs []XYs
draw.LineStyle
Color color.Color
}
▾ Example (Hexagons)
ExamplePolygon_hexagons creates a heat map with hexagon shapes.
The output of this example is at
https://github.com/gonum/plot/blob/master/plotter/testdata/polygon_hexagons_golden.png.
Code:
hex := func(x, y, r float64) plotter.XYs {
g := make(plotter.XYs, 6)
for i := range g {
g[i].X = x + r*math.Cos(math.Pi/3*float64(i))
g[i].Y = y + r*math.Sin(math.Pi/3*float64(i))
}
return g
}
var err error
p := plot.New()
p.Title.Text = "Hexagons"
p.X.Label.Text = "X"
p.Y.Label.Text = "Y"
colorMap := moreland.SmoothBlueRed()
colorMap.SetMax(2)
colorMap.SetMin(-2)
colorMap.SetConvergePoint(0)
const (
r = math.Pi / 4
x0 = 0.0
y0 = 0.0
nx = 5
ny = 5
)
dx := 3 * r
dy := r * math.Sqrt(3)
xstart := []float64{x0, x0 - 1.5*r}
ystart := []float64{y0, y0 - r}
for i, xmin := range xstart {
ymin := ystart[i]
x := xmin
for ix := 0; ix < nx; ix++ {
y := ymin
for iy := 0; iy < ny; iy++ {
var poly *plotter.Polygon
poly, err = plotter.NewPolygon(hex(x, y, r))
if err != nil {
log.Panic(err)
}
poly.Color, err = colorMap.At(math.Sin(x) + math.Sin(y))
if err != nil {
log.Panic(err)
}
poly.LineStyle.Width = 0
p.Add(poly)
y += dy
}
x += dx
}
}
if err = p.Save(100, 100, "testdata/polygon_hexagons.png"); err != nil {
log.Panic(err)
}
▾ Example (Holes)
ExamplePolygon_holes draws a polygon with holes, showing how
the different built-in vg backends render polygons with holes.
The output of this example is at
https://github.com/gonum/plot/blob/master/plotter/testdata/polygon_holes_golden.png,
https://github.com/gonum/plot/blob/master/plotter/testdata/polygon_holes_golden.svg,
https://github.com/gonum/plot/blob/master/plotter/testdata/polygon_holes_golden.pdf, and
https://github.com/gonum/plot/blob/master/plotter/testdata/polygon_holes_golden.eps.
Code:
outer1 := plotter.XYs{{X: 0, Y: 0}, {X: 4, Y: 0}, {X: 4, Y: 4}, {X: 0, Y: 4}}
inner1 := plotter.XYs{{X: 0.5, Y: 0.5}, {X: 1.5, Y: 0.5}, {X: 1.5, Y: 1.5}, {X: 0.5, Y: 1.5}}
inner2 := plotter.XYs{{X: 3.5, Y: 2.5}, {X: 2.5, Y: 2.5}, {X: 2.5, Y: 3.5}, {X: 3.5, Y: 3.5}}
poly, err := plotter.NewPolygon(outer1, inner1, inner2)
if err != nil {
log.Panic(err)
}
poly.Color = color.NRGBA{B: 255, A: 255}
p := plot.New()
p.Title.Text = "Polygon with holes"
p.X.Label.Text = "X"
p.Y.Label.Text = "Y"
p.Add(poly)
p.Legend.Add("key", poly)
p.Legend.TextStyle.Font.Size = vg.Points(8)
p.Legend.TextStyle.Color = color.White
p.Legend.ThumbnailWidth = vg.Points(10)
err = p.Save(100, 100, "testdata/polygon_holes.png")
if err != nil {
log.Panic(err)
}
err = p.Save(100, 100, "testdata/polygon_holes.svg")
if err != nil {
log.Panic(err)
}
err = p.Save(100, 100, "testdata/polygon_holes.pdf")
if err != nil {
log.Panic(err)
}
err = p.Save(100, 100, "testdata/polygon_holes.eps")
if err != nil {
log.Panic(err)
}
func NewPolygon(xys ...XYer) (*Polygon, error)
NewPolygon returns a polygon that uses the default line style and
no fill color, where xys are the rings of the polygon.
Different backends may render overlapping rings and self-intersections
differently, but all built-in backends treat inner rings
with the opposite winding order from the outer ring as
holes.
func (pts *Polygon) DataRange() (xmin, xmax, ymin, ymax float64)
DataRange returns the minimum and maximum
x and y values, implementing the plot.DataRanger
interface.
func (*Polygon) Plot
¶
func (pts *Polygon) Plot(c draw.Canvas, plt *plot.Plot)
Plot draws the polygon, implementing the plot.Plotter
interface.
func (pts *Polygon) Thumbnail(c *draw.Canvas)
Thumbnail creates the thumbnail for the Polygon,
implementing the plot.Thumbnailer interface.
QuartPlot implements the Plotter interface, drawing
a plot to represent the distribution of values.
This style of the plot appears in Tufte's "The Visual
Display of Quantitative Information".
type QuartPlot struct {
Offset vg.Length
MedianStyle draw.GlyphStyle
WhiskerStyle draw.LineStyle
Horizontal bool
}
▾ Example
Code:
rnd := rand.New(rand.NewSource(1))
n := 100
uniform := make(plotter.Values, n)
normal := make(plotter.Values, n)
expon := make(plotter.Values, n)
for i := 0; i < n; i++ {
uniform[i] = rnd.Float64()
normal[i] = rnd.NormFloat64()
expon[i] = rnd.ExpFloat64()
}
qp1, err := plotter.NewQuartPlot(0, uniform)
if err != nil {
log.Panic(err)
}
qp2, err := plotter.NewQuartPlot(1, normal)
if err != nil {
log.Panic(err)
}
qp3, err := plotter.NewQuartPlot(2, expon)
if err != nil {
log.Panic(err)
}
p1 := plot.New()
p1.Title.Text = "Quartile Plot"
p1.Y.Label.Text = "plotter.Values"
p1.Add(qp1, qp2, qp3)
p1.NominalX("Uniform\nDistribution", "Normal\nDistribution",
"Exponential\nDistribution")
err = p1.Save(200, 200, "testdata/verticalQuartPlot.png")
if err != nil {
log.Panic(err)
}
qp1.Horizontal = true
qp2.Horizontal = true
qp3.Horizontal = true
p2 := plot.New()
p2.Title.Text = "Quartile Plot"
p2.X.Label.Text = "plotter.Values"
p2.Add(qp1, qp2, qp3)
p2.NominalY("Uniform\nDistribution", "Normal\nDistribution",
"Exponential\nDistribution")
err = p2.Save(200, 200, "testdata/horizontalQuartPlot.png")
if err != nil {
log.Panic(err)
}
p3 := plot.New()
p3.Title.Text = "Box Plot"
p3.Y.Label.Text = "plotter.Values"
w := vg.Points(10)
for x := 0.0; x < 3.0; x++ {
b0, err := plotter.NewQuartPlot(x, uniform)
if err != nil {
log.Panic(err)
}
b0.Offset = -w
b1, err := plotter.NewQuartPlot(x, normal)
if err != nil {
log.Panic(err)
}
b2, err := plotter.NewQuartPlot(x, expon)
if err != nil {
log.Panic(err)
}
b2.Offset = w
p3.Add(b0, b1, b2)
}
p3.Add(plotter.NewGlyphBoxes())
p3.NominalX("Group 0", "Group 1", "Group 2")
err = p3.Save(200, 200, "testdata/groupedQuartPlot.png")
if err != nil {
log.Panic(err)
}
func NewQuartPlot(loc float64, values Valuer) (*QuartPlot, error)
NewQuartPlot returns a new QuartPlot that represents
the distribution of the given values.
An error is returned if the plot is created with
no values.
The fence values are 1.5x the interquartile before
the first quartile and after the third quartile. Any
value that is outside of the fences are drawn as
Outside points. The adjacent values (to which the
whiskers stretch) are the minimum and maximum
values that are not outside the fences.
func (b *QuartPlot) DataRange() (float64, float64, float64, float64)
DataRange returns the minimum and maximum x
and y values, implementing the plot.DataRanger
interface.
func (b *QuartPlot) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox
GlyphBoxes returns a slice of GlyphBoxes for the plot,
implementing the plot.GlyphBoxer interface.
func (b *QuartPlot) OutsideLabels(labels Labeller) (*Labels, error)
OutsideLabels returns a *Labels that will plot
a label for each of the outside points. The
labels are assumed to correspond to the
points used to create the plot.
func (*QuartPlot) Plot
¶
func (b *QuartPlot) Plot(c draw.Canvas, plt *plot.Plot)
Plot draws the QuartPlot on Canvas c and Plot plt.
A Sankey diagram presents stock and flow data as rectangles representing
the amount of each stock and lines between the stocks representing the
amount of each flow.
type Sankey struct {
Color color.Color
StockBarWidth vg.Length
LineStyle draw.LineStyle
TextStyle text.Style
FlowStyle func(group string) (color.Color, draw.LineStyle)
StockStyle func(label string, category int) (lbl string, ts text.Style, xOff, yOff vg.Length, c color.Color, ls draw.LineStyle)
}
▾ Example (Grouped)
ExampleSankey_grouped creates a sankey diagram with grouped flows.
The output can be found at https://github.com/gonum/plot/blob/master/plotter/testdata/sankeyGrouped_golden.png.
Code:
p := plot.New()
c := vgimg.New(vg.Points(300), vg.Points(180))
dc := draw.New(c)
const (
treeType int = iota
consumer
fate
)
categoryLabels := []string{"Tree type", "Consumer", "Fate"}
flows := []plotter.Flow{
{
SourceCategory: treeType,
SourceLabel: "LargeLargeLargeLargeLargeLargeLargeLargeLarge",
ReceptorCategory: consumer,
ReceptorLabel: "Mohamed",
Group: "Apples",
Value: 5,
},
{
SourceCategory: treeType,
SourceLabel: "LargeLargeLargeLargeLargeLargeLargeLargeLarge",
ReceptorCategory: consumer,
ReceptorLabel: "Mohamed",
Group: "Dates",
Value: 3,
},
{
SourceCategory: treeType,
SourceLabel: "Small",
ReceptorCategory: consumer,
ReceptorLabel: "Mohamed",
Group: "Lychees",
Value: 2,
},
{
SourceCategory: treeType,
SourceLabel: "LargeLargeLargeLargeLargeLargeLargeLargeLarge",
ReceptorCategory: consumer,
ReceptorLabel: "Sofia",
Group: "Apples",
Value: 3,
},
{
SourceCategory: treeType,
SourceLabel: "LargeLargeLargeLargeLargeLargeLargeLargeLarge",
ReceptorCategory: consumer,
ReceptorLabel: "Sofia",
Group: "Dates",
Value: 4,
},
{
SourceCategory: treeType,
SourceLabel: "Small",
ReceptorCategory: consumer,
ReceptorLabel: "Sofia",
Group: "Apples",
Value: 1,
},
{
SourceCategory: treeType,
SourceLabel: "LargeLargeLargeLargeLargeLargeLargeLargeLarge",
ReceptorCategory: consumer,
ReceptorLabel: "Wei",
Group: "Lychees",
Value: 6,
},
{
SourceCategory: treeType,
SourceLabel: "Small",
ReceptorCategory: consumer,
ReceptorLabel: "Wei",
Group: "Apples",
Value: 3,
},
{
SourceCategory: consumer,
SourceLabel: "Mohamed",
ReceptorCategory: fate,
ReceptorLabel: "Eaten",
Group: "Apples",
Value: 4,
},
{
SourceCategory: consumer,
SourceLabel: "Mohamed",
ReceptorCategory: fate,
ReceptorLabel: "Waste",
Group: "Apples",
Value: 1,
},
{
SourceCategory: consumer,
SourceLabel: "Mohamed",
ReceptorCategory: fate,
ReceptorLabel: "Eaten",
Group: "Dates",
Value: 3,
},
{
SourceCategory: consumer,
SourceLabel: "Mohamed",
ReceptorCategory: fate,
ReceptorLabel: "Waste",
Group: "Lychees",
Value: 2,
},
{
SourceCategory: consumer,
SourceLabel: "Sofia",
ReceptorCategory: fate,
ReceptorLabel: "Eaten",
Group: "Apples",
Value: 4,
},
{
SourceCategory: consumer,
SourceLabel: "Sofia",
ReceptorCategory: fate,
ReceptorLabel: "Eaten",
Group: "Dates",
Value: 3,
},
{
SourceCategory: consumer,
SourceLabel: "Sofia",
ReceptorCategory: fate,
ReceptorLabel: "Waste",
Group: "Dates",
Value: 1,
},
{
SourceCategory: consumer,
SourceLabel: "Wei",
ReceptorCategory: fate,
ReceptorLabel: "Eaten",
Group: "Lychees",
Value: 6,
},
{
SourceCategory: consumer,
SourceLabel: "Wei",
ReceptorCategory: fate,
ReceptorLabel: "Eaten",
Group: "Apples",
Value: 2,
},
{
SourceCategory: consumer,
SourceLabel: "Wei",
ReceptorCategory: fate,
ReceptorLabel: "Waste",
Group: "Apples",
Value: 1,
},
{
SourceCategory: treeType,
SourceLabel: "LargeLargeLargeLargeLargeLargeLargeLargeLarge",
ReceptorCategory: fate,
ReceptorLabel: "Waste",
Group: "Apples",
Value: 1,
},
{
SourceCategory: treeType,
SourceLabel: "LargeLargeLargeLargeLargeLargeLargeLargeLarge",
ReceptorCategory: fate,
ReceptorLabel: "Waste",
Group: "Dates",
Value: 1,
},
{
SourceCategory: treeType,
SourceLabel: "Small",
ReceptorCategory: fate,
ReceptorLabel: "Waste",
Group: "Lychees",
Value: 0.3,
},
}
sankey, err := plotter.NewSankey(flows...)
if err != nil {
log.Panic(err)
}
sankey.FlowStyle = func(group string) (color.Color, draw.LineStyle) {
switch group {
case "Lychees":
return color.NRGBA{R: 242, G: 169, B: 178, A: 100}, sankey.LineStyle
case "Apples":
return color.NRGBA{R: 91, G: 194, B: 54, A: 100}, sankey.LineStyle
case "Dates":
return color.NRGBA{R: 112, G: 22, B: 0, A: 100}, sankey.LineStyle
default:
panic(fmt.Errorf("invalid group %s", group))
}
}
sankey.StockStyle = func(label string, category int) (string, text.Style, vg.Length, vg.Length, color.Color, draw.LineStyle) {
if label == "Small" && category == treeType {
ts := sankey.TextStyle
ts.Rotation = 0.0
ts.XAlign = draw.XRight
ls := sankey.LineStyle
ls.Color = color.White
xOff := -sankey.StockBarWidth / 2
yOff := vg.Length(0)
return "small", ts, xOff, yOff, color.Black, ls
}
if label == "LargeLargeLargeLargeLargeLargeLargeLargeLarge" && category == treeType {
min, max, err := sankey.StockRange(label, category)
if err != nil {
log.Panic(err)
}
_, yTr := p.Transforms(&dc)
barHeight := yTr(max) - yTr(min)
if sankey.TextStyle.Width(label) > barHeight {
return "large", sankey.TextStyle, 0, 0, color.White, sankey.LineStyle
}
}
return label, sankey.TextStyle, 0, 0, color.White, sankey.LineStyle
}
p.Add(sankey)
p.Y.Label.Text = "Number of fruit pieces"
p.NominalX(categoryLabels...)
legendLabels, thumbs := sankey.Thumbnailers()
for i, l := range legendLabels {
t := thumbs[i]
p.Legend.Add(l, t)
}
p.Legend.Top = true
p.X.Max = 3.05
p.Add(plotter.NewGlyphBoxes())
p.Draw(dc)
pngimg := vgimg.PngCanvas{Canvas: c}
f, err := os.Create("testdata/sankeyGrouped.png")
if err != nil {
log.Panic(err)
}
if _, err = pngimg.WriteTo(f); err != nil {
log.Panic(err)
}
▾ Example (Simple)
ExampleSankey_sample creates a simple sankey diagram.
The output can be found at https://github.com/gonum/plot/blob/master/plotter/testdata/sankeySimple_golden.png.
Code:
p := plot.New()
const (
treeType int = iota
consumer
fate
)
categoryLabels := []string{"Tree type", "Consumer", "Fate"}
flows := []plotter.Flow{
{
SourceCategory: treeType,
SourceLabel: "Large",
ReceptorCategory: consumer,
ReceptorLabel: "Mohamed",
Value: 5,
},
{
SourceCategory: treeType,
SourceLabel: "Small",
ReceptorCategory: consumer,
ReceptorLabel: "Mohamed",
Value: 2,
},
{
SourceCategory: treeType,
SourceLabel: "Large",
ReceptorCategory: consumer,
ReceptorLabel: "Sofia",
Value: 3,
},
{
SourceCategory: treeType,
SourceLabel: "Small",
ReceptorCategory: consumer,
ReceptorLabel: "Sofia",
Value: 1,
},
{
SourceCategory: treeType,
SourceLabel: "Large",
ReceptorCategory: consumer,
ReceptorLabel: "Wei",
Value: 6,
},
{
SourceCategory: consumer,
SourceLabel: "Mohamed",
ReceptorCategory: fate,
ReceptorLabel: "Eaten",
Value: 6,
},
{
SourceCategory: consumer,
SourceLabel: "Mohamed",
ReceptorCategory: fate,
ReceptorLabel: "Waste",
Value: 1,
},
{
SourceCategory: consumer,
SourceLabel: "Sofia",
ReceptorCategory: fate,
ReceptorLabel: "Eaten",
Value: 3,
},
{
SourceCategory: consumer,
SourceLabel: "Sofia",
ReceptorCategory: fate,
ReceptorLabel: "Waste",
Value: 0.5,
},
{
SourceCategory: consumer,
SourceLabel: "Wei",
ReceptorCategory: fate,
ReceptorLabel: "Eaten",
Value: 5,
},
{
SourceCategory: consumer,
SourceLabel: "Wei",
ReceptorCategory: fate,
ReceptorLabel: "Waste",
Value: 1,
},
{
SourceCategory: treeType,
SourceLabel: "Large",
ReceptorCategory: fate,
ReceptorLabel: "Waste",
Value: 1,
},
{
SourceCategory: treeType,
SourceLabel: "Small",
ReceptorCategory: fate,
ReceptorLabel: "Waste",
Value: 0.3,
},
}
sankey, err := plotter.NewSankey(flows...)
if err != nil {
log.Panic(err)
}
p.Add(sankey)
p.Y.Label.Text = "Number of apples"
p.NominalX(categoryLabels...)
err = p.Save(vg.Points(300), vg.Points(180), "testdata/sankeySimple.png")
if err != nil {
log.Panic(err)
}
func NewSankey(flows ...Flow) (*Sankey, error)
NewSankey creates a new Sankey diagram with the specified
flows and stocks.
func (s *Sankey) DataRange() (xmin, xmax, ymin, ymax float64)
DataRange implements the plot.DataRanger interface.
func (s *Sankey) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox
GlyphBoxes implements the GlyphBoxer interface.
func (*Sankey) Plot
¶
func (s *Sankey) Plot(c draw.Canvas, plt *plot.Plot)
Plot implements the plot.Plotter interface.
func (s *Sankey) StockRange(label string, category int) (min, max float64, err error)
StockRange returns the minimum and maximum value on the value axis
for the stock with the specified label and category.
func (s *Sankey) Thumbnailers() (legendLabels []string, thumbnailers []plot.Thumbnailer)
Thumbnailers creates a group of objects that can be used to
add legend entries for the different flow groups in this
diagram, as well as the flow group labels that correspond to them.
Scatter implements the Plotter interface, drawing
a glyph for each of a set of points.
type Scatter struct {
XYs
GlyphStyleFunc func(int) draw.GlyphStyle
draw.GlyphStyle
}
▾ Example
ExampleScatter draws some scatter points, a line,
and a line with points.
Code:
rnd := rand.New(rand.NewSource(1))
randomPoints := func(n int) plotter.XYs {
pts := make(plotter.XYs, n)
for i := range pts {
if i == 0 {
pts[i].X = rnd.Float64()
} else {
pts[i].X = pts[i-1].X + rnd.Float64()
}
pts[i].Y = pts[i].X + 10*rnd.Float64()
}
return pts
}
n := 15
scatterData := randomPoints(n)
lineData := randomPoints(n)
linePointsData := randomPoints(n)
p := plot.New()
p.Title.Text = "Points Example"
p.X.Label.Text = "X"
p.Y.Label.Text = "Y"
p.Add(plotter.NewGrid())
s, err := plotter.NewScatter(scatterData)
if err != nil {
log.Panic(err)
}
s.GlyphStyle.Color = color.RGBA{R: 255, B: 128, A: 255}
s.GlyphStyle.Radius = vg.Points(3)
l, err := plotter.NewLine(lineData)
if err != nil {
log.Panic(err)
}
l.LineStyle.Width = vg.Points(1)
l.LineStyle.Dashes = []vg.Length{vg.Points(5), vg.Points(5)}
l.LineStyle.Color = color.RGBA{B: 255, A: 255}
lpLine, lpPoints, err := plotter.NewLinePoints(linePointsData)
if err != nil {
log.Panic(err)
}
lpLine.Color = color.RGBA{G: 255, A: 255}
lpPoints.Shape = draw.CircleGlyph{}
lpPoints.Color = color.RGBA{R: 255, A: 255}
p.Add(s, l, lpLine, lpPoints)
p.Legend.Add("scatter", s)
p.Legend.Add("line", l)
p.Legend.Add("line points", lpLine, lpPoints)
err = p.Save(200, 200, "testdata/scatter.png")
if err != nil {
log.Panic(err)
}
▾ Example (Bubbles)
ExampleScatter_bubbles draws some scatter points.
Each point is plotted with a different radius size depending on
external criteria.
Code:
rnd := rand.New(rand.NewSource(1))
randomTriples := func(n int) plotter.XYZs {
data := make(plotter.XYZs, n)
for i := range data {
if i == 0 {
data[i].X = rnd.Float64()
} else {
data[i].X = data[i-1].X + 2*rnd.Float64()
}
data[i].Y = data[i].X + 10*rnd.Float64()
data[i].Z = data[i].X
}
return data
}
n := 10
scatterData := randomTriples(n)
minZ, maxZ := math.Inf(1), math.Inf(-1)
for _, xyz := range scatterData {
if xyz.Z > maxZ {
maxZ = xyz.Z
}
if xyz.Z < minZ {
minZ = xyz.Z
}
}
p := plot.New()
p.Title.Text = "Bubbles"
p.X.Label.Text = "X"
p.Y.Label.Text = "Y"
sc, err := plotter.NewScatter(scatterData)
if err != nil {
log.Panic(err)
}
sc.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 := scatterData.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(sc)
err = p.Save(200, 200, "testdata/bubbles.png")
if err != nil {
log.Panic(err)
}
▾ Example (Color)
ExampleScatter_color draws a colored scatter plot.
Each point is plotted with a different color depending on
external criteria.
Code:
rnd := rand.New(rand.NewSource(1))
randomTriples := func(n int) plotter.XYZs {
data := make(plotter.XYZs, n)
for i := range data {
if i == 0 {
data[i].X = rnd.Float64()
} else {
data[i].X = data[i-1].X + 2*rnd.Float64()
}
data[i].Y = data[i].X + 10*rnd.Float64()
data[i].Z = data[i].X
}
return data
}
n := 15
scatterData := randomTriples(n)
minZ, maxZ := math.Inf(1), math.Inf(-1)
for _, xyz := range scatterData {
if xyz.Z > maxZ {
maxZ = xyz.Z
}
if xyz.Z < minZ {
minZ = xyz.Z
}
}
colors := moreland.Kindlmann()
colors.SetMax(maxZ)
colors.SetMin(minZ)
p := plot.New()
p.Title.Text = "Colored Points Example"
p.X.Label.Text = "X"
p.Y.Label.Text = "Y"
p.Add(plotter.NewGrid())
sc, err := plotter.NewScatter(scatterData)
if err != nil {
log.Panic(err)
}
sc.GlyphStyleFunc = func(i int) draw.GlyphStyle {
_, _, z := scatterData.XYZ(i)
d := (z - minZ) / (maxZ - minZ)
rng := maxZ - minZ
k := d*rng + minZ
c, err := colors.At(k)
if err != nil {
log.Panic(err)
}
return draw.GlyphStyle{Color: c, Radius: vg.Points(3), Shape: draw.CircleGlyph{}}
}
p.Add(sc)
thumbs := plotter.PaletteThumbnailers(colors.Palette(n))
for i := len(thumbs) - 1; i >= 0; i-- {
t := thumbs[i]
if i != 0 && i != len(thumbs)-1 {
p.Legend.Add("", t)
continue
}
var val int
switch i {
case 0:
val = int(minZ)
case len(thumbs) - 1:
val = int(maxZ)
}
p.Legend.Add(fmt.Sprintf("%d", val), t)
}
const legendWidth = vg.Centimeter
p.Legend.XOffs = legendWidth
img := vgimg.New(300, 230)
dc := draw.New(img)
dc = draw.Crop(dc, 0, -legendWidth, 0, 0)
p.Draw(dc)
w, err := os.Create("testdata/scatterColor.png")
if err != nil {
log.Panic(err)
}
defer w.Close()
png := vgimg.PngCanvas{Canvas: img}
if _, err = png.WriteTo(w); err != nil {
log.Panic(err)
}
if err = w.Close(); err != nil {
log.Panic(err)
}
func NewScatter(xys XYer) (*Scatter, error)
NewScatter returns a Scatter that uses the
default glyph style.
func (pts *Scatter) DataRange() (xmin, xmax, ymin, ymax float64)
DataRange returns the minimum and maximum
x and y values, implementing the plot.DataRanger
interface.
func (pts *Scatter) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox
GlyphBoxes returns a slice of plot.GlyphBoxes,
implementing the plot.GlyphBoxer interface.
func (*Scatter) Plot
¶
func (pts *Scatter) Plot(c draw.Canvas, plt *plot.Plot)
Plot draws the Scatter, implementing the plot.Plotter
interface.
func (pts *Scatter) Thumbnail(c *draw.Canvas)
Thumbnail the thumbnail for the Scatter,
implementing the plot.Thumbnailer interface.
StepKind specifies a form of a connection of two consecutive points.
type StepKind int
const (
NoStep StepKind = iota
PreStep
MidStep
PostStep
)
ValueLabels implements both the Valuer
and Labeller interfaces.
type ValueLabels []struct {
Value float64
Label string
}
func (ValueLabels) Label
¶
func (vs ValueLabels) Label(i int) string
Label returns the label of item i.
func (ValueLabels) Len
¶
func (vs ValueLabels) Len() int
Len returns the number of items.
func (ValueLabels) Value
¶
func (vs ValueLabels) Value(i int) float64
Value returns the value of item i.
Valuer wraps the Len and Value methods.
type Valuer interface {
Len() int
Value(int) float64
}
Values implements the Valuer interface.
type Values []float64
func CopyValues(vs Valuer) (Values, error)
CopyValues returns a Values that is a copy of the values
from a Valuer, or an error if there are no values, or if one of
the copied values is a NaN or Infinity.
func (Values) Len
¶
func (vs Values) Len() int
func (Values) Value
¶
func (vs Values) Value(i int) float64
XErrorBars implements the plot.Plotter, plot.DataRanger,
and plot.GlyphBoxer interfaces, drawing horizontal error
bars, denoting error in Y values.
type XErrorBars struct {
XYs
XErrors
draw.LineStyle
CapWidth vg.Length
}
func NewXErrorBars(xerrs interface {
XYer
XErrorer
}) (*XErrorBars, error)
Returns a new XErrorBars plotter, or an error on failure. The error values
from the XErrorer interface are interpreted as relative to the corresponding
X value. The errors for a given X value are computed by taking the absolute
value of the error returned by the XErrorer and subtracting the first and
adding the second to the X value.
func (e *XErrorBars) DataRange() (xmin, xmax, ymin, ymax float64)
DataRange implements the plot.DataRanger interface.
func (e *XErrorBars) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox
GlyphBoxes implements the plot.GlyphBoxer interface.
func (*XErrorBars) Plot
¶
func (e *XErrorBars) Plot(c draw.Canvas, p *plot.Plot)
Plot implements the Plotter interface, drawing labels.
XErrorer wraps the XError method.
type XErrorer interface {
XError(int) (float64, float64)
}
XErrors implements the XErrorer interface.
type XErrors Errors
func (XErrors) XError
¶
func (xe XErrors) XError(i int) (float64, float64)
XValues implements the Valuer interface,
returning the x value from an XYer.
type XValues struct {
XYer
}
func (XValues) Value
¶
func (xs XValues) Value(i int) float64
XY is an x and y value.
type XY struct{ X, Y float64 }
XYLabeller combines the XYer and Labeller types.
type XYLabeller interface {
XYer
Labeller
}
XYLabels holds XY data with labels.
The ith label corresponds to the ith XY.
type XYLabels struct {
XYs
Labels []string
}
func (XYLabels) Label
¶
func (l XYLabels) Label(i int) string
Label returns the label for point index i.
XYValues implements the XYer interface, returning
the x and y values from an XYZer.
type XYValues struct{ XYZer }
func (XYValues) XY
¶
func (xy XYValues) XY(i int) (float64, float64)
XY implements the XY method of the XYer interface.
XYZ is an x, y and z value.
type XYZ struct{ X, Y, Z float64 }
XYZer wraps the Len and XYZ methods.
type XYZer interface {
Len() int
XYZ(int) (float64, float64, float64)
XY(int) (float64, float64)
}
XYZs implements the XYZer interface using a slice.
type XYZs []XYZ
func CopyXYZs(data XYZer) (XYZs, error)
CopyXYZs copies an XYZer.
func (XYZs) Len
¶
func (xyz XYZs) Len() int
Len implements the Len method of the XYZer interface.
func (XYZs) XY
¶
func (xyz XYZs) XY(i int) (float64, float64)
XY implements the XY method of the XYer interface.
func (XYZs) XYZ
¶
func (xyz XYZs) XYZ(i int) (float64, float64, float64)
XYZ implements the XYZ method of the XYZer interface.
XYer wraps the Len and XY methods.
type XYer interface {
Len() int
XY(int) (x, y float64)
}
XYs implements the XYer interface.
type XYs []XY
func CopyXYs(data XYer) (XYs, error)
CopyXYs returns an XYs that is a copy of the x and y values from
an XYer, or an error if one of the data points contains a NaN or
Infinity.
func (XYs) Len
¶
func (xys XYs) Len() int
func (XYs) XY
¶
func (xys XYs) XY(i int) (float64, float64)
YErrorBars implements the plot.Plotter, plot.DataRanger,
and plot.GlyphBoxer interfaces, drawing vertical error
bars, denoting error in Y values.
type YErrorBars struct {
XYs
YErrors
draw.LineStyle
CapWidth vg.Length
}
func NewYErrorBars(yerrs interface {
XYer
YErrorer
}) (*YErrorBars, error)
NewYErrorBars returns a new YErrorBars plotter, or an error on failure.
The error values from the YErrorer interface are interpreted as relative
to the corresponding Y value. The errors for a given Y value are computed
by taking the absolute value of the error returned by the YErrorer
and subtracting the first and adding the second to the Y value.
func (e *YErrorBars) DataRange() (xmin, xmax, ymin, ymax float64)
DataRange implements the plot.DataRanger interface.
func (e *YErrorBars) GlyphBoxes(plt *plot.Plot) []plot.GlyphBox
GlyphBoxes implements the plot.GlyphBoxer interface.
func (*YErrorBars) Plot
¶
func (e *YErrorBars) Plot(c draw.Canvas, p *plot.Plot)
Plot implements the Plotter interface, drawing labels.
YErrorer wraps the YError method.
type YErrorer interface {
YError(int) (float64, float64)
}
YErrors implements the YErrorer interface.
type YErrors Errors
func (YErrors) YError
¶
func (ye YErrors) YError(i int) (float64, float64)
YValues implements the Valuer interface,
returning the y value from an XYer.
type YValues struct {
XYer
}
func (YValues) Value
¶
func (ys YValues) Value(i int) float64