diff --git a/src/backend/hypothesis_test/HypothesisTest.h b/src/backend/hypothesis_test/HypothesisTest.h --- a/src/backend/hypothesis_test/HypothesisTest.h +++ b/src/backend/hypothesis_test/HypothesisTest.h @@ -64,13 +64,14 @@ QString testName(); QString statsTable(); - void performTwoSampleTTest(); - void performTwoSampleIndependentTTest(bool equal_variance); + void performTwoSampleIndependentTTest(bool categorical_variable, bool equal_variance); void performTwoSamplePairedTTest(); - void PerformOneSampleTTest(); + void performOneSampleTTest(); void performTwoSampleIndependentZTest(); void performTwoSamplePairedZTest(); - void PerformOneSampleZTest(); + void performOneSampleZTest(); + + void performLeveneTest(bool categorical_variable); //virtual methods // QIcon icon() const override; QMenu* createContextMenu() override; diff --git a/src/backend/hypothesis_test/HypothesisTest.cpp b/src/backend/hypothesis_test/HypothesisTest.cpp --- a/src/backend/hypothesis_test/HypothesisTest.cpp +++ b/src/backend/hypothesis_test/HypothesisTest.cpp @@ -107,9 +107,9 @@ return d->m_stats_table; } -void HypothesisTest::performTwoSampleIndependentTTest(bool equal_variance) { +void HypothesisTest::performTwoSampleIndependentTTest(bool categorical_variable, bool equal_variance) { d->m_currTestName = "

Two Sample Independent T Test

"; - d->performTwoSampleIndependentTest(HypothesisTestPrivate::TestT, equal_variance); + d->performTwoSampleIndependentTest(HypothesisTestPrivate::TestT, categorical_variable, equal_variance); } void HypothesisTest::performTwoSamplePairedTTest() { @@ -117,9 +117,9 @@ d->performTwoSamplePairedTest(HypothesisTestPrivate::TestT); } -void HypothesisTest::PerformOneSampleTTest() { +void HypothesisTest::performOneSampleTTest() { d->m_currTestName = "

One Sample T Test

"; - d->PerformOneSampleTest(HypothesisTestPrivate::TestT); + d->performOneSampleTest(HypothesisTestPrivate::TestT); } void HypothesisTest::performTwoSampleIndependentZTest() { @@ -132,13 +132,15 @@ d->performTwoSamplePairedTest(HypothesisTestPrivate::TestZ); } -void HypothesisTest::PerformOneSampleZTest() { +void HypothesisTest::performOneSampleZTest() { d->m_currTestName = "

One Sample Z Test

"; - d->PerformOneSampleTest(HypothesisTestPrivate::TestZ); + d->performOneSampleTest(HypothesisTestPrivate::TestZ); } - - +void HypothesisTest::performLeveneTest(bool categorical_variable) { + d->m_currTestName = "

Levene Test for Equality of Variance

"; + d->performLeveneTest(categorical_variable); +} /****************************************************************************** * Private Implementations @@ -177,7 +179,7 @@ /**************************Two Sample Independent *************************************/ -void HypothesisTestPrivate::performTwoSampleIndependentTest(TestType test, bool equal_variance) { +void HypothesisTestPrivate::performTwoSampleIndependentTest(TestType test,bool categorical_variable, bool equal_variance) { QString test_name; double value; @@ -197,7 +199,7 @@ QString col1_name = m_columns[0]->name(); QString col2_name = m_columns[1]->name(); - if (m_columns[0]->columnMode() == AbstractColumn::Integer || m_columns[0]->columnMode() == AbstractColumn::Numeric) { + if (!categorical_variable && (m_columns[0]->columnMode() == AbstractColumn::Integer || m_columns[0]->columnMode() == AbstractColumn::Numeric)) { for (int i = 0; i < 2; i++) { findStats(m_columns[i], n[i], sum[i], mean[i], std[i]); @@ -208,23 +210,40 @@ } } } else { - ErrorType error_code = findStatsCategorical(m_columns[0], m_columns[1], n, sum, mean, std, col1_name, col2_name); + QMap col_name; + int np; + int total_rows; + + countPartitions(m_columns[0], np, total_rows); + if (np != 2) { + printError( i18n("Number of Categorical Variable in Column %1 is not equal to 2", m_columns[0]->name())); + emit q->changed(); + return; + } + + ErrorType error_code = findStatsCategorical(m_columns[0], m_columns[1], n, sum, mean, std, col_name, np, total_rows); + switch (error_code) { case ErrorUnqualSize: { printError( i18n("Unequal size between Column %1 and Column %2", m_columns[0]->name(), m_columns[1]->name())); emit q->changed(); return; - } case ErrorNotTwoCategoricalVariables: { - printError( i18n("Number of Categorical Variable in Column %1 is not equal to 2", m_columns[0]->name())); - emit q->changed(); - return; - } case ErrorEmptyColumn: { + }case ErrorEmptyColumn: { printError("At least one of selected column is empty"); emit q->changed(); return; } case NoError: break; } + + QMapIterator i(col_name); + while (i.hasNext()) { + i.next(); + if (i.value() == 1) + col1_name = i.key(); + else + col2_name = i.key(); + } } QVariant row_major[] = {"", "N", "Sum", "Mean", "Std", @@ -316,9 +335,6 @@ return; } case NoError: break; - default: - emit q->changed(); - return; } if (n == -1) { @@ -371,7 +387,7 @@ /******************************** One Sample ***************************************/ -void HypothesisTestPrivate::PerformOneSampleTest(TestType test) { +void HypothesisTestPrivate::performOneSampleTest(TestType test) { QString test_name; double value; int df = 0; @@ -401,7 +417,7 @@ return; } case NoError: break; - default: { + case ErrorEmptyColumn: { emit q->changed(); return; } @@ -442,6 +458,232 @@ } +/**************************************Levene Test****************************************/ +void HypothesisTestPrivate::performLeveneTest(bool categorical_variable) { + QString test_name; + double f_value; + int df = 0; // degree of freedom + double p_value = 0; + int np = 0; // number of partitions + int n = 0; + int total_rows = 0; + clearGlobalVariables(); + + if (m_columns.size() != 2) { + printError("Inappropriate number of columns selected"); + emit q->changed(); + return; + } + + if (!categorical_variable && (m_columns[0]->columnMode() == AbstractColumn::Integer || m_columns[0]->columnMode() == AbstractColumn::Numeric)) + np = m_columns.size(); + else + countPartitions(m_columns[0], np, n); + + if (np < 2) { + printError("select atleast two columns/ classes"); + emit q->changed(); + return; + } + + double* yi_bar = new double[np]; + double* zi_bar = new double[np]; + double zi_bar_bar = 0; + double* ni = new double[np]; + + for (int i = 0; i < np; i++) { + yi_bar[i] = 0; + zi_bar[i] = 0; + ni[i] = 0; + } + + QString* col_names = new QString[np]; + if (!categorical_variable && (m_columns[0]->columnMode() == AbstractColumn::Integer || m_columns[0]->columnMode() == AbstractColumn::Numeric)) { + total_rows = m_columns[0]->rowCount(); + + double value = 0; + for (int j = 0; j < total_rows; j++) { + int number_nan_cols = 0; + for (int i = 0; i < np; i++) { + value = m_columns[i]->valueAt(j); + if (std::isnan(value)) { + number_nan_cols++; + continue; + } + yi_bar[i] += value; + ni[i]++; + n++; + } + if (number_nan_cols == np) { + total_rows = j; + break; + } + } + + for (int i = 0; i < np; i++) { + if (ni[i] > 0) + yi_bar[i] = yi_bar[i] / ni[i]; + } + + for (int j = 0; j < total_rows; j++) { + for (int i = 0; i < np; i++) { + value = m_columns[i]->valueAt(j); + if (!(std::isnan(value))) + zi_bar[i] += abs(value - yi_bar[i]); + } + } + + for (int i = 0; i < np; i++) { + zi_bar_bar += zi_bar[i]; + if (ni[i] > 0) + zi_bar[i] = zi_bar[i] / ni[i]; + } + + zi_bar_bar = zi_bar_bar / n; + + double numerator_value = 0; + double denominator_value = 0; + + for (int j = 0; j < total_rows; j++) { + for (int i = 0; i < np; i++) { + value = m_columns[i]->valueAt(j); + if (!(std::isnan(value))) { + double zij = abs(value - yi_bar[i]); + denominator_value += qPow( (zij - zi_bar[i]), 2); + } + } + } + + for (int i = 0; i < np; i++) { + col_names[i] = m_columns[i]->name(); + numerator_value += ni[i]*qPow( (zi_bar[i]-zi_bar_bar), 2); + } + + f_value = ((n - np) / (np - 1)) * (numerator_value / denominator_value); + +// qDebug() << "n is " << n; +// qDebug() << "fvalue is " << f_value; +// qDebug() << "numerator is " << numerator_value; +// qDebug() << "denominator is " << denominator_value; + + } + else { + QMap classname_to_index; + + AbstractColumn::ColumnMode original_col_mode = m_columns[0]->columnMode(); + m_columns[0]->setColumnMode(AbstractColumn::Text); + + int partition_number = 1; + QString name; + double value; + int class_index; + + for (int j = 0; j < n; j++) { + name = m_columns[0]->textAt(j); + value = m_columns[1]->valueAt(j); + + if (std::isnan(value)) { + n = j; + break; + } + + if (classname_to_index[name] == 0) { + classname_to_index[name] = partition_number; + partition_number++; + } + + class_index = classname_to_index[name]-1; + ni[class_index]++; + yi_bar[class_index] += value; + } + + for (int i = 0; i < np; i++) { + if (ni[i] > 0) + yi_bar[i] = yi_bar[i] / ni[i]; + } + + for (int j = 0; j < n; j++) { + name = m_columns[0]->textAt(j); + value = m_columns[1]->valueAt(j); + class_index = classname_to_index[name] - 1; + zi_bar[class_index] += abs(value - yi_bar[class_index]); + } + + for (int i = 0; i < np; i++) { + zi_bar_bar += zi_bar[i]; + zi_bar[i] = zi_bar[i] / ni[i]; + } + + zi_bar_bar = zi_bar_bar / n; + + double numerator_value = 0; + double denominator_value = 0; + + for (int j = 0; j < n; j++) { + name = m_columns[0]->textAt(j); + value = m_columns[1]->valueAt(j); + class_index = classname_to_index[name] - 1; + double zij = abs(value - yi_bar[class_index]); + denominator_value += qPow( (zij - zi_bar[class_index]), 2); + } + + for (int i = 0; i < np; i++) + numerator_value += ni[i]*qPow( (zi_bar[i]-zi_bar_bar), 2); + + f_value = ((n - np) / (np - 1)) * (numerator_value / denominator_value); + +// qDebug() << "n is " << n; +// qDebug() << "fvalue is " << f_value; +// qDebug() << "numerator is " << numerator_value; +// qDebug() << "denominator is " << denominator_value; + + QMapIterator i(classname_to_index); + while (i.hasNext()) { + i.next(); + col_names[i.value()-1] = i.key(); + } + m_columns[0]->setColumnMode(original_col_mode); + } + + df = n - np; + + int row_count = np+1; + int column_count = 4; + + + QVariant* row_major = new QVariant[row_count*column_count]; + // header data; + row_major[0] = ""; row_major[1] = "Ni"; row_major[2] = "Yi_bar"; row_major[3] = "Zi_bar"; + + // table data + for (int row_i = 1; row_i < row_count; row_i++) { + row_major[row_i*column_count] = col_names[row_i-1]; + row_major[row_i*column_count + 1] = ni[row_i-1]; + row_major[row_i*column_count + 2] = yi_bar[row_i-1]; + row_major[row_i*column_count + 3] = zi_bar[row_i-1]; + + } + m_stats_table = getHtmlTable(row_count, column_count, row_major); + + p_value = nsl_stats_fdist_p(f_value, static_cast(np-1), df); + + printLine(0, "Null Hypothesis: Variance is equal between all classes", "blue"); + printLine(1, "Alternate Hypothesis: Variance is not equal in at-least one pair of classes", "blue"); + printLine(2, i18n("Significance level is %1", m_significance_level), "blue"); + printLine(4, i18n("F Value is %1 ", f_value), "green"); + printLine(5, i18n("P Value is %1 ", p_value), "green"); + printLine(6, i18n("Degree of Freedom is %1", df), "green"); + + if (p_value <= m_significance_level) { + q->m_view->setResultLine(5, i18n("We can safely reject Null Hypothesis for significance level %1", m_significance_level), Qt::ToolTipRole); + printLine(8, "Requirement for homogeneity is not met", "red"); + } else { + q->m_view->setResultLine(5, i18n("There is a plausibility for Null Hypothesis to be true"), Qt::ToolTipRole); + printLine(8, "Requirement for homogeneity is met", "green"); + } + emit q->changed(); + return; +} /***************************************Helper Functions*************************************/ HypothesisTestPrivate::ErrorType HypothesisTestPrivate::findStats(const Column* column, int &count, double &sum, double &mean, double &std) { @@ -519,75 +761,81 @@ return HypothesisTestPrivate::NoError; } -HypothesisTestPrivate::ErrorType HypothesisTestPrivate::findStatsCategorical(const Column *column1, const Column *column2, int n[], double sum[], double mean[], double std[], QString &col1_name, QString &col2_name) { - // clearing and initialising variables; +void HypothesisTestPrivate::countPartitions(Column *column, int &np, int &total_rows) { + total_rows = column->rowCount(); + np = 0; + QString cell_value; + QMap discovered_categorical_var; - const Column* columns[] = {column1, column2}; + AbstractColumn::ColumnMode original_col_mode = column->columnMode(); + column->setColumnMode(AbstractColumn::Text); - for (int i = 0; i < 2; i++) { + for (int i = 0; i < total_rows; i++) { + cell_value = column->textAt(i); + + if (cell_value.isEmpty()) { + total_rows = i; + break; + } + + if (discovered_categorical_var[cell_value]) + continue; + + discovered_categorical_var[cell_value] = true; + np++; + } + column->setColumnMode(original_col_mode); +} + +HypothesisTestPrivate::ErrorType HypothesisTestPrivate::findStatsCategorical(Column *column1, Column *column2, int n[], double sum[], double mean[], double std[], QMap &col_name, const int &np, const int &total_rows) { + Column* columns[] = {column1, column2}; + for (int i = 0; i < np; i++) { + n[i] = 0; sum[i] = 0; mean[i] = 0; std[i] = 0; - n[i] = 0; } + AbstractColumn::ColumnMode original_col_mode = columns[0]->columnMode(); + columns[0]->setColumnMode(AbstractColumn::Text); - int count_temp = columns[0]->rowCount(); - col1_name = ""; - col2_name = ""; - for (int i = 0; i < count_temp; i++) { + int partition_number = 1; + for (int i = 0; i < total_rows; i++) { QString name = columns[0]->textAt(i); + + name = columns[0]->textAt(i); double value = columns[1]->valueAt(i); - if (name.isEmpty() || std::isnan(value)) { - if (name.isEmpty() && std::isnan(value)) - break; - else - return HypothesisTestPrivate::ErrorUnqualSize; + if (std::isnan(value)) { + columns[0]->setColumnMode(original_col_mode); + return HypothesisTestPrivate::ErrorUnqualSize; } - if (name == col1_name) { - n[0]++; - sum[0] += value; - } else if (name == col2_name) { - n[1]++; - sum[1] += value; - } else if (col1_name.isEmpty()) { - n[0]++; - sum[0] += value; - col1_name = name; - } else if (col2_name.isEmpty()) { - n[1]++; - sum[1] += value; - col2_name = name; + if (col_name[name] == 0) { + col_name[name] = partition_number; + partition_number++; } - else - return HypothesisTestPrivate::ErrorNotTwoCategoricalVariables; - } - - if (col1_name.isEmpty() || col2_name.isEmpty()) - return HypothesisTestPrivate::ErrorNotTwoCategoricalVariables; - - mean[0] = sum[0]/n[0]; - mean[1] = sum[1]/n[1]; + n[col_name[name]-1]++; + sum[col_name[name]-1] += value; + } + for (int i = 0; i < np; i++) + mean[i] = sum[i] / n[i]; - for (int i = 0; i < n[0]+n[1]; i++) { + for (int i = 0; i < total_rows; i++) { QString name = columns[0]->textAt(i); double value = columns[1]->valueAt(i); - if (name == col1_name) - std[0] += qPow( (value - mean[0]), 2); - else - std[1] += qPow( (value - mean[1]), 2); + std[col_name[name]-1] += qPow( (value - mean[col_name[name]-1]), 2); } - for (int i = 0; i < 2; i++) { + for (int i = 0; i < np; i++) { if (n[i] > 1) std[i] = std[i] / (n[i] - 1); std[i] = qSqrt(std[i]); } + columns[0]->setColumnMode(original_col_mode); return HypothesisTestPrivate::NoError; } diff --git a/src/backend/hypothesis_test/HypothesisTestPrivate.h b/src/backend/hypothesis_test/HypothesisTestPrivate.h --- a/src/backend/hypothesis_test/HypothesisTestPrivate.h +++ b/src/backend/hypothesis_test/HypothesisTestPrivate.h @@ -40,15 +40,16 @@ virtual ~HypothesisTestPrivate(); enum TestType {TestT, TestZ}; - enum ErrorType {ErrorUnqualSize, ErrorNotTwoCategoricalVariables, ErrorEmptyColumn, NoError}; + enum ErrorType {ErrorUnqualSize, ErrorEmptyColumn, NoError}; QString name() const; void setDataSourceSpreadsheet(Spreadsheet* spreadsheet); void setColumns(QStringList cols); - void performTwoSampleIndependentTest(TestType test, bool equal_variance = true); + void performTwoSampleIndependentTest(TestType test, bool categorical_variable = false, bool equal_variance = true); void performTwoSamplePairedTest(TestType test); - void PerformOneSampleTest(TestType test); + void performOneSampleTest(TestType test); + void performLeveneTest(bool categorical_variable); HypothesisTest* const q; HypothesisTest::DataSourceType dataSourceType{HypothesisTest::DataSourceSpreadsheet}; @@ -65,9 +66,11 @@ QString m_stats_table; HypothesisTest::TailType tail_type; private: + void countPartitions(Column* column, int &np, int &total_rows); + ErrorType findStats(const Column* column,int &count, double &sum, double &mean, double &std); ErrorType findStatsPaired(const Column *column1, const Column *column2, int &count, double &sum, double &mean, double &std); - ErrorType findStatsCategorical(const Column *column1, const Column *column2, int n[2], double sum[2], double mean[2], double std[2], QString &col1_name, QString &col2_name); + ErrorType findStatsCategorical(Column *column1, Column *column2, int n[], double sum[], double mean[], double std[], QMap &col_name, const int &np, const int &total_rows); double getPValue(const TestType &test, double &value, const QString &col1_name, const QString &col2_name, const int df); QString getHtmlTable(int row, int column, QVariant *row_major); diff --git a/src/kdefrontend/dockwidgets/HypothesisTestDock.h b/src/kdefrontend/dockwidgets/HypothesisTestDock.h --- a/src/kdefrontend/dockwidgets/HypothesisTestDock.h +++ b/src/kdefrontend/dockwidgets/HypothesisTestDock.h @@ -83,8 +83,9 @@ // void nameChanged(); // void commentChanged(); void dataSourceTypeChanged(int); - void doHypothesisTest(); void showHypothesisTest(); + void doHypothesisTest(); + void performLeveneTest(); void spreadsheetChanged(const QModelIndex&); void col1CatIndexChanged(int index); // void connectionChanged(); diff --git a/src/kdefrontend/dockwidgets/HypothesisTestDock.cpp b/src/kdefrontend/dockwidgets/HypothesisTestDock.cpp --- a/src/kdefrontend/dockwidgets/HypothesisTestDock.cpp +++ b/src/kdefrontend/dockwidgets/HypothesisTestDock.cpp @@ -80,6 +80,8 @@ // making all test blocks invisible at starting. ui.lCol1Categorical->setVisible(false); ui.cbCol1Categorical->setVisible(false); + ui.pbLeveneTest->setVisible(false); + ui.chbCategorical->setVisible(false); ui.lCol1->setVisible(false); ui.cbCol1->setVisible(false); ui.lCol2->setVisible(false); @@ -187,13 +189,15 @@ // connect(ui.cbTest, static_cast(&QComboBox::currentIndexChanged), this, &HypothesisTestDock::showHypothesisTest); // connect(ui.cbTestType, static_cast(&QComboBox::currentIndexChanged), this, &HypothesisTestDock::showHypothesisTest); connect(ui.pbPerformTest, &QPushButton::clicked, this, &HypothesisTestDock::doHypothesisTest); + connect(ui.pbLeveneTest, &QPushButton::clicked, this, &HypothesisTestDock::performLeveneTest); + //connecting null hypothesis and alternate hypothesis radio button connect(ui.rbH1OneTail1, &QRadioButton::toggled, this, &HypothesisTestDock::onRbH1OneTail1Toggled); connect(ui.rbH1OneTail2, &QRadioButton::toggled, this, &HypothesisTestDock::onRbH1OneTail2Toggled); connect(ui.rbH1TwoTail, &QRadioButton::toggled, this, &HypothesisTestDock::onRbH1TwoTailToggled); - connect(ui.cbCol1Categorical, QOverload::of(&QComboBox::currentIndexChanged), this, &HypothesisTestDock::col1CatIndexChanged); + connect(ui.cbCol1Categorical, static_cast(&QComboBox::currentIndexChanged), this, &HypothesisTestDock::col1CatIndexChanged); } void HypothesisTestDock::setHypothesisTest(HypothesisTest* HypothesisTest) { @@ -268,6 +272,8 @@ ui.lCol2->setVisible(two_sample_independent || two_sample_paired || one_sample); ui.cbCol2->setVisible(two_sample_independent || two_sample_paired || one_sample); ui.chbEqualVariance->setVisible(ttest && two_sample_independent); + ui.chbCategorical->setVisible(ttest && two_sample_independent); + ui.pbLeveneTest->setVisible(ttest && two_sample_independent); ui.chbEqualVariance->setChecked(true); ui.pbPerformTest->setEnabled(two_sample_independent || two_sample_paired || one_sample); @@ -304,7 +310,7 @@ if(two_sample_independent) { cols << ui.cbCol1Categorical->currentText() << ui.cbCol2->currentText(); m_hypothesisTest->setColumns(cols); - m_hypothesisTest->performTwoSampleIndependentTTest( ui.chbEqualVariance->isChecked()); + m_hypothesisTest->performTwoSampleIndependentTTest(ui.chbCategorical->isChecked(), ui.chbEqualVariance->isChecked()); } else if(two_sample_paired) { cols << ui.cbCol1->currentText(); @@ -315,7 +321,7 @@ else if(one_sample){ cols << ui.cbCol1->currentText(); m_hypothesisTest->setColumns(cols); - m_hypothesisTest->PerformOneSampleTTest(); + m_hypothesisTest->performOneSampleTTest(); } } else if(ztest) { @@ -334,11 +340,21 @@ else if(one_sample){ cols << ui.cbCol1->currentText(); m_hypothesisTest->setColumns(cols); - m_hypothesisTest->PerformOneSampleZTest(); + m_hypothesisTest->performOneSampleZTest(); } } } +void HypothesisTestDock::performLeveneTest() { + + QStringList cols; + cols << ui.cbCol1Categorical->currentText() << ui.cbCol2->currentText(); + m_hypothesisTest->setColumns(cols); + m_hypothesisTest->setSignificanceLevel(ui.leAlpha->text()); + + m_hypothesisTest->performLeveneTest(ui.chbCategorical->isChecked()); +} + //void HypothesisTestDock::setModelIndexFromAspect(TreeViewComboBox* cb, const AbstractAspect* aspect) { // if (aspect) // cb->setCurrentModelIndex(m_aspectTreeModel->modelIndexOfAspect(aspect)); @@ -469,7 +485,7 @@ //} void HypothesisTestDock::dataSourceTypeChanged(int index) { - HypothesisTest::DataSourceType type = (HypothesisTest::DataSourceType)index; + HypothesisTest::DataSourceType type = static_cast(index); bool showDatabase = (type == HypothesisTest::DataSourceDatabase); ui.lSpreadsheet->setVisible(!showDatabase); cbSpreadsheet->setVisible(!showDatabase); @@ -519,9 +535,13 @@ if (col1->columnMode() == AbstractColumn::Integer || col1->columnMode() == AbstractColumn::Numeric) { ui.lCol2->setText( i18n("Independent Variable")); + ui.chbCategorical->setChecked(false); + ui.chbCategorical->setEnabled(true); } else { ui.lCol2->setText( i18n("Dependent Variable")); + ui.chbCategorical->setChecked(true); + ui.chbCategorical->setEnabled(false); } } diff --git a/src/kdefrontend/hypothesis_test/HypothesisTestView.h b/src/kdefrontend/hypothesis_test/HypothesisTestView.h --- a/src/kdefrontend/hypothesis_test/HypothesisTestView.h +++ b/src/kdefrontend/hypothesis_test/HypothesisTestView.h @@ -49,7 +49,6 @@ class QItemSelection; class QLabel; class QTextEdit; -class QLineEdit; class HypothesisTestView : public QWidget { Q_OBJECT @@ -75,7 +74,7 @@ HypothesisTest* m_hypothesisTest; QLabel* m_testName; - QLabel* m_statsTable; + QTextEdit* m_statsTable; QWidget* m_summaryResults; QLabel* m_resultLine[10]; diff --git a/src/kdefrontend/hypothesis_test/HypothesisTestView.cpp b/src/kdefrontend/hypothesis_test/HypothesisTestView.cpp --- a/src/kdefrontend/hypothesis_test/HypothesisTestView.cpp +++ b/src/kdefrontend/hypothesis_test/HypothesisTestView.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -59,10 +60,11 @@ HypothesisTestView::HypothesisTestView(HypothesisTest* hypothesisTest) : QWidget(), m_hypothesisTest(hypothesisTest), m_testName(new QLabel()), - m_statsTable(new QLabel()), + m_statsTable(new QTextEdit()), m_summaryResults(new QWidget()){ - m_summaryResults->setMouseTracking(true); + m_statsTable->setReadOnly(true); + auto* layout = new QVBoxLayout(this); layout->addWidget(m_testName); layout->addWidget(m_statsTable); @@ -166,7 +168,7 @@ void HypothesisTestView::changed() { m_testName->setText(m_hypothesisTest->testName()); - m_statsTable->setText(m_hypothesisTest->statsTable()); + m_statsTable->setHtml(m_hypothesisTest->statsTable()); } void HypothesisTestView::exportToFile(const QString& path, const bool exportHeader, const QString& separator, QLocale::Language language) const { diff --git a/src/kdefrontend/ui/dockwidgets/hypothesistestdock.ui b/src/kdefrontend/ui/dockwidgets/hypothesistestdock.ui --- a/src/kdefrontend/ui/dockwidgets/hypothesistestdock.ui +++ b/src/kdefrontend/ui/dockwidgets/hypothesistestdock.ui @@ -36,7 +36,7 @@ 280 - 710 + 740 77 23 @@ -49,7 +49,7 @@ 10 - 480 + 510 341 106 @@ -126,7 +126,7 @@ 10 - 610 + 640 217 81 @@ -304,8 +304,8 @@ - 12 - 433 + 20 + 460 125 23 @@ -404,6 +404,32 @@ Qt::Vertical + + + + 20 + 430 + 158 + 25 + + + + Categorical Variable + + + + + + 270 + 430 + 121 + 25 + + + + Levene's Test + +