github.com/tobgu/qframe@v0.4.0/contrib/gonum/qplot/plotter.go (about)

     1  package qplot
     2  
     3  import (
     4  	"strconv"
     5  
     6  	"github.com/tobgu/qframe"
     7  	"github.com/tobgu/qframe/qerrors"
     8  	"github.com/tobgu/qframe/types"
     9  )
    10  
    11  // LabelFunc returns a string representation of
    12  // the value in row i.
    13  type LabelFunc func(i int) string
    14  
    15  // LabelOfString returns a StringView compatible LabelFunc
    16  func LabelOfString(view qframe.StringView) LabelFunc {
    17  	return func(i int) string {
    18  		return *view.ItemAt(i)
    19  	}
    20  }
    21  
    22  // LabelOfEnum returns a EnumView compatible LabelFunc
    23  func LabelOfEnum(view qframe.EnumView) LabelFunc {
    24  	return func(i int) string {
    25  		return *view.ItemAt(i)
    26  	}
    27  }
    28  
    29  // LabelOfFloat returns a FloatView compatible LabelFunc
    30  // fmt determines the float format when creating a string
    31  func LabelOfFloat(fmt byte, view qframe.FloatView) LabelFunc {
    32  	return func(i int) string {
    33  		return strconv.FormatFloat(view.ItemAt(i), fmt, -1, 64)
    34  	}
    35  }
    36  
    37  // LabelOfInt returns an IntView compatible LabelFunc
    38  func LabelOfInt(view qframe.IntView) LabelFunc {
    39  	return func(i int) string {
    40  		return strconv.FormatInt(int64(view.ItemAt(i)), 10)
    41  	}
    42  }
    43  
    44  // LabelOfBool returns a BoolView compatible LabelFunc
    45  func LabelOfBool(view qframe.BoolView) LabelFunc {
    46  	return func(i int) string {
    47  		return strconv.FormatBool(view.ItemAt(i))
    48  	}
    49  }
    50  
    51  // Labeller implements the Labeller interface
    52  // defined in gonum.org/v1/plot/plotter. It accepts
    53  // any of the predefined LabelFunc methods in this
    54  // package or a custom function may be specified.
    55  type Labeller struct {
    56  	len int
    57  	fn  LabelFunc
    58  }
    59  
    60  // Label returns the label at i
    61  func (l Labeller) Label(i int) string { return l.fn(i) }
    62  
    63  // NewLabeller returns a new Labeller
    64  func NewLabeller(len int, fn LabelFunc) Labeller {
    65  	return Labeller{len: len, fn: fn}
    66  }
    67  
    68  // XYLabeller implements the XYLabeller interface
    69  // defined in gonum.org/v1/plot/plotter.
    70  // It is a union of the Labeller and XYer
    71  // types defined in this package.
    72  type XYLabeller struct {
    73  	Labeller
    74  	XYer
    75  }
    76  
    77  // ValueFunc returns a float representation of
    78  // the value in row i.
    79  type ValueFunc func(i int) float64
    80  
    81  // NewValueFunc returns a ValueFunc for column col
    82  // if it is a numeric column, or returns an error.
    83  func NewValueFunc(col string, qf qframe.QFrame) (ValueFunc, error) {
    84  	if !isNumCol(col, qf) {
    85  		return nil, qerrors.New("NewValueFunc", "Column %s is not a numeric value", col)
    86  	}
    87  	if !qf.Contains(col) {
    88  		return nil, qerrors.New("NewValueFunc", "QFrame does not contain column %s", col)
    89  	}
    90  	switch qf.ColumnTypeMap()[col] {
    91  	case types.Int:
    92  		return ValueOfInt(qf.MustIntView(col)), nil
    93  	case types.Float:
    94  		return ValueOfFloat(qf.MustFloatView(col)), nil
    95  	default:
    96  		panic(qerrors.New("NewValueFunc", "forgot to support a new column type?"))
    97  	}
    98  }
    99  
   100  // MustNewValueFunc returns a ValueFunc and panics when
   101  // an error is encountered.
   102  func MustNewValueFunc(col string, qf qframe.QFrame) ValueFunc {
   103  	fn, err := NewValueFunc(col, qf)
   104  	if err != nil {
   105  		panic(qerrors.Propagate("MustNewValueFunc", err))
   106  	}
   107  	return fn
   108  }
   109  
   110  // ValueOfInt returns an IntView compatible ValueFunc
   111  func ValueOfInt(view qframe.IntView) ValueFunc {
   112  	return func(i int) float64 {
   113  		return float64(view.ItemAt(i))
   114  	}
   115  }
   116  
   117  // ValueOfFloat returns an FloatView compatible ValueFunc
   118  func ValueOfFloat(view qframe.FloatView) ValueFunc {
   119  	return func(i int) float64 {
   120  		return view.ItemAt(i)
   121  	}
   122  }
   123  
   124  // Valuer implements the Valuer interface
   125  // defined in gonum.org/v1/plot/plotter.Valuer
   126  type Valuer struct {
   127  	len int
   128  	fn  ValueFunc
   129  }
   130  
   131  // Len returns the length of the underlying view
   132  func (v Valuer) Len() int { return v.len }
   133  
   134  // Value returns the value in row i of the underlying view
   135  func (v Valuer) Value(i int) float64 { return v.fn(i) }
   136  
   137  // NewValuer returns a new Valuer from the values
   138  // in col. The column must be a numeric type.
   139  func NewValuer(col string, qf qframe.QFrame) (Valuer, error) {
   140  	fn, err := NewValueFunc(col, qf)
   141  	if err != nil {
   142  		return Valuer{}, err
   143  	}
   144  	return Valuer{len: qf.Len(), fn: fn}, nil
   145  }
   146  
   147  // MustNewValuer returns a new Valuer from the values
   148  // in col.
   149  func MustNewValuer(col string, qf qframe.QFrame) Valuer {
   150  	valuer, err := NewValuer(col, qf)
   151  	if err != nil {
   152  		panic(qerrors.Propagate("MustNewValuer", err))
   153  	}
   154  	return valuer
   155  }
   156  
   157  // XYer implements the XYer interface
   158  // defined in gonum.org/v1/plot/plotter.
   159  type XYer struct {
   160  	len int
   161  	xfn ValueFunc
   162  	yfn ValueFunc
   163  }
   164  
   165  // Len returns the length of the underlying view
   166  func (xy XYer) Len() int { return xy.len }
   167  
   168  // XY returns the values of X and Y in the underlying view
   169  func (xy XYer) XY(i int) (float64, float64) { return xy.xfn(i), xy.yfn(i) }
   170  
   171  // NewXYer returns a new XYer from the values
   172  // in column x and y. Both columns must have numeric types.
   173  func NewXYer(x, y string, qf qframe.QFrame) (XYer, error) {
   174  	xvals, err := NewValueFunc(x, qf)
   175  	if err != nil {
   176  		return XYer{}, qerrors.Propagate("NewXYer", err)
   177  	}
   178  	yvals, err := NewValueFunc(y, qf)
   179  	if err != nil {
   180  		return XYer{}, qerrors.Propagate("NewXYer", err)
   181  	}
   182  	return XYer{len: qf.Len(), xfn: xvals, yfn: yvals}, nil
   183  }
   184  
   185  // MustNewXYer returns a new XYer from the values
   186  // in column x and y. Both columns must have numeric types.
   187  func MustNewXYer(x, y string, qf qframe.QFrame) XYer {
   188  	xyer, err := NewXYer(x, y, qf)
   189  	if err != nil {
   190  		panic(qerrors.Propagate("MustNewXYer", err))
   191  	}
   192  	return xyer
   193  }
   194  
   195  // XYZer implements the XYZer interface
   196  // defined in gonum.org/v1/plot/plotter
   197  type XYZer struct {
   198  	len int
   199  	xfn ValueFunc
   200  	yfn ValueFunc
   201  	zfn ValueFunc
   202  }
   203  
   204  // Len returns the length of the underlying view
   205  func (xyz XYZer) Len() int { return xyz.len }
   206  
   207  // XYZ returns the values of X, Y, and Z in the underlying view
   208  func (xyz XYZer) XYZ(i int) (float64, float64, float64) {
   209  	return xyz.xfn(i), xyz.yfn(i), xyz.zfn(i)
   210  }
   211  
   212  // XY returns the values of X and Y in the underlying view
   213  func (xyz XYZer) XY(i int) (float64, float64) {
   214  	return xyz.xfn(i), xyz.yfn(i)
   215  }
   216  
   217  // NewXYZer returns a new XYZer from the values
   218  // in column x, y, and z. All columns must have numeric types.
   219  func NewXYZer(x, y, z string, qf qframe.QFrame) (XYZer, error) {
   220  	xvals, err := NewValueFunc(x, qf)
   221  	if err != nil {
   222  		return XYZer{}, qerrors.Propagate("NewXYZer", err)
   223  	}
   224  	yvals, err := NewValueFunc(y, qf)
   225  	if err != nil {
   226  		return XYZer{}, qerrors.Propagate("NewXYZer", err)
   227  	}
   228  	zvals, err := NewValueFunc(z, qf)
   229  	if err != nil {
   230  		return XYZer{}, qerrors.Propagate("NewXYZer", err)
   231  	}
   232  	return XYZer{len: qf.Len(), xfn: xvals, yfn: yvals, zfn: zvals}, nil
   233  }
   234  
   235  // MustNewXYZer returns a new XYZer from the values
   236  // in column x, y, and z. All columns must have numeric types.
   237  func MustNewXYZer(x, y, z string, qf qframe.QFrame) XYZer {
   238  	xyzer, err := NewXYZer(x, y, z, qf)
   239  	if err != nil {
   240  		panic(qerrors.Propagate("MustNewXYZer", err))
   241  	}
   242  	return xyzer
   243  }
   244  
   245  // YErrorer implements the YErrorer interface
   246  // defined in gonum.org/v1/plot/plotter
   247  type YErrorer struct {
   248  	low  ValueFunc
   249  	high ValueFunc
   250  }
   251  
   252  // YError returns the low and high error values in the underlying view.
   253  func (ye YErrorer) YError(i int) (float64, float64) { return ye.low(i), ye.high(i) }
   254  
   255  // NewYErrorer returns a new YErrorer for the values in
   256  // column low and high of the QFrame. All columns must have
   257  // numeric types.
   258  func NewYErrorer(low, high string, qf qframe.QFrame) (YErrorer, error) {
   259  	lowFn, err := NewValueFunc(low, qf)
   260  	if err != nil {
   261  		return YErrorer{}, qerrors.Propagate("NewYErrorer", err)
   262  	}
   263  	highFn, err := NewValueFunc(high, qf)
   264  	if err != nil {
   265  		return YErrorer{}, qerrors.Propagate("NewYErrorer", err)
   266  	}
   267  	return YErrorer{low: lowFn, high: highFn}, nil
   268  }
   269  
   270  // NewYErrorer returns a new YErrorer for the values in
   271  // column low and high of the QFrame. All columns must have
   272  // numeric types.
   273  func MustNewYErrorer(low, high string, qf qframe.QFrame) YErrorer {
   274  	y, err := NewYErrorer(low, high, qf)
   275  	if err != nil {
   276  		panic(qerrors.Propagate("MustNewYErrorer", err))
   277  	}
   278  	return y
   279  }
   280  
   281  // XErrorer implements the XErrorer interface
   282  // defined in gonum.org/v1/plot/plotter
   283  type XErrorer struct {
   284  	low  ValueFunc
   285  	high ValueFunc
   286  }
   287  
   288  // XError returns the low and high error values in the underlying view.
   289  func (xe XErrorer) XError(i int) (float64, float64) { return xe.low(i), xe.high(i) }
   290  
   291  // NewXErrorer returns a new XErrorer for the values in
   292  // column low and high of the QFrame. All columns must have
   293  // numeric types.
   294  func NewXErrorer(low, high string, qf qframe.QFrame) (XErrorer, error) {
   295  	lowFn, err := NewValueFunc(low, qf)
   296  	if err != nil {
   297  		return XErrorer{}, qerrors.Propagate("NewXErrorer", err)
   298  	}
   299  	highFn, err := NewValueFunc(high, qf)
   300  	if err != nil {
   301  		return XErrorer{}, qerrors.Propagate("NewXErrorer", err)
   302  	}
   303  	return XErrorer{low: lowFn, high: highFn}, nil
   304  }
   305  
   306  // MustNewXErrorer returns a new XErrorer for the values in
   307  // column low and high of the QFrame. All columns must have
   308  // numeric types.
   309  func MustNewXErrorer(low, high string, qf qframe.QFrame) XErrorer {
   310  	x, err := NewXErrorer(low, high, qf)
   311  	if err != nil {
   312  		panic(qerrors.Propagate("MustNewXErrorer", err))
   313  	}
   314  	return x
   315  }
   316  
   317  // TODO:
   318  // GridXYZ is used in HeatMap plotters but is too
   319  // specific AFAICT to be generalized here. It can easily
   320  // be implemented by wrapping a QFrame or composing
   321  // several ValueFunc together.