Skip to content

Commit 6fa950a

Browse files
committed
ref qax-os#65, new formula functions: SKEW.P and SLOPE, remove no-required format default
1 parent 5a27932 commit 6fa950a

File tree

6 files changed

+160
-35
lines changed

6 files changed

+160
-35
lines changed

calc.go

+60-14
Original file line numberDiff line numberDiff line change
@@ -640,7 +640,9 @@ type formulaFuncs struct {
640640
// SIN
641641
// SINH
642642
// SKEW
643+
// SKEW.P
643644
// SLN
645+
// SLOPE
644646
// SMALL
645647
// SQRT
646648
// SQRTPI
@@ -8860,14 +8862,20 @@ func (fn *formulaFuncs) min(mina bool, argsList *list.List) formulaArg {
88608862
return newNumberFormulaArg(min)
88618863
}
88628864

8863-
// pearsonProduct is an implementation of the formula functions PEARSON and
8864-
// RSQ.
8865+
// pearsonProduct is an implementation of the formula functions PEARSON, RSQ
8866+
// and SLOPE.
88658867
func (fn *formulaFuncs) pearsonProduct(name string, argsList *list.List) formulaArg {
88668868
if argsList.Len() != 2 {
88678869
return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires 2 arguments", name))
88688870
}
8869-
array1 := argsList.Front().Value.(formulaArg).ToList()
8870-
array2 := argsList.Back().Value.(formulaArg).ToList()
8871+
var array1, array2 []formulaArg
8872+
if name == "SLOPE" {
8873+
array1 = argsList.Back().Value.(formulaArg).ToList()
8874+
array2 = argsList.Front().Value.(formulaArg).ToList()
8875+
} else {
8876+
array1 = argsList.Front().Value.(formulaArg).ToList()
8877+
array2 = argsList.Back().Value.(formulaArg).ToList()
8878+
}
88718879
if len(array1) != len(array2) {
88728880
return newErrorFormulaArg(formulaErrorNA, formulaErrorNA)
88738881
}
@@ -8898,7 +8906,10 @@ func (fn *formulaFuncs) pearsonProduct(name string, argsList *list.List) formula
88988906
if name == "RSQ" {
88998907
return newNumberFormulaArg(math.Pow(sum/math.Sqrt(deltaX*deltaY), 2))
89008908
}
8901-
return newNumberFormulaArg(sum / math.Sqrt(deltaX*deltaY))
8909+
if name == "PEARSON" {
8910+
return newNumberFormulaArg(sum / math.Sqrt(deltaX*deltaY))
8911+
}
8912+
return newNumberFormulaArg(sum / deltaX)
89028913
}
89038914

89048915
// PEARSON function calculates the Pearson Product-Moment Correlation
@@ -9268,16 +9279,19 @@ func (fn *formulaFuncs) RSQ(argsList *list.List) formulaArg {
92689279
return fn.pearsonProduct("RSQ", argsList)
92699280
}
92709281

9271-
// SKEW function calculates the skewness of the distribution of a supplied set
9272-
// of values. The syntax of the function is:
9273-
//
9274-
// SKEW(number1,[number2],...)
9275-
//
9276-
func (fn *formulaFuncs) SKEW(argsList *list.List) formulaArg {
9282+
// skew is an implementation of the formula functions SKEW and SKEW.P.
9283+
func (fn *formulaFuncs) skew(name string, argsList *list.List) formulaArg {
92779284
if argsList.Len() < 1 {
9278-
return newErrorFormulaArg(formulaErrorVALUE, "SKEW requires at least 1 argument")
9285+
return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires at least 1 argument", name))
9286+
}
9287+
mean := fn.AVERAGE(argsList)
9288+
var stdDev formulaArg
9289+
var count, summer float64
9290+
if name == "SKEW" {
9291+
stdDev = fn.STDEV(argsList)
9292+
} else {
9293+
stdDev = fn.STDEVP(argsList)
92799294
}
9280-
mean, stdDev, count, summer := fn.AVERAGE(argsList), fn.STDEV(argsList), 0.0, 0.0
92819295
for arg := argsList.Front(); arg != nil; arg = arg.Next() {
92829296
token := arg.Value.(formulaArg)
92839297
switch token.Type {
@@ -9300,11 +9314,43 @@ func (fn *formulaFuncs) SKEW(argsList *list.List) formulaArg {
93009314
}
93019315
}
93029316
if count > 2 {
9303-
return newNumberFormulaArg(summer * (count / ((count - 1) * (count - 2))))
9317+
if name == "SKEW" {
9318+
return newNumberFormulaArg(summer * (count / ((count - 1) * (count - 2))))
9319+
}
9320+
return newNumberFormulaArg(summer / count)
93049321
}
93059322
return newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)
93069323
}
93079324

9325+
// SKEW function calculates the skewness of the distribution of a supplied set
9326+
// of values. The syntax of the function is:
9327+
//
9328+
// SKEW(number1,[number2],...)
9329+
//
9330+
func (fn *formulaFuncs) SKEW(argsList *list.List) formulaArg {
9331+
return fn.skew("SKEW", argsList)
9332+
}
9333+
9334+
// SKEWdotP function calculates the skewness of the distribution of a supplied
9335+
// set of values. The syntax of the function is:
9336+
//
9337+
// SKEW.P(number1,[number2],...)
9338+
//
9339+
func (fn *formulaFuncs) SKEWdotP(argsList *list.List) formulaArg {
9340+
return fn.skew("SKEW.P", argsList)
9341+
}
9342+
9343+
// SLOPE returns the slope of the linear regression line through data points in
9344+
// known_y's and known_x's. The slope is the vertical distance divided by the
9345+
// horizontal distance between any two points on the line, which is the rate
9346+
// of change along the regression line. The syntax of the function is:
9347+
//
9348+
// SLOPE(known_y's,known_x's)
9349+
//
9350+
func (fn *formulaFuncs) SLOPE(argsList *list.List) formulaArg {
9351+
return fn.pearsonProduct("SLOPE", argsList)
9352+
}
9353+
93089354
// SMALL function returns the k'th smallest value from an array of numeric
93099355
// values. The syntax of the function is:
93109356
//

calc_test.go

+97
Original file line numberDiff line numberDiff line change
@@ -1157,6 +1157,12 @@ func TestCalcCellValue(t *testing.T) {
11571157
"=SKEW(1,2,3,4,3)": "-0.404796008910937",
11581158
"=SKEW(A1:B2)": "0",
11591159
"=SKEW(A1:D3)": "0",
1160+
// SKEW.P
1161+
"=SKEW.P(1,2,3,4,3)": "-0.27154541788364",
1162+
"=SKEW.P(A1:B2)": "0",
1163+
"=SKEW.P(A1:D3)": "0",
1164+
// SLOPE
1165+
"=SLOPE(A1:A4,B1:B4)": "1",
11601166
// SMALL
11611167
"=SMALL(A1:A5,1)": "0",
11621168
"=SMALL(A1:B5,2)": "1",
@@ -3063,6 +3069,14 @@ func TestCalcCellValue(t *testing.T) {
30633069
"=SKEW()": "SKEW requires at least 1 argument",
30643070
"=SKEW(\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax",
30653071
"=SKEW(0)": "#DIV/0!",
3072+
// SKEW.P
3073+
"=SKEW.P()": "SKEW.P requires at least 1 argument",
3074+
"=SKEW.P(\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax",
3075+
"=SKEW.P(0)": "#DIV/0!",
3076+
// SLOPE
3077+
"=SLOPE()": "SLOPE requires 2 arguments",
3078+
"=SLOPE(A1:A2,B1:B1)": "#N/A",
3079+
"=SLOPE(A4,A4)": "#DIV/0!",
30663080
// SMALL
30673081
"=SMALL()": "SMALL requires 2 arguments",
30683082
"=SMALL(A1:A5,0)": "k should be > 0",
@@ -4968,6 +4982,89 @@ func TestCalcMODE(t *testing.T) {
49684982
}
49694983
}
49704984

4985+
func TestCalcPEARSON(t *testing.T) {
4986+
cellData := [][]interface{}{
4987+
{"x", "y"},
4988+
{1, 10.11},
4989+
{2, 22.9},
4990+
{2, 27.61},
4991+
{3, 27.61},
4992+
{4, 11.15},
4993+
{5, 31.08},
4994+
{6, 37.9},
4995+
{7, 33.49},
4996+
{8, 21.05},
4997+
{9, 27.01},
4998+
{10, 45.78},
4999+
{11, 31.32},
5000+
{12, 50.57},
5001+
{13, 45.48},
5002+
{14, 40.94},
5003+
{15, 53.76},
5004+
{16, 36.18},
5005+
{17, 49.77},
5006+
{18, 55.66},
5007+
{19, 63.83},
5008+
{20, 63.6},
5009+
}
5010+
f := prepareCalcData(cellData)
5011+
formulaList := map[string]string{
5012+
"=PEARSON(A2:A22,B2:B22)": "0.864129542184994",
5013+
}
5014+
for formula, expected := range formulaList {
5015+
assert.NoError(t, f.SetCellFormula("Sheet1", "C1", formula))
5016+
result, err := f.CalcCellValue("Sheet1", "C1")
5017+
assert.NoError(t, err, formula)
5018+
assert.Equal(t, expected, result, formula)
5019+
}
5020+
}
5021+
5022+
func TestCalcRSQ(t *testing.T) {
5023+
cellData := [][]interface{}{
5024+
{"known_y's", "known_x's"},
5025+
{2, 22.9},
5026+
{7, 33.49},
5027+
{8, 34.5},
5028+
{3, 27.61},
5029+
{4, 19.5},
5030+
{1, 10.11},
5031+
{6, 37.9},
5032+
{5, 31.08},
5033+
}
5034+
f := prepareCalcData(cellData)
5035+
formulaList := map[string]string{
5036+
"=RSQ(A2:A9,B2:B9)": "0.711666290486784",
5037+
}
5038+
for formula, expected := range formulaList {
5039+
assert.NoError(t, f.SetCellFormula("Sheet1", "C1", formula))
5040+
result, err := f.CalcCellValue("Sheet1", "C1")
5041+
assert.NoError(t, err, formula)
5042+
assert.Equal(t, expected, result, formula)
5043+
}
5044+
}
5045+
5046+
func TestCalcSLOP(t *testing.T) {
5047+
cellData := [][]interface{}{
5048+
{"known_x's", "known_y's"},
5049+
{1, 3},
5050+
{2, 7},
5051+
{3, 17},
5052+
{4, 20},
5053+
{5, 20},
5054+
{6, 27},
5055+
}
5056+
f := prepareCalcData(cellData)
5057+
formulaList := map[string]string{
5058+
"=SLOPE(A2:A7,B2:B7)": "0.200826446280992",
5059+
}
5060+
for formula, expected := range formulaList {
5061+
assert.NoError(t, f.SetCellFormula("Sheet1", "C1", formula))
5062+
result, err := f.CalcCellValue("Sheet1", "C1")
5063+
assert.NoError(t, err, formula)
5064+
assert.Equal(t, expected, result, formula)
5065+
}
5066+
}
5067+
49715068
func TestCalcSHEET(t *testing.T) {
49725069
f := NewFile()
49735070
f.NewSheet("Sheet2")

chart.go

+1-6
Original file line numberDiff line numberDiff line change
@@ -479,16 +479,11 @@ func parseFormatChartSet(formatSet string) (*formatChart, error) {
479479
},
480480
Format: formatPicture{
481481
FPrintsWithSheet: true,
482-
FLocksWithSheet: false,
483-
NoChangeAspect: false,
484-
OffsetX: 0,
485-
OffsetY: 0,
486482
XScale: 1.0,
487483
YScale: 1.0,
488484
},
489485
Legend: formatChartLegend{
490-
Position: "bottom",
491-
ShowLegendKey: false,
486+
Position: "bottom",
492487
},
493488
Title: formatChartTitle{
494489
Name: " ",

picture.go

-5
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,6 @@ import (
3131
func parseFormatPictureSet(formatSet string) (*formatPicture, error) {
3232
format := formatPicture{
3333
FPrintsWithSheet: true,
34-
FLocksWithSheet: false,
35-
NoChangeAspect: false,
36-
Autofit: false,
37-
OffsetX: 0,
38-
OffsetY: 0,
3934
XScale: 1.0,
4035
YScale: 1.0,
4136
}

shape.go

+1-6
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,10 @@ func parseFormatShapeSet(formatSet string) (*formatShape, error) {
2525
Height: 160,
2626
Format: formatPicture{
2727
FPrintsWithSheet: true,
28-
FLocksWithSheet: false,
29-
NoChangeAspect: false,
30-
OffsetX: 0,
31-
OffsetY: 0,
3228
XScale: 1.0,
3329
YScale: 1.0,
3430
},
35-
Line: formatLine{Width: 1},
36-
Macro: "",
31+
Line: formatLine{Width: 1},
3732
}
3833
err := json.Unmarshal([]byte(formatSet), &format)
3934
return &format, err

table.go

+1-4
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,7 @@ import (
2323
// parseFormatTableSet provides a function to parse the format settings of the
2424
// table with default value.
2525
func parseFormatTableSet(formatSet string) (*formatTable, error) {
26-
format := formatTable{
27-
TableStyle: "",
28-
ShowRowStripes: true,
29-
}
26+
format := formatTable{ShowRowStripes: true}
3027
err := json.Unmarshal(parseFormatSet(formatSet), &format)
3128
return &format, err
3229
}

0 commit comments

Comments
 (0)