mirror of
https://github.com/lukewilson2002/autotrader.git
synced 2025-08-03 05:29:32 +00:00
Gave the Series, Frame, and Signals a lot of needed love
This commit is contained in:
287
series.go
287
series.go
@@ -3,9 +3,9 @@ package autotrader
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
df "github.com/rocketlaunchr/dataframe-go"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
@@ -14,9 +14,7 @@ type Series interface {
|
||||
|
||||
// Reading data.
|
||||
|
||||
// Copy returns a new Series with a copy of the original data and Series name. start is an EasyIndex and len is the number of items to copy from start onward. If len is negative then all items from start to the end of the series are copied. If there are not enough items to copy then the maximum amount is returned. If there are no items to copy then an empty DataSeries is returned.
|
||||
//
|
||||
// If start is out of bounds then nil is returned.
|
||||
// Copy returns a new Series with a copy of the original data and Series name. start is an EasyIndex and count is the number of items to copy from start onward. If count is negative then all items from start to the end of the series are copied. If there are not enough items to copy then the maximum amount is returned. If there are no items to copy then an empty DataSeries is returned.
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
@@ -25,35 +23,36 @@ type Series interface {
|
||||
// Copy(-10, -1) - copy the last 10 items
|
||||
//
|
||||
// All signals are disconnected from the copy. The copy has its value function reset to its own Value.
|
||||
Copy(start, len int) Series
|
||||
Copy(start, count int) Series
|
||||
Len() int
|
||||
Name() string // Name returns the immutable name of the Series.
|
||||
Float(i int) float64
|
||||
Int(i int) int64
|
||||
Int(i int) int
|
||||
Str(i int) string
|
||||
Time(i int) time.Time
|
||||
Value(i int) interface{}
|
||||
ValueRange(start, end int) []interface{}
|
||||
Values() []interface{} // Values is the same as ValueRange(0, -1).
|
||||
Value(i int) any
|
||||
ValueRange(start, end int) []any
|
||||
Values() []any // Values is the same as ValueRange(0, -1).
|
||||
|
||||
// Writing data.
|
||||
|
||||
Reverse() Series
|
||||
SetName(name string) Series
|
||||
SetValue(i int, val interface{}) Series
|
||||
Push(val interface{}) Series
|
||||
SetValue(i int, val any) Series
|
||||
Push(val any) Series
|
||||
|
||||
// Functional.
|
||||
|
||||
Filter(f func(i int, val interface{}) bool) Series // Where returns a new Series with only the values that return true for the given function.
|
||||
Map(f func(i int, val interface{}) interface{}) Series // Map returns a new Series with the values modified by the given function.
|
||||
MapReverse(f func(i int, val interface{}) interface{}) Series // MapReverse is the same as Map but it starts from the last item and works backwards.
|
||||
Filter(f func(i int, val any) bool) Series // Where returns a new Series with only the values that return true for the given function.
|
||||
Map(f func(i int, val any) any) Series // Map returns a new Series with the values modified by the given function.
|
||||
MapReverse(f func(i int, val any) any) Series // MapReverse is the same as Map but it starts from the last item and works backwards.
|
||||
|
||||
// Statistical functions.
|
||||
|
||||
Rolling(period int) *RollingSeries
|
||||
|
||||
// WithValueFunc is used to implement other types of Series that may modify the values by applying a function before returning them, for example. This returns a Series that is a copy of the original with the new value function used whenever a value is requested outside of the Value() method, which will still return the original value.
|
||||
WithValueFunc(value func(i int) interface{}) Series
|
||||
WithValueFunc(value func(i int) any) Series
|
||||
}
|
||||
|
||||
var _ Series = (*AppliedSeries)(nil) // Compile-time interface check.
|
||||
@@ -61,23 +60,23 @@ var _ Series = (*AppliedSeries)(nil) // Compile-time interface check.
|
||||
// AppliedSeries is like Series, but it applies a function to each row of data before returning it.
|
||||
type AppliedSeries struct {
|
||||
Series
|
||||
apply func(s *AppliedSeries, i int, val interface{}) interface{}
|
||||
apply func(s *AppliedSeries, i int, val any) any
|
||||
}
|
||||
|
||||
func NewAppliedSeries(s Series, apply func(s *AppliedSeries, i int, val interface{}) interface{}) *AppliedSeries {
|
||||
func NewAppliedSeries(s Series, apply func(s *AppliedSeries, i int, val any) any) *AppliedSeries {
|
||||
appliedSeries := &AppliedSeries{apply: apply}
|
||||
appliedSeries.Series = s.WithValueFunc(appliedSeries.Value)
|
||||
return appliedSeries
|
||||
}
|
||||
|
||||
func (s *AppliedSeries) Copy(start, len int) Series {
|
||||
return NewAppliedSeries(s.Series.Copy(start, len), s.apply)
|
||||
func (s *AppliedSeries) Copy(start, count int) Series {
|
||||
return NewAppliedSeries(s.Series.Copy(start, count), s.apply)
|
||||
}
|
||||
|
||||
// Value returns the value of the underlying Series item after applying the function.
|
||||
//
|
||||
// See also: ValueUnapplied()
|
||||
func (s *AppliedSeries) Value(i int) interface{} {
|
||||
func (s *AppliedSeries) Value(i int) any {
|
||||
return s.apply(s, EasyIndex(i, s.Series.Len()), s.Series.Value(i))
|
||||
}
|
||||
|
||||
@@ -86,10 +85,14 @@ func (s *AppliedSeries) Value(i int) interface{} {
|
||||
// This is equivalent to:
|
||||
//
|
||||
// s.Series.Value(i)
|
||||
func (s *AppliedSeries) ValueUnapplied(i int) interface{} {
|
||||
func (s *AppliedSeries) ValueUnapplied(i int) any {
|
||||
return s.Series.Value(i)
|
||||
}
|
||||
|
||||
func (s *AppliedSeries) Reverse() Series {
|
||||
return NewAppliedSeries(s.Series.Reverse(), s.apply)
|
||||
}
|
||||
|
||||
// SetValue sets the value of the underlying Series item without applying the function.
|
||||
//
|
||||
// This may give unexpected results, as the function will still be applied when the value is requested.
|
||||
@@ -97,35 +100,35 @@ func (s *AppliedSeries) ValueUnapplied(i int) interface{} {
|
||||
// For example:
|
||||
//
|
||||
// series := NewSeries(1, 2, 3) // Pseudo-code.
|
||||
// applied := NewAppliedSeries(series, func(_ *AppliedSeries, _ int, val interface{}) interface{} {
|
||||
// applied := NewAppliedSeries(series, func(_ *AppliedSeries, _ int, val any) any {
|
||||
// return val.(int) * 2
|
||||
// })
|
||||
// applied.SetValue(0, 10)
|
||||
// applied.Value(0) // 20
|
||||
// series.Value(0) // 1
|
||||
func (s *AppliedSeries) SetValue(i int, val interface{}) Series {
|
||||
func (s *AppliedSeries) SetValue(i int, val any) Series {
|
||||
_ = s.Series.SetValue(i, val)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *AppliedSeries) Push(val interface{}) Series {
|
||||
func (s *AppliedSeries) Push(val any) Series {
|
||||
_ = s.Series.Push(val)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *AppliedSeries) Filter(f func(i int, val interface{}) bool) Series {
|
||||
func (s *AppliedSeries) Filter(f func(i int, val any) bool) Series {
|
||||
return NewAppliedSeries(s.Series.Filter(f), s.apply)
|
||||
}
|
||||
|
||||
func (s *AppliedSeries) Map(f func(i int, val interface{}) interface{}) Series {
|
||||
func (s *AppliedSeries) Map(f func(i int, val any) any) Series {
|
||||
return NewAppliedSeries(s.Series.Map(f), s.apply)
|
||||
}
|
||||
|
||||
func (s *AppliedSeries) MapReverse(f func(i int, val interface{}) interface{}) Series {
|
||||
func (s *AppliedSeries) MapReverse(f func(i int, val any) any) Series {
|
||||
return NewAppliedSeries(s.Series.MapReverse(f), s.apply)
|
||||
}
|
||||
|
||||
func (s *AppliedSeries) WithValueFunc(value func(i int) interface{}) Series {
|
||||
func (s *AppliedSeries) WithValueFunc(value func(i int) any) Series {
|
||||
return &AppliedSeries{Series: s.Series.WithValueFunc(value), apply: s.apply}
|
||||
}
|
||||
|
||||
@@ -142,13 +145,13 @@ func NewRollingSeries(s Series, period int) *RollingSeries {
|
||||
return series
|
||||
}
|
||||
|
||||
func (s *RollingSeries) Copy(start, len int) Series {
|
||||
return NewRollingSeries(s.Series.Copy(start, len), s.period)
|
||||
func (s *RollingSeries) Copy(start, count int) Series {
|
||||
return NewRollingSeries(s.Series.Copy(start, count), s.period)
|
||||
}
|
||||
|
||||
// Value returns []interface{} up to `period` long. The last item in the slice is the item at i. If i is out of bounds, nil is returned.
|
||||
func (s *RollingSeries) Value(i int) interface{} {
|
||||
items := make([]interface{}, 0, s.period)
|
||||
// Value returns []any up to `period` long. The last item in the slice is the item at i. If i is out of bounds, nil is returned.
|
||||
func (s *RollingSeries) Value(i int) any {
|
||||
items := make([]any, 0, s.period)
|
||||
i = EasyIndex(i, s.Len())
|
||||
if i < 0 || i >= s.Len() {
|
||||
return items
|
||||
@@ -160,25 +163,29 @@ func (s *RollingSeries) Value(i int) interface{} {
|
||||
return items
|
||||
}
|
||||
|
||||
func (s *RollingSeries) SetValue(i int, val interface{}) Series {
|
||||
func (s *RollingSeries) Reverse() Series {
|
||||
return NewRollingSeries(s.Series.Reverse(), s.period)
|
||||
}
|
||||
|
||||
func (s *RollingSeries) SetValue(i int, val any) Series {
|
||||
_ = s.Series.SetValue(i, val)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *RollingSeries) Push(val interface{}) Series {
|
||||
func (s *RollingSeries) Push(val any) Series {
|
||||
_ = s.Series.Push(val)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *RollingSeries) Filter(f func(i int, val interface{}) bool) Series {
|
||||
func (s *RollingSeries) Filter(f func(i int, val any) bool) Series {
|
||||
return NewRollingSeries(s.Series.Filter(f), s.period)
|
||||
}
|
||||
|
||||
func (s *RollingSeries) Map(f func(i int, val interface{}) interface{}) Series {
|
||||
func (s *RollingSeries) Map(f func(i int, val any) any) Series {
|
||||
return NewRollingSeries(s.Series.Map(f), s.period)
|
||||
}
|
||||
|
||||
func (s *RollingSeries) MapReverse(f func(i int, val interface{}) interface{}) Series {
|
||||
func (s *RollingSeries) MapReverse(f func(i int, val any) any) Series {
|
||||
return NewRollingSeries(s.Series.MapReverse(f), s.period)
|
||||
}
|
||||
|
||||
@@ -188,9 +195,9 @@ func (s *RollingSeries) Average() *AppliedSeries {
|
||||
}
|
||||
|
||||
func (s *RollingSeries) Mean() *AppliedSeries {
|
||||
return NewAppliedSeries(s, func(_ *AppliedSeries, _ int, v interface{}) interface{} {
|
||||
return NewAppliedSeries(s, func(_ *AppliedSeries, _ int, v any) any {
|
||||
switch v := v.(type) {
|
||||
case []interface{}:
|
||||
case []any:
|
||||
if len(v) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -217,9 +224,9 @@ func (s *RollingSeries) Mean() *AppliedSeries {
|
||||
}
|
||||
|
||||
func (s *RollingSeries) EMA() *AppliedSeries {
|
||||
return NewAppliedSeries(s, func(_ *AppliedSeries, i int, v interface{}) interface{} {
|
||||
return NewAppliedSeries(s, func(_ *AppliedSeries, i int, v any) any {
|
||||
switch v := v.(type) {
|
||||
case []interface{}:
|
||||
case []any:
|
||||
if len(v) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -246,9 +253,9 @@ func (s *RollingSeries) EMA() *AppliedSeries {
|
||||
}
|
||||
|
||||
func (s *RollingSeries) Median() *AppliedSeries {
|
||||
return NewAppliedSeries(s, func(_ *AppliedSeries, _ int, v interface{}) interface{} {
|
||||
return NewAppliedSeries(s, func(_ *AppliedSeries, _ int, v any) any {
|
||||
switch v := v.(type) {
|
||||
case []interface{}:
|
||||
case []any:
|
||||
if len(v) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -257,7 +264,7 @@ func (s *RollingSeries) Median() *AppliedSeries {
|
||||
if len(v) == 0 {
|
||||
return float64(0)
|
||||
}
|
||||
slices.SortFunc(v, func(a, b interface{}) bool {
|
||||
slices.SortFunc(v, func(a, b any) bool {
|
||||
x, y := a.(float64), b.(float64)
|
||||
return x < y || (math.IsNaN(x) && !math.IsNaN(y))
|
||||
})
|
||||
@@ -269,7 +276,7 @@ func (s *RollingSeries) Median() *AppliedSeries {
|
||||
if len(v) == 0 {
|
||||
return int64(0)
|
||||
}
|
||||
slices.SortFunc(v, func(a, b interface{}) bool {
|
||||
slices.SortFunc(v, func(a, b any) bool {
|
||||
x, y := a.(int64), b.(int64)
|
||||
return x < y
|
||||
})
|
||||
@@ -287,9 +294,9 @@ func (s *RollingSeries) Median() *AppliedSeries {
|
||||
}
|
||||
|
||||
func (s *RollingSeries) StdDev() *AppliedSeries {
|
||||
return NewAppliedSeries(s, func(_ *AppliedSeries, i int, v interface{}) interface{} {
|
||||
return NewAppliedSeries(s, func(_ *AppliedSeries, i int, v any) any {
|
||||
switch v := v.(type) {
|
||||
case []interface{}:
|
||||
case []any:
|
||||
if len(v) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -317,7 +324,7 @@ func (s *RollingSeries) StdDev() *AppliedSeries {
|
||||
})
|
||||
}
|
||||
|
||||
func (s *RollingSeries) WithValueFunc(value func(i int) interface{}) Series {
|
||||
func (s *RollingSeries) WithValueFunc(value func(i int) any) Series {
|
||||
return &RollingSeries{Series: s.Series.WithValueFunc(value), period: s.period}
|
||||
}
|
||||
|
||||
@@ -326,24 +333,25 @@ func (s *RollingSeries) WithValueFunc(value func(i int) interface{}) Series {
|
||||
// Signals:
|
||||
// - LengthChanged(int) - when the data is appended or an item is removed.
|
||||
// - NameChanged(string) - when the name is changed.
|
||||
// - ValueChanged(int, any) - when a value is changed.
|
||||
type DataSeries struct {
|
||||
SignalManager
|
||||
data df.Series
|
||||
value func(i int) interface{}
|
||||
name string
|
||||
data []any
|
||||
value func(i int) any
|
||||
}
|
||||
|
||||
func NewDataSeries(data df.Series) *DataSeries {
|
||||
func NewDataSeries(name string, vals ...any) *DataSeries {
|
||||
dataSeries := &DataSeries{
|
||||
SignalManager: SignalManager{},
|
||||
data: data,
|
||||
name: name,
|
||||
data: vals,
|
||||
}
|
||||
dataSeries.value = dataSeries.Value
|
||||
return dataSeries
|
||||
}
|
||||
|
||||
// Copy returns a new DataSeries with a copy of the original data and Series name. start is an EasyIndex and len is the number of items to copy from start onward. If len is negative then all items from start to the end of the series are copied. If there are not enough items to copy then the maximum amount is returned. If there are no items to copy then an empty DataSeries is returned.
|
||||
//
|
||||
// If start is out of bounds then nil is returned.
|
||||
// Copy returns a new DataSeries with a copy of the original data and Series name. start is an EasyIndex and count is the number of items to copy from start onward. If count is negative then all items from start to the end of the series are copied. If there are not enough items to copy then the maximum amount is returned. If there are no items to copy then an empty DataSeries is returned.
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
@@ -352,132 +360,130 @@ func NewDataSeries(data df.Series) *DataSeries {
|
||||
// Copy(-10, -1) - copy the last 10 items
|
||||
//
|
||||
// All signals are disconnected from the copy. The copy has its value function reset to its own Value.
|
||||
func (s *DataSeries) Copy(start, len int) Series {
|
||||
func (s *DataSeries) Copy(start, count int) Series {
|
||||
if s.Len() == 0 {
|
||||
return NewDataSeries(s.name)
|
||||
}
|
||||
start = EasyIndex(start, s.Len())
|
||||
var _end *int
|
||||
if start < 0 || start >= s.Len() {
|
||||
return nil
|
||||
} else if len >= 0 {
|
||||
end := start + len
|
||||
if end < s.Len() {
|
||||
if end < start {
|
||||
copy := s.data.Copy()
|
||||
copy.Reset()
|
||||
series := &DataSeries{SignalManager{}, copy, nil}
|
||||
series.value = series.Value
|
||||
return series
|
||||
}
|
||||
_end = &end
|
||||
}
|
||||
var end int
|
||||
start = Max(Min(start, s.Len()), 0)
|
||||
if count < 0 {
|
||||
end = s.Len()
|
||||
} else {
|
||||
end = Min(start+count, s.Len())
|
||||
}
|
||||
return &DataSeries{
|
||||
SignalManager: SignalManager{},
|
||||
data: s.data.Copy(df.Range{Start: &start, End: _end}),
|
||||
value: s.value,
|
||||
if end <= start {
|
||||
return NewDataSeries(s.name) // Return an empty series.
|
||||
}
|
||||
data := make([]any, end-start)
|
||||
copy(data, s.data[start:end])
|
||||
return NewDataSeries(s.name, data...)
|
||||
}
|
||||
|
||||
func (s *DataSeries) Name() string {
|
||||
return s.data.Name()
|
||||
return s.name
|
||||
}
|
||||
|
||||
func (s *DataSeries) SetName(name string) Series {
|
||||
if name == s.Name() {
|
||||
if name == s.name {
|
||||
return s
|
||||
}
|
||||
s.data.Rename(name)
|
||||
s.name = name
|
||||
s.SignalEmit("NameChanged", name)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *DataSeries) Len() int {
|
||||
if s.data == nil {
|
||||
return 0
|
||||
}
|
||||
return s.data.NRows()
|
||||
return len(s.data)
|
||||
}
|
||||
|
||||
func (s *DataSeries) Push(value interface{}) Series {
|
||||
if s.data != nil {
|
||||
s.data.Append(value)
|
||||
s.SignalEmit("LengthChanged", s.Len())
|
||||
func (s *DataSeries) Reverse() Series {
|
||||
if len(s.data) != 0 {
|
||||
sort.Slice(s.data, func(i, j int) bool {
|
||||
return i > j
|
||||
})
|
||||
for i, v := range s.data {
|
||||
s.SignalEmit("ValueChanged", i, v)
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *DataSeries) SetValue(i int, val interface{}) Series {
|
||||
if s.data != nil {
|
||||
s.data.Update(EasyIndex(i, s.Len()), val)
|
||||
func (s *DataSeries) Push(value any) Series {
|
||||
s.data = append(s.data, value)
|
||||
s.SignalEmit("LengthChanged", s.Len())
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *DataSeries) SetValue(i int, val any) Series {
|
||||
if i = EasyIndex(i, s.Len()); i < s.Len() && i >= 0 {
|
||||
s.data[i] = val
|
||||
s.SignalEmit("ValueChanged", i, val)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *DataSeries) Value(i int) interface{} {
|
||||
if s.data == nil {
|
||||
func (s *DataSeries) Value(i int) any {
|
||||
i = EasyIndex(i, s.Len())
|
||||
if i >= s.Len() || i < 0 {
|
||||
return nil
|
||||
}
|
||||
i = EasyIndex(i, s.Len()) // Allow for negative indexing.
|
||||
return s.data.Value(i)
|
||||
return s.data[i]
|
||||
}
|
||||
|
||||
// ValueRange returns a slice of values from start to end, including start and end. The first value is at index 0. A negative value for start or end can be used to get values from the latest, like Python's negative indexing. If end is less than zero, it will be sliced from start to the last item. If start or end is out of bounds, nil is returned. If start is greater than end, nil is returned.
|
||||
func (s *DataSeries) ValueRange(start, end int) []interface{} {
|
||||
if s.data == nil {
|
||||
return nil
|
||||
}
|
||||
// ValueRange returns a copy of values from start to start+count. If count is negative then all items from start to the end of the series are returned. If there are not enough items to return then the maximum amount is returned. If there are no items to return then an empty slice is returned.
|
||||
func (s *DataSeries) ValueRange(start, count int) []any {
|
||||
start = EasyIndex(start, s.Len())
|
||||
if end < 0 {
|
||||
end = s.Len() - 1
|
||||
start = Max(Min(start, s.Len()), 0)
|
||||
if count < 0 {
|
||||
count = s.Len() - start
|
||||
} else {
|
||||
count = Min(count, s.Len()-start)
|
||||
}
|
||||
if start < 0 || start >= s.Len() || end >= s.Len() || start > end {
|
||||
return nil
|
||||
if count <= 0 {
|
||||
return []any{}
|
||||
}
|
||||
|
||||
items := make([]interface{}, end-start+1)
|
||||
for i := start; i <= end; i++ {
|
||||
items[i-start] = s.value(i)
|
||||
}
|
||||
end := start + count
|
||||
items := make([]any, count)
|
||||
copy(items, s.data[start:end])
|
||||
return items
|
||||
}
|
||||
|
||||
func (s *DataSeries) Values() []interface{} {
|
||||
if s.data == nil {
|
||||
return nil
|
||||
}
|
||||
// Values returns a copy of all values. If there are no values, an empty slice is returned.
|
||||
//
|
||||
// Same as:
|
||||
//
|
||||
// ValueRange(0, -1)
|
||||
func (s *DataSeries) Values() []any {
|
||||
return s.ValueRange(0, -1)
|
||||
}
|
||||
|
||||
// Float returns the value at index i as a float64. If the value is not a float64 then NaN is returned.
|
||||
func (s *DataSeries) Float(i int) float64 {
|
||||
val := s.value(i)
|
||||
if val == nil {
|
||||
return 0
|
||||
}
|
||||
switch val := val.(type) {
|
||||
case float64:
|
||||
return val
|
||||
default:
|
||||
return 0
|
||||
return math.NaN()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DataSeries) Int(i int) int64 {
|
||||
// Int returns the value at index i as an int64. If the value is not an int64 then 0 is returned.
|
||||
func (s *DataSeries) Int(i int) int {
|
||||
val := s.value(i)
|
||||
if val == nil {
|
||||
return 0
|
||||
}
|
||||
switch val := val.(type) {
|
||||
case int64:
|
||||
case int:
|
||||
return val
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// Str returns the value at index i as a string. If the value is not a string then "" is returned.
|
||||
func (s *DataSeries) Str(i int) string {
|
||||
val := s.value(i)
|
||||
if val == nil {
|
||||
return ""
|
||||
}
|
||||
switch val := val.(type) {
|
||||
case string:
|
||||
return val
|
||||
@@ -486,11 +492,9 @@ func (s *DataSeries) Str(i int) string {
|
||||
}
|
||||
}
|
||||
|
||||
// Time returns the value at index i as a time.Time. If the value is not a time.Time then time.Time{} is returned.
|
||||
func (s *DataSeries) Time(i int) time.Time {
|
||||
val := s.value(i)
|
||||
if val == nil {
|
||||
return time.Time{}
|
||||
}
|
||||
switch val := val.(type) {
|
||||
case time.Time:
|
||||
return val
|
||||
@@ -499,37 +503,30 @@ func (s *DataSeries) Time(i int) time.Time {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DataSeries) Filter(f func(i int, val interface{}) bool) Series {
|
||||
if s.data == nil {
|
||||
return nil
|
||||
}
|
||||
series := &DataSeries{SignalManager{}, df.NewSeriesGeneric(s.data.Name(), (interface{})(nil), nil), s.value}
|
||||
func (s *DataSeries) Filter(f func(i int, val any) bool) Series {
|
||||
series := NewDataSeries(s.name, make([]any, 0, s.Len())...)
|
||||
for i := 0; i < s.Len(); i++ {
|
||||
if val := series.value(i); f(i, val) {
|
||||
if val := s.value(i); f(i, val) {
|
||||
series.Push(val)
|
||||
}
|
||||
}
|
||||
return series
|
||||
}
|
||||
|
||||
func (s *DataSeries) Map(f func(i int, val interface{}) interface{}) Series {
|
||||
if s.data == nil {
|
||||
return nil
|
||||
}
|
||||
series := &DataSeries{SignalManager{}, s.data.Copy(), s.value}
|
||||
// Map returns a new series with the same length as the original series. The value at each index is replaced by the value returned by the function f.
|
||||
func (s *DataSeries) Map(f func(i int, val any) any) Series {
|
||||
series := s.Copy(0, -1)
|
||||
for i := 0; i < s.Len(); i++ {
|
||||
series.SetValue(i, f(i, series.value(i)))
|
||||
series.SetValue(i, f(i, series.Value(i)))
|
||||
}
|
||||
return series
|
||||
}
|
||||
|
||||
func (s *DataSeries) MapReverse(f func(i int, val interface{}) interface{}) Series {
|
||||
if s.data == nil {
|
||||
return nil
|
||||
}
|
||||
series := &DataSeries{SignalManager{}, s.data.Copy(), s.value}
|
||||
// MapReverse returns a new series with the same length as the original series. The value at each index is replaced by the value returned by the function f. The values are processed in reverse order.
|
||||
func (s *DataSeries) MapReverse(f func(i int, val any) any) Series {
|
||||
series := s.Copy(0, -1)
|
||||
for i := s.Len() - 1; i >= 0; i-- {
|
||||
series.SetValue(i, f(i, series.value(i)))
|
||||
series.SetValue(i, f(i, series.Value(i)))
|
||||
}
|
||||
return series
|
||||
}
|
||||
@@ -538,7 +535,7 @@ func (s *DataSeries) Rolling(period int) *RollingSeries {
|
||||
return NewRollingSeries(s, period)
|
||||
}
|
||||
|
||||
func (s *DataSeries) WithValueFunc(value func(i int) interface{}) Series {
|
||||
func (s *DataSeries) WithValueFunc(value func(i int) any) Series {
|
||||
copy := s.Copy(0, -1).(*DataSeries)
|
||||
copy.value = value
|
||||
return copy
|
||||
|
Reference in New Issue
Block a user