ref #65, new formula functions: F.TEST and FTEST
This commit is contained in:
parent
46336bc788
commit
0030e800ca
81
calc.go
81
calc.go
|
@ -443,6 +443,8 @@ type formulaFuncs struct {
|
||||||
// FLOOR.MATH
|
// FLOOR.MATH
|
||||||
// FLOOR.PRECISE
|
// FLOOR.PRECISE
|
||||||
// FORMULATEXT
|
// FORMULATEXT
|
||||||
|
// F.TEST
|
||||||
|
// FTEST
|
||||||
// FV
|
// FV
|
||||||
// FVSCHEDULE
|
// FVSCHEDULE
|
||||||
// GAMMA
|
// GAMMA
|
||||||
|
@ -6468,7 +6470,7 @@ func getGammaContFraction(fA, fX float64) float64 {
|
||||||
fQk = math.Nextafter(f3, f3) - math.Nextafter(f4, f4)
|
fQk = math.Nextafter(f3, f3) - math.Nextafter(f4, f4)
|
||||||
)
|
)
|
||||||
if fQk != 0 {
|
if fQk != 0 {
|
||||||
var fR = fPk / fQk
|
fR := fPk / fQk
|
||||||
bFinished = math.Abs((fApprox-fR)/fR) <= fHalfMachEps
|
bFinished = math.Abs((fApprox-fR)/fR) <= fHalfMachEps
|
||||||
fApprox = fR
|
fApprox = fR
|
||||||
}
|
}
|
||||||
|
@ -6486,8 +6488,8 @@ func getGammaContFraction(fA, fX float64) float64 {
|
||||||
|
|
||||||
// getLogGammaHelper is a part of implementation of the function getLogGamma.
|
// getLogGammaHelper is a part of implementation of the function getLogGamma.
|
||||||
func getLogGammaHelper(fZ float64) float64 {
|
func getLogGammaHelper(fZ float64) float64 {
|
||||||
var _fg = 6.024680040776729583740234375
|
_fg := 6.024680040776729583740234375
|
||||||
var zgHelp = fZ + _fg - 0.5
|
zgHelp := fZ + _fg - 0.5
|
||||||
return math.Log(getLanczosSum(fZ)) + (fZ-0.5)*math.Log(zgHelp) - zgHelp
|
return math.Log(getLanczosSum(fZ)) + (fZ-0.5)*math.Log(zgHelp) - zgHelp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6511,7 +6513,7 @@ func getGammaHelper(fZ float64) float64 {
|
||||||
|
|
||||||
// getLogGamma calculates the natural logarithm of the gamma function.
|
// getLogGamma calculates the natural logarithm of the gamma function.
|
||||||
func getLogGamma(fZ float64) float64 {
|
func getLogGamma(fZ float64) float64 {
|
||||||
var fMaxGammaArgument = 171.624376956302
|
fMaxGammaArgument := 171.624376956302
|
||||||
if fZ >= fMaxGammaArgument {
|
if fZ >= fMaxGammaArgument {
|
||||||
return getLogGammaHelper(fZ)
|
return getLogGammaHelper(fZ)
|
||||||
}
|
}
|
||||||
|
@ -7578,6 +7580,77 @@ func (fn *formulaFuncs) FINV(argsList *list.List) formulaArg {
|
||||||
return newNumberFormulaArg((1/calcBetainv(1-(1-probability.Number), d2.Number/2, d1.Number/2, 0, 1) - 1) * (d2.Number / d1.Number))
|
return newNumberFormulaArg((1/calcBetainv(1-(1-probability.Number), d2.Number/2, d1.Number/2, 0, 1) - 1) * (d2.Number / d1.Number))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FdotTEST function returns the F-Test for two supplied arrays. I.e. the
|
||||||
|
// function returns the two-tailed probability that the variances in the two
|
||||||
|
// supplied arrays are not significantly different. The syntax of the Ftest
|
||||||
|
// function is:
|
||||||
|
//
|
||||||
|
// F.TEST(array1,array2)
|
||||||
|
//
|
||||||
|
func (fn *formulaFuncs) FdotTEST(argsList *list.List) formulaArg {
|
||||||
|
if argsList.Len() != 2 {
|
||||||
|
return newErrorFormulaArg(formulaErrorVALUE, "F.TEST requires 2 arguments")
|
||||||
|
}
|
||||||
|
array1 := argsList.Front().Value.(formulaArg)
|
||||||
|
array2 := argsList.Back().Value.(formulaArg)
|
||||||
|
left, right := array1.ToList(), array2.ToList()
|
||||||
|
collectMatrix := func(args []formulaArg) (n, accu float64) {
|
||||||
|
var p, sum float64
|
||||||
|
for _, arg := range args {
|
||||||
|
if num := arg.ToNumber(); num.Type == ArgNumber {
|
||||||
|
x := num.Number - p
|
||||||
|
y := x / (n + 1)
|
||||||
|
p += y
|
||||||
|
accu += n * x * y
|
||||||
|
n++
|
||||||
|
sum += num.Number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
nums, accu := collectMatrix(left)
|
||||||
|
f3 := nums - 1
|
||||||
|
if nums == 1 {
|
||||||
|
return newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)
|
||||||
|
}
|
||||||
|
f1 := accu / (nums - 1)
|
||||||
|
if f1 == 0 {
|
||||||
|
return newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)
|
||||||
|
}
|
||||||
|
nums, accu = collectMatrix(right)
|
||||||
|
f4 := nums - 1
|
||||||
|
if nums == 1 {
|
||||||
|
return newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)
|
||||||
|
}
|
||||||
|
f2 := accu / (nums - 1)
|
||||||
|
if f2 == 0 {
|
||||||
|
return newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)
|
||||||
|
}
|
||||||
|
args := list.New()
|
||||||
|
args.PushBack(newNumberFormulaArg(f1 / f2))
|
||||||
|
args.PushBack(newNumberFormulaArg(f3))
|
||||||
|
args.PushBack(newNumberFormulaArg(f4))
|
||||||
|
probability := (1 - fn.FDIST(args).Number) * 2
|
||||||
|
if probability > 1 {
|
||||||
|
probability = 2 - probability
|
||||||
|
}
|
||||||
|
return newNumberFormulaArg(probability)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FTEST function returns the F-Test for two supplied arrays. I.e. the function
|
||||||
|
// returns the two-tailed probability that the variances in the two supplied
|
||||||
|
// arrays are not significantly different. The syntax of the Ftest function
|
||||||
|
// is:
|
||||||
|
//
|
||||||
|
// FTEST(array1,array2)
|
||||||
|
//
|
||||||
|
func (fn *formulaFuncs) FTEST(argsList *list.List) formulaArg {
|
||||||
|
if argsList.Len() != 2 {
|
||||||
|
return newErrorFormulaArg(formulaErrorVALUE, "FTEST requires 2 arguments")
|
||||||
|
}
|
||||||
|
return fn.FdotTEST(argsList)
|
||||||
|
}
|
||||||
|
|
||||||
// LOGINV function calculates the inverse of the Cumulative Log-Normal
|
// LOGINV function calculates the inverse of the Cumulative Log-Normal
|
||||||
// Distribution Function of x, for a supplied probability. The syntax of the
|
// Distribution Function of x, for a supplied probability. The syntax of the
|
||||||
// function is:
|
// function is:
|
||||||
|
|
45
calc_test.go
45
calc_test.go
|
@ -4292,6 +4292,51 @@ func TestCalcCHITESTandCHISQdotTEST(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCalcFTEST(t *testing.T) {
|
||||||
|
cellData := [][]interface{}{
|
||||||
|
{"Group 1", "Group 2"},
|
||||||
|
{3.5, 9.2},
|
||||||
|
{4.7, 8.2},
|
||||||
|
{6.2, 7.3},
|
||||||
|
{4.9, 6.1},
|
||||||
|
{3.8, 5.4},
|
||||||
|
{5.5, 7.8},
|
||||||
|
{7.1, 5.9},
|
||||||
|
{6.7, 8.4},
|
||||||
|
{3.9, 7.7},
|
||||||
|
{4.6, 6.6},
|
||||||
|
}
|
||||||
|
f := prepareCalcData(cellData)
|
||||||
|
formulaList := map[string]string{
|
||||||
|
"=FTEST(A2:A11,B2:B11)": "0.95403555939413",
|
||||||
|
"=F.TEST(A2:A11,B2:B11)": "0.95403555939413",
|
||||||
|
}
|
||||||
|
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{
|
||||||
|
"=FTEST()": "FTEST requires 2 arguments",
|
||||||
|
"=FTEST(A2:A2,B2:B2)": "#DIV/0!",
|
||||||
|
"=FTEST(A12:A14,B2:B4)": "#DIV/0!",
|
||||||
|
"=FTEST(A2:A4,B2:B2)": "#DIV/0!",
|
||||||
|
"=FTEST(A2:A4,B12:B14)": "#DIV/0!",
|
||||||
|
"=F.TEST()": "F.TEST requires 2 arguments",
|
||||||
|
"=F.TEST(A2:A2,B2:B2)": "#DIV/0!",
|
||||||
|
"=F.TEST(A12:A14,B2:B4)": "#DIV/0!",
|
||||||
|
"=F.TEST(A2:A4,B2:B2)": "#DIV/0!",
|
||||||
|
"=F.TEST(A2:A4,B12:B14)": "#DIV/0!",
|
||||||
|
}
|
||||||
|
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 TestCalcIRR(t *testing.T) {
|
func TestCalcIRR(t *testing.T) {
|
||||||
cellData := [][]interface{}{{-1}, {0.2}, {0.24}, {0.288}, {0.3456}, {0.4147}}
|
cellData := [][]interface{}{{-1}, {0.2}, {0.24}, {0.288}, {0.3456}, {0.4147}}
|
||||||
f := prepareCalcData(cellData)
|
f := prepareCalcData(cellData)
|
||||||
|
|
Loading…
Reference in New Issue