summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--calc.go61
-rw-r--r--calc_test.go91
-rw-r--r--numfmt.go6
-rw-r--r--xmlWorkbook.go2
4 files changed, 138 insertions, 22 deletions
diff --git a/calc.go b/calc.go
index b0876a8..d921c35 100644
--- a/calc.go
+++ b/calc.go
@@ -549,6 +549,7 @@ type formulaFuncs struct {
// MINUTE
// MIRR
// MOD
+// MODE
// MONTH
// MROUND
// MULTINOMIAL
@@ -673,6 +674,7 @@ type formulaFuncs struct {
// TRIMMEAN
// TRUE
// TRUNC
+// T.TEST
// TTEST
// TYPE
// UNICHAR
@@ -7974,6 +7976,51 @@ func (fn *formulaFuncs) LOGNORMDIST(argsList *list.List) formulaArg {
return fn.NORMSDIST(args)
}
+// MODE function returns the statistical mode (the most frequently occurring
+// value) of a list of supplied numbers. If there are 2 or more most
+// frequently occurring values in the supplied data, the function returns the
+// lowest of these values The syntax of the function is:
+//
+// MODE(number1,[number2],...)
+//
+func (fn *formulaFuncs) MODE(argsList *list.List) formulaArg {
+ if argsList.Len() < 1 {
+ return newErrorFormulaArg(formulaErrorVALUE, "MODE requires at least 1 argument")
+ }
+ var values []float64
+ for arg := argsList.Front(); arg != nil; arg = arg.Next() {
+ cells := arg.Value.(formulaArg)
+ if cells.Type != ArgMatrix && cells.ToNumber().Type != ArgNumber {
+ return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
+ }
+ for _, cell := range cells.ToList() {
+ if num := cell.ToNumber(); num.Type == ArgNumber {
+ values = append(values, num.Number)
+ }
+ }
+ }
+ sort.Float64s(values)
+ cnt := len(values)
+ var count, modeCnt int
+ var mode float64
+ for i := 0; i < cnt; i++ {
+ count = 0
+ for j := 0; j < cnt; j++ {
+ if j != i && values[j] == values[i] {
+ count++
+ }
+ }
+ if count > modeCnt {
+ modeCnt = count
+ mode = values[i]
+ }
+ }
+ if modeCnt == 0 {
+ return newErrorFormulaArg(formulaErrorNA, formulaErrorNA)
+ }
+ return newNumberFormulaArg(mode)
+}
+
// NEGBINOMdotDIST function calculates the probability mass function or the
// cumulative distribution function for the Negative Binomial Distribution.
// This gives the probability that there will be a given number of failures
@@ -9342,6 +9389,20 @@ func (fn *formulaFuncs) TTEST(argsList *list.List) formulaArg {
return fn.tTest(array1.Matrix, array2.Matrix, tails.Number, typeArg.Number)
}
+// TdotTEST function calculates the probability associated with the Student's T
+// Test, which is commonly used for identifying whether two data sets are
+// likely to have come from the same two underlying populations with the same
+// mean. The syntax of the function is:
+//
+// T.TEST(array1,array2,tails,type)
+//
+func (fn *formulaFuncs) TdotTEST(argsList *list.List) formulaArg {
+ if argsList.Len() != 4 {
+ return newErrorFormulaArg(formulaErrorVALUE, "T.TEST requires 4 arguments")
+ }
+ return fn.TTEST(argsList)
+}
+
// TRIMMEAN function calculates the trimmed mean (or truncated mean) of a
// supplied set of values. The syntax of the function is:
//
diff --git a/calc_test.go b/calc_test.go
index 9b8b226..f6499de 100644
--- a/calc_test.go
+++ b/calc_test.go
@@ -4865,6 +4865,43 @@ func TestCalcISFORMULA(t *testing.T) {
}
}
+func TestCalcMODE(t *testing.T) {
+ cellData := [][]interface{}{
+ {1, 1},
+ {1, 1},
+ {2, 2},
+ {2, 2},
+ {3, 2},
+ {3},
+ {3},
+ {4},
+ {4},
+ {4},
+ }
+ f := prepareCalcData(cellData)
+ formulaList := map[string]string{
+ "=MODE(A1:A10)": "3",
+ "=MODE(B1:B6)": "2",
+ }
+ 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)
+ }
+ calcError := map[string]string{
+ "=MODE()": "MODE requires at least 1 argument",
+ "=MODE(0,\"\")": "#VALUE!",
+ "=MODE(D1:D3)": "#N/A",
+ }
+ for formula, expected := range calcError {
+ assert.NoError(t, f.SetCellFormula("Sheet1", "C1", formula))
+ result, err := f.CalcCellValue("Sheet1", "C1")
+ assert.EqualError(t, err, expected, formula)
+ assert.Equal(t, "", result, formula)
+ }
+}
+
func TestCalcSHEET(t *testing.T) {
f := NewFile()
f.NewSheet("Sheet2")
@@ -4914,12 +4951,18 @@ func TestCalcTTEST(t *testing.T) {
}
f := prepareCalcData(cellData)
formulaList := map[string]string{
- "=TTEST(A1:A12,B1:B12,1,1)": "0.44907068944428",
- "=TTEST(A1:A12,B1:B12,1,2)": "0.436717306029283",
- "=TTEST(A1:A12,B1:B12,1,3)": "0.436722015384755",
- "=TTEST(A1:A12,B1:B12,2,1)": "0.898141378888559",
- "=TTEST(A1:A12,B1:B12,2,2)": "0.873434612058567",
- "=TTEST(A1:A12,B1:B12,2,3)": "0.873444030769511",
+ "=TTEST(A1:A12,B1:B12,1,1)": "0.44907068944428",
+ "=TTEST(A1:A12,B1:B12,1,2)": "0.436717306029283",
+ "=TTEST(A1:A12,B1:B12,1,3)": "0.436722015384755",
+ "=TTEST(A1:A12,B1:B12,2,1)": "0.898141378888559",
+ "=TTEST(A1:A12,B1:B12,2,2)": "0.873434612058567",
+ "=TTEST(A1:A12,B1:B12,2,3)": "0.873444030769511",
+ "=T.TEST(A1:A12,B1:B12,1,1)": "0.44907068944428",
+ "=T.TEST(A1:A12,B1:B12,1,2)": "0.436717306029283",
+ "=T.TEST(A1:A12,B1:B12,1,3)": "0.436722015384755",
+ "=T.TEST(A1:A12,B1:B12,2,1)": "0.898141378888559",
+ "=T.TEST(A1:A12,B1:B12,2,2)": "0.873434612058567",
+ "=T.TEST(A1:A12,B1:B12,2,3)": "0.873444030769511",
}
for formula, expected := range formulaList {
assert.NoError(t, f.SetCellFormula("Sheet1", "C1", formula))
@@ -4928,18 +4971,30 @@ func TestCalcTTEST(t *testing.T) {
assert.Equal(t, expected, result, formula)
}
calcError := map[string]string{
- "=TTEST()": "TTEST requires 4 arguments",
- "=TTEST(\"\",B1:B12,1,1)": "#NUM!",
- "=TTEST(A1:A12,\"\",1,1)": "#NUM!",
- "=TTEST(A1:A12,B1:B12,\"\",1)": "strconv.ParseFloat: parsing \"\": invalid syntax",
- "=TTEST(A1:A12,B1:B12,1,\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax",
- "=TTEST(A1:A12,B1:B12,0,1)": "#NUM!",
- "=TTEST(A1:A12,B1:B12,1,0)": "#NUM!",
- "=TTEST(A1:A2,B1:B1,1,1)": "#N/A",
- "=TTEST(A13:A14,B13:B14,1,1)": "#NUM!",
- "=TTEST(A12:A13,B12:B13,1,1)": "#DIV/0!",
- "=TTEST(A13:A14,B13:B14,1,2)": "#NUM!",
- "=TTEST(D1:D4,E1:E4,1,3)": "#NUM!",
+ "=TTEST()": "TTEST requires 4 arguments",
+ "=TTEST(\"\",B1:B12,1,1)": "#NUM!",
+ "=TTEST(A1:A12,\"\",1,1)": "#NUM!",
+ "=TTEST(A1:A12,B1:B12,\"\",1)": "strconv.ParseFloat: parsing \"\": invalid syntax",
+ "=TTEST(A1:A12,B1:B12,1,\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax",
+ "=TTEST(A1:A12,B1:B12,0,1)": "#NUM!",
+ "=TTEST(A1:A12,B1:B12,1,0)": "#NUM!",
+ "=TTEST(A1:A2,B1:B1,1,1)": "#N/A",
+ "=TTEST(A13:A14,B13:B14,1,1)": "#NUM!",
+ "=TTEST(A12:A13,B12:B13,1,1)": "#DIV/0!",
+ "=TTEST(A13:A14,B13:B14,1,2)": "#NUM!",
+ "=TTEST(D1:D4,E1:E4,1,3)": "#NUM!",
+ "=T.TEST()": "T.TEST requires 4 arguments",
+ "=T.TEST(\"\",B1:B12,1,1)": "#NUM!",
+ "=T.TEST(A1:A12,\"\",1,1)": "#NUM!",
+ "=T.TEST(A1:A12,B1:B12,\"\",1)": "strconv.ParseFloat: parsing \"\": invalid syntax",
+ "=T.TEST(A1:A12,B1:B12,1,\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax",
+ "=T.TEST(A1:A12,B1:B12,0,1)": "#NUM!",
+ "=T.TEST(A1:A12,B1:B12,1,0)": "#NUM!",
+ "=T.TEST(A1:A2,B1:B1,1,1)": "#N/A",
+ "=T.TEST(A13:A14,B13:B14,1,1)": "#NUM!",
+ "=T.TEST(A12:A13,B12:B13,1,1)": "#DIV/0!",
+ "=T.TEST(A13:A14,B13:B14,1,2)": "#NUM!",
+ "=T.TEST(D1:D4,E1:E4,1,3)": "#NUM!",
}
for formula, expected := range calcError {
assert.NoError(t, f.SetCellFormula("Sheet1", "C1", formula))
diff --git a/numfmt.go b/numfmt.go
index b48c36a..6cb7fc7 100644
--- a/numfmt.go
+++ b/numfmt.go
@@ -33,9 +33,9 @@ type languageInfo struct {
type numberFormat struct {
section []nfp.Section
t time.Time
- sectionIdx int
- isNumeric, hours, seconds bool
- number float64
+ sectionIdx int
+ isNumeric, hours, seconds bool
+ number float64
ap, afterPoint, beforePoint, localCode, result, value, valueSectionType string
}
diff --git a/xmlWorkbook.go b/xmlWorkbook.go
index a500a34..a0fce15 100644
--- a/xmlWorkbook.go
+++ b/xmlWorkbook.go
@@ -40,9 +40,9 @@ type xlsxWorkbook struct {
FileVersion *xlsxFileVersion `xml:"fileVersion"`
FileSharing *xlsxExtLst `xml:"fileSharing"`
WorkbookPr *xlsxWorkbookPr `xml:"workbookPr"`
- WorkbookProtection *xlsxWorkbookProtection `xml:"workbookProtection"`
AlternateContent *xlsxAlternateContent `xml:"mc:AlternateContent"`
DecodeAlternateContent *xlsxInnerXML `xml:"http://schemas.openxmlformats.org/markup-compatibility/2006 AlternateContent"`
+ WorkbookProtection *xlsxWorkbookProtection `xml:"workbookProtection"`
BookViews *xlsxBookViews `xml:"bookViews"`
Sheets xlsxSheets `xml:"sheets"`
FunctionGroups *xlsxExtLst `xml:"functionGroups"`