ref #65, #1196: fix the compatibility issue and added new formula function

- New formula functions: MODE and T.TEST
pull/2/head
xuri 3 years ago
parent 26174a2c43
commit 5bf4bce9d4
No known key found for this signature in database
GPG Key ID: BA5E5BB1C948EDF7

@ -549,6 +549,7 @@ type formulaFuncs struct {
// MINUTE // MINUTE
// MIRR // MIRR
// MOD // MOD
// MODE
// MONTH // MONTH
// MROUND // MROUND
// MULTINOMIAL // MULTINOMIAL
@ -673,6 +674,7 @@ type formulaFuncs struct {
// TRIMMEAN // TRIMMEAN
// TRUE // TRUE
// TRUNC // TRUNC
// T.TEST
// TTEST // TTEST
// TYPE // TYPE
// UNICHAR // UNICHAR
@ -7974,6 +7976,51 @@ func (fn *formulaFuncs) LOGNORMDIST(argsList *list.List) formulaArg {
return fn.NORMSDIST(args) 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 // NEGBINOMdotDIST function calculates the probability mass function or the
// cumulative distribution function for the Negative Binomial Distribution. // cumulative distribution function for the Negative Binomial Distribution.
// This gives the probability that there will be a given number of failures // 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) 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 // TRIMMEAN function calculates the trimmed mean (or truncated mean) of a
// supplied set of values. The syntax of the function is: // supplied set of values. The syntax of the function is:
// //

@ -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) { func TestCalcSHEET(t *testing.T) {
f := NewFile() f := NewFile()
f.NewSheet("Sheet2") f.NewSheet("Sheet2")
@ -4914,12 +4951,18 @@ func TestCalcTTEST(t *testing.T) {
} }
f := prepareCalcData(cellData) f := prepareCalcData(cellData)
formulaList := map[string]string{ formulaList := map[string]string{
"=TTEST(A1:A12,B1:B12,1,1)": "0.44907068944428", "=TTEST(A1:A12,B1:B12,1,1)": "0.44907068944428",
"=TTEST(A1:A12,B1:B12,1,2)": "0.436717306029283", "=TTEST(A1:A12,B1:B12,1,2)": "0.436717306029283",
"=TTEST(A1:A12,B1:B12,1,3)": "0.436722015384755", "=TTEST(A1:A12,B1:B12,1,3)": "0.436722015384755",
"=TTEST(A1:A12,B1:B12,2,1)": "0.898141378888559", "=TTEST(A1:A12,B1:B12,2,1)": "0.898141378888559",
"=TTEST(A1:A12,B1:B12,2,2)": "0.873434612058567", "=TTEST(A1:A12,B1:B12,2,2)": "0.873434612058567",
"=TTEST(A1:A12,B1:B12,2,3)": "0.873444030769511", "=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 { for formula, expected := range formulaList {
assert.NoError(t, f.SetCellFormula("Sheet1", "C1", formula)) assert.NoError(t, f.SetCellFormula("Sheet1", "C1", formula))
@ -4928,18 +4971,30 @@ func TestCalcTTEST(t *testing.T) {
assert.Equal(t, expected, result, formula) assert.Equal(t, expected, result, formula)
} }
calcError := map[string]string{ calcError := map[string]string{
"=TTEST()": "TTEST requires 4 arguments", "=TTEST()": "TTEST requires 4 arguments",
"=TTEST(\"\",B1:B12,1,1)": "#NUM!", "=TTEST(\"\",B1:B12,1,1)": "#NUM!",
"=TTEST(A1:A12,\"\",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,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,0,1)": "#NUM!",
"=TTEST(A1:A12,B1:B12,1,0)": "#NUM!", "=TTEST(A1:A12,B1:B12,1,0)": "#NUM!",
"=TTEST(A1:A2,B1:B1,1,1)": "#N/A", "=TTEST(A1:A2,B1:B1,1,1)": "#N/A",
"=TTEST(A13:A14,B13:B14,1,1)": "#NUM!", "=TTEST(A13:A14,B13:B14,1,1)": "#NUM!",
"=TTEST(A12:A13,B12:B13,1,1)": "#DIV/0!", "=TTEST(A12:A13,B12:B13,1,1)": "#DIV/0!",
"=TTEST(A13:A14,B13:B14,1,2)": "#NUM!", "=TTEST(A13:A14,B13:B14,1,2)": "#NUM!",
"=TTEST(D1:D4,E1:E4,1,3)": "#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 { for formula, expected := range calcError {
assert.NoError(t, f.SetCellFormula("Sheet1", "C1", formula)) assert.NoError(t, f.SetCellFormula("Sheet1", "C1", formula))

@ -33,9 +33,9 @@ type languageInfo struct {
type numberFormat struct { type numberFormat struct {
section []nfp.Section section []nfp.Section
t time.Time t time.Time
sectionIdx int sectionIdx int
isNumeric, hours, seconds bool isNumeric, hours, seconds bool
number float64 number float64
ap, afterPoint, beforePoint, localCode, result, value, valueSectionType string ap, afterPoint, beforePoint, localCode, result, value, valueSectionType string
} }

@ -40,9 +40,9 @@ type xlsxWorkbook struct {
FileVersion *xlsxFileVersion `xml:"fileVersion"` FileVersion *xlsxFileVersion `xml:"fileVersion"`
FileSharing *xlsxExtLst `xml:"fileSharing"` FileSharing *xlsxExtLst `xml:"fileSharing"`
WorkbookPr *xlsxWorkbookPr `xml:"workbookPr"` WorkbookPr *xlsxWorkbookPr `xml:"workbookPr"`
WorkbookProtection *xlsxWorkbookProtection `xml:"workbookProtection"`
AlternateContent *xlsxAlternateContent `xml:"mc:AlternateContent"` AlternateContent *xlsxAlternateContent `xml:"mc:AlternateContent"`
DecodeAlternateContent *xlsxInnerXML `xml:"http://schemas.openxmlformats.org/markup-compatibility/2006 AlternateContent"` DecodeAlternateContent *xlsxInnerXML `xml:"http://schemas.openxmlformats.org/markup-compatibility/2006 AlternateContent"`
WorkbookProtection *xlsxWorkbookProtection `xml:"workbookProtection"`
BookViews *xlsxBookViews `xml:"bookViews"` BookViews *xlsxBookViews `xml:"bookViews"`
Sheets xlsxSheets `xml:"sheets"` Sheets xlsxSheets `xml:"sheets"`
FunctionGroups *xlsxExtLst `xml:"functionGroups"` FunctionGroups *xlsxExtLst `xml:"functionGroups"`

Loading…
Cancel
Save