Avatar (Fabio Alessandro Locati|Fale)'s blog

Sensible datetime scale for Gonum Plot

June 28, 2021

Few months ago I posted a library for sensible int scale for Gonum Plot. There is a similar package I’ve developed to handle timescales.

The integer one, being based on a recursive function, works with any number scale. Differently, this one will only work well with a timescale between 2 days and a couple of years. Extending it is not hard since it’s enough to add additional case statements in the switch, but I’ve not found use-cases for different timeframes so far. If you add additional options, please commit them back!

At the moment, the rules are: If the difference between the min and max date is below 10 days, every day is marked. If the difference between the min and max date is between 10 and 90 days, every fifth day is marked. If the difference between the min and max date is between 90 and 180days, every first and fifteenth day of the month is marked. If the difference between the min and max date is over 180 days, every first day of the month is marked.

You can find the sdt package here, and an example of its usage is:

package main

import (
	"time"

	"github.com/fale/sdt"
	"gonum.org/v1/plot"
	"gonum.org/v1/plot/plotter"
	"gonum.org/v1/plot/plotutil"
	"gonum.org/v1/plot/vg"
)

func main() {
	p, err := plot.New()
	if err != nil {
		return
	}

	line := plotter.XYs{
		plotter.XY{X: float64(time.Date(2021, time.June, 10, 0, 0, 0, 0, time.UTC).Unix()), Y: float64(4)},
		plotter.XY{X: float64(time.Date(2021, time.June, 11, 0, 0, 0, 0, time.UTC).Unix()), Y: float64(1)},
		plotter.XY{X: float64(time.Date(2021, time.June, 12, 0, 0, 0, 0, time.UTC).Unix()), Y: float64(8)},
		plotter.XY{X: float64(time.Date(2021, time.June, 13, 0, 0, 0, 0, time.UTC).Unix()), Y: float64(3)},
		plotter.XY{X: float64(time.Date(2021, time.June, 14, 0, 0, 0, 0, time.UTC).Unix()), Y: float64(5)},
	}

	p.Add(plotter.NewGrid())
	err = plotutil.AddLinePoints(p,
		"First", line,
	)
	if err != nil {
		return
	}

	p.X.Tick.Marker = sdt.Ticks{Format: "02/01"}

	if err := p.Save(10*vg.Inch, 5*vg.Inch, "points.png"); err != nil {
		return
	}
	return
}

The important line is the number 35:

p.X.Tick.Marker = sdt.Ticks{Format: "02/01"}

In this line, the object that implements the Ticker (sdt.Ticks) interface from GoNum Plot is used for the X ax.

You can also use it for X ax in a similar way by exchanging X with Y in the highlighted line.

I hope this helps other people as much as it helped me!