120 lines
2.7 KiB
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
|
||
|
}
|