Skip to content

Commit 26174a2

Browse files
committed
This closes #1196, fix the compatibility issue and added new formula function
ref #65, new formula functions: TINV and TTEST
1 parent ecbc6e2 commit 26174a2

File tree

3 files changed

+203
-1
lines changed

3 files changed

+203
-1
lines changed

calc.go

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,12 +666,14 @@ type formulaFuncs struct {
666666
// TIMEVALUE
667667
// T.INV
668668
// T.INV.2T
669+
// TINV
669670
// TODAY
670671
// TRANSPOSE
671672
// TRIM
672673
// TRIMMEAN
673674
// TRUE
674675
// TRUNC
676+
// TTEST
675677
// TYPE
676678
// UNICHAR
677679
// UNICODE
@@ -9201,6 +9203,145 @@ func (fn *formulaFuncs) TdotINVdot2T(argsList *list.List) formulaArg {
92019203
}, degrees.Number/2, degrees.Number))
92029204
}
92039205

9206+
// TINV function calculates the inverse of the two-tailed Student's T
9207+
// Distribution, which is a continuous probability distribution that is
9208+
// frequently used for testing hypotheses on small sample data sets. The
9209+
// syntax of the function is:
9210+
//
9211+
// TINV(probability,degrees_freedom)
9212+
//
9213+
func (fn *formulaFuncs) TINV(argsList *list.List) formulaArg {
9214+
if argsList.Len() != 2 {
9215+
return newErrorFormulaArg(formulaErrorVALUE, "TINV requires 2 arguments")
9216+
}
9217+
return fn.TdotINVdot2T(argsList)
9218+
}
9219+
9220+
// tTest calculates the probability associated with the Student's T Test.
9221+
func tTest(bTemplin bool, pMat1, pMat2 [][]formulaArg, nC1, nC2, nR1, nR2 int, fT, fF float64) (float64, float64, bool) {
9222+
var fCount1, fCount2, fSum1, fSumSqr1, fSum2, fSumSqr2 float64
9223+
var fVal formulaArg
9224+
for i := 0; i < nC1; i++ {
9225+
for j := 0; j < nR1; j++ {
9226+
fVal = pMat1[i][j].ToNumber()
9227+
if fVal.Type == ArgNumber {
9228+
fSum1 += fVal.Number
9229+
fSumSqr1 += fVal.Number * fVal.Number
9230+
fCount1++
9231+
}
9232+
}
9233+
}
9234+
for i := 0; i < nC2; i++ {
9235+
for j := 0; j < nR2; j++ {
9236+
fVal = pMat2[i][j].ToNumber()
9237+
if fVal.Type == ArgNumber {
9238+
fSum2 += fVal.Number
9239+
fSumSqr2 += fVal.Number * fVal.Number
9240+
fCount2++
9241+
}
9242+
}
9243+
}
9244+
if fCount1 < 2.0 || fCount2 < 2.0 {
9245+
return 0, 0, false
9246+
}
9247+
if bTemplin {
9248+
fS1 := (fSumSqr1 - fSum1*fSum1/fCount1) / (fCount1 - 1) / fCount1
9249+
fS2 := (fSumSqr2 - fSum2*fSum2/fCount2) / (fCount2 - 1) / fCount2
9250+
if fS1+fS2 == 0 {
9251+
return 0, 0, false
9252+
}
9253+
c := fS1 / (fS1 + fS2)
9254+
fT = math.Abs(fSum1/fCount1-fSum2/fCount2) / math.Sqrt(fS1+fS2)
9255+
fF = 1 / (c*c/(fCount1-1) + (1-c)*(1-c)/(fCount2-1))
9256+
return fT, fF, true
9257+
}
9258+
fS1 := (fSumSqr1 - fSum1*fSum1/fCount1) / (fCount1 - 1)
9259+
fS2 := (fSumSqr2 - fSum2*fSum2/fCount2) / (fCount2 - 1)
9260+
fT = math.Abs(fSum1/fCount1-fSum2/fCount2) / math.Sqrt((fCount1-1)*fS1+(fCount2-1)*fS2) * math.Sqrt(fCount1*fCount2*(fCount1+fCount2-2)/(fCount1+fCount2))
9261+
fF = fCount1 + fCount2 - 2
9262+
return fT, fF, true
9263+
}
9264+
9265+
// tTest is an implementation of the formula function TTEST.
9266+
func (fn *formulaFuncs) tTest(pMat1, pMat2 [][]formulaArg, fTails, fTyp float64) formulaArg {
9267+
var fT, fF float64
9268+
nC1 := len(pMat1)
9269+
nC2 := len(pMat2)
9270+
nR1 := len(pMat1[0])
9271+
nR2 := len(pMat2[0])
9272+
ok := true
9273+
if fTyp == 1 {
9274+
if nC1 != nC2 || nR1 != nR2 {
9275+
return newErrorFormulaArg(formulaErrorNA, formulaErrorNA)
9276+
}
9277+
var fCount, fSum1, fSum2, fSumSqrD float64
9278+
var fVal1, fVal2 formulaArg
9279+
for i := 0; i < nC1; i++ {
9280+
for j := 0; j < nR1; j++ {
9281+
fVal1 = pMat1[i][j].ToNumber()
9282+
fVal2 = pMat2[i][j].ToNumber()
9283+
if fVal1.Type != ArgNumber || fVal2.Type != ArgNumber {
9284+
continue
9285+
}
9286+
fSum1 += fVal1.Number
9287+
fSum2 += fVal2.Number
9288+
fSumSqrD += (fVal1.Number - fVal2.Number) * (fVal1.Number - fVal2.Number)
9289+
fCount++
9290+
}
9291+
}
9292+
if fCount < 1 {
9293+
return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
9294+
}
9295+
fSumD := fSum1 - fSum2
9296+
fDivider := fCount*fSumSqrD - fSumD*fSumD
9297+
if fDivider == 0 {
9298+
return newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)
9299+
}
9300+
fT = math.Abs(fSumD) * math.Sqrt((fCount-1)/fDivider)
9301+
fF = fCount - 1
9302+
} else if fTyp == 2 {
9303+
fT, fF, ok = tTest(false, pMat1, pMat2, nC1, nC2, nR1, nR2, fT, fF)
9304+
} else {
9305+
fT, fF, ok = tTest(true, pMat1, pMat2, nC1, nC2, nR1, nR2, fT, fF)
9306+
}
9307+
if !ok {
9308+
return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
9309+
}
9310+
return newNumberFormulaArg(getTDist(fT, fF, fTails))
9311+
}
9312+
9313+
// TTEST function calculates the probability associated with the Student's T
9314+
// Test, which is commonly used for identifying whether two data sets are
9315+
// likely to have come from the same two underlying populations with the same
9316+
// mean. The syntax of the function is:
9317+
//
9318+
// TTEST(array1,array2,tails,type)
9319+
//
9320+
func (fn *formulaFuncs) TTEST(argsList *list.List) formulaArg {
9321+
if argsList.Len() != 4 {
9322+
return newErrorFormulaArg(formulaErrorVALUE, "TTEST requires 4 arguments")
9323+
}
9324+
var array1, array2, tails, typeArg formulaArg
9325+
array1 = argsList.Front().Value.(formulaArg)
9326+
array2 = argsList.Front().Next().Value.(formulaArg)
9327+
if tails = argsList.Front().Next().Next().Value.(formulaArg).ToNumber(); tails.Type != ArgNumber {
9328+
return tails
9329+
}
9330+
if typeArg = argsList.Back().Value.(formulaArg).ToNumber(); typeArg.Type != ArgNumber {
9331+
return typeArg
9332+
}
9333+
if len(array1.Matrix) == 0 || len(array2.Matrix) == 0 {
9334+
return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
9335+
}
9336+
if tails.Number != 1 && tails.Number != 2 {
9337+
return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
9338+
}
9339+
if typeArg.Number != 1 && typeArg.Number != 2 && typeArg.Number != 3 {
9340+
return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
9341+
}
9342+
return fn.tTest(array1.Matrix, array2.Matrix, tails.Number, typeArg.Number)
9343+
}
9344+
92049345
// TRIMMEAN function calculates the trimmed mean (or truncated mean) of a
92059346
// supplied set of values. The syntax of the function is:
92069347
//

calc_test.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1173,6 +1173,9 @@ func TestCalcCellValue(t *testing.T) {
11731173
// T.INV.2T
11741174
"=T.INV.2T(1,10)": "0",
11751175
"=T.INV.2T(0.5,10)": "0.699812061312432",
1176+
// TINV
1177+
"=TINV(1,10)": "0",
1178+
"=TINV(0.5,10)": "0.699812061312432",
11761179
// TRIMMEAN
11771180
"=TRIMMEAN(A1:B4,10%)": "2.5",
11781181
"=TRIMMEAN(A1:B4,70%)": "2.5",
@@ -3067,6 +3070,12 @@ func TestCalcCellValue(t *testing.T) {
30673070
"=T.INV.2T(0.25,\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax",
30683071
"=T.INV.2T(0,10)": "#NUM!",
30693072
"=T.INV.2T(0.25,0.5)": "#NUM!",
3073+
// TINV
3074+
"=TINV()": "TINV requires 2 arguments",
3075+
"=TINV(\"\",10)": "strconv.ParseFloat: parsing \"\": invalid syntax",
3076+
"=TINV(0.25,\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax",
3077+
"=TINV(0,10)": "#NUM!",
3078+
"=TINV(0.25,0.5)": "#NUM!",
30703079
// TRIMMEAN
30713080
"=TRIMMEAN()": "TRIMMEAN requires 2 arguments",
30723081
"=TRIMMEAN(A1,\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax",
@@ -4888,6 +4897,58 @@ func TestCalcSHEETS(t *testing.T) {
48884897
}
48894898
}
48904899

4900+
func TestCalcTTEST(t *testing.T) {
4901+
cellData := [][]interface{}{
4902+
{4, 8, nil, 1, 1},
4903+
{5, 3, nil, 1, 1},
4904+
{2, 7},
4905+
{5, 3},
4906+
{8, 5},
4907+
{9, 2},
4908+
{3, 2},
4909+
{2, 7},
4910+
{3, 9},
4911+
{8, 4},
4912+
{9, 4},
4913+
{5, 7},
4914+
}
4915+
f := prepareCalcData(cellData)
4916+
formulaList := map[string]string{
4917+
"=TTEST(A1:A12,B1:B12,1,1)": "0.44907068944428",
4918+
"=TTEST(A1:A12,B1:B12,1,2)": "0.436717306029283",
4919+
"=TTEST(A1:A12,B1:B12,1,3)": "0.436722015384755",
4920+
"=TTEST(A1:A12,B1:B12,2,1)": "0.898141378888559",
4921+
"=TTEST(A1:A12,B1:B12,2,2)": "0.873434612058567",
4922+
"=TTEST(A1:A12,B1:B12,2,3)": "0.873444030769511",
4923+
}
4924+
for formula, expected := range formulaList {
4925+
assert.NoError(t, f.SetCellFormula("Sheet1", "C1", formula))
4926+
result, err := f.CalcCellValue("Sheet1", "C1")
4927+
assert.NoError(t, err, formula)
4928+
assert.Equal(t, expected, result, formula)
4929+
}
4930+
calcError := map[string]string{
4931+
"=TTEST()": "TTEST requires 4 arguments",
4932+
"=TTEST(\"\",B1:B12,1,1)": "#NUM!",
4933+
"=TTEST(A1:A12,\"\",1,1)": "#NUM!",
4934+
"=TTEST(A1:A12,B1:B12,\"\",1)": "strconv.ParseFloat: parsing \"\": invalid syntax",
4935+
"=TTEST(A1:A12,B1:B12,1,\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax",
4936+
"=TTEST(A1:A12,B1:B12,0,1)": "#NUM!",
4937+
"=TTEST(A1:A12,B1:B12,1,0)": "#NUM!",
4938+
"=TTEST(A1:A2,B1:B1,1,1)": "#N/A",
4939+
"=TTEST(A13:A14,B13:B14,1,1)": "#NUM!",
4940+
"=TTEST(A12:A13,B12:B13,1,1)": "#DIV/0!",
4941+
"=TTEST(A13:A14,B13:B14,1,2)": "#NUM!",
4942+
"=TTEST(D1:D4,E1:E4,1,3)": "#NUM!",
4943+
}
4944+
for formula, expected := range calcError {
4945+
assert.NoError(t, f.SetCellFormula("Sheet1", "C1", formula))
4946+
result, err := f.CalcCellValue("Sheet1", "C1")
4947+
assert.EqualError(t, err, expected, formula)
4948+
assert.Equal(t, "", result, formula)
4949+
}
4950+
}
4951+
48914952
func TestCalcZTEST(t *testing.T) {
48924953
f := NewFile()
48934954
assert.NoError(t, f.SetSheetRow("Sheet1", "A1", &[]int{4, 5, 2, 5, 8, 9, 3, 2, 3, 8, 9, 5}))

xmlWorksheet.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,9 @@ type xlsxWorksheet struct {
5858
OleObjects *xlsxInnerXML `xml:"oleObjects"`
5959
Controls *xlsxInnerXML `xml:"controls"`
6060
WebPublishItems *xlsxInnerXML `xml:"webPublishItems"`
61+
AlternateContent *xlsxAlternateContent `xml:"mc:AlternateContent"`
6162
TableParts *xlsxTableParts `xml:"tableParts"`
6263
ExtLst *xlsxExtLst `xml:"extLst"`
63-
AlternateContent *xlsxAlternateContent `xml:"mc:AlternateContent"`
6464
DecodeAlternateContent *xlsxInnerXML `xml:"http://schemas.openxmlformats.org/markup-compatibility/2006 AlternateContent"`
6565
}
6666

0 commit comments

Comments
 (0)