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