1 /***************************************************************************
2 File : Anova.cpp
3 Project : QtiPlot
4 --------------------------------------------------------------------
5 Copyright : (C) 2010 by Ion Vasilief
6 Email (use @ for *) : ion_vasilief*yahoo.fr
7 Description : ANOVA
8
9 ***************************************************************************/
10
11 /***************************************************************************
12 * *
13 * This program is free software; you can redistribute it and/or modify *
14 * it under the terms of the GNU General Public License as published by *
15 * the Free Software Foundation; either version 2 of the License, or *
16 * (at your option) any later version. *
17 * *
18 * This program is distributed in the hope that it will be useful, *
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
21 * GNU General Public License for more details. *
22 * *
23 * You should have received a copy of the GNU General Public License *
24 * along with this program; if not, write to the Free Software *
25 * Foundation, Inc., 51 Franklin Street, Fifth Floor, *
26 * Boston, MA 02110-1301 USA *
27 * *
28 ***************************************************************************/
29 #include "Anova.h"
30
31 #include <QApplication>
32 #include <QLocale>
33
Anova(ApplicationWindow * parent,bool twoWay,double level)34 Anova::Anova(ApplicationWindow *parent, bool twoWay, double level)
35 : StatisticTest(parent, 0.0, level),
36 d_two_way(twoWay),
37 d_show_interactions(true),
38 d_anova_type(anova_fixed)
39 {
40 setObjectName(QObject::tr("ANOVA"));
41 }
42
addSample(const QString & colName,int aLevel,int bLevel)43 bool Anova::addSample(const QString& colName, int aLevel, int bLevel)
44 {
45 if (!d_n){
46 if (d_two_way){
47 d_factorA_levels << aLevel;
48 d_factorB_levels << bLevel;
49 }
50 return setData(colName);
51 }
52
53 Statistics *sample = new Statistics((ApplicationWindow *)this->parent(), colName);
54 if (!sample->dataSize()){
55 delete sample;
56 return false;
57 }
58
59 d_data_samples << sample;
60 if (d_two_way){
61 d_factorA_levels << aLevel;
62 d_factorB_levels << bLevel;
63 }
64 return true;
65 }
66
run()67 bool Anova::run()
68 {
69 if (!d_n)
70 return false;
71 if (d_two_way && d_data_samples.size() < 2){
72 QMessageBox::critical((ApplicationWindow *)parent(), QObject::tr("Attention!"),
73 QObject::tr("Two-Way ANOVA requires three or more data samples."));
74 return false;
75 } else if (!d_data_samples.size() && !d_two_way){
76 QMessageBox::critical((ApplicationWindow *)parent(), QObject::tr("Attention!"),
77 QObject::tr("One-Way ANOVA requires two or more data samples."));
78 return false;
79 }
80
81 QApplication::setOverrideCursor(Qt::WaitCursor);
82
83 if (d_two_way && !twoWayANOVA())
84 return false;
85 else if(!oneWayANOVA())
86 return false;
87
88 QApplication::restoreOverrideCursor();
89 return true;
90 }
91
twoWayANOVA()92 bool Anova::twoWayANOVA()
93 {
94 QList<int> aLevels;
95 QList<int> bLevels;
96 int samples = d_data_samples.size() + 1;
97 for (int i = 0; i < samples; i++){
98 int level = d_factorA_levels[i];
99 if (!aLevels.contains(level))
100 aLevels << level;
101
102 level = d_factorB_levels[i];
103 if (!bLevels.contains(level))
104 bLevels << level;
105 }
106
107 if (aLevels.size() < 2){
108 QApplication::restoreOverrideCursor();
109 QMessageBox::critical((ApplicationWindow *)parent(), QObject::tr("Attention!"),
110 QObject::tr("Factor A must have two or more levels."));
111 return false;
112 }
113 if (bLevels.size() < 2){
114 QApplication::restoreOverrideCursor();
115 QMessageBox::critical((ApplicationWindow *)parent(), QObject::tr("Attention!"),
116 QObject::tr("Factor B must have two or more levels."));
117 return false;
118 }
119
120 for (int i = 1; i <= aLevels.size(); i++){
121 for (int j = 1; j <= bLevels.size(); j++){
122 bool levelCombinationExists = false;
123 for (int k = 0; k < samples; k++){
124 if (d_factorA_levels[k] == i && d_factorB_levels[k] == j){
125 levelCombinationExists = true;
126 break;
127 }
128 }
129
130 if (!levelCombinationExists){
131 QApplication::restoreOverrideCursor();
132 QMessageBox::critical((ApplicationWindow *)parent(), QObject::tr("Attention!"),
133 QObject::tr("There are no data points in Factor A '%1' and Factor B '%2' level combination.").arg(levelName(i)).arg(levelName(j, true)));
134 return false;
135 }
136 }
137 }
138
139 unsigned int n = d_n;
140 foreach(Statistics *sample, d_data_samples)
141 n += sample->dataSize();
142
143 double *data = (double *)malloc(n*sizeof(double));
144 if (!data){
145 QApplication::restoreOverrideCursor();
146 memoryErrorMessage();
147 return false;
148 }
149
150 long J[2] = {aLevels.size(), bLevels.size()};
151
152 long f[n][2];
153 for (unsigned int i = 0; i < d_n; i++){
154 data[i] = d_data[i];
155 f[i][0] = d_factorA_levels[0];
156 f[i][1] = d_factorB_levels[0];
157 }
158
159 int aux = d_n;
160 int s = 1;
161 foreach(Statistics *sample, d_data_samples){
162 int size = sample->dataSize();
163 double *sampleData = sample->data();
164 for (int i = 0; i < size; i++){
165 data[aux] = sampleData[i];
166 f[aux][0] = d_factorA_levels[s];
167 f[aux][1] = d_factorB_levels[s];
168 aux++;
169 }
170 s++;
171 }
172
173 d_att = tamu_anova_twoway(data, f, n, J, d_anova_type);
174
175 free(data);
176
177 QApplication::restoreOverrideCursor();
178 return true;
179 }
180
oneWayANOVA()181 bool Anova::oneWayANOVA()
182 {
183 unsigned int n = d_n;
184 foreach(Statistics *sample, d_data_samples)
185 n += sample->dataSize();
186
187 long *factor = (long *)malloc(n*sizeof(long));
188 double *data = (double *)malloc(n*sizeof(double));
189 if (!data || !factor){
190 QApplication::restoreOverrideCursor();
191 memoryErrorMessage();
192 return false;
193 }
194
195 for (unsigned int i = 0; i < d_n; i++){
196 factor[i] = 1.0;
197 data[i] = d_data[i];
198 }
199
200 int aux = d_n;
201 int samples = 1;
202 foreach(Statistics *sample, d_data_samples){
203 int size = sample->dataSize();
204 samples++;
205 double *sampleData = sample->data();
206 for (int i = 0; i < size; i++){
207 data[aux] = sampleData[i];
208 factor[aux] = samples;
209 aux++;
210 }
211 }
212
213 d_at = tamu_anova(data, factor, n, samples);
214
215 free(data);
216 free(factor);
217 return true;
218 }
219
levelName(int level,bool b)220 QString Anova::levelName(int level, bool b)
221 {
222 QString l = QObject::tr("A");
223 if (b)
224 l = QObject::tr("B");
225 return l + QString::number(level);
226 }
227
logInfo()228 QString Anova::logInfo()
229 {
230 ApplicationWindow *app = (ApplicationWindow *)parent();
231 QLocale l = app->locale();
232 int p = app->d_decimal_digits;
233 QString sep = "\t";
234 QString sep1 = "-----------------------------------------------------------------------------------------------------------------------------\n";
235
236 QString s = "[" + QDateTime::currentDateTime().toString(Qt::LocalDate)+ " \"" + d_table->objectName() + "\"]\t";
237 if (d_two_way){
238 s += QObject::tr("Two-Way ANOVA") + " ";
239 switch(d_anova_type){
240 case 0:
241 s += QObject::tr("Fixed Model");
242 break;
243 case 1:
244 s += QObject::tr("Random Model");
245 break;
246 case 2:
247 s += QObject::tr("Mixed Model");
248 break;
249 }
250 s += "\n";
251 } else
252 s += QObject::tr("One-Way ANOVA") + "\n";
253
254 s += "\n";
255
256 if (d_descriptive_statistics){
257 s += Statistics::logInfo();
258 foreach(Statistics *sample, d_data_samples)
259 s += sample->logInfo(false);
260 }
261
262 if (d_two_way){
263 s += QObject::tr("Selected Data") + "\n\n";
264 s += QObject::tr("Sample") + sep + QObject::tr("Factor A Level") + sep + QObject::tr("Factor B Level") + "\n";
265 s += sep1;
266 s += sampleName() + sep + levelName(d_factorA_levels[0]) + sep + levelName(d_factorB_levels[0], true) + "\n";
267 int i = 1;
268 foreach(Statistics *sample, d_data_samples){
269 s += sample->sampleName() + sep + levelName(d_factorA_levels[i]) + sep + levelName(d_factorB_levels[i], true) + "\n";
270 i++;
271 }
272 s += sep1 + "\n";
273 } else {
274 s += QObject::tr("Null Hypothesis") + ":\t\t\t" + QObject::tr("The means of all selected datasets are equal") + "\n";
275 s += QObject::tr("Alternative Hypothesis") + ":\t\t" + QObject::tr("The means of one or more selected datasets are different") + "\n\n";
276 }
277
278 s += tr("ANOVA") + "\n\n";
279 s += QObject::tr("Source") + sep + QObject::tr("DoF") + sep + QObject::tr("Sum of Squares") + sep;
280 s += QObject::tr("Mean Square") + sep + QObject::tr("F Value") + sep + QObject::tr("P Value") + "\n";
281 s += sep1;
282 if (d_two_way){
283 s += QObject::tr("A") + sep + QString::number(d_att.dfA) + sep + l.toString(d_att.SSA, 'g', p) + sep + l.toString(d_att.MSA, 'g', p) + sep;
284 s += l.toString(d_att.FA, 'g', p) + sep + l.toString(d_att.pA, 'g', p) + "\n";
285 s += QObject::tr("B") + sep + QString::number(d_att.dfB) + sep + l.toString(d_att.SSB, 'g', p) + sep + l.toString(d_att.MSB, 'g', p) + sep;
286 s += l.toString(d_att.FB, 'g', p) + sep + l.toString(d_att.pB, 'g', p) + "\n";
287 if (d_show_interactions){
288 s += QObject::tr("A") + "*" + QObject::tr("B") + sep + QString::number(d_att.dfAB) + sep + l.toString(d_att.SSAB, 'g', p) + sep + l.toString(d_att.MSAB, 'g', p) + sep;
289 s += l.toString(d_att.FAB, 'g', p) + sep + l.toString(d_att.pAB, 'g', p) + "\n";
290 }
291 s += QObject::tr("Error") + sep + QString::number(d_att.dfE) + sep + l.toString(d_att.SSE, 'g', p) + sep + l.toString(d_att.MSE, 'g', p) + "\n";
292 s += QObject::tr("Total") + sep + QString::number(d_att.dfT) + sep + l.toString(d_att.SST, 'g', p) + "\n";
293 } else {
294 s += QObject::tr("Model") + sep + QString::number(d_at.dfTr) + sep + l.toString(d_at.SSTr, 'g', p) + sep + l.toString(d_at.MSTr, 'g', p) + sep;
295 s += l.toString(d_at.F, 'g', p) + sep + l.toString(d_at.p, 'g', p) + "\n";
296 s += QObject::tr("Error") + sep + QString::number(d_at.dfE) + sep + l.toString(d_at.SSE, 'g', p) + sep + l.toString(d_at.MSE, 'g', p) + "\n";
297 s += QObject::tr("Total") + sep + QString::number(d_at.dfT) + sep + l.toString(d_at.SST, 'g', p) + "\n";
298 }
299
300 s += sep1;
301 s += "\n";
302
303 if (!d_two_way){
304 s += QObject::tr("At the %1 level, the population means").arg(l.toString(d_significance_level, 'g', 6)) + " ";
305 if (d_at.p < d_significance_level)
306 s += QObject::tr("are significantly different");
307 else
308 s += QObject::tr("are not significantly different");
309
310 s += ".\n\n";
311 }
312
313 return s;
314 }
315
outputResultsTo(Table * t)316 void Anova::outputResultsTo(Table *t)
317 {
318 if (!t)
319 return;
320
321 int rows = t->numRows();
322 if (d_two_way)
323 t->setNumRows(rows + 5);
324 else
325 t->setNumRows(rows + 3);
326
327 if (t->numCols() < 6)
328 t->setNumCols(6);
329
330 if (d_two_way){
331 t->setText(rows, 0, QObject::tr("A"));
332 t->setText(rows + 1, 0, QObject::tr("B"));
333 t->setText(rows + 2, 0, QObject::tr("A*B"));
334 t->setText(rows + 3, 0, QObject::tr("Error"));
335 t->setText(rows + 4, 0, QObject::tr("Total"));
336 t->setColumnType(0, Table::Text);
337
338 t->setCell(rows, 1, d_att.dfA);
339 t->setCell(rows, 2, d_att.SSA);
340 t->setCell(rows, 3, d_att.MSA);
341 t->setCell(rows, 4, d_att.FA);
342 t->setCell(rows, 5, d_att.pA);
343
344 rows++;
345 t->setCell(rows, 1, d_att.dfB);
346 t->setCell(rows, 2, d_att.SSB);
347 t->setCell(rows, 3, d_att.MSB);
348 t->setCell(rows, 4, d_att.FB);
349 t->setCell(rows, 5, d_att.pB);
350
351 rows++;
352 t->setCell(rows, 1, d_att.dfAB);
353 t->setCell(rows, 2, d_att.SSAB);
354 t->setCell(rows, 3, d_att.MSAB);
355 t->setCell(rows, 4, d_att.FAB);
356 t->setCell(rows, 5, d_att.pAB);
357
358 rows++;
359 t->setCell(rows, 1, d_att.dfE);
360 t->setCell(rows, 2, d_att.SSE);
361 t->setCell(rows, 3, d_att.MSE);
362
363 rows++;
364 t->setCell(rows, 1, d_att.dfT);
365 t->setCell(rows, 2, d_att.SST);
366 } else {
367 t->setText(rows, 0, QObject::tr("Model"));
368 t->setText(rows + 1, 0, QObject::tr("Error"));
369 t->setText(rows + 2, 0, QObject::tr("Total"));
370 t->setColumnType(0, Table::Text);
371
372 t->setCell(rows, 1, d_at.dfTr);
373 t->setCell(rows, 2, d_at.SSTr);
374 t->setCell(rows, 3, d_at.MSTr);
375 t->setCell(rows, 4, d_at.F);
376 t->setCell(rows, 5, d_at.p);
377
378 rows++;
379 t->setCell(rows, 1, d_at.dfE);
380 t->setCell(rows, 2, d_at.SSE);
381 t->setCell(rows, 3, d_at.MSE);
382
383 rows++;
384 t->setCell(rows, 1, d_at.dfT);
385 t->setCell(rows, 2, d_at.SST);
386 }
387
388 for (int i = 0; i < t->numCols(); i++)
389 t->table()->adjustColumn(i);
390 }
391
resultTable(const QString & name)392 Table * Anova::resultTable(const QString& name)
393 {
394 ApplicationWindow *app = (ApplicationWindow *)parent();
395 Table *t = 0;
396 if (d_two_way){
397 t = app->newTable(5, 6);
398
399 t->setText(0, 0, QObject::tr("A"));
400 t->setText(1, 0, QObject::tr("B"));
401 t->setText(2, 0, QObject::tr("A*B"));
402 t->setText(3, 0, QObject::tr("Error"));
403 t->setText(4, 0, QObject::tr("Total"));
404 t->setColumnType(0, Table::Text);
405
406 t->setCell(0, 1, d_att.dfA);
407 t->setCell(0, 2, d_att.SSA);
408 t->setCell(0, 3, d_att.MSA);
409 t->setCell(0, 4, d_att.FA);
410 t->setCell(0, 5, d_att.pA);
411
412 t->setCell(1, 1, d_att.dfB);
413 t->setCell(1, 2, d_att.SSB);
414 t->setCell(1, 3, d_att.MSB);
415 t->setCell(1, 4, d_att.FB);
416 t->setCell(1, 5, d_att.pB);
417
418 t->setCell(2, 1, d_att.dfAB);
419 t->setCell(2, 2, d_att.SSAB);
420 t->setCell(2, 3, d_att.MSAB);
421 t->setCell(2, 4, d_att.FAB);
422 t->setCell(2, 5, d_att.pAB);
423
424 t->setCell(3, 1, d_att.dfE);
425 t->setCell(3, 2, d_att.SSE);
426 t->setCell(3, 3, d_att.MSE);
427
428 t->setCell(4, 1, d_att.dfT);
429 t->setCell(4, 2, d_att.SST);
430 } else {
431 t = app->newTable(3, 6);
432
433 t->setText(0, 0, QObject::tr("Model"));
434 t->setText(1, 0, QObject::tr("Error"));
435 t->setText(2, 0, QObject::tr("Total"));
436 t->setColumnType(0, Table::Text);
437
438 t->setCell(0, 1, d_at.dfTr);
439 t->setCell(0, 2, d_at.SSTr);
440 t->setCell(0, 3, d_at.MSTr);
441 t->setCell(0, 4, d_at.F);
442 t->setCell(0, 5, d_at.p);
443
444 t->setCell(1, 1, d_at.dfE);
445 t->setCell(1, 2, d_at.SSE);
446 t->setCell(1, 3, d_at.MSE);
447
448 t->setCell(2, 1, d_at.dfT);
449 t->setCell(2, 2, d_at.SST);
450 }
451
452 QStringList header = QStringList() << QObject::tr("Source") << QObject::tr("DoF") << QObject::tr("Sum of Squares")
453 << QObject::tr("Mean Square") << QObject::tr("F Value") << QObject::tr("P Value");
454 t->setHeader(header);
455
456 for (int i = 0; i < t->numCols(); i++)
457 t->table()->adjustColumn(i);
458
459 t->setWindowLabel(objectName() + " " + QObject::tr("Result Table"));
460 t->show();
461 return t;
462 }
463
freeMemory()464 void Anova::freeMemory()
465 {
466 Statistics::freeMemory();
467 foreach(Statistics *sample, d_data_samples)
468 delete sample;
469 }
470