1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
11 #include <sfx2/dispatch.hxx>
12 #include <svl/zforlist.hxx>
13 #include <svl/undo.hxx>
15 #include "formulacell.hxx"
16 #include "rangelst.hxx"
17 #include "scitems.hxx"
19 #include "document.hxx"
20 #include "uiitems.hxx"
21 #include "reffact.hxx"
22 #include "strload.hxx"
23 #include "docfunc.hxx"
24 #include "StatisticsDialogs.hrc"
25 #include "TableFillingAndNavigationTools.hxx"
27 #include "AnalysisOfVarianceDialog.hxx"
32 struct StatisticCalculation
{
35 const char* aResultRangeName
;
38 static StatisticCalculation lclBasicStatistics
[] =
40 { STR_ANOVA_LABEL_GROUPS
, NULL
, NULL
},
41 { STRID_CALC_COUNT
, "=COUNT(%RANGE%)", "COUNT_RANGE" },
42 { STRID_CALC_SUM
, "=SUM(%RANGE%)", "SUM_RANGE" },
43 { STRID_CALC_MEAN
, "=AVERAGE(%RANGE%)", "MEAN_RANGE" },
44 { STRID_CALC_VARIANCE
, "=VAR(%RANGE%)", "VAR_RANGE" },
48 static sal_Int16 lclAnovaLabels
[] =
50 STR_ANOVA_LABEL_SOURCE_OF_VARIATION
,
55 STR_ANOVA_LABEL_P_VALUE
,
56 STR_ANOVA_LABEL_F_CRITICAL
,
60 static const OUString
strWildcardRange("%RANGE%");
61 static const OUString
strWildcardNumber("%NUMBER%");
63 OUString
lclCreateMultiParameterFormula(
64 ScRangeList
& aRangeList
, const OUString
& aFormulaTemplate
,
65 const OUString
& aWildcard
, ScDocument
* pDocument
,
66 ScAddress::Details
& aAddressDetails
)
69 for (size_t i
= 0; i
< aRangeList
.size(); i
++)
71 OUString
aRangeString(aRangeList
[i
]->Format(SCR_ABS
, pDocument
, aAddressDetails
));
72 OUString aFormulaString
= aFormulaTemplate
.replaceAll(aWildcard
, aRangeString
);
73 aResult
+= aFormulaString
;
74 if(i
!= aRangeList
.size() - 1) // Not Last
80 void lclMakeSubRangesList(ScRangeList
& rRangeList
, ScRange
& rInputRange
, ScStatisticsInputOutputDialog::GroupedBy aGroupedBy
)
82 boost::scoped_ptr
<DataRangeIterator
> pIterator
;
83 if (aGroupedBy
== ScStatisticsInputOutputDialog::BY_COLUMN
)
84 pIterator
.reset(new DataRangeByColumnIterator(rInputRange
));
86 pIterator
.reset(new DataRangeByRowIterator(rInputRange
));
88 for( ; pIterator
->hasNext(); pIterator
->next() )
90 ScRange aRange
= pIterator
->get();
91 rRangeList
.Append(aRange
);
97 ScAnalysisOfVarianceDialog::ScAnalysisOfVarianceDialog(
98 SfxBindings
* pSfxBindings
, SfxChildWindow
* pChildWindow
,
99 Window
* pParent
, ScViewData
* pViewData
) :
100 ScStatisticsInputOutputDialog(
101 pSfxBindings
, pChildWindow
, pParent
, pViewData
,
102 "AnalysisOfVarianceDialog", "modules/scalc/ui/analysisofvariancedialog.ui" ),
103 meFactor(SINGLE_FACTOR
)
105 get(mpAlphaField
, "alpha-spin");
106 get(mpSingleFactorRadio
, "radio-single-factor");
107 get(mpTwoFactorRadio
, "radio-two-factor");
108 get(mpRowsPerSampleField
, "rows-per-sample-spin");
110 mpSingleFactorRadio
->SetToggleHdl( LINK( this, ScAnalysisOfVarianceDialog
, FactorChanged
) );
111 mpTwoFactorRadio
->SetToggleHdl( LINK( this, ScAnalysisOfVarianceDialog
, FactorChanged
) );
113 mpSingleFactorRadio
->Check(true);
114 mpTwoFactorRadio
->Check(false);
119 ScAnalysisOfVarianceDialog::~ScAnalysisOfVarianceDialog()
122 bool ScAnalysisOfVarianceDialog::Close()
124 return DoClose( ScAnalysisOfVarianceDialogWrapper::GetChildWindowId() );
127 sal_Int16
ScAnalysisOfVarianceDialog::GetUndoNameId()
129 return STR_ANALYSIS_OF_VARIANCE_UNDO_NAME
;
132 IMPL_LINK_NOARG( ScAnalysisOfVarianceDialog
, FactorChanged
)
134 if (mpSingleFactorRadio
->IsChecked())
136 mpGroupByRowsRadio
->Enable(true);
137 mpGroupByColumnsRadio
->Enable(true);
138 mpRowsPerSampleField
->Enable(false);
139 meFactor
= SINGLE_FACTOR
;
141 else if (mpTwoFactorRadio
->IsChecked())
143 mpGroupByRowsRadio
->Enable(false);
144 mpGroupByColumnsRadio
->Enable(false);
145 mpRowsPerSampleField
->Enable(false); // Rows per sample not yet implemented
146 meFactor
= TWO_FACTOR
;
152 void ScAnalysisOfVarianceDialog::RowColumn(ScRangeList
& rRangeList
, AddressWalkerWriter
& aOutput
, FormulaTemplate
& aTemplate
,
153 OUString
& sFormula
, GroupedBy aGroupedBy
, ScRange
* pResultRange
)
155 if (pResultRange
!= NULL
)
156 pResultRange
->aStart
= aOutput
.current();
157 if (!sFormula
.isEmpty())
159 for (size_t i
= 0; i
< rRangeList
.size(); i
++)
161 ScRange
* pRange
= rRangeList
[i
];
162 aTemplate
.setTemplate(sFormula
);
163 aTemplate
.applyRange(strWildcardRange
, *pRange
);
164 aOutput
.writeFormula(aTemplate
.getTemplate());
165 if (pResultRange
!= NULL
)
166 pResultRange
->aEnd
= aOutput
.current();
172 sal_Int16 aLabelId
= (aGroupedBy
== BY_COLUMN
) ? STR_COLUMN_LABEL_TEMPLATE
: STR_ROW_LABEL_TEMPLATE
;
173 OUString
aLabelTemplate(SC_STRLOAD(RID_STATISTICS_DLGS
, aLabelId
));
175 for (size_t i
= 0; i
< rRangeList
.size(); i
++)
177 aTemplate
.setTemplate(aLabelTemplate
);
178 aTemplate
.applyNumber(strWildcardNumber
, i
+ 1);
179 aOutput
.writeString(aTemplate
.getTemplate());
180 if (pResultRange
!= NULL
)
181 pResultRange
->aEnd
= aOutput
.current();
187 void ScAnalysisOfVarianceDialog::AnovaSingleFactor(AddressWalkerWriter
& output
, FormulaTemplate
& aTemplate
)
189 output
.writeBoldString(SC_STRLOAD(RID_STATISTICS_DLGS
, STR_ANOVA_SINGLE_FACTOR_LABEL
));
192 double aAlphaValue
= mpAlphaField
->GetValue() / 100.0;
193 output
.writeString("Alpha");
195 output
.writeValue(aAlphaValue
);
196 aTemplate
.autoReplaceAddress("%ALPHA%", output
.current());
201 for(sal_Int32 i
= 0; lclBasicStatistics
[i
].aLabelId
!= 0; i
++)
203 output
.writeString(SC_STRLOAD(RID_STATISTICS_DLGS
, lclBasicStatistics
[i
].aLabelId
));
208 // Collect aRangeList
209 ScRangeList aRangeList
;
210 lclMakeSubRangesList(aRangeList
, mInputRange
, mGroupedBy
);
215 for(sal_Int32 i
= 0; lclBasicStatistics
[i
].aLabelId
!= 0; i
++)
218 ScRange aResultRange
;
219 OUString sFormula
= OUString::createFromAscii(lclBasicStatistics
[i
].aFormula
);
220 RowColumn(aRangeList
, output
, aTemplate
, sFormula
, mGroupedBy
, &aResultRange
);
222 if (lclBasicStatistics
[i
].aResultRangeName
!= NULL
)
224 OUString sResultRangeName
= OUString::createFromAscii(lclBasicStatistics
[i
].aResultRangeName
);
225 aTemplate
.autoReplaceRange("%" + sResultRangeName
+ "%", aResultRange
);
229 output
.nextRow(); // Blank row
231 // Write ANOVA labels
232 output
.resetColumn();
233 for(sal_Int32 i
= 0; lclAnovaLabels
[i
] != 0; i
++)
235 output
.writeString(SC_STRLOAD(RID_STATISTICS_DLGS
, lclAnovaLabels
[i
]));
240 aTemplate
.autoReplaceRange("%FIRST_COLUMN%", *aRangeList
[0]);
245 output
.resetColumn();
246 output
.writeString(SC_STRLOAD(RID_STATISTICS_DLGS
, STR_ANOVA_LABEL_BETWEEN_GROUPS
));
251 aTemplate
.setTemplate("=SUMPRODUCT(%SUM_RANGE%;%MEAN_RANGE%)-SUM(%SUM_RANGE%)^2/SUM(%COUNT_RANGE%)");
252 aTemplate
.autoReplaceAddress("%BETWEEN_SS%", output
.current());
253 output
.writeFormula(aTemplate
.getTemplate());
257 aTemplate
.setTemplate("=COUNT(%SUM_RANGE%)-1");
258 aTemplate
.autoReplaceAddress("%BETWEEN_DF%", output
.current());
259 output
.writeFormula(aTemplate
.getTemplate());
263 aTemplate
.setTemplate("=%BETWEEN_SS% / %BETWEEN_DF%");
264 aTemplate
.autoReplaceAddress("%BETWEEN_MS%", output
.current());
265 output
.writeFormula(aTemplate
.getTemplate());
269 aTemplate
.setTemplate("=%BETWEEN_MS% / %WITHIN_MS%");
270 aTemplate
.applyAddress("%WITHIN_MS%", output
.current(-1, 1));
271 aTemplate
.autoReplaceAddress("%F_VAL%", output
.current());
272 output
.writeFormula(aTemplate
.getTemplate());
276 aTemplate
.setTemplate("=FDIST(%F_VAL%; %BETWEEN_DF%; %WITHIN_DF%");
277 aTemplate
.applyAddress("%WITHIN_DF%", output
.current(-3, 1));
278 output
.writeFormula(aTemplate
.getTemplate());
282 aTemplate
.setTemplate("=FINV(%ALPHA%; %BETWEEN_DF%; %WITHIN_DF%");
283 aTemplate
.applyAddress("%WITHIN_DF%", output
.current(-4, 1));
284 output
.writeFormula(aTemplate
.getTemplate());
291 output
.resetColumn();
292 output
.writeString(SC_STRLOAD(RID_STATISTICS_DLGS
, STR_ANOVA_LABEL_WITHIN_GROUPS
));
296 OUString aSSPart
= lclCreateMultiParameterFormula(aRangeList
, OUString("DEVSQ(%RANGE%)"), strWildcardRange
, mDocument
, mAddressDetails
);
297 aTemplate
.setTemplate("=SUM(%RANGE%)");
298 aTemplate
.applyString(strWildcardRange
, aSSPart
);
299 aTemplate
.autoReplaceAddress("%WITHIN_SS%", output
.current());
300 output
.writeFormula(aTemplate
.getTemplate());
304 aTemplate
.setTemplate("=SUM(%COUNT_RANGE%)-COUNT(%COUNT_RANGE%)");
305 aTemplate
.autoReplaceAddress("%WITHIN_DF%", output
.current());
306 output
.writeFormula(aTemplate
.getTemplate());
310 aTemplate
.setTemplate("=%WITHIN_SS% / %WITHIN_DF%");
311 output
.writeFormula(aTemplate
.getTemplate());
318 output
.resetColumn();
319 output
.writeString(SC_STRLOAD(RID_STATISTICS_DLGS
, STR_ANOVA_LABEL_TOTAL
));
323 aTemplate
.setTemplate("=DEVSQ(%RANGE_LIST%)");
324 aTemplate
.applyRangeList("%RANGE_LIST%", aRangeList
);
325 output
.writeFormula(aTemplate
.getTemplate());
329 aTemplate
.setTemplate("=SUM(%COUNT_RANGE%) - 1");
330 output
.writeFormula(aTemplate
.getTemplate());
335 void ScAnalysisOfVarianceDialog::AnovaTwoFactor(AddressWalkerWriter
& output
, FormulaTemplate
& aTemplate
)
337 output
.writeBoldString(SC_STRLOAD(RID_STATISTICS_DLGS
, STR_ANOVA_TWO_FACTOR_LABEL
));
340 double aAlphaValue
= mpAlphaField
->GetValue() / 100.0;
341 output
.writeString("Alpha");
343 output
.writeValue(aAlphaValue
);
344 aTemplate
.autoReplaceAddress("%ALPHA%", output
.current());
349 for(sal_Int32 i
= 0; lclBasicStatistics
[i
].aLabelId
!= 0; i
++)
351 output
.writeString(SC_STRLOAD(RID_STATISTICS_DLGS
, lclBasicStatistics
[i
].aLabelId
));
356 ScRangeList aColumnRangeList
;
357 ScRangeList aRowRangeList
;
359 lclMakeSubRangesList(aColumnRangeList
, mInputRange
, BY_COLUMN
);
360 lclMakeSubRangesList(aRowRangeList
, mInputRange
, BY_ROW
);
362 // Write ColumnX values
364 for(sal_Int32 i
= 0; lclBasicStatistics
[i
].aLabelId
!= 0; i
++)
367 ScRange aResultRange
;
368 OUString sFormula
= OUString::createFromAscii(lclBasicStatistics
[i
].aFormula
);
369 RowColumn(aColumnRangeList
, output
, aTemplate
, sFormula
, BY_COLUMN
, &aResultRange
);
370 if (lclBasicStatistics
[i
].aResultRangeName
!= NULL
)
372 OUString sResultRangeName
= OUString::createFromAscii(lclBasicStatistics
[i
].aResultRangeName
);
373 aTemplate
.autoReplaceRange("%" + sResultRangeName
+ "_COLUMN%", aResultRange
);
381 for(sal_Int32 i
= 0; lclBasicStatistics
[i
].aLabelId
!= 0; i
++)
384 ScRange aResultRange
;
385 OUString sFormula
= OUString::createFromAscii(lclBasicStatistics
[i
].aFormula
);
386 RowColumn(aRowRangeList
, output
, aTemplate
, sFormula
, BY_ROW
, &aResultRange
);
388 if (lclBasicStatistics
[i
].aResultRangeName
!= NULL
)
390 OUString sResultRangeName
= OUString::createFromAscii(lclBasicStatistics
[i
].aResultRangeName
);
391 aTemplate
.autoReplaceRange("%" + sResultRangeName
+ "_ROW%", aResultRange
);
397 // Write ANOVA labels
398 for(sal_Int32 i
= 0; lclAnovaLabels
[i
] != 0; i
++)
400 output
.writeString(SC_STRLOAD(RID_STATISTICS_DLGS
, lclAnovaLabels
[i
]));
405 // Setup auto-replace strings
406 aTemplate
.autoReplaceRange(strWildcardRange
, mInputRange
);
407 aTemplate
.autoReplaceRange("%FIRST_COLUMN%", *aColumnRangeList
[0]);
408 aTemplate
.autoReplaceRange("%FIRST_ROW%", *aRowRangeList
[0]);
413 output
.resetColumn();
414 output
.writeString("Rows");
418 aTemplate
.setTemplate("=SUMPRODUCT(%SUM_RANGE_ROW%;%MEAN_RANGE_ROW%) - SUM(%RANGE%)^2 / COUNT(%RANGE%)");
419 aTemplate
.autoReplaceAddress("%ROW_SS%", output
.current());
420 output
.writeFormula(aTemplate
.getTemplate());
424 aTemplate
.setTemplate("=MAX(%COUNT_RANGE_COLUMN%) - 1");
425 aTemplate
.autoReplaceAddress("%ROW_DF%", output
.current());
426 output
.writeFormula(aTemplate
.getTemplate());
430 aTemplate
.setTemplate("=%ROW_SS% / %ROW_DF%");
431 aTemplate
.autoReplaceAddress("%MS_ROW%", output
.current());
432 output
.writeFormula(aTemplate
.getTemplate());
436 aTemplate
.setTemplate("=%MS_ROW% / %MS_ERROR%");
437 aTemplate
.applyAddress("%MS_ERROR%", output
.current(-1, 2));
438 aTemplate
.autoReplaceAddress("%F_ROW%", output
.current());
439 output
.writeFormula(aTemplate
.getTemplate());
443 aTemplate
.setTemplate("=FDIST(%F_ROW%; %ROW_DF%; %ERROR_DF%");
444 aTemplate
.applyAddress("%ERROR_DF%", output
.current(-3, 2));
445 output
.writeFormula(aTemplate
.getTemplate());
449 aTemplate
.setTemplate("=FINV(%ALPHA%; %ROW_DF%; %ERROR_DF%");
450 aTemplate
.applyAddress("%ERROR_DF%", output
.current(-4, 2));
451 output
.writeFormula(aTemplate
.getTemplate());
459 output
.resetColumn();
460 output
.writeString("Columns");
464 aTemplate
.setTemplate("=SUMPRODUCT(%SUM_RANGE_COLUMN%;%MEAN_RANGE_COLUMN%) - SUM(%RANGE%)^2 / COUNT(%RANGE%)");
465 aTemplate
.autoReplaceAddress("%COLUMN_SS%", output
.current());
466 output
.writeFormula(aTemplate
.getTemplate());
470 aTemplate
.setTemplate("=MAX(%COUNT_RANGE_ROW%) - 1");
471 aTemplate
.autoReplaceAddress("%COLUMN_DF%", output
.current());
472 output
.writeFormula(aTemplate
.getTemplate());
476 aTemplate
.setTemplate("=%COLUMN_SS% / %COLUMN_DF%");
477 aTemplate
.autoReplaceAddress("%MS_COLUMN%", output
.current());
478 output
.writeFormula(aTemplate
.getTemplate());
482 aTemplate
.setTemplate("=%MS_COLUMN% / %MS_ERROR%");
483 aTemplate
.applyAddress("%MS_ERROR%", output
.current(-1, 1));
484 aTemplate
.autoReplaceAddress("%F_COLUMN%", output
.current());
485 output
.writeFormula(aTemplate
.getTemplate());
489 aTemplate
.setTemplate("=FDIST(%F_COLUMN%; %COLUMN_DF%; %ERROR_DF%");
490 aTemplate
.applyAddress("%ERROR_DF%", output
.current(-3, 1));
491 output
.writeFormula(aTemplate
.getTemplate());
495 aTemplate
.setTemplate("=FINV(%ALPHA%; %COLUMN_DF%; %ERROR_DF%");
496 aTemplate
.applyAddress("%ERROR_DF%", output
.current(-4, 1));
497 output
.writeFormula(aTemplate
.getTemplate());
505 output
.resetColumn();
506 output
.writeString("Error");
510 aTemplate
.setTemplate("=SUMSQ(%RANGE%)+SUM(%RANGE%)^2/COUNT(%RANGE%) - (SUMPRODUCT(%SUM_RANGE_ROW%;%MEAN_RANGE_ROW%) + SUMPRODUCT(%SUM_RANGE_COLUMN%;%MEAN_RANGE_COLUMN%))");
511 aTemplate
.autoReplaceAddress("%ERROR_SS%", output
.current());
512 output
.writeFormula(aTemplate
.getTemplate());
516 aTemplate
.setTemplate("=%TOTAL_DF% - %ROW_DF% - %COLUMN_DF%");
517 aTemplate
.applyAddress("%TOTAL_DF%", output
.current(0,1,0));
518 aTemplate
.autoReplaceAddress("%ERROR_DF%", output
.current());
519 output
.writeFormula(aTemplate
.getTemplate());
523 aTemplate
.setTemplate("=%ERROR_SS% / %ERROR_DF%");
524 output
.writeFormula(aTemplate
.getTemplate());
531 output
.resetColumn();
532 output
.writeString("Total");
536 aTemplate
.setTemplate("=SUM(%ROW_SS%;%COLUMN_SS%;%ERROR_SS%)");
537 output
.writeFormula(aTemplate
.getTemplate());
541 aTemplate
.setTemplate("=COUNT(%RANGE%)-1");
542 output
.writeFormula(aTemplate
.getTemplate());
547 ScRange
ScAnalysisOfVarianceDialog::ApplyOutput(ScDocShell
* pDocShell
)
549 AddressWalkerWriter
output(mOutputAddress
, pDocShell
, mDocument
,
550 formula::FormulaGrammar::mergeToGrammar(formula::FormulaGrammar::GRAM_ENGLISH
, mAddressDetails
.eConv
));
551 FormulaTemplate
aTemplate(mDocument
);
553 if (meFactor
== SINGLE_FACTOR
)
555 AnovaSingleFactor(output
, aTemplate
);
557 else if (meFactor
== TWO_FACTOR
)
559 AnovaTwoFactor(output
, aTemplate
);
562 return ScRange(output
.mMinimumAddress
, output
.mMaximumAddress
);
565 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */