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/.
12 #include <string_view>
14 #include <rangelst.hxx>
15 #include <reffact.hxx>
16 #include <TableFillingAndNavigationTools.hxx>
17 #include <AnalysisOfVarianceDialog.hxx>
18 #include <scresid.hxx>
19 #include <strings.hrc>
24 struct StatisticCalculation
{
27 const char* aResultRangeName
;
30 StatisticCalculation
const lclBasicStatistics
[] =
32 { STR_ANOVA_LABEL_GROUPS
, nullptr, nullptr },
33 { STRID_CALC_COUNT
, "=COUNT(%RANGE%)", "COUNT_RANGE" },
34 { STRID_CALC_SUM
, "=SUM(%RANGE%)", "SUM_RANGE" },
35 { STRID_CALC_MEAN
, "=AVERAGE(%RANGE%)", "MEAN_RANGE" },
36 { STRID_CALC_VARIANCE
, "=VAR(%RANGE%)", "VAR_RANGE" },
37 { {}, nullptr, nullptr }
40 const TranslateId lclAnovaLabels
[] =
42 STR_ANOVA_LABEL_SOURCE_OF_VARIATION
,
47 STR_ANOVA_LABEL_P_VALUE
,
48 STR_ANOVA_LABEL_F_CRITICAL
,
52 constexpr OUString strWildcardRange
= u
"%RANGE%"_ustr
;
54 OUString
lclCreateMultiParameterFormula(
55 ScRangeList
& aRangeList
, const OUString
& aFormulaTemplate
,
56 std::u16string_view aWildcard
, const ScDocument
& rDocument
,
57 const ScAddress::Details
& aAddressDetails
)
59 OUStringBuffer aResult
;
60 for (size_t i
= 0; i
< aRangeList
.size(); i
++)
62 OUString
aRangeString(aRangeList
[i
].Format(rDocument
, ScRefFlags::RANGE_ABS_3D
, aAddressDetails
));
63 OUString aFormulaString
= aFormulaTemplate
.replaceAll(aWildcard
, aRangeString
);
64 aResult
.append(aFormulaString
);
65 if(i
!= aRangeList
.size() - 1) // Not Last
68 return aResult
.makeStringAndClear();
71 void lclMakeSubRangesList(ScRangeList
& rRangeList
, const ScRange
& rInputRange
, ScStatisticsInputOutputDialog::GroupedBy aGroupedBy
)
73 std::unique_ptr
<DataRangeIterator
> pIterator
;
74 if (aGroupedBy
== ScStatisticsInputOutputDialog::BY_COLUMN
)
75 pIterator
.reset(new DataRangeByColumnIterator(rInputRange
));
77 pIterator
.reset(new DataRangeByRowIterator(rInputRange
));
79 for( ; pIterator
->hasNext(); pIterator
->next() )
81 ScRange aRange
= pIterator
->get();
82 rRangeList
.push_back(aRange
);
88 ScAnalysisOfVarianceDialog::ScAnalysisOfVarianceDialog(
89 SfxBindings
* pSfxBindings
, SfxChildWindow
* pChildWindow
,
90 weld::Window
* pParent
, ScViewData
& rViewData
)
91 : ScStatisticsInputOutputDialog(
92 pSfxBindings
, pChildWindow
, pParent
, rViewData
,
93 u
"modules/scalc/ui/analysisofvariancedialog.ui"_ustr
,
94 u
"AnalysisOfVarianceDialog"_ustr
)
95 , meFactor(SINGLE_FACTOR
)
96 , mxAlphaField(m_xBuilder
->weld_spin_button(u
"alpha-spin"_ustr
))
97 , mxSingleFactorRadio(m_xBuilder
->weld_radio_button(u
"radio-single-factor"_ustr
))
98 , mxTwoFactorRadio(m_xBuilder
->weld_radio_button(u
"radio-two-factor"_ustr
))
99 , mxRowsPerSampleField(m_xBuilder
->weld_spin_button(u
"rows-per-sample-spin"_ustr
))
101 mxSingleFactorRadio
->connect_toggled( LINK( this, ScAnalysisOfVarianceDialog
, FactorChanged
) );
102 mxTwoFactorRadio
->connect_toggled( LINK( this, ScAnalysisOfVarianceDialog
, FactorChanged
) );
104 mxSingleFactorRadio
->set_active(true);
105 mxTwoFactorRadio
->set_active(false);
110 ScAnalysisOfVarianceDialog::~ScAnalysisOfVarianceDialog()
114 void ScAnalysisOfVarianceDialog::Close()
116 DoClose( ScAnalysisOfVarianceDialogWrapper::GetChildWindowId() );
119 TranslateId
ScAnalysisOfVarianceDialog::GetUndoNameId()
121 return STR_ANALYSIS_OF_VARIANCE_UNDO_NAME
;
124 IMPL_LINK_NOARG( ScAnalysisOfVarianceDialog
, FactorChanged
, weld::Toggleable
&, void )
129 void ScAnalysisOfVarianceDialog::FactorChanged()
131 if (mxSingleFactorRadio
->get_active())
133 mxGroupByRowsRadio
->set_sensitive(true);
134 mxGroupByColumnsRadio
->set_sensitive(true);
135 mxRowsPerSampleField
->set_sensitive(false);
136 meFactor
= SINGLE_FACTOR
;
138 else if (mxTwoFactorRadio
->get_active())
140 mxGroupByRowsRadio
->set_sensitive(false);
141 mxGroupByColumnsRadio
->set_sensitive(false);
142 mxRowsPerSampleField
->set_sensitive(false); // Rows per sample not yet implemented
143 meFactor
= TWO_FACTOR
;
147 void ScAnalysisOfVarianceDialog::RowColumn(ScRangeList
& rRangeList
, AddressWalkerWriter
& aOutput
, FormulaTemplate
& aTemplate
,
148 const OUString
& sFormula
, GroupedBy aGroupedBy
, ScRange
* pResultRange
)
150 if (pResultRange
!= nullptr)
151 pResultRange
->aStart
= aOutput
.current();
152 if (!sFormula
.isEmpty())
154 for (size_t i
= 0; i
< rRangeList
.size(); i
++)
156 ScRange
const & rRange
= rRangeList
[i
];
157 aTemplate
.setTemplate(sFormula
);
158 aTemplate
.applyRange(strWildcardRange
, rRange
);
159 aOutput
.writeFormula(aTemplate
.getTemplate());
160 if (pResultRange
!= nullptr)
161 pResultRange
->aEnd
= aOutput
.current();
167 TranslateId pLabelId
= (aGroupedBy
== BY_COLUMN
) ? STR_COLUMN_LABEL_TEMPLATE
: STR_ROW_LABEL_TEMPLATE
;
168 OUString
aLabelTemplate(ScResId(pLabelId
));
170 for (size_t i
= 0; i
< rRangeList
.size(); i
++)
172 aTemplate
.setTemplate(aLabelTemplate
);
173 aTemplate
.applyNumber(u
"%NUMBER%", i
+ 1);
174 aOutput
.writeString(aTemplate
.getTemplate());
175 if (pResultRange
!= nullptr)
176 pResultRange
->aEnd
= aOutput
.current();
182 void ScAnalysisOfVarianceDialog::AnovaSingleFactor(AddressWalkerWriter
& output
, FormulaTemplate
& aTemplate
)
184 output
.writeBoldString(ScResId(STR_ANOVA_SINGLE_FACTOR_LABEL
));
187 double aAlphaValue
= mxAlphaField
->get_value() / 100.0;
188 output
.writeString(ScResId(STR_LABEL_ALPHA
));
190 output
.writeValue(aAlphaValue
);
191 aTemplate
.autoReplaceAddress(u
"%ALPHA%"_ustr
, output
.current());
196 for(sal_Int32 i
= 0; lclBasicStatistics
[i
].aLabelId
; i
++)
198 output
.writeString(ScResId(lclBasicStatistics
[i
].aLabelId
));
203 // Collect aRangeList
204 ScRangeList aRangeList
;
205 lclMakeSubRangesList(aRangeList
, mInputRange
, mGroupedBy
);
210 for(sal_Int32 i
= 0; lclBasicStatistics
[i
].aLabelId
; i
++)
213 ScRange aResultRange
;
214 OUString sFormula
= OUString::createFromAscii(lclBasicStatistics
[i
].aFormula
);
215 RowColumn(aRangeList
, output
, aTemplate
, sFormula
, mGroupedBy
, &aResultRange
);
217 if (lclBasicStatistics
[i
].aResultRangeName
!= nullptr)
219 OUString sResultRangeName
= OUString::createFromAscii(lclBasicStatistics
[i
].aResultRangeName
);
220 aTemplate
.autoReplaceRange("%" + sResultRangeName
+ "%", aResultRange
);
224 output
.nextRow(); // Blank row
226 // Write ANOVA labels
227 output
.resetColumn();
228 for(sal_Int32 i
= 0; lclAnovaLabels
[i
]; i
++)
230 output
.writeString(ScResId(lclAnovaLabels
[i
]));
235 aTemplate
.autoReplaceRange(u
"%FIRST_COLUMN%"_ustr
, aRangeList
[0]);
240 output
.resetColumn();
241 output
.writeString(ScResId(STR_ANOVA_LABEL_BETWEEN_GROUPS
));
245 aTemplate
.setTemplate("=SUMPRODUCT(%SUM_RANGE%;%MEAN_RANGE%)-SUM(%SUM_RANGE%)^2/SUM(%COUNT_RANGE%)");
246 aTemplate
.autoReplaceAddress(u
"%BETWEEN_SS%"_ustr
, output
.current());
247 output
.writeFormula(aTemplate
.getTemplate());
251 aTemplate
.setTemplate("=COUNT(%SUM_RANGE%)-1");
252 aTemplate
.autoReplaceAddress(u
"%BETWEEN_DF%"_ustr
, output
.current());
253 output
.writeFormula(aTemplate
.getTemplate());
257 aTemplate
.setTemplate("=%BETWEEN_SS% / %BETWEEN_DF%");
258 aTemplate
.autoReplaceAddress(u
"%BETWEEN_MS%"_ustr
, output
.current());
259 output
.writeFormula(aTemplate
.getTemplate());
263 aTemplate
.setTemplate("=%BETWEEN_MS% / %WITHIN_MS%");
264 aTemplate
.applyAddress(u
"%WITHIN_MS%", output
.current(-1, 1));
265 aTemplate
.autoReplaceAddress(u
"%F_VAL%"_ustr
, output
.current());
266 output
.writeFormula(aTemplate
.getTemplate());
270 aTemplate
.setTemplate("=FDIST(%F_VAL%; %BETWEEN_DF%; %WITHIN_DF%");
271 aTemplate
.applyAddress(u
"%WITHIN_DF%", output
.current(-3, 1));
272 output
.writeFormula(aTemplate
.getTemplate());
276 aTemplate
.setTemplate("=FINV(%ALPHA%; %BETWEEN_DF%; %WITHIN_DF%");
277 aTemplate
.applyAddress(u
"%WITHIN_DF%", output
.current(-4, 1));
278 output
.writeFormula(aTemplate
.getTemplate());
285 output
.resetColumn();
286 output
.writeString(ScResId(STR_ANOVA_LABEL_WITHIN_GROUPS
));
290 OUString aSSPart
= lclCreateMultiParameterFormula(aRangeList
, u
"DEVSQ(%RANGE%)"_ustr
, strWildcardRange
, mDocument
, mAddressDetails
);
291 aTemplate
.setTemplate("=SUM(%RANGE%)");
292 aTemplate
.applyString(strWildcardRange
, aSSPart
);
293 aTemplate
.autoReplaceAddress(u
"%WITHIN_SS%"_ustr
, output
.current());
294 output
.writeFormula(aTemplate
.getTemplate());
298 aTemplate
.setTemplate("=SUM(%COUNT_RANGE%)-COUNT(%COUNT_RANGE%)");
299 aTemplate
.autoReplaceAddress(u
"%WITHIN_DF%"_ustr
, output
.current());
300 output
.writeFormula(aTemplate
.getTemplate());
304 aTemplate
.setTemplate("=%WITHIN_SS% / %WITHIN_DF%");
305 output
.writeFormula(aTemplate
.getTemplate());
312 output
.resetColumn();
313 output
.writeString(ScResId(STR_ANOVA_LABEL_TOTAL
));
317 aTemplate
.setTemplate("=DEVSQ(%RANGE_LIST%)");
318 aTemplate
.applyRangeList(u
"%RANGE_LIST%", aRangeList
, ';');
319 output
.writeFormula(aTemplate
.getTemplate());
323 aTemplate
.setTemplate("=SUM(%COUNT_RANGE%) - 1");
324 output
.writeFormula(aTemplate
.getTemplate());
329 void ScAnalysisOfVarianceDialog::AnovaTwoFactor(AddressWalkerWriter
& output
, FormulaTemplate
& aTemplate
)
331 output
.writeBoldString(ScResId(STR_ANOVA_TWO_FACTOR_LABEL
));
334 double aAlphaValue
= mxAlphaField
->get_value() / 100.0;
335 output
.writeString("Alpha");
337 output
.writeValue(aAlphaValue
);
338 aTemplate
.autoReplaceAddress(u
"%ALPHA%"_ustr
, output
.current());
343 for(sal_Int32 i
= 0; lclBasicStatistics
[i
].aLabelId
; i
++)
345 output
.writeString(ScResId(lclBasicStatistics
[i
].aLabelId
));
350 ScRangeList aColumnRangeList
;
351 ScRangeList aRowRangeList
;
353 lclMakeSubRangesList(aColumnRangeList
, mInputRange
, BY_COLUMN
);
354 lclMakeSubRangesList(aRowRangeList
, mInputRange
, BY_ROW
);
356 // Write ColumnX values
358 for(sal_Int32 i
= 0; lclBasicStatistics
[i
].aLabelId
; i
++)
361 ScRange aResultRange
;
362 OUString sFormula
= OUString::createFromAscii(lclBasicStatistics
[i
].aFormula
);
363 RowColumn(aColumnRangeList
, output
, aTemplate
, sFormula
, BY_COLUMN
, &aResultRange
);
364 if (lclBasicStatistics
[i
].aResultRangeName
!= nullptr)
366 OUString sResultRangeName
= OUString::createFromAscii(lclBasicStatistics
[i
].aResultRangeName
);
367 aTemplate
.autoReplaceRange("%" + sResultRangeName
+ "_COLUMN%", aResultRange
);
375 for(sal_Int32 i
= 0; lclBasicStatistics
[i
].aLabelId
; i
++)
378 ScRange aResultRange
;
379 OUString sFormula
= OUString::createFromAscii(lclBasicStatistics
[i
].aFormula
);
380 RowColumn(aRowRangeList
, output
, aTemplate
, sFormula
, BY_ROW
, &aResultRange
);
382 if (lclBasicStatistics
[i
].aResultRangeName
!= nullptr)
384 OUString sResultRangeName
= OUString::createFromAscii(lclBasicStatistics
[i
].aResultRangeName
);
385 aTemplate
.autoReplaceRange("%" + sResultRangeName
+ "_ROW%", aResultRange
);
391 // Write ANOVA labels
392 for(sal_Int32 i
= 0; lclAnovaLabels
[i
]; i
++)
394 output
.writeString(ScResId(lclAnovaLabels
[i
]));
399 // Setup auto-replace strings
400 aTemplate
.autoReplaceRange(strWildcardRange
, mInputRange
);
401 aTemplate
.autoReplaceRange(u
"%FIRST_COLUMN%"_ustr
, aColumnRangeList
[0]);
402 aTemplate
.autoReplaceRange(u
"%FIRST_ROW%"_ustr
, aRowRangeList
[0]);
407 output
.resetColumn();
408 output
.writeString("Rows");
412 aTemplate
.setTemplate("=SUMPRODUCT(%SUM_RANGE_ROW%;%MEAN_RANGE_ROW%) - SUM(%RANGE%)^2 / COUNT(%RANGE%)");
413 aTemplate
.autoReplaceAddress(u
"%ROW_SS%"_ustr
, output
.current());
414 output
.writeFormula(aTemplate
.getTemplate());
418 aTemplate
.setTemplate("=MAX(%COUNT_RANGE_COLUMN%) - 1");
419 aTemplate
.autoReplaceAddress(u
"%ROW_DF%"_ustr
, output
.current());
420 output
.writeFormula(aTemplate
.getTemplate());
424 aTemplate
.setTemplate("=%ROW_SS% / %ROW_DF%");
425 aTemplate
.autoReplaceAddress(u
"%MS_ROW%"_ustr
, output
.current());
426 output
.writeFormula(aTemplate
.getTemplate());
430 aTemplate
.setTemplate("=%MS_ROW% / %MS_ERROR%");
431 aTemplate
.applyAddress(u
"%MS_ERROR%", output
.current(-1, 2));
432 aTemplate
.autoReplaceAddress(u
"%F_ROW%"_ustr
, output
.current());
433 output
.writeFormula(aTemplate
.getTemplate());
437 aTemplate
.setTemplate("=FDIST(%F_ROW%; %ROW_DF%; %ERROR_DF%");
438 aTemplate
.applyAddress(u
"%ERROR_DF%", output
.current(-3, 2));
439 output
.writeFormula(aTemplate
.getTemplate());
443 aTemplate
.setTemplate("=FINV(%ALPHA%; %ROW_DF%; %ERROR_DF%");
444 aTemplate
.applyAddress(u
"%ERROR_DF%", output
.current(-4, 2));
445 output
.writeFormula(aTemplate
.getTemplate());
453 output
.resetColumn();
454 output
.writeString("Columns");
458 aTemplate
.setTemplate("=SUMPRODUCT(%SUM_RANGE_COLUMN%;%MEAN_RANGE_COLUMN%) - SUM(%RANGE%)^2 / COUNT(%RANGE%)");
459 aTemplate
.autoReplaceAddress(u
"%COLUMN_SS%"_ustr
, output
.current());
460 output
.writeFormula(aTemplate
.getTemplate());
464 aTemplate
.setTemplate("=MAX(%COUNT_RANGE_ROW%) - 1");
465 aTemplate
.autoReplaceAddress(u
"%COLUMN_DF%"_ustr
, output
.current());
466 output
.writeFormula(aTemplate
.getTemplate());
470 aTemplate
.setTemplate("=%COLUMN_SS% / %COLUMN_DF%");
471 aTemplate
.autoReplaceAddress(u
"%MS_COLUMN%"_ustr
, output
.current());
472 output
.writeFormula(aTemplate
.getTemplate());
476 aTemplate
.setTemplate("=%MS_COLUMN% / %MS_ERROR%");
477 aTemplate
.applyAddress(u
"%MS_ERROR%", output
.current(-1, 1));
478 aTemplate
.autoReplaceAddress(u
"%F_COLUMN%"_ustr
, output
.current());
479 output
.writeFormula(aTemplate
.getTemplate());
483 aTemplate
.setTemplate("=FDIST(%F_COLUMN%; %COLUMN_DF%; %ERROR_DF%");
484 aTemplate
.applyAddress(u
"%ERROR_DF%", output
.current(-3, 1));
485 output
.writeFormula(aTemplate
.getTemplate());
489 aTemplate
.setTemplate("=FINV(%ALPHA%; %COLUMN_DF%; %ERROR_DF%");
490 aTemplate
.applyAddress(u
"%ERROR_DF%", output
.current(-4, 1));
491 output
.writeFormula(aTemplate
.getTemplate());
499 output
.resetColumn();
500 output
.writeString("Error");
504 aTemplate
.setTemplate("=SUMSQ(%RANGE%)+SUM(%RANGE%)^2/COUNT(%RANGE%) - (SUMPRODUCT(%SUM_RANGE_ROW%;%MEAN_RANGE_ROW%) + SUMPRODUCT(%SUM_RANGE_COLUMN%;%MEAN_RANGE_COLUMN%))");
505 aTemplate
.autoReplaceAddress(u
"%ERROR_SS%"_ustr
, output
.current());
506 output
.writeFormula(aTemplate
.getTemplate());
510 aTemplate
.setTemplate("=%TOTAL_DF% - %ROW_DF% - %COLUMN_DF%");
511 aTemplate
.applyAddress(u
"%TOTAL_DF%", output
.current(0,1));
512 aTemplate
.autoReplaceAddress(u
"%ERROR_DF%"_ustr
, output
.current());
513 output
.writeFormula(aTemplate
.getTemplate());
517 aTemplate
.setTemplate("=%ERROR_SS% / %ERROR_DF%");
518 output
.writeFormula(aTemplate
.getTemplate());
525 output
.resetColumn();
526 output
.writeString("Total");
530 aTemplate
.setTemplate("=SUM(%ROW_SS%;%COLUMN_SS%;%ERROR_SS%)");
531 output
.writeFormula(aTemplate
.getTemplate());
535 aTemplate
.setTemplate("=COUNT(%RANGE%)-1");
536 output
.writeFormula(aTemplate
.getTemplate());
541 ScRange
ScAnalysisOfVarianceDialog::ApplyOutput(ScDocShell
* pDocShell
)
543 AddressWalkerWriter
output(mOutputAddress
, pDocShell
, mDocument
,
544 formula::FormulaGrammar::mergeToGrammar(formula::FormulaGrammar::GRAM_ENGLISH
, mAddressDetails
.eConv
));
545 FormulaTemplate
aTemplate(&mDocument
);
547 if (meFactor
== SINGLE_FACTOR
)
549 AnovaSingleFactor(output
, aTemplate
);
551 else if (meFactor
== TWO_FACTOR
)
553 AnovaTwoFactor(output
, aTemplate
);
556 return ScRange(output
.mMinimumAddress
, output
.mMaximumAddress
);
559 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */