ref #65, new formula functions: CHITEST and CHISQ.TEST

This commit is contained in:
xuri 2022-03-27 11:53:45 +08:00
parent 17141a9638
commit f8d763d0bd
No known key found for this signature in database
GPG Key ID: BA5E5BB1C948EDF7
3 changed files with 163 additions and 10 deletions

121
calc.go
View File

@ -356,6 +356,8 @@ type formulaFuncs struct {
// CHAR
// CHIDIST
// CHIINV
// CHITEST
// CHISQ.TEST
// CHOOSE
// CLEAN
// CODE
@ -1243,7 +1245,7 @@ func isOperatorPrefixToken(token efp.Token) bool {
return (token.TValue == "-" && token.TType == efp.TokenTypeOperatorPrefix) || (ok && token.TType == efp.TokenTypeOperatorInfix)
}
// isOperand determine if the token is parse operand perand.
// isOperand determine if the token is parse operand.
func isOperand(token efp.Token) bool {
return token.TType == efp.TokenTypeOperand && (token.TSubType == efp.TokenSubTypeNumber || token.TSubType == efp.TokenSubTypeText)
}
@ -4685,7 +4687,7 @@ func (fn *formulaFuncs) SERIESSUM(argsList *list.List) formulaArg {
if num.Type != ArgNumber {
return num
}
result += num.Number * math.Pow(x.Number, (n.Number+(m.Number*float64(i))))
result += num.Number * math.Pow(x.Number, n.Number+(m.Number*i))
i++
}
return newNumberFormulaArg(result)
@ -6224,7 +6226,7 @@ func (fn *formulaFuncs) BINOMdotDISTdotRANGE(argsList *list.List) formulaArg {
return newNumberFormulaArg(sum)
}
// binominv implement inverse of the binomial distribution calcuation.
// binominv implement inverse of the binomial distribution calculation.
func binominv(n, p, alpha float64) float64 {
q, i, sum, max := 1-p, 0.0, 0.0, 0.0
n = math.Floor(n)
@ -6292,11 +6294,55 @@ func (fn *formulaFuncs) CHIDIST(argsList *list.List) formulaArg {
if x.Type != ArgNumber {
return x
}
degress := argsList.Back().Value.(formulaArg).ToNumber()
if degress.Type != ArgNumber {
return degress
degrees := argsList.Back().Value.(formulaArg).ToNumber()
if degrees.Type != ArgNumber {
return degrees
}
return newNumberFormulaArg(1 - (incompleteGamma(degress.Number/2, x.Number/2) / math.Gamma(degress.Number/2)))
logSqrtPi, sqrtPi := math.Log(math.Sqrt(math.Pi)), 1/math.Sqrt(math.Pi)
var e, s, z, c, y float64
a, x1, even := x.Number/2, x.Number, int(degrees.Number)%2 == 0
if degrees.Number > 1 {
y = math.Exp(-a)
}
args := list.New()
args.PushBack(newNumberFormulaArg(-math.Sqrt(x1)))
o := fn.NORMSDIST(args)
s = 2 * o.Number
if even {
s = y
}
if degrees.Number > 2 {
x1 = (degrees.Number - 1) / 2
z = 0.5
if even {
z = 1
}
if a > 20 {
e = logSqrtPi
if even {
e = 0
}
c = math.Log(a)
for z <= x1 {
e = math.Log(z) + e
s += math.Exp(c*z - a - e)
z += 1
}
return newNumberFormulaArg(s)
}
e = sqrtPi / math.Sqrt(a)
if even {
e = 1
}
c = 0
for z <= x1 {
e = e * (a / z)
c = c + e
z += 1
}
return newNumberFormulaArg(c*y + s)
}
return newNumberFormulaArg(s)
}
// CHIINV function calculates the inverse of the right-tailed probability of
@ -6325,6 +6371,65 @@ func (fn *formulaFuncs) CHIINV(argsList *list.List) formulaArg {
return newNumberFormulaArg(gammainv(1-probability.Number, 0.5*deg.Number, 2.0))
}
// CHITEST function uses the chi-square test to calculate the probability that
// the differences between two supplied data sets (of observed and expected
// frequencies), are likely to be simply due to sampling error, or if they are
// likely to be real. The syntax of the function is:
//
// CHITEST(actual_range,expected_range)
//
func (fn *formulaFuncs) CHITEST(argsList *list.List) formulaArg {
if argsList.Len() != 2 {
return newErrorFormulaArg(formulaErrorVALUE, "CHITEST requires 2 arguments")
}
actual, expected := argsList.Front().Value.(formulaArg), argsList.Back().Value.(formulaArg)
actualList, expectedList := actual.ToList(), expected.ToList()
rows := len(actual.Matrix)
columns := len(actualList) / rows
if len(actualList) != len(expectedList) || len(actualList) == 1 {
return newErrorFormulaArg(formulaErrorNA, formulaErrorNA)
}
var result float64
var degrees int
for i := 0; i < len(actualList); i++ {
a, e := actualList[i].ToNumber(), expectedList[i].ToNumber()
if a.Type == ArgNumber && e.Type == ArgNumber {
if e.Number == 0 {
return newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)
}
if e.Number < 0 {
return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
}
result += (a.Number - e.Number) * (a.Number - e.Number) / e.Number
}
}
if rows == 1 {
degrees = columns - 1
} else if columns == 1 {
degrees = rows - 1
} else {
degrees = (columns - 1) * (rows - 1)
}
args := list.New()
args.PushBack(newNumberFormulaArg(result))
args.PushBack(newNumberFormulaArg(float64(degrees)))
return fn.CHIDIST(args)
}
// CHISQdotTEST function performs the chi-square test on two supplied data sets
// (of observed and expected frequencies), and returns the probability that
// the differences between the sets are simply due to sampling error. The
// syntax of the function is:
//
// CHISQ.TEST(actual_range,expected_range)
//
func (fn *formulaFuncs) CHISQdotTEST(argsList *list.List) formulaArg {
if argsList.Len() != 2 {
return newErrorFormulaArg(formulaErrorVALUE, "CHISQ.TEST requires 2 arguments")
}
return fn.CHITEST(argsList)
}
// confidence is an implementation of the formula functions CONFIDENCE and
// CONFIDENCE.NORM.
func (fn *formulaFuncs) confidence(name string, argsList *list.List) formulaArg {
@ -7096,7 +7201,7 @@ func (fn *formulaFuncs) EXPONDIST(argsList *list.List) formulaArg {
// FdotDIST function calculates the Probability Density Function or the
// Cumulative Distribution Function for the F Distribution. This function is
// frequently used used to measure the degree of diversity between two data
// frequently used to measure the degree of diversity between two data
// sets. The syntax of the function is:
//
// F.DIST(x,deg_freedom1,deg_freedom2,cumulative)

View File

@ -843,7 +843,9 @@ func TestCalcCellValue(t *testing.T) {
"=BINOM.INV(100,0.5,90%)": "56",
// CHIDIST
"=CHIDIST(0.5,3)": "0.918891411654676",
"=CHIDIST(8,3)": "0.0460117056892315",
"=CHIDIST(8,3)": "0.0460117056892314",
"=CHIDIST(40,4)": "4.32842260712097E-08",
"=CHIDIST(42,4)": "1.66816329414062E-08",
// CHIINV
"=CHIINV(0.5,1)": "0.454936423119572",
"=CHIINV(0.75,1)": "0.101531044267622",
@ -4213,6 +4215,52 @@ func TestCalcHLOOKUP(t *testing.T) {
}
}
func TestCalcCHITESTandCHISQdotTEST(t *testing.T) {
cellData := [][]interface{}{
{nil, "Observed Frequencies", nil, nil, "Expected Frequencies"},
{nil, "men", "women", nil, nil, "men", "women"},
{"answer a", 33, 39, nil, "answer a", 26.25, 31.5},
{"answer b", 62, 62, nil, "answer b", 57.75, 61.95},
{"answer c", 10, 4, nil, "answer c", 21, 11.55},
{nil, -1, 0},
}
f := prepareCalcData(cellData)
formulaList := map[string]string{
"=CHITEST(B3:C5,F3:G5)": "0.000699102758787672",
"=CHITEST(B3:C3,F3:G3)": "0.0605802098655177",
"=CHITEST(B3:B4,F3:F4)": "0.152357748933542",
"=CHITEST(B4:B6,F3:F5)": "7.07076951440726E-25",
"=CHISQ.TEST(B3:C5,F3:G5)": "0.000699102758787672",
"=CHISQ.TEST(B3:C3,F3:G3)": "0.0605802098655177",
"=CHISQ.TEST(B3:B4,F3:F4)": "0.152357748933542",
"=CHISQ.TEST(B4:B6,F3:F5)": "7.07076951440726E-25",
}
for formula, expected := range formulaList {
assert.NoError(t, f.SetCellFormula("Sheet1", "I1", formula))
result, err := f.CalcCellValue("Sheet1", "I1")
assert.NoError(t, err, formula)
assert.Equal(t, expected, result, formula)
}
calcError := map[string]string{
"=CHITEST()": "CHITEST requires 2 arguments",
"=CHITEST(B3:C5,F3:F4)": "#N/A",
"=CHITEST(B3:B3,F3:F3)": "#N/A",
"=CHITEST(F3:F5,B4:B6)": "#NUM!",
"=CHITEST(F3:F5,C4:C6)": "#DIV/0!",
"=CHISQ.TEST()": "CHISQ.TEST requires 2 arguments",
"=CHISQ.TEST(B3:C5,F3:F4)": "#N/A",
"=CHISQ.TEST(B3:B3,F3:F3)": "#N/A",
"=CHISQ.TEST(F3:F5,B4:B6)": "#NUM!",
"=CHISQ.TEST(F3:F5,C4:C6)": "#DIV/0!",
}
for formula, expected := range calcError {
assert.NoError(t, f.SetCellFormula("Sheet1", "I1", formula))
result, err := f.CalcCellValue("Sheet1", "I1")
assert.EqualError(t, err, expected, formula)
assert.Equal(t, "", result, formula)
}
}
func TestCalcIRR(t *testing.T) {
cellData := [][]interface{}{{-1}, {0.2}, {0.24}, {0.288}, {0.3456}, {0.4147}}
f := prepareCalcData(cellData)

View File

@ -128,7 +128,7 @@ type StandardEncryptionVerifier struct {
EncryptedVerifierHash []byte
}
// Decrypt API decrypt the CFB file format with ECMA-376 agile encryption and
// Decrypt API decrypts the CFB file format with ECMA-376 agile encryption and
// standard encryption. Support cryptographic algorithm: MD4, MD5, RIPEMD-160,
// SHA1, SHA256, SHA384 and SHA512 currently.
func Decrypt(raw []byte, opt *Options) (packageBuf []byte, err error) {