summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxuri <xuri.me@gmail.com>2022-04-16 13:53:16 +0800
committerxuri <xuri.me@gmail.com>2022-04-16 13:53:16 +0800
commit6fa950a4f852bd45b81c941877732ec516dcc673 (patch)
tree2abd993aeb119331646bab9c9f66c2978b9f82da
parent5a279321bb494141fb12ac010a33da4a78c6a309 (diff)
ref #65, new formula functions: SKEW.P and SLOPE, remove no-required format default
-rw-r--r--calc.go74
-rw-r--r--calc_test.go97
-rw-r--r--chart.go7
-rw-r--r--picture.go5
-rw-r--r--shape.go7
-rw-r--r--table.go5
6 files changed, 160 insertions, 35 deletions
diff --git a/calc.go b/calc.go
index a90bbc3..03d467b 100644
--- a/calc.go
+++ b/calc.go
@@ -640,7 +640,9 @@ type formulaFuncs struct {
// SIN
// SINH
// SKEW
+// SKEW.P
// SLN
+// SLOPE
// SMALL
// SQRT
// SQRTPI
@@ -8860,14 +8862,20 @@ func (fn *formulaFuncs) min(mina bool, argsList *list.List) formulaArg {
return newNumberFormulaArg(min)
}
-// pearsonProduct is an implementation of the formula functions PEARSON and
-// RSQ.
+// pearsonProduct is an implementation of the formula functions PEARSON, RSQ
+// and SLOPE.
func (fn *formulaFuncs) pearsonProduct(name string, argsList *list.List) formulaArg {
if argsList.Len() != 2 {
return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires 2 arguments", name))
}
- array1 := argsList.Front().Value.(formulaArg).ToList()
- array2 := argsList.Back().Value.(formulaArg).ToList()
+ var array1, array2 []formulaArg
+ if name == "SLOPE" {
+ array1 = argsList.Back().Value.(formulaArg).ToList()
+ array2 = argsList.Front().Value.(formulaArg).ToList()
+ } else {
+ array1 = argsList.Front().Value.(formulaArg).ToList()
+ array2 = argsList.Back().Value.(formulaArg).ToList()
+ }
if len(array1) != len(array2) {
return newErrorFormulaArg(formulaErrorNA, formulaErrorNA)
}
@@ -8898,7 +8906,10 @@ func (fn *formulaFuncs) pearsonProduct(name string, argsList *list.List) formula
if name == "RSQ" {
return newNumberFormulaArg(math.Pow(sum/math.Sqrt(deltaX*deltaY), 2))
}
- return newNumberFormulaArg(sum / math.Sqrt(deltaX*deltaY))
+ if name == "PEARSON" {
+ return newNumberFormulaArg(sum / math.Sqrt(deltaX*deltaY))
+ }
+ return newNumberFormulaArg(sum / deltaX)
}
// PEARSON function calculates the Pearson Product-Moment Correlation
@@ -9268,16 +9279,19 @@ func (fn *formulaFuncs) RSQ(argsList *list.List) formulaArg {
return fn.pearsonProduct("RSQ", argsList)
}
-// SKEW function calculates the skewness of the distribution of a supplied set
-// of values. The syntax of the function is:
-//
-// SKEW(number1,[number2],...)
-//
-func (fn *formulaFuncs) SKEW(argsList *list.List) formulaArg {
+// skew is an implementation of the formula functions SKEW and SKEW.P.
+func (fn *formulaFuncs) skew(name string, argsList *list.List) formulaArg {
if argsList.Len() < 1 {
- return newErrorFormulaArg(formulaErrorVALUE, "SKEW requires at least 1 argument")
+ return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires at least 1 argument", name))
+ }
+ mean := fn.AVERAGE(argsList)
+ var stdDev formulaArg
+ var count, summer float64
+ if name == "SKEW" {
+ stdDev = fn.STDEV(argsList)
+ } else {
+ stdDev = fn.STDEVP(argsList)
}
- mean, stdDev, count, summer := fn.AVERAGE(argsList), fn.STDEV(argsList), 0.0, 0.0
for arg := argsList.Front(); arg != nil; arg = arg.Next() {
token := arg.Value.(formulaArg)
switch token.Type {
@@ -9300,11 +9314,43 @@ func (fn *formulaFuncs) SKEW(argsList *list.List) formulaArg {
}
}
if count > 2 {
- return newNumberFormulaArg(summer * (count / ((count - 1) * (count - 2))))
+ if name == "SKEW" {
+ return newNumberFormulaArg(summer * (count / ((count - 1) * (count - 2))))
+ }
+ return newNumberFormulaArg(summer / count)
}
return newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)
}
+// SKEW function calculates the skewness of the distribution of a supplied set
+// of values. The syntax of the function is:
+//
+// SKEW(number1,[number2],...)
+//
+func (fn *formulaFuncs) SKEW(argsList *list.List) formulaArg {
+ return fn.skew("SKEW", argsList)
+}
+
+// SKEWdotP function calculates the skewness of the distribution of a supplied
+// set of values. The syntax of the function is:
+//
+// SKEW.P(number1,[number2],...)
+//
+func (fn *formulaFuncs) SKEWdotP(argsList *list.List) formulaArg {
+ return fn.skew("SKEW.P", argsList)
+}
+
+// SLOPE returns the slope of the linear regression line through data points in
+// known_y's and known_x's. The slope is the vertical distance divided by the
+// horizontal distance between any two points on the line, which is the rate
+// of change along the regression line. The syntax of the function is:
+//
+// SLOPE(known_y's,known_x's)
+//
+func (fn *formulaFuncs) SLOPE(argsList *list.List) formulaArg {
+ return fn.pearsonProduct("SLOPE", argsList)
+}
+
// SMALL function returns the k'th smallest value from an array of numeric
// values. The syntax of the function is:
//
diff --git a/calc_test.go b/calc_test.go
index a7f88b0..6cae4a3 100644
--- a/calc_test.go
+++ b/calc_test.go
@@ -1157,6 +1157,12 @@ func TestCalcCellValue(t *testing.T) {
"=SKEW(1,2,3,4,3)": "-0.404796008910937",
"=SKEW(A1:B2)": "0",
"=SKEW(A1:D3)": "0",
+ // SKEW.P
+ "=SKEW.P(1,2,3,4,3)": "-0.27154541788364",
+ "=SKEW.P(A1:B2)": "0",
+ "=SKEW.P(A1:D3)": "0",
+ // SLOPE
+ "=SLOPE(A1:A4,B1:B4)": "1",
// SMALL
"=SMALL(A1:A5,1)": "0",
"=SMALL(A1:B5,2)": "1",
@@ -3063,6 +3069,14 @@ func TestCalcCellValue(t *testing.T) {
"=SKEW()": "SKEW requires at least 1 argument",
"=SKEW(\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax",
"=SKEW(0)": "#DIV/0!",
+ // SKEW.P
+ "=SKEW.P()": "SKEW.P requires at least 1 argument",
+ "=SKEW.P(\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax",
+ "=SKEW.P(0)": "#DIV/0!",
+ // SLOPE
+ "=SLOPE()": "SLOPE requires 2 arguments",
+ "=SLOPE(A1:A2,B1:B1)": "#N/A",
+ "=SLOPE(A4,A4)": "#DIV/0!",
// SMALL
"=SMALL()": "SMALL requires 2 arguments",
"=SMALL(A1:A5,0)": "k should be > 0",
@@ -4968,6 +4982,89 @@ func TestCalcMODE(t *testing.T) {
}
}
+func TestCalcPEARSON(t *testing.T) {
+ cellData := [][]interface{}{
+ {"x", "y"},
+ {1, 10.11},
+ {2, 22.9},
+ {2, 27.61},
+ {3, 27.61},
+ {4, 11.15},
+ {5, 31.08},
+ {6, 37.9},
+ {7, 33.49},
+ {8, 21.05},
+ {9, 27.01},
+ {10, 45.78},
+ {11, 31.32},
+ {12, 50.57},
+ {13, 45.48},
+ {14, 40.94},
+ {15, 53.76},
+ {16, 36.18},
+ {17, 49.77},
+ {18, 55.66},
+ {19, 63.83},
+ {20, 63.6},
+ }
+ f := prepareCalcData(cellData)
+ formulaList := map[string]string{
+ "=PEARSON(A2:A22,B2:B22)": "0.864129542184994",
+ }
+ for formula, expected := range formulaList {
+ assert.NoError(t, f.SetCellFormula("Sheet1", "C1", formula))
+ result, err := f.CalcCellValue("Sheet1", "C1")
+ assert.NoError(t, err, formula)
+ assert.Equal(t, expected, result, formula)
+ }
+}
+
+func TestCalcRSQ(t *testing.T) {
+ cellData := [][]interface{}{
+ {"known_y's", "known_x's"},
+ {2, 22.9},
+ {7, 33.49},
+ {8, 34.5},
+ {3, 27.61},
+ {4, 19.5},
+ {1, 10.11},
+ {6, 37.9},
+ {5, 31.08},
+ }
+ f := prepareCalcData(cellData)
+ formulaList := map[string]string{
+ "=RSQ(A2:A9,B2:B9)": "0.711666290486784",
+ }
+ for formula, expected := range formulaList {
+ assert.NoError(t, f.SetCellFormula("Sheet1", "C1", formula))
+ result, err := f.CalcCellValue("Sheet1", "C1")
+ assert.NoError(t, err, formula)
+ assert.Equal(t, expected, result, formula)
+ }
+}
+
+func TestCalcSLOP(t *testing.T) {
+ cellData := [][]interface{}{
+ {"known_x's", "known_y's"},
+ {1, 3},
+ {2, 7},
+ {3, 17},
+ {4, 20},
+ {5, 20},
+ {6, 27},
+ }
+ f := prepareCalcData(cellData)
+ formulaList := map[string]string{
+ "=SLOPE(A2:A7,B2:B7)": "0.200826446280992",
+ }
+ for formula, expected := range formulaList {
+ assert.NoError(t, f.SetCellFormula("Sheet1", "C1", formula))
+ result, err := f.CalcCellValue("Sheet1", "C1")
+ assert.NoError(t, err, formula)
+ assert.Equal(t, expected, result, formula)
+ }
+}
+
func TestCalcSHEET(t *testing.T) {
f := NewFile()
f.NewSheet("Sheet2")
diff --git a/chart.go b/chart.go
index 8f521fa..f740a2b 100644
--- a/chart.go
+++ b/chart.go
@@ -479,16 +479,11 @@ func parseFormatChartSet(formatSet string) (*formatChart, error) {
},
Format: formatPicture{
FPrintsWithSheet: true,
- FLocksWithSheet: false,
- NoChangeAspect: false,
- OffsetX: 0,
- OffsetY: 0,
XScale: 1.0,
YScale: 1.0,
},
Legend: formatChartLegend{
- Position: "bottom",
- ShowLegendKey: false,
+ Position: "bottom",
},
Title: formatChartTitle{
Name: " ",
diff --git a/picture.go b/picture.go
index 515f15f..919262c 100644
--- a/picture.go
+++ b/picture.go
@@ -31,11 +31,6 @@ import (
func parseFormatPictureSet(formatSet string) (*formatPicture, error) {
format := formatPicture{
FPrintsWithSheet: true,
- FLocksWithSheet: false,
- NoChangeAspect: false,
- Autofit: false,
- OffsetX: 0,
- OffsetY: 0,
XScale: 1.0,
YScale: 1.0,
}
diff --git a/shape.go b/shape.go
index 8aefeea..db76867 100644
--- a/shape.go
+++ b/shape.go
@@ -25,15 +25,10 @@ func parseFormatShapeSet(formatSet string) (*formatShape, error) {
Height: 160,
Format: formatPicture{
FPrintsWithSheet: true,
- FLocksWithSheet: false,
- NoChangeAspect: false,
- OffsetX: 0,
- OffsetY: 0,
XScale: 1.0,
YScale: 1.0,
},
- Line: formatLine{Width: 1},
- Macro: "",
+ Line: formatLine{Width: 1},
}
err := json.Unmarshal([]byte(formatSet), &format)
return &format, err
diff --git a/table.go b/table.go
index 0311a8e..b01c1cb 100644
--- a/table.go
+++ b/table.go
@@ -23,10 +23,7 @@ import (
// parseFormatTableSet provides a function to parse the format settings of the
// table with default value.
func parseFormatTableSet(formatSet string) (*formatTable, error) {
- format := formatTable{
- TableStyle: "",
- ShowRowStripes: true,
- }
+ format := formatTable{ShowRowStripes: true}
err := json.Unmarshal(parseFormatSet(formatSet), &format)
return &format, err
}