From d0f5c0746fc192630b4bdfa94ac886e7ebdf2078 Mon Sep 17 00:00:00 2001 From: wangfeng Date: Wed, 8 Feb 2023 13:26:18 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E7=BB=9F=E4=B8=80=E4=BA=86=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E7=B1=BB=E5=9E=8B=E4=BB=A5=E5=8F=8A=E5=88=87=E6=8D=A2?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- formula/abs.go | 4 +- formula/ma.go | 27 ++- formula/sma.go | 8 +- formula/sma_test.go | 2 +- formula/sum.go | 26 ++- formula/type.go | 15 ++ formula/wma.go | 18 +- generic.go | 23 ++- generic_append.go | 2 +- generic_diff.go | 27 ++- generic_ewm.go | 57 +++--- generic_number.go | 6 +- generic_rolling.go | 21 ++- generic_sum.go | 4 +- rolling_mean.go | 6 +- rolling_sum.go | 6 +- rolling_v1.go | 2 + series.go | 17 +- stat/builtin.go | 37 +++- stat/errors.go | 12 ++ stat/type.go | 65 +++++++ stat/type_bool.go | 53 ++++++ stat/type_dtype.go | 18 ++ stat/type_dtype_test.go | 385 ++++++++++++++++++++++++++++++++++++++++ stat/type_float32.go | 133 ++++++++++++++ stat/type_float64.go | 131 ++++++++++++++ stat/type_int64.go | 15 ++ type_float32.go | 4 +- type_float64.go | 4 +- type_int64.go | 4 +- 30 files changed, 995 insertions(+), 137 deletions(-) create mode 100644 formula/type.go create mode 100644 stat/errors.go create mode 100644 stat/type_bool.go create mode 100644 stat/type_dtype.go create mode 100644 stat/type_dtype_test.go create mode 100644 stat/type_float32.go create mode 100644 stat/type_float64.go create mode 100644 stat/type_int64.go diff --git a/formula/abs.go b/formula/abs.go index dad45d5..5df50e1 100644 --- a/formula/abs.go +++ b/formula/abs.go @@ -6,7 +6,7 @@ import ( ) func ABS(S pandas.Series) pandas.Series { - s := S.Float() + s := S.DTypes() d := stat.Abs(s) - return pandas.NewSeries(pandas.SERIES_TYPE_FLOAT32, "", d) + return pandas.NewSeries(pandas.SERIES_TYPE_DTYPE, "", d) } diff --git a/formula/ma.go b/formula/ma.go index f593f67..c0a3cb2 100644 --- a/formula/ma.go +++ b/formula/ma.go @@ -2,23 +2,22 @@ package formula import ( "gitee.com/quant1x/pandas" - "gitee.com/quant1x/pandas/exception" "gitee.com/quant1x/pandas/stat" ) // MA 计算移动均线 // 求序列的N日简单移动平均值, 返回序列 -func MA(S pandas.Series, N any) any { - var X []float32 - switch v := N.(type) { - case int: - X = stat.Repeat[float32](float32(v), S.Len()) - case pandas.Series: - vs := v.Values() - X = pandas.SliceToFloat32(vs) - X = stat.Align(X, pandas.Nil2Float32, S.Len()) - default: - panic(exception.New(1, "error window")) - } - return S.Rolling(X).Mean().Values() +func MA(S pandas.Series, N any) []stat.DType { + //var X []float32 + //switch v := N.(type) { + //case int: + // X = stat.Repeat[float32](float32(v), S.Len()) + //case pandas.Series: + // vs := v.Values() + // X = pandas.SliceToFloat32(vs) + // X = stat.Align(X, pandas.Nil2Float32, S.Len()) + //default: + // panic(exception.New(1, "error window")) + //} + return S.Rolling(N).Mean().DTypes() } diff --git a/formula/sma.go b/formula/sma.go index 161c7e8..485317e 100644 --- a/formula/sma.go +++ b/formula/sma.go @@ -43,7 +43,7 @@ func SMA_v5(S pandas.Series, N any, M int) any { panic(exception.New(1, "error window")) } k := X[0] - x := S.EWM(pandas.EW{Alpha: pandas.Nil2Float64, Callback: func(idx int) pandas.DType { + x := S.EWM(pandas.EW{Alpha: pandas.Nil2Float64, Callback: func(idx int) stat.DType { j := X[idx] if j == 0 { j = 1 @@ -77,12 +77,12 @@ func SMA_v3(S pandas.Series, N any, M int) any { if M == 0 { M = 1 } - x := S.Rolling(N).Apply(func(S pandas.Series, N float32) float32 { + x := S.Rolling(N).Apply(func(S pandas.Series, N stat.DType) stat.DType { r := S.EWM(pandas.EW{Alpha: float64(M) / float64(N), Adjust: false}).Mean().Values().([]float64) if len(r) == 0 { - return stat.Nil2Float32 + return stat.DTypeNaN } - return float32(r[len(r)-1]) + return stat.DType(r[len(r)-1]) }).Values() return x } diff --git a/formula/sma_test.go b/formula/sma_test.go index a52ba9c..e373bd0 100644 --- a/formula/sma_test.go +++ b/formula/sma_test.go @@ -34,7 +34,7 @@ func TestSMA(t *testing.T) { x[idx] = t }) //x := stat.Where(v2, as, bs) - n := barslast(pandas.NewSeries(pandas.SERIES_TYPE_FLOAT32, "", x)) + n := BARSLAST(pandas.NewSeries(pandas.SERIES_TYPE_FLOAT32, "", x)) fmt.Println(n[len(n)-10:]) //r1 := SMA(CLOSE, pandas.NewSeries(pandas.SERIES_TYPE_FLOAT32, "", n), 1) r1 := SMA(CLOSE, 6, 1) diff --git a/formula/sum.go b/formula/sum.go index 3d1da45..932d307 100644 --- a/formula/sum.go +++ b/formula/sum.go @@ -2,24 +2,22 @@ package formula import ( "gitee.com/quant1x/pandas" - "gitee.com/quant1x/pandas/exception" - "gitee.com/quant1x/pandas/stat" ) // SUM 求累和 // 如果N=0, 则从第一个有效值累加到当前 // 下一步再统一返回值 func SUM(S pandas.Series, N any) any { - var X []float32 - switch v := N.(type) { - case int: - X = stat.Repeat[float32](float32(v), S.Len()) - case pandas.Series: - vs := v.Values() - X = pandas.SliceToFloat32(vs) - X = stat.Align(X, pandas.Nil2Float32, S.Len()) - default: - panic(exception.New(1, "error window")) - } - return S.Rolling(X).Sum().Values() + //var X []float32 + //switch v := N.(type) { + //case int: + // X = stat.Repeat[float32](float32(v), S.Len()) + //case pandas.Series: + // vs := v.Values() + // X = pandas.SliceToFloat32(vs) + // X = stat.Align(X, pandas.Nil2Float32, S.Len()) + //default: + // panic(exception.New(1, "error window")) + //} + return S.Rolling(N).Sum().Values() } diff --git a/formula/type.go b/formula/type.go new file mode 100644 index 0000000..7fa6126 --- /dev/null +++ b/formula/type.go @@ -0,0 +1,15 @@ +package formula + +//func abc (N any) []stat.DType { +// var X []stat.DType +// switch v := N.(type) { +// case int: +// X = float32(v) +// case pandas.Series: +// vs := v.Values() +// fs := pandas.SliceToFloat32(vs) +// X = fs[len(fs)-1] +// default: +// panic(exception.New(1, "error window")) +// } +//} diff --git a/formula/wma.go b/formula/wma.go index 4e0aae6..0bb2f7f 100644 --- a/formula/wma.go +++ b/formula/wma.go @@ -8,27 +8,23 @@ import ( // WMA 通达信S序列的N日加权移动平均 Yn = (1*X1+2*X2+3*X3+...+n*Xn)/(1+2+3+...+Xn) func WMA(S pandas.Series, N any) any { - var X []float32 + var X []stat.DType switch v := N.(type) { case int: - X = stat.Repeat[float32](float32(v), S.Len()) + X = stat.Repeat[stat.DType](stat.DType(v), S.Len()) case pandas.Series: - vs := v.Values() - X = pandas.SliceToFloat32(vs) - X = stat.Align(X, pandas.Nil2Float32, S.Len()) + vs := v.DTypes() + X = stat.Align(vs, stat.DTypeNaN, S.Len()) default: panic(exception.New(1, "error window")) } - return S.Rolling(X).Apply(func(S pandas.Series, N float32) float32 { + return S.Rolling(X).Apply(func(S pandas.Series, N stat.DType) stat.DType { if S.Len() == 0 { - return stat.Nil2Float32 + return stat.DTypeNaN } - x := pandas.ToFloat32(S) - //fmt.Println(x) + x := S.DTypes() x = stat.Reverse(x) - //fmt.Println(x) v := stat.CumSum(x) - //fmt.Println(v) v1 := stat.Sum(v) v2 := v1 * 2 / N / (N + 1) return v2 diff --git a/generic.go b/generic.go index 1be6504..d419982 100644 --- a/generic.go +++ b/generic.go @@ -156,6 +156,11 @@ func (self *NDFrame) Float() []float32 { return ToFloat32(self) } +// DType 计算以这个函数为主 +func (self *NDFrame) DTypes() []stat.DType { + return stat.Slice2DType(self.Values()) +} + func (self *NDFrame) Empty() Series { var frame NDFrame if self.type_ == SERIES_TYPE_STRING { @@ -269,38 +274,38 @@ func (self *NDFrame) Shift(periods int) Series { } } -func (self *NDFrame) Mean() float64 { +func (self *NDFrame) Mean() stat.DType { if self.Len() < 1 { return NaN() } - fs := make([]float64, 0) + fs := make([]stat.DType, 0) self.Apply(func(idx int, v any) { - f := AnyToFloat64(v) + f := stat.Any2DType(v) fs = append(fs, f) }) stdDev := avx2.Mean(fs) return stdDev } -func (self *NDFrame) StdDev() float64 { +func (self *NDFrame) StdDev() stat.DType { if self.Len() < 1 { return NaN() } - values := make([]float64, self.Len()) + values := make([]stat.DType, self.Len()) self.Apply(func(idx int, v any) { - values[idx] = AnyToFloat64(v) + values[idx] = stat.Any2DType(v) }) stdDev := gs.StdDev(values, nil) return stdDev } -func (self *NDFrame) Std() float64 { +func (self *NDFrame) Std() stat.DType { if self.Len() < 1 { return NaN() } - values := make([]float64, self.Len()) + values := make([]stat.DType, self.Len()) self.Apply(func(idx int, v any) { - values[idx] = AnyToFloat64(v) + values[idx] = stat.Any2DType(v) }) stdDev := stat.Std(values) return stdDev diff --git a/generic_append.go b/generic_append.go index bac368b..2dec92f 100644 --- a/generic_append.go +++ b/generic_append.go @@ -23,7 +23,7 @@ func (self *NDFrame) insert(idx, size int, v any) { } // Append 批量增加记录 -func (self *NDFrame) Append(values ...interface{}) { +func (self *NDFrame) Append(values ...any) { size := 0 for idx, v := range values { switch val := v.(type) { diff --git a/generic_diff.go b/generic_diff.go index ec24539..f9ba946 100644 --- a/generic_diff.go +++ b/generic_diff.go @@ -13,44 +13,43 @@ func (self *NDFrame) Diff(param any) (s Series) { if !(self.type_ == SERIES_TYPE_INT64 || self.type_ == SERIES_TYPE_FLOAT32 || self.type_ == SERIES_TYPE_FLOAT64) { return NewSeries(SERIES_TYPE_INVAILD, "", "") } - var N []float32 + var N []stat.DType switch v := param.(type) { case int: - N = stat.Repeat[float32](float32(v), self.Len()) + N = stat.Repeat[stat.DType](stat.DType(v), self.Len()) case Series: - vs := v.Values() - N = SliceToFloat32(vs) - N = stat.Align(N, Nil2Float32, self.Len()) + vs := v.DTypes() + N = stat.Align(vs, stat.DTypeNaN, self.Len()) default: //periods = 1 - N = stat.Repeat[float32](float32(1), self.Len()) + N = stat.Repeat[stat.DType](stat.DType(1), self.Len()) } r := RollingAndExpandingMixin{ window: N, series: self, } - var d []float64 - var front = Nil2Float64 + var d []stat.DType + var front = stat.DTypeNaN for _, block := range r.getBlocks() { vs := reflect.ValueOf(block.Values()) vl := vs.Len() if vl == 0 { - d = append(d, Nil2Float64) + d = append(d, stat.DTypeNaN) continue } vf := vs.Index(0).Interface() vc := vs.Index(vl - 1).Interface() - cu := AnyToFloat64(vc) - cf := AnyToFloat64(vf) - if Float64IsNaN(cu) || Float64IsNaN(front) { + cu := stat.Any2DType(vc) + cf := stat.Any2DType(vf) + if stat.DTypeIsNaN(cu) || stat.DTypeIsNaN(front) { front = cf - d = append(d, Nil2Float64) + d = append(d, stat.DTypeNaN) continue } diff := cu - front d = append(d, diff) front = cf } - s = NewSeries(SERIES_TYPE_FLOAT64, r.series.Name(), d) + s = NewSeries(SERIES_TYPE_DTYPE, r.series.Name(), d) return } diff --git a/generic_ewm.go b/generic_ewm.go index 140d108..640cb8d 100644 --- a/generic_ewm.go +++ b/generic_ewm.go @@ -1,11 +1,10 @@ package pandas import ( + "gitee.com/quant1x/pandas/stat" "math" ) -type DType = float64 - type AlphaType int // https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.ewm.html @@ -22,25 +21,25 @@ const ( // EW (Factor) 指数加权(EW)计算Alpha 结构属性非0即为有效启动同名算法 type EW struct { - Com float64 // 根据质心指定衰减 - Span float64 // 根据跨度指定衰减 - Halflife float64 // 根据半衰期指定衰减 - Alpha float64 // 直接指定的平滑因子α - Adjust bool // 除以期初的衰减调整系数以核算 相对权重的不平衡(将 EWMA 视为移动平均线) - IgnoreNA bool // 计算权重时忽略缺失值 - Callback func(idx int) DType + Com stat.DType // 根据质心指定衰减 + Span stat.DType // 根据跨度指定衰减 + Halflife stat.DType // 根据半衰期指定衰减 + Alpha stat.DType // 直接指定的平滑因子α + Adjust bool // 除以期初的衰减调整系数以核算 相对权重的不平衡(将 EWMA 视为移动平均线) + IgnoreNA bool // 计算权重时忽略缺失值 + Callback func(idx int) stat.DType } // ExponentialMovingWindow 加权移动窗口 type ExponentialMovingWindow struct { - data Series // 序列 - atype AlphaType // 计算方式: com/span/halflefe/alpha - param DType // 参数类型为浮点 - adjust bool // 默认为真, 是否调整, 默认真时, 计算序列的EW移动平均线, 为假时, 计算指数加权递归 - ignoreNA bool // 默认为假, 计算权重时是否忽略缺失值NaN - minPeriods int // 默认为0, 窗口中具有值所需的最小观测值数,否则结果为NaN - axis int // {0,1}, 默认为0, 0跨行计算, 1跨列计算 - cb func(idx int) DType + data Series // 序列 + atype AlphaType // 计算方式: com/span/halflefe/alpha + param stat.DType // 参数类型为浮点 + adjust bool // 默认为真, 是否调整, 默认真时, 计算序列的EW移动平均线, 为假时, 计算指数加权递归 + ignoreNA bool // 默认为假, 计算权重时是否忽略缺失值NaN + minPeriods int // 默认为0, 窗口中具有值所需的最小观测值数,否则结果为NaN + axis int // {0,1}, 默认为0, 0跨行计算, 1跨列计算 + cb func(idx int) stat.DType } // EWM provides exponential weighted calculations. @@ -75,7 +74,7 @@ func (s *NDFrame) EWM(alpha EW) ExponentialMovingWindow { } func (w ExponentialMovingWindow) Mean() Series { - var alpha DType + var alpha stat.DType switch w.atype { case AlphaNil: @@ -106,7 +105,7 @@ func (w ExponentialMovingWindow) Mean() Series { return w.applyMean(w.data, alpha) } -func (w ExponentialMovingWindow) applyMean(data Series, alpha DType) Series { +func (w ExponentialMovingWindow) applyMean(data Series, alpha stat.DType) Series { if w.adjust { w.adjustedMean(data, alpha, w.ignoreNA) } else { @@ -115,11 +114,11 @@ func (w ExponentialMovingWindow) applyMean(data Series, alpha DType) Series { return data } -func (w ExponentialMovingWindow) adjustedMean(data Series, alpha DType, ignoreNA bool) { +func (w ExponentialMovingWindow) adjustedMean(data Series, alpha stat.DType, ignoreNA bool) { var ( - values = data.Values().([]float64) - weight DType = 1 - last = values[0] + values = data.Values().([]stat.DType) + weight stat.DType = 1 + last = values[0] ) alpha = 1 - alpha @@ -127,7 +126,7 @@ func (w ExponentialMovingWindow) adjustedMean(data Series, alpha DType, ignoreNA w := alpha*weight + 1 x := values[t] - if Float64IsNaN(x) { + if stat.DTypeIsNaN(x) { if ignoreNA { weight = w } @@ -141,15 +140,15 @@ func (w ExponentialMovingWindow) adjustedMean(data Series, alpha DType, ignoreNA } } -func (w ExponentialMovingWindow) notadjustedMean(data Series, alpha DType, ignoreNA bool) { +func (w ExponentialMovingWindow) notadjustedMean(data Series, alpha stat.DType, ignoreNA bool) { hasCallback := false - if Float64IsNaN(alpha) { + if stat.DTypeIsNaN(alpha) { hasCallback = true alpha = w.cb(0) } var ( count int - values = data.Values().([]float64) + values = data.Values().([]stat.DType) beta = 1 - alpha last = values[0] ) @@ -160,7 +159,7 @@ func (w ExponentialMovingWindow) notadjustedMean(data Series, alpha DType, ignor for t := 1; t < len(values); t++ { x := values[t] - if Float64IsNaN(x) { + if stat.DTypeIsNaN(x) { values[t] = last continue } @@ -170,7 +169,7 @@ func (w ExponentialMovingWindow) notadjustedMean(data Series, alpha DType, ignor } // yt = (1−α)*y(t−1) + α*x(t) last = (beta * last) + (alpha * x) - if Float64IsNaN(last) { + if stat.DTypeIsNaN(last) { last = values[t-1] } values[t] = last diff --git a/generic_number.go b/generic_number.go index 243467d..0887659 100644 --- a/generic_number.go +++ b/generic_number.go @@ -23,7 +23,7 @@ type Number64 interface { ~int64 | ~uint64 | float64 | int | uint } -// NumberOfCPUBits The number of CPU bits is related +// NumberOfCPUBitsRelated The number of CPU bits is related type NumberOfCPUBitsRelated interface { ~int | ~uint | ~uintptr } @@ -100,7 +100,7 @@ func Mean[T Number](x []T) float64 { } // any转number -func value_to_number[T Number](v any, nil2t T, bool2t func(b bool) T, string2t func(s string, v any) T) T { +func valueToNumber[T Number](v any, nil2t T, bool2t func(b bool) T, string2t func(s string, v any) T) T { switch val := v.(type) { case nil: // 这个地方判断nil值 return nil2t @@ -137,7 +137,7 @@ func value_to_number[T Number](v any, nil2t T, bool2t func(b bool) T, string2t f } // 指针转number -func point_to_number[T Number](v any, nil2t T, bool2t func(b bool) T, string2t func(s string, v any) T) T { +func pointToNumber[T Number](v any, nil2t T, bool2t func(b bool) T, string2t func(s string, v any) T) T { switch val := v.(type) { case *int8: if val == nil { diff --git a/generic_rolling.go b/generic_rolling.go index 72762a6..8c0494d 100644 --- a/generic_rolling.go +++ b/generic_rolling.go @@ -7,22 +7,21 @@ import ( // RollingAndExpandingMixin 滚动和扩展静态横切 type RollingAndExpandingMixin struct { - window []float32 + window []stat.DType series Series } // Rolling RollingAndExpandingMixin func (self *NDFrame) Rolling(param any) RollingAndExpandingMixin { - var N []float32 + var N []stat.DType switch v := param.(type) { case int: - N = stat.Repeat[float32](float32(v), self.Len()) - case []float32: - N = stat.Align(v, Nil2Float32, self.Len()) + N = stat.Repeat[stat.DType](stat.DType(v), self.Len()) + case []stat.DType: + N = stat.Align(v, stat.DTypeNaN, self.Len()) case Series: - vs := v.Values() - N = SliceToFloat32(vs) - N = stat.Align(N, Nil2Float32, self.Len()) + vs := v.DTypes() + N = stat.Align(vs, stat.DTypeNaN, self.Len()) default: panic(exception.New(1, "error window")) } @@ -36,7 +35,7 @@ func (self *NDFrame) Rolling(param any) RollingAndExpandingMixin { func (r RollingAndExpandingMixin) getBlocks() (blocks []Series) { for i := 0; i < r.series.Len(); i++ { N := r.window[i] - if Float32IsNaN(N) || int(N) > i+1 { + if stat.DTypeIsNaN(N) || int(N) > i+1 { blocks = append(blocks, r.series.Empty()) continue } @@ -50,11 +49,11 @@ func (r RollingAndExpandingMixin) getBlocks() (blocks []Series) { } // Apply 接受一个回调 -func (r RollingAndExpandingMixin) Apply(f func(S Series, N float32) float32) (s Series) { +func (r RollingAndExpandingMixin) Apply(f func(S Series, N stat.DType) stat.DType) (s Series) { s = r.series.Empty() for i, block := range r.getBlocks() { if block.Len() == 0 { - s.Append(Nil2Float32) + s.Append(stat.DTypeNaN) continue } v := f(block, r.window[i]) diff --git a/generic_sum.go b/generic_sum.go index 4266956..5136622 100644 --- a/generic_sum.go +++ b/generic_sum.go @@ -2,7 +2,7 @@ package pandas import "gitee.com/quant1x/pandas/stat" -func (self *NDFrame) Sum() float64 { - fs := ToFloat64(self) +func (self *NDFrame) Sum() stat.DType { + fs := self.DTypes() return stat.Sum(fs) } diff --git a/rolling_mean.go b/rolling_mean.go index b0405c5..52a4ed5 100644 --- a/rolling_mean.go +++ b/rolling_mean.go @@ -1,11 +1,13 @@ package pandas +import "gitee.com/quant1x/pandas/stat" + // Mean returns the rolling mean. func (r RollingAndExpandingMixin) Mean() (s Series) { - var d []float64 + var d []stat.DType for _, block := range r.getBlocks() { d = append(d, block.Mean()) } - s = NewSeries(SERIES_TYPE_FLOAT64, r.series.Name(), d) + s = NewSeries(SERIES_TYPE_DTYPE, r.series.Name(), d) return } diff --git a/rolling_sum.go b/rolling_sum.go index 3e4d479..fc3a289 100644 --- a/rolling_sum.go +++ b/rolling_sum.go @@ -1,10 +1,12 @@ package pandas +import "gitee.com/quant1x/pandas/stat" + func (r RollingAndExpandingMixin) Sum() Series { - var d []float64 + var d []stat.DType for _, block := range r.getBlocks() { d = append(d, block.Sum()) } - s := NewSeries(SERIES_TYPE_FLOAT64, r.series.Name(), d) + s := NewSeries(SERIES_TYPE_DTYPE, r.series.Name(), d) return s } diff --git a/rolling_v1.go b/rolling_v1.go index ad88d37..32f14f5 100644 --- a/rolling_v1.go +++ b/rolling_v1.go @@ -1,12 +1,14 @@ package pandas // RollingWindowV1 is used for rolling window calculations. +// Deprecated: 使用RollingAndExpandingMixin type RollingWindowV1 struct { window int series Series } // RollingV1 滑动窗口 +// Deprecated: 使用RollingAndExpandingMixin func (self *NDFrame) RollingV1(window int) RollingWindowV1 { return RollingWindowV1{ window: window, diff --git a/series.go b/series.go index 0ca39e7..95b4620 100644 --- a/series.go +++ b/series.go @@ -2,6 +2,7 @@ package pandas import ( "fmt" + "gitee.com/quant1x/pandas/stat" "reflect" ) @@ -16,7 +17,8 @@ const ( SERIES_TYPE_INT64 = reflect.Int64 // int64 SERIES_TYPE_FLOAT32 = reflect.Float32 // float32 SERIES_TYPE_FLOAT64 = reflect.Float64 // float64 - SERIES_TYPE_STRING = reflect.String // string + SERIES_TYPE_DTYPE = SERIES_TYPE_FLOAT64 + SERIES_TYPE_STRING = reflect.String // string ) // StringFormatter is used to convert a value @@ -39,6 +41,8 @@ type Series interface { NaN() any // Float 强制转成[]float32 Float() []float32 + // DTypes 强制转[]stat.DType + DTypes() []stat.DType // sort.Interface @@ -63,13 +67,14 @@ type Series interface { // 使用可选的时间频率按所需的周期数移动索引. Shift(periods int) Series // RollingV1 creates new RollingWindowV1 + // Deprecated: 使用RollingAndExpandingMixin RollingV1(window int) RollingWindowV1 // Rolling 序列化版本 Rolling(param any) RollingAndExpandingMixin // Mean calculates the average value of a series - Mean() float64 + Mean() stat.DType // StdDev calculates the standard deviation of a series - StdDev() float64 + StdDev() stat.DType // FillNa Fill NA/NaN values using the specified method. FillNa(v any, inplace bool) // Max 找出最大值 @@ -79,7 +84,7 @@ type Series interface { // Select 选取一段记录 Select(r Range) Series // Append 增加一批记录 - Append(values ...interface{}) + Append(values ...any) // Apply 接受一个回调函数 Apply(f func(idx int, v any)) // Diff 元素的第一个离散差 @@ -87,9 +92,9 @@ type Series interface { // Ref 引用其它周期的数据 Ref(param any) (s Series) // Std 计算标准差 - Std() float64 + Std() stat.DType // Sum 计算累和 - Sum() float64 + Sum() stat.DType // EWM Provide exponentially weighted (EW) calculations. // // Exactly one of ``com``, ``span``, ``halflife``, or ``alpha`` must be diff --git a/stat/builtin.go b/stat/builtin.go index f9c0d5d..3a49319 100644 --- a/stat/builtin.go +++ b/stat/builtin.go @@ -3,6 +3,8 @@ package stat import ( "github.com/viterin/vek" "math" + "reflect" + "strings" ) var ( @@ -10,6 +12,12 @@ var ( Nil2Float64 = float64(0) // Nil2Float32 nil指针转换float32 Nil2Float32 = float32(0) + DTypeNaN = DType(0) +) + +var ( + // IgnoreParseExceptions 忽略解析异常 + IgnoreParseExceptions bool = true ) // 初始化 avx2 @@ -20,14 +28,31 @@ func init() { Nil2Float64 = math.NaN() // 这个转换是对的, NaN对float32也有效 Nil2Float32 = float32(Nil2Float64) + DTypeNaN = DType(Nil2Float64) } -// Float32IsNaN 判断float32是否NaN -func Float32IsNaN(f float32) bool { - return Float64IsNaN(float64(f)) +// 从指针/地址提取值 +// Extract value from pointer +func extraceValueFromPointer(v any) (any, bool) { + vv := reflect.ValueOf(v) + if vv.Kind() == reflect.Pointer { + if vv.IsNil() { + return nil, true + } + ve := vv.Elem() + return ve.Interface(), true + } + return v, false + + //kind := reflect.ValueOf(v).Kind() + //return nil, reflect.Pointer == kind } -// Float64IsNaN 判断float64是否NaN -func Float64IsNaN(f float64) bool { - return math.IsNaN(f) || math.IsInf(f, 0) +// IsEmpty Code to test if string is empty +func IsEmpty(s string) bool { + if strings.TrimSpace(s) == "" { + return true + } else { + return false + } } diff --git a/stat/errors.go b/stat/errors.go new file mode 100644 index 0000000..7dcbec7 --- /dev/null +++ b/stat/errors.go @@ -0,0 +1,12 @@ +package stat + +import "gitee.com/quant1x/pandas/exception" + +const ( + errorTypeBase = 0 +) + +var ( + // 不支持的类型 + ErrUnsupportedType = exception.New(errorTypeBase+0, "Unsupported type") +) diff --git a/stat/type.go b/stat/type.go index 1ee3dc0..da0d0e3 100644 --- a/stat/type.go +++ b/stat/type.go @@ -2,6 +2,7 @@ package stat import ( "math" + "math/big" "reflect" ) @@ -13,10 +14,37 @@ type StatType interface { ~int32 | ~int64 | ~float32 | ~float64 } +type BigFloat = big.Float // 预留将来可能扩展float + +type Number8 interface { + ~int8 | ~uint8 +} + +type Number16 interface { + ~int16 | ~uint16 +} + +type Number32 interface { + ~int32 | ~uint32 | float32 +} + +type Number64 interface { + ~int64 | ~uint64 | float64 | int | uint +} + type MoveType interface { StatType | ~bool | ~string } +type Integer interface { + Number8 | Number16 | Number32 | Number64 +} + +// Number int和uint的长度取决于CPU是多少位 +type Number interface { + Integer | Float +} + // 随便输入一个什么值 func typeDefault[T StatType](x T) T { xv := reflect.ValueOf(x) @@ -29,3 +57,40 @@ func typeDefault[T StatType](x T) T { } return T(0) } + +// any转number +func valueToNumber[T Number](v any, nil2t T, bool2t func(b bool) T, string2t func(s string, v any) T) T { + switch val := v.(type) { + case nil: // 这个地方判断nil值 + return nil2t + case int8: + return T(val) + case uint8: + return T(val) + case int16: + return T(val) + case uint16: + return T(val) + case int32: + return T(val) + case uint32: + return T(val) + case int64: + return T(val) + case uint64: + return T(val) + case int: + return T(val) + case uint: + return T(val) + case float32: + return T(val) + case float64: + return T(val) + case bool: + return bool2t(val) + case string: + return string2t(val, v) + } + return T(0) +} diff --git a/stat/type_bool.go b/stat/type_bool.go new file mode 100644 index 0000000..4e1dd82 --- /dev/null +++ b/stat/type_bool.go @@ -0,0 +1,53 @@ +package stat + +const ( + Nil2Bool = false // 空指针转bool + BoolNaN = false // bool 无效值 + True2Bool = true // true转bool + False2Bool = false // false 转bool + True2Float32 float32 = float32(1) // true转float32 + False2Float32 float32 = float32(0) // false转float32 + + StringBad2Bool = false // 字符串解析bool异常 + StringTrue2Bool = true // 字符串true转bool + StringFalse2Bool = false // 字符串false转bool +) + +func isTrue(s string) bool { + if s == "true" || s == "TRUE" || s == "True" || s == "1" || s == "真" || s == "对" || s == "好" { + return true + } else { + return false + } +} + +func isFalse(s string) bool { + if s == "false" || s == "FALSE" || s == "False" || s == "0" || s == "假" || s == "错" || s == "坏" { + return true + } else { + return false + } +} + +func boolToInt64(b bool) int64 { + if b { + return True2Int64 + } + return False2Int64 +} + +// bool转float32 +func boolToFloat32(b bool) float32 { + if b { + return True2Float32 + } + return False2Float32 +} + +// bool转float64 +func boolToFloat64(b bool) float64 { + if b { + return True2Float64 + } + return False2Float64 +} diff --git a/stat/type_dtype.go b/stat/type_dtype.go new file mode 100644 index 0000000..c87da20 --- /dev/null +++ b/stat/type_dtype.go @@ -0,0 +1,18 @@ +package stat + +type DType = float64 + +// DTypeIsNaN 判断DType是否NaN +func DTypeIsNaN(d DType) bool { + return Float64IsNaN(d) +} + +// Slice2DType 切片转DType +func Slice2DType(v any) []DType { + return SliceToFloat64(v) +} + +// Any2DType any转DType +func Any2DType(v any) DType { + return AnyToFloat64(v) +} diff --git a/stat/type_dtype_test.go b/stat/type_dtype_test.go new file mode 100644 index 0000000..e7ef7fb --- /dev/null +++ b/stat/type_dtype_test.go @@ -0,0 +1,385 @@ +package stat + +import ( + "fmt" + "testing" +) + +func TestSlice2DType(t *testing.T) { + // +} + +func Test_point_to_1float32(t *testing.T) { + var p1 *int8 + f1 := AnyToFloat32(p1) + fmt.Printf("*int8 to float32=%f\n", f1) + + var v1 int8 = 1 + p1 = &v1 + f1 = AnyToFloat32(p1) + fmt.Printf("*int8 to float32=%f\n", f1) +} + +func Test_point_to_float32(t *testing.T) { + type args struct { + v any + } + + // 指针声明 + var pInt8 *int8 + var pUint8 *uint8 + var pInt16 *int16 + var pUint16 *uint16 + var pInt32 *int32 + var pUint32 *uint32 + var pInt64 *int64 + var pUint64 *uint64 + var pInt *int + var pUint *uint + var pFloat32 *float32 + var pFloat64 *float64 + var pBoolTrue *bool + var pBoolFalse *bool + var pStr1 *string + var pStr2 *string + + vInt8 := int8(1) + pInt8 = &vInt8 + vUint8 := uint8(1) + pUint8 = &vUint8 + + vInt16 := int16(1) + pInt16 = &vInt16 + vUint16 := uint16(1) + pUint16 = &vUint16 + + vInt32 := int32(1) + pInt32 = &vInt32 + vUint32 := uint32(1) + pUint32 = &vUint32 + + vInt64 := int64(1) + pInt64 = &vInt64 + vUint64 := uint64(1) + pUint64 = &vUint64 + + vInt := int(1) + pInt = &vInt + vUint := uint(1) + pUint = &vUint + + vFloat32 := float32(1) + pFloat32 = &vFloat32 + vFloat64 := float64(1) + pFloat64 = &vFloat64 + + vBoolTrue := true + pBoolTrue = &vBoolTrue + vBoolFalse := false + pBoolFalse = &vBoolFalse + + vStr1 := "abc" + pStr1 = &vStr1 + vStr2 := "1.23" + pStr2 = &vStr2 + + tests := []struct { + name string + args args + want float32 + }{ + { + name: "T01: int8", + args: args{ + pInt8, + }, + want: float32(1), + }, + { + name: "T02: uint8", + args: args{ + pUint8, + }, + want: float32(1), + }, + { + name: "T03: int16", + args: args{ + pInt16, + }, + want: float32(1), + }, + { + name: "T04: uint16", + args: args{ + pUint16, + }, + want: float32(1), + }, + { + name: "T05: int32", + args: args{ + pInt32, + }, + want: float32(1), + }, + { + name: "T06: uint32", + args: args{ + pUint32, + }, + want: float32(1), + }, + { + name: "T07: int64", + args: args{ + pInt64, + }, + want: float32(1), + }, + { + name: "T08: uint64", + args: args{ + pUint64, + }, + want: float32(1), + }, + { + name: "T09: int", + args: args{ + pInt, + }, + want: float32(1), + }, + { + name: "T10: uint", + args: args{ + pUint, + }, + want: float32(1), + }, + { + name: "T11: float32", + args: args{ + pFloat32, + }, + want: float32(1), + }, + { + name: "T12: float64", + args: args{ + pFloat64, + }, + want: float32(1), + }, + { + name: "T13: true", + args: args{ + pBoolTrue, + }, + want: float32(1), + }, + { + name: "T14: false", + args: args{ + pBoolFalse, + }, + want: float32(0), + }, + { + name: "T15: str1", + args: args{ + pStr1, + }, + want: Nil2Float32, + }, + { + name: "T16: str2", + args: args{ + pStr2, + }, + want: float32(1.23), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + //got := point_to_float32(tt.args.v) + //if got != tt.want { + // if !IsNaN(float64(tt.want)) { + // t.Errorf("point_to_float32() = %v, want %v", got, tt.want) + // } else if !IsNaN(float64(got)) { + // t.Errorf("point_to_float32() = %v, want %v", got, tt.want) + // } + //} + if got := AnyToFloat32(tt.args.v); got != tt.want && !(Float64IsNaN(float64(tt.want)) && Float64IsNaN(float64(got))) { + t.Errorf("AnyToFloat32() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_value_to_float32(t *testing.T) { + type args struct { + v any + } + + vInt8 := int8(1) + vUint8 := uint8(1) + + vInt16 := int16(1) + vUint16 := uint16(1) + + vInt32 := int32(1) + vUint32 := uint32(1) + + vInt64 := int64(1) + vUint64 := uint64(1) + + vInt := int(1) + vUint := uint(1) + + vFloat32 := float32(1) + vFloat64 := float64(1) + + //vBoolTrue := true + //vBoolFalse := false + + vStr1 := "abc" + vStr2 := "1.23" + + // 组装测试用例 + tests := []struct { + name string + args args + want float32 + }{ + { + name: "T01: int8", + args: args{ + vInt8, + }, + want: float32(1), + }, + { + name: "T02: uint8", + args: args{ + vUint8, + }, + want: float32(1), + }, + { + name: "T03: int16", + args: args{ + vInt16, + }, + want: float32(1), + }, + { + name: "T04: uint16", + args: args{ + vUint16, + }, + want: float32(1), + }, + { + name: "T05: int32", + args: args{ + vInt32, + }, + want: float32(1), + }, + { + name: "T06: uint32", + args: args{ + vUint32, + }, + want: float32(1), + }, + { + name: "T07: int64", + args: args{ + vInt64, + }, + want: float32(1), + }, + { + name: "T08: uint64", + args: args{ + vUint64, + }, + want: float32(1), + }, + { + name: "T09: int", + args: args{ + vInt, + }, + want: float32(1), + }, + { + name: "T10: uint", + args: args{ + vUint, + }, + want: float32(1), + }, + { + name: "T11: float32", + args: args{ + vFloat32, + }, + want: float32(1), + }, + { + name: "T12: float64", + args: args{ + vFloat64, + }, + want: float32(1), + }, + { + name: "T13: true", + args: args{ + true, + }, + want: float32(1), + }, + { + name: "T14: false", + args: args{ + false, + }, + want: float32(0), + }, + { + name: "T15: str1", + args: args{ + vStr1, + }, + want: Nil2Float32, + }, + { + name: "T16: str2", + args: args{ + vStr2, + }, + want: float32(1.23), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + //got := value_to_float32(tt.args.v) + //if got != tt.want { + // if !IsNaN(float64(tt.want)) { + // t.Errorf("value_to_float32() = %v, want %v", got, tt.want) + // } else if !IsNaN(float64(got)) { + // t.Errorf("value_to_float32() = %v, want %v", got, tt.want) + // } + //} + if got := AnyToFloat32(tt.args.v); !(got == tt.want || (Float64IsNaN(float64(tt.want)) && Float64IsNaN(float64(got)))) { + t.Errorf("AnyToFloat32() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/stat/type_float32.go b/stat/type_float32.go new file mode 100644 index 0000000..dd70c57 --- /dev/null +++ b/stat/type_float32.go @@ -0,0 +1,133 @@ +package stat + +import ( + "fmt" + "gitee.com/quant1x/pandas/exception" + "github.com/mymmsc/gox/logger" + "github.com/viterin/vek/vek32" + "golang.org/x/exp/slices" + "math" + "reflect" + "strconv" +) + +const ( + errorFloat32Base = errorTypeBase + int(reflect.Float32)*100 +) + +const ( + MaxFloat32 = float32(math.MaxFloat32) // float32最大值 + MinFloat32 = float32(math.SmallestNonzeroFloat32) // float32最小值 + StringTrue2Float32 float32 = float32(1) // 字符串true转float32 + StringFalse2Float32 float32 = float32(0) // 字符串false转float32 +) + +// Float32IsNaN 判断float32是否NaN +func Float32IsNaN(f float32) bool { + return Float64IsNaN(float64(f)) +} + +// 普通的处理方式, 将切片强制转换成float32 +func slice_any_to_float32[T Number](s []T) []float32 { + count := len(s) + if count == 0 { + return []float32{} + } + d := make([]float32, count) + for idx, iv := range s { + // 强制转换 + d[idx] = float32(iv) + } + return d +} + +// SliceToFloat32 any输入只能是一维slice或者数组 +func SliceToFloat32(v any) []float32 { + var vs []float32 + switch values := v.(type) { + case []int8: + return slice_any_to_float32(values) + case []uint8: + return slice_any_to_float32(values) + case []int16: + return slice_any_to_float32(values) + case []uint16: + return slice_any_to_float32(values) + case []int32: // 加速 + return vek32.FromInt32(values) + case []uint32: + return slice_any_to_float32(values) + case []int64: // 加速 + return vek32.FromInt64(values) + case []uint64: + return slice_any_to_float32(values) + case []int: + return slice_any_to_float32(values) + case []uint: + return slice_any_to_float32(values) + case []float32: // 克隆 + return slices.Clone(values) + case []float64: // 加速 + return vek32.FromFloat64(values) + case []bool: + count := len(values) + if count == 0 { + return []float32{} + } + // 加速 + return vek32.FromBool(values) + case []string: + count := len(values) + if count == 0 { + return []float32{} + } + vs = make([]float32, count) + for idx, iv := range values { + vs[idx] = float32(AnyToFloat64(iv)) + } + default: + vv := reflect.ValueOf(v) + vk := vv.Kind() + panic(exception.New(errorFloat32Base+0, fmt.Sprintf("Unsupported type: %s", vk.String()))) + } + return []float32{} +} + +// ParseFloat32 字符串转float32 +func ParseFloat32(s string, v any) float32 { + defer func() { + // 解析失败以后输出日志, 以备检查 + if err := recover(); err != nil { + logger.Errorf("ParseFloat32 %+v, error=%+v\n", v, err) + } + }() + + if IsEmpty(s) { + // TODO:NaN是针对64位, 这样直接转换应该有问题, 需要进一步确认 + return Nil2Float32 + } + if isTrue(s) { + return StringTrue2Float32 + } else if isFalse(s) { + return StringFalse2Float32 + } + + f, err := strconv.ParseFloat(s, 32) + if err == nil { + return float32(f) + } + if IgnoreParseExceptions { + return Nil2Float32 + } + _ = v.(float32) // Intentionally panic + return Nil2Float32 +} + +func AnyToFloat32(v any) float32 { + if vv, ok := extraceValueFromPointer(v); ok { + v = vv + } + + f := valueToNumber(v, Nil2Float32, boolToFloat32, ParseFloat32) + return f +} diff --git a/stat/type_float64.go b/stat/type_float64.go new file mode 100644 index 0000000..fb58c23 --- /dev/null +++ b/stat/type_float64.go @@ -0,0 +1,131 @@ +package stat + +import ( + "fmt" + "gitee.com/quant1x/pandas/exception" + "github.com/mymmsc/gox/logger" + "github.com/viterin/vek" + "golang.org/x/exp/slices" + "math" + "reflect" + "strconv" +) + +const ( + errorFloat64Base = errorTypeBase + int(reflect.Float64)*100 + + MaxFloat64 float64 = float64(math.MaxFloat64) // float64最大值 + MinFloat64 float64 = float64(math.SmallestNonzeroFloat64) // float64最小值 + True2Float64 float64 = float64(1) // true转float64 + False2Float64 float64 = float64(0) // false转float64 + StringNil2Float float64 = float64(0) // deprecated: 字符串空指针转float64 + StringBad2Float float64 = float64(0) // deprecated: 字符串解析float64异常 + StringTrue2Float64 float64 = float64(1) // 字符串true转float64 + StringFalse2Float64 float64 = float64(0) // 字符串false转float64 +) + +// Float64IsNaN 判断float64是否NaN +func Float64IsNaN(f float64) bool { + return math.IsNaN(f) || math.IsInf(f, 0) +} + +func slice_any_to_float64[T Number](s []T) []float64 { + count := len(s) + if count == 0 { + return []float64{} + } + d := make([]float64, count) + for idx, iv := range s { + d[idx] = float64(iv) + } + return d +} + +// ParseFloat64 字符串转float64 +// 任意组合的nan字符串都会被解析成NaN +func ParseFloat64(s string, v any) float64 { + defer func() { + // 解析失败以后输出日志, 以备检查 + if err := recover(); err != nil { + logger.Errorf("ParseFloat64 %+v, error=%+v\n", v, err) + } + }() + if IsEmpty(s) { + return Nil2Float64 + } + if isTrue(s) { + return StringTrue2Float64 + } else if isFalse(s) { + return StringFalse2Float64 + } + f, err := strconv.ParseFloat(s, 64) + if err == nil { + return f + } + if IgnoreParseExceptions { + return Nil2Float64 + } + _ = v.(float64) // Intentionally panic + return Nil2Float64 +} + +func AnyToFloat64(v any) float64 { + if vv, ok := extraceValueFromPointer(v); ok { + v = vv + } + + f := valueToNumber(v, Nil2Float64, boolToFloat64, ParseFloat64) + return f +} + +// SliceToFloat64 any输入只能是一维slice或者数组 +func SliceToFloat64(v any) []float64 { + var vs []float64 + switch values := v.(type) { + case []int8: + return slice_any_to_float64(values) + case []uint8: + return slice_any_to_float64(values) + case []int16: + return slice_any_to_float64(values) + case []uint16: + return slice_any_to_float64(values) + case []int32: // 加速 + return vek.FromInt32(values) + case []uint32: + return slice_any_to_float64(values) + case []int64: // 加速 + return vek.FromInt64(values) + case []uint64: + return slice_any_to_float64(values) + case []int: + return slice_any_to_float64(values) + case []uint: + return slice_any_to_float64(values) + case []float32: // 加速 + return vek.FromFloat32(values) + case []float64: // 克隆 + return slices.Clone(values) + case []bool: + count := len(values) + if count == 0 { + return []float64{} + } + // 加速 + return vek.FromBool(values) + case []string: + count := len(values) + if count == 0 { + return []float64{} + } + vs = make([]float64, count) + for idx, iv := range values { + vs[idx] = AnyToFloat64(iv) + } + default: + vv := reflect.ValueOf(v) + vk := vv.Kind() + panic(exception.New(errorFloat64Base+0, fmt.Sprintf("Unsupported type: %s", vk.String()))) + } + return []float64{} +} diff --git a/stat/type_int64.go b/stat/type_int64.go new file mode 100644 index 0000000..741fd8d --- /dev/null +++ b/stat/type_int64.go @@ -0,0 +1,15 @@ +package stat + +import "math" + +const ( + MaxInt64 = int64(math.MaxInt64) + MinInt64 = int64(math.MinInt64) + Nil2Int64 = int64(0) // 空指针转int64 + Int64NaN = int64(0) // int64 无效值 + True2Int64 = int64(1) // true转int64 + False2Int64 = int64(0) // false 转int64 + StringBad2Int64 = int64(0) // 字符串解析int64异常 + StringTrue2Int64 = int64(1) // 字符串true转int64 + StringFalse2Int64 = int64(0) // 字符串false转int64 +) diff --git a/type_float32.go b/type_float32.go index ffbab61..4244e91 100644 --- a/type_float32.go +++ b/type_float32.go @@ -67,8 +67,8 @@ func AnyToFloat32(v any) float32 { // v = vv.Elem() // vv.Float() //} - return point_to_number[float32](v, Nil2Float32, boolToFloat32, ParseFloat32) + return pointToNumber[float32](v, Nil2Float32, boolToFloat32, ParseFloat32) } - f := value_to_number[float32](v, Nil2Float32, boolToFloat32, ParseFloat32) + f := valueToNumber[float32](v, Nil2Float32, boolToFloat32, ParseFloat32) return f } diff --git a/type_float64.go b/type_float64.go index 8408bf7..a2c0e78 100644 --- a/type_float64.go +++ b/type_float64.go @@ -61,8 +61,8 @@ func float64ToString(v float64) string { func AnyToFloat64(v any) float64 { if isPoint(v) { - return point_to_number[float64](v, Nil2Float64, boolToFloat64, ParseFloat64) + return pointToNumber[float64](v, Nil2Float64, boolToFloat64, ParseFloat64) } - f := value_to_number[float64](v, Nil2Float64, boolToFloat64, ParseFloat64) + f := valueToNumber[float64](v, Nil2Float64, boolToFloat64, ParseFloat64) return f } diff --git a/type_int64.go b/type_int64.go index aedf6fb..b92bee5 100644 --- a/type_int64.go +++ b/type_int64.go @@ -63,8 +63,8 @@ func int64ToString(v int64) string { // AnyToInt64 any转换int64 func AnyToInt64(v any) int64 { if isPoint(v) { - return point_to_number[int64](v, Nil2Int64, boolToInt64, ParseInt64) + return pointToNumber[int64](v, Nil2Int64, boolToInt64, ParseInt64) } - f := value_to_number[int64](v, Nil2Int64, boolToInt64, ParseInt64) + f := valueToNumber[int64](v, Nil2Int64, boolToInt64, ParseInt64) return f } -- Gitee From 747860a50c1763a79e736bee532fc72898a75ae9 Mon Sep 17 00:00:00 2001 From: wangfeng Date: Wed, 8 Feb 2023 13:29:22 +0800 Subject: [PATCH 2/2] =?UTF-8?q?#I6DLC4=20=E5=AE=9E=E7=8E=B0BARSLAST?= =?UTF-8?q?=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- formula/README.md | 2 +- formula/barslast.go | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/formula/README.md b/formula/README.md index deffd8a..e576b57 100644 --- a/formula/README.md +++ b/formula/README.md @@ -34,7 +34,7 @@ formula | 1 | EVERY | EVERY(CLOSE>O,5),最近N天是否都是True | EVERY(CLOSE>LOW,5) | [X] | [X] | | 1 | EXIST | EXIST(CLOSE>O,5),最近N天是否都是True | EXIST(CLOSE>LOW,5) | [X] | [X] | | 1 | FILTER | FILTER函数,S满足条件后,将其后N周期内的数据置为0 | FILTER(CLOSE>LOW,5) | [X] | [X] | -| 1 | BARSLAST | 上一次条件成立到当前的周期数 | BARSLAST(X) | [X] | [X] | +| 1 | BARSLAST | 上一次条件成立到当前的周期数 | BARSLAST(X) | [√] | [√] | | 1 | BARSLASTCOUNT | 统计连续满足S条件的周期数 | BARSLASTCOUNT(X) | [X] | [X] | | 1 | BARSSINCEN | N周期内第一次S条件成立到现在的周期数 | BARSSINCEN(S,N) | [X] | [X] | | 1 | CROSS | 判断向上金叉穿越,两个序列互换就是判断向下死叉穿越 | CROSS(MA(C,5),MA(C,10)) | [X] | [X] | diff --git a/formula/barslast.go b/formula/barslast.go index a779fb8..fd7ba80 100644 --- a/formula/barslast.go +++ b/formula/barslast.go @@ -5,13 +5,13 @@ import ( "gitee.com/quant1x/pandas/stat" ) -// 为了测试SMA,BARSLAST必须要先实现, 给SMA提供序列换参数, 以便验证, python那边还没实现 -func barslast(S pandas.Series) []float32 { - fs := pandas.ToFloat32(S) - as := stat.Repeat[float32](1, S.Len()) - bs := stat.Repeat[float32](0, S.Len()) +// BARSLAST 为了测试SMA,BARSLAST必须要先实现, 给SMA提供序列换参数, 以便验证, python那边还没实现 +func BARSLAST(S pandas.Series) []stat.DType { + fs := S.DTypes() + as := stat.Repeat[stat.DType](1, S.Len()) + bs := stat.Repeat[stat.DType](0, S.Len()) x := stat.Where(fs, as, bs) - M := []float32{0} + M := []stat.DType{0} M = append(M, x...) for i := 1; i < len(M); i++ { if int(M[i]) != 0 { @@ -19,7 +19,6 @@ func barslast(S pandas.Series) []float32 { } else { M[i] = M[i-1] + 1 } - //fmt.Println(M[i]) } return M[1:] } -- Gitee