go-chart/linear_series.go

120 lines
2.7 KiB
Go

package chart
import (
"fmt"
)
// Interface Assertions.
var (
_ Series = (*LinearSeries)(nil)
_ FirstValuesProvider = (*LinearSeries)(nil)
_ LastValuesProvider = (*LinearSeries)(nil)
)
// LinearSeries is a series that plots a line in a given domain.
type LinearSeries struct {
Name string
Style Style
YAxis YAxisType
XValues []float64
InnerSeries LinearCoefficientProvider
m float64
b float64
stdev float64
avg float64
}
// GetName returns the name of the time series.
func (ls LinearSeries) GetName() string {
return ls.Name
}
// GetStyle returns the line style.
func (ls LinearSeries) GetStyle() Style {
return ls.Style
}
// GetYAxis returns which YAxis the series draws on.
func (ls LinearSeries) GetYAxis() YAxisType {
return ls.YAxis
}
// Len returns the number of elements in the series.
func (ls LinearSeries) Len() int {
return len(ls.XValues)
}
// GetEndIndex returns the effective limit end.
func (ls LinearSeries) GetEndIndex() int {
return len(ls.XValues) - 1
}
// GetValues gets a value at a given index.
func (ls *LinearSeries) GetValues(index int) (x, y float64) {
if ls.InnerSeries == nil || len(ls.XValues) == 0 {
return
}
if ls.IsZero() {
ls.computeCoefficients()
}
x = ls.XValues[index]
y = (ls.m * ls.normalize(x)) + ls.b
return
}
// GetFirstValues computes the first linear regression value.
func (ls *LinearSeries) GetFirstValues() (x, y float64) {
if ls.InnerSeries == nil || len(ls.XValues) == 0 {
return
}
if ls.IsZero() {
ls.computeCoefficients()
}
x, y = ls.GetValues(0)
return
}
// GetLastValues computes the last linear regression value.
func (ls *LinearSeries) GetLastValues() (x, y float64) {
if ls.InnerSeries == nil || len(ls.XValues) == 0 {
return
}
if ls.IsZero() {
ls.computeCoefficients()
}
x, y = ls.GetValues(ls.GetEndIndex())
return
}
// Render renders the series.
func (ls *LinearSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) {
Draw.LineSeries(r, canvasBox, xrange, yrange, ls.Style.InheritFrom(defaults), ls)
}
// Validate validates the series.
func (ls LinearSeries) Validate() error {
if ls.InnerSeries == nil {
return fmt.Errorf("linear regression series requires InnerSeries to be set")
}
return nil
}
// IsZero returns if the linear series has computed coefficients or not.
func (ls LinearSeries) IsZero() bool {
return ls.m == 0 && ls.b == 0
}
// computeCoefficients computes the `m` and `b` terms in the linear formula given by `y = mx+b`.
func (ls *LinearSeries) computeCoefficients() {
ls.m, ls.b, ls.stdev, ls.avg = ls.InnerSeries.Coefficients()
}
func (ls *LinearSeries) normalize(xvalue float64) float64 {
if ls.avg > 0 && ls.stdev > 0 {
return (xvalue - ls.avg) / ls.stdev
}
return xvalue
}