diff --git a/src/backend/generalTest/CorrelationCoefficient.cpp b/src/backend/generalTest/CorrelationCoefficient.cpp index e241c6268..232eed271 100644 --- a/src/backend/generalTest/CorrelationCoefficient.cpp +++ b/src/backend/generalTest/CorrelationCoefficient.cpp @@ -1,437 +1,436 @@ /*************************************************************************** File : CorrelationCoefficient.cpp Project : LabPlot Description : Finding Correlation Coefficient on data provided -------------------------------------------------------------------- Copyright : (C) 2019 Devanshu Agarwal(agarwaldevanshu8@gmail.com) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "CorrelationCoefficient.h" #include "GeneralTest.h" #include "kdefrontend/generalTest/CorrelationCoefficientView.h" #include "backend/spreadsheet/Spreadsheet.h" #include "backend/core/column/Column.h" #include "backend/lib/macros.h" #include #include #include #include #include #include #include #include #include #include #include #include extern "C" { #include "backend/nsl/nsl_stats.h" } CorrelationCoefficient::CorrelationCoefficient(const QString& name) : GeneralTest(name, AspectType::CorrelationCoefficient) { } CorrelationCoefficient::~CorrelationCoefficient() { } void CorrelationCoefficient::performTest(Test test, bool categoricalVariable) { //QDEBUG("in perform test"); m_statsTable = ""; m_tooltips.clear(); m_correlationValue = 0; m_statisticValue.clear(); m_pValue.clear(); for (int i = 0; i < RESULTLINESCOUNT; i++) m_resultLine[i]->clear(); switch (test) { case CorrelationCoefficient::Test::Pearson: { m_currTestName = "

" + i18n("Pearson's r Correlation Test") + "

"; performPearson(categoricalVariable); break; } case CorrelationCoefficient::Test::Kendall: m_currTestName = "

" + i18n("Kendall's Rank Correlation Test") + "

"; performKendall(); break; case CorrelationCoefficient::Test::Spearman: { m_currTestName = "

" + i18n("Spearman Correlation Coefficient Test") + "

"; performSpearman(); break; } } emit changed(); } double CorrelationCoefficient::correlationValue() const{ return m_correlationValue; } QList CorrelationCoefficient::statisticValue() const{ return m_statisticValue; } QList CorrelationCoefficient::pValue() const{ return m_pValue; } /*************************************************************************************************************************** * Private Implementations * ************************************************************************************************************************/ /*********************************************Pearson r ******************************************************************/ //Formulaes are taken from https://www.statisticssolutions.com/correlation-pearson-kendall-spearman/ // variables: // N = total number of observations // sumColx = sum of values in colx // sumSqColx = sum of square of values in colx // sumColxColy = sum of product of values in colx and coly //TODO: support for col1 is categorical. -//TODO: add automatic test //TODO: add tooltip for correlation value result //TODO: find p value void CorrelationCoefficient::performPearson(bool categoricalVariable) { //QDEBUG("in pearson"); if (m_columns.count() != 2) { printError("Select only 2 columns "); return; } if (categoricalVariable) { printLine(1, "currently categorical variable not supported", "blue"); return; } QString col1Name = m_columns[0]->name(); QString col2Name = m_columns[1]->name(); if (!isNumericOrInteger(m_columns[1])) { printError("Column " + col2Name + " should contain only numeric or interger values"); } int N = findCount(m_columns[0]); if (N != findCount(m_columns[1])) { printError("Number of data values in Column: " + col1Name + "and Column: " + col2Name + "are not equal"); return; } double sumCol1 = findSum(m_columns[0], N); double sumCol2 = findSum(m_columns[1], N); double sumSqCol1 = findSumSq(m_columns[0], N); double sumSqCol2 = findSumSq(m_columns[1], N); double sumCol12 = 0; for (int i = 0; i < N; i++) sumCol12 += m_columns[0]->valueAt(i) * m_columns[1]->valueAt(i); // printing table; // cell constructor structure; data, level, rowSpanCount, m_columnspanCount, isHeader; QList rowMajor; int level = 0; // horizontal header QString sigma = UTF8_QSTRING("Σ"); rowMajor.append(new Cell("", level, true)); rowMajor.append(new Cell("N", level, true, "Total Number of Observations")); rowMajor.append(new Cell(QString(sigma + "Scores"), level, true, "Sum of Scores in each column")); rowMajor.append(new Cell(QString(sigma + "Scores2"), level, true, "Sum of Squares of scores in each column")); rowMajor.append(new Cell(QString(sigma + "(" + UTF8_QSTRING("∏") + "Scores)"), level, true, "Sum of product of scores of both columns")); //data with vertical header. level++; rowMajor.append(new Cell(col1Name, level, true)); rowMajor.append(new Cell(N, level)); rowMajor.append(new Cell(sumCol1, level)); rowMajor.append(new Cell(sumSqCol1, level)); rowMajor.append(new Cell(sumCol12, level, false, "", 2, 1)); level++; rowMajor.append(new Cell(col2Name, level, true)); rowMajor.append(new Cell(N, level)); rowMajor.append(new Cell(sumCol2, level)); rowMajor.append(new Cell(sumSqCol2, level)); m_statsTable += getHtmlTable3(rowMajor); m_correlationValue = (N * sumCol12 - sumCol1*sumCol2) / sqrt((N * sumSqCol1 - gsl_pow_2(sumCol1)) * (N * sumSqCol2 - gsl_pow_2(sumCol2))); printLine(0, QString("Correlation Value is %1").arg(round(m_correlationValue)), "green"); } /***********************************************Kendall ******************************************************************/ // used knight algorithm for fast performance O(nlogn) rather than O(n^2) // http://adereth.github.io/blog/2013/10/30/efficiently-computing-kendalls-tau/ // TODO: Change date format type to original for numeric type; // TODO: add tooltips. // TODO: Compute tauB for ties. // TODO: find P Value from Z Value void CorrelationCoefficient::performKendall() { QDEBUG("in perform kendall") if (m_columns.count() != 2) { printError("Select only 2 columns "); return; } QString col1Name = m_columns[0]->name(); QString col2Name = m_columns[1]->name(); int N = findCount(m_columns[0]); if (N != findCount(m_columns[1])) { printError("Number of data values in Column: " + col1Name + "and Column: " + col2Name + "are not equal"); QDEBUG("unequal number of rows") return; } int col2Ranks[N]; if (isNumericOrInteger(m_columns[0]) || isNumericOrInteger(m_columns[1])) { if (isNumericOrInteger(m_columns[0]) && isNumericOrInteger(m_columns[1])) { for (int i = 0; i < N; i++) col2Ranks[int(m_columns[0]->valueAt(i)) - 1] = int(m_columns[1]->valueAt(i)); } else { printError(QString("Ranking System should be same for both Column: %1 and Column: %2
" "Hint: Check for data types of columns").arg(col1Name).arg(col2Name)); QDEBUG("ranking system not same") return; } } else { AbstractColumn::ColumnMode origCol1Mode = m_columns[0]->columnMode(); AbstractColumn::ColumnMode origCol2Mode = m_columns[1]->columnMode(); m_columns[0]->setColumnMode(AbstractColumn::Text); m_columns[1]->setColumnMode(AbstractColumn::Text); QMap ValueToRank; for (int i = 0; i < N; i++) { if (ValueToRank[m_columns[0]->textAt(i)] != 0) { printError("Currently ties are not supported"); m_columns[0]->setColumnMode(origCol1Mode); m_columns[1]->setColumnMode(origCol2Mode); return; } ValueToRank[m_columns[0]->textAt(i)] = i + 1; } for (int i = 0; i < N; i++) col2Ranks[i] = ValueToRank[m_columns[1]->textAt(i)]; m_columns[0]->setColumnMode(origCol1Mode); m_columns[1]->setColumnMode(origCol2Mode); } int nPossiblePairs = (N * (N - 1)) / 2; int nDiscordant = findDiscordants(col2Ranks, 0, N - 1); int nCorcordant = nPossiblePairs - nDiscordant; m_correlationValue = double(nCorcordant - nDiscordant) / nPossiblePairs; m_statisticValue.append((3 * (nCorcordant - nDiscordant)) / sqrt(N * (N- 1) * (2 * N + 5) / 2)); printLine(0 , QString("Number of Discordants are %1").arg(nDiscordant), "green"); printLine(1 , QString("Number of Concordant are %1").arg(nCorcordant), "green"); printLine(2 , QString("Tau a is %1").arg(round(m_correlationValue)), "green"); printLine(3 , QString("Z Value is %1").arg(round(m_statisticValue[0])), "green"); return; } /***********************************************Spearman ******************************************************************/ // All formulaes and symbols are taken from : https://www.statisticshowto.datasciencecentral.com/spearman-rank-correlation-definition-calculate/ void CorrelationCoefficient::performSpearman() { if (m_columns.count() != 2) { printError("Select only 2 columns "); return; } QString col1Name = m_columns[0]->name(); QString col2Name = m_columns[1]->name(); int N = findCount(m_columns[0]); if (N != findCount(m_columns[1])) { printError("Number of data values in Column: " + col1Name + "and Column: " + col2Name + "are not equal"); return; } QMap col1Ranks; convertToRanks(m_columns[0], N, col1Ranks); QMap col2Ranks; convertToRanks(m_columns[1], N, col2Ranks); double ranksCol1Mean = 0; double ranksCol2Mean = 0; // QString ranks1 = ""; // QString ranks2 = ""; for (int i = 0; i < N; i++) { ranksCol1Mean += col1Ranks[int(m_columns[0]->valueAt(i))]; ranksCol2Mean += col2Ranks[int(m_columns[1]->valueAt(i))]; // ranks1 += ", " + QString::number(col1Ranks[m_columns[0]->valueAt(i)]); // ranks2 += ", " + QString::number(col2Ranks[m_columns[1]->valueAt(i)]); } ranksCol1Mean = ranksCol1Mean / N; ranksCol2Mean = ranksCol2Mean / N; //QDEBUG("ranks 1 and ranks2 are " ); //QDEBUG(ranks1); //QDEBUG(ranks2); //QDEBUG("Mean ranks are " << ranksCol1Mean << ranksCol2Mean); double s12 = 0; double s1 = 0; double s2 = 0; for (int i = 0; i < N; i++) { double centeredRank_1 = col1Ranks[int(m_columns[0]->valueAt(i))] - ranksCol1Mean; double centeredRank_2 = col2Ranks[int(m_columns[1]->valueAt(i))] - ranksCol2Mean; s12 += centeredRank_1 * centeredRank_2; s1 += gsl_pow_2(centeredRank_1); s2 += gsl_pow_2(centeredRank_2); } s12 = s12 / N; s1 = s1 / N; s2 = s2 / N; //QDEBUG("s12, s1, s2 are " << s12 << " " << s1 << " " << s2); m_correlationValue = s12 / std::sqrt(s1 * s2); printLine(0, QString("Spearman Rank Correlation value is %1").arg(m_correlationValue), "green"); } /***********************************************Helper Functions******************************************************************/ int CorrelationCoefficient::findDiscordants(int *ranks, int start, int end) { if (start >= end) return 0; int mid = (start + end) / 2; int leftDiscordants = findDiscordants(ranks, start, mid); int rightDiscordants = findDiscordants(ranks, mid + 1, end); int len = end - start + 1; int leftLen = mid - start + 1; int rightLen = end - mid; int leftLenRemain = leftLen; int leftRanks[leftLen]; int rightRanks[rightLen]; for (int i = 0; i < leftLen; i++) leftRanks[i] = ranks[start + i]; for (int i = leftLen; i < leftLen + rightLen; i++) rightRanks[i - leftLen] = ranks[start + i]; int mergeDiscordants = 0; int i = 0, j = 0, k =0; while (i < len) { if (j >= leftLen) { ranks[start + i] = rightRanks[k]; k++; } else if (k >= rightLen) { ranks[start + i] = leftRanks[j]; j++; } else if (leftRanks[j] < rightRanks[k]) { ranks[start + i] = leftRanks[j]; j++; leftLenRemain--; } else if (leftRanks[j] > rightRanks[k]) { ranks[start + i] = rightRanks[k]; mergeDiscordants += leftLenRemain; k++; } i++; } return leftDiscordants + rightDiscordants + mergeDiscordants; } void CorrelationCoefficient::convertToRanks(const Column* col, int N, QMap &ranks) { if (!isNumericOrInteger(col)) return; //QDEBUG("in convert to ranks"); double* sortedList = new double[N]; for (int i = 0; i < N; i++) sortedList[i] = col->valueAt(i); std::sort(sortedList, sortedList + N, std::greater()); // QString debug_sortedList = ""; ranks.clear(); for (int i = 0; i < N; i++) { ranks[sortedList[i]] = i + 1; // debug_sortedList += ", " + QString::number(sortedList[i]); } //QDEBUG("sorted list is " << debug_sortedList); delete[] sortedList; } void CorrelationCoefficient::convertToRanks(const Column* col, QMap &ranks) { convertToRanks(col, findCount(col), ranks); } /***********************************************Virtual Functions******************************************************************/ QWidget* CorrelationCoefficient::view() const { if (!m_partView) { m_view = new CorrelationCoefficientView(const_cast(this)); m_partView = m_view; } return m_partView; } diff --git a/src/backend/generalTest/GeneralTest.cpp b/src/backend/generalTest/GeneralTest.cpp index 1b0e47557..de2af219e 100644 --- a/src/backend/generalTest/GeneralTest.cpp +++ b/src/backend/generalTest/GeneralTest.cpp @@ -1,558 +1,562 @@ /*************************************************************************** File : GeneralTest.cpp Project : LabPlot Description : Doing Hypothesis-Test on data provided -------------------------------------------------------------------- Copyright : (C) 2019 Devanshu Agarwal(agarwaldevanshu8@gmail.com) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "GeneralTest.h" #include "kdefrontend/generalTest/HypothesisTestView.h" #include "backend/spreadsheet/Spreadsheet.h" #include "backend/core/column/Column.h" #include "backend/lib/macros.h" //#include //#include //#include #include #include //#include //#include //#include #include #include #include extern "C" { #include "backend/nsl/nsl_stats.h" } GeneralTest::GeneralTest(const QString& name, const AspectType& type) : AbstractPart(name, type), m_summaryLayout(new QVBoxLayout()) { - for (int i = 0; i < 10; i++) { + m_currTestName = i18n("Result Table"); + for (int i = 0; i < RESULTLINESCOUNT; i++) { m_resultLine[i] = new QLabel(); m_summaryLayout->addWidget(m_resultLine[i]); } } GeneralTest::~GeneralTest() { + delete m_summaryLayout; + for (int i = 0; i < RESULTLINESCOUNT; i++) + delete m_resultLine[i]; } void GeneralTest::setDataSourceType(DataSourceType type) { if (type != m_dataSourceType) m_dataSourceType = type; } GeneralTest::DataSourceType GeneralTest::dataSourceType() const { return m_dataSourceType; } void GeneralTest::setDataSourceSpreadsheet(Spreadsheet* spreadsheet) { m_dataSourceSpreadsheet = spreadsheet; for (auto* col : m_dataSourceSpreadsheet->children()) m_allColumns << col->name(); } QString GeneralTest::testName() { return m_currTestName; } QString GeneralTest::statsTable() { return m_statsTable; } QMap GeneralTest::tooltips() { return m_tooltips; } QVBoxLayout* GeneralTest::summaryLayout() { return m_summaryLayout; } void GeneralTest::setColumns(QStringList cols) { m_columns.clear(); Column* column = new Column("column"); for (QString col : cols) { if (!cols.isEmpty()) { column = m_dataSourceSpreadsheet->column(col); m_columns.append(column); } } - delete[] column; + delete column; } void GeneralTest::setColumns(const QVector &cols) { m_columns = cols; } /******************************************************************************************************************** * Protected functions implementations [Helper Functions] ********************************************************************************************************************/ QString GeneralTest::round(QVariant number, int precision) { if (number.userType() == QMetaType::Double || number.userType() == QMetaType::Float) { double multiplierPrecision = gsl_pow_int(10, precision); int tempNum = int(number.toDouble()*multiplierPrecision*10); if (tempNum % 10 < 5) return QString::number((tempNum/10) / multiplierPrecision); else return QString::number((tempNum/10 + 1) / multiplierPrecision); } return i18n("%1", number.toString()); } bool GeneralTest::isNumericOrInteger(const Column* column) { return (column->columnMode() == AbstractColumn::Numeric || column->columnMode() == AbstractColumn::Integer); } int GeneralTest::findCount(const Column *column) { int N = column->rowCount(); switch (column->columnMode()) { case (AbstractColumn::Numeric): case (AbstractColumn::Integer): { for (int i = 0; i < N; i++) if (std::isnan(column->valueAt(i))) { N = i; break; } break; } case (AbstractColumn::Month): case (AbstractColumn::Day): case (AbstractColumn::Text): { for (int i = 0; i < N; i++) if (column->textAt(i).isEmpty()) { N = i; break; } break; } case (AbstractColumn::DateTime): break; } return N; } double GeneralTest::findSum(const Column *column, int N) { if (!isNumericOrInteger(column)) return 0; if (N < 0) N = findCount(column); double sum = 0; for (int i = 0; i < N; i++) sum += column->valueAt(i); return sum; } double GeneralTest::findSumSq(const Column *column, int N) { if (!isNumericOrInteger(column)) return 0; if (N < 0) N = findCount(column); double sumSq = 0; for (int i = 0; i < N; i++) sumSq += gsl_pow_2(column->valueAt(i)); return sumSq; } double GeneralTest::findMean(const Column *column, int N) { if (!isNumericOrInteger(column)) return 0; if (N < 0) N = findCount(column); double sum = findSum(column, N); return sum / N; } double GeneralTest::findStd(const Column *column, int N, double mean) { if (!isNumericOrInteger(column)) return 0; double std = 0; for (int i = 0; i < N; i++) { double row = column->valueAt(i); std += gsl_pow_2( (row - mean)); } if (N > 1) std = std / (N-1); std = sqrt(std); return std; } double GeneralTest::findStd(const Column *column, int N) { if (!isNumericOrInteger(column)) return 0; if (N < 0) N = findCount(column); double mean = findMean(column, N); return findStd(column, N, mean); } GeneralTest::ErrorType GeneralTest::findStats(const Column* column, int& count, double& sum, double& mean, double& std) { count = findCount(column); sum = findSum(column, count); mean = findMean(column, count); std = findStd(column, count, mean); if (count < 1) return GeneralTest::ErrorEmptyColumn; return GeneralTest::NoError; } GeneralTest::ErrorType GeneralTest::findStatsPaired(const Column* column1, const Column* column2, int& count, double& sum, double& mean, double& std) { sum = 0; mean = 0; std = 0; int count1 = column1->rowCount(); int count2 = column2->rowCount(); count = qMin(count1, count2); double cell1, cell2; for (int i = 0; i < count; i++) { cell1 = column1->valueAt(i); cell2 = column2->valueAt(i); if (std::isnan(cell1) || std::isnan(cell2)) { if (std::isnan(cell1) && std::isnan(cell2)) count = i; else return GeneralTest::ErrorUnqualSize; break; } sum += cell1 - cell2; } if (count < 1) return GeneralTest::ErrorEmptyColumn; mean = sum / count; double row; for (int i = 0; i < count; i++) { cell1 = column1->valueAt(i); cell2 = column2->valueAt(i); row = cell1 - cell2; std += gsl_pow_2( (row - mean)); } if (count > 1) std = std / (count-1); std = sqrt(std); return GeneralTest::NoError; } void GeneralTest::countPartitions(Column* column, int& np, int& totalRows) { totalRows = column->rowCount(); np = 0; QString cellValue; QMap discoveredCategoricalVar; AbstractColumn::ColumnMode originalColMode = column->columnMode(); column->setColumnMode(AbstractColumn::Text); for (int i = 0; i < totalRows; i++) { cellValue = column->textAt(i); if (cellValue.isEmpty()) { totalRows = i; break; } if (discoveredCategoricalVar[cellValue]) continue; discoveredCategoricalVar[cellValue] = true; np++; } column->setColumnMode(originalColMode); } GeneralTest::ErrorType GeneralTest::findStatsCategorical(Column* column1, Column* column2, int n[], double sum[], double mean[], double std[], QMap& colName, const int& np, const int& totalRows) { Column* columns[] = {column1, column2}; for (int i = 0; i < np; i++) { n[i] = 0; sum[i] = 0; mean[i] = 0; std[i] = 0; } AbstractColumn::ColumnMode originalColMode = columns[0]->columnMode(); columns[0]->setColumnMode(AbstractColumn::Text); int partitionNumber = 1; for (int i = 0; i < totalRows; i++) { QString name = columns[0]->textAt(i); double value = columns[1]->valueAt(i); if (std::isnan(value)) { columns[0]->setColumnMode(originalColMode); return GeneralTest::ErrorUnqualSize; } if (colName[name] == 0) { colName[name] = partitionNumber; partitionNumber++; } n[colName[name]-1]++; sum[colName[name]-1] += value; } for (int i = 0; i < np; i++) mean[i] = sum[i] / n[i]; for (int i = 0; i < totalRows; i++) { QString name = columns[0]->textAt(i); double value = columns[1]->valueAt(i); std[colName[name]-1] += gsl_pow_2( (value - mean[colName[name]-1])); } for (int i = 0; i < np; i++) { if (n[i] > 1) std[i] = std[i] / (n[i] - 1); std[i] = sqrt(std[i]); } columns[0]->setColumnMode(originalColMode); if (isNumericOrInteger(columns[0])) { } return GeneralTest::NoError; } QString GeneralTest::getHtmlTable(int row, int column, QVariant* rowMajor) { if (row < 1 || column < 1) return QString(); QString table; table = "" "" " "; QString bg = "tg-0pky"; bool pky = true; QString element; table += " "; for (int j = 0; j < column; j++) { element = rowMajor[j].toString(); table += " "; } table += " "; if (pky) bg = "tg-0pky"; else bg = "tg-btxf"; pky = !pky; for (int i = 1; i < row; i++) { table += " "; QString element = round(rowMajor[i*column]); table += " "; for (int j = 1; j < column; j++) { element = round(rowMajor[i*column+j]); table += " "; } table += " "; if (pky) bg = "tg-0pky"; else bg = "tg-btxf"; pky = !pky; } table += "
" + i18n("%1", element) + "
" + i18n("%1", element) + "" + i18n("%1", element) + "
"; return table; } QString GeneralTest::getHtmlTable3(const QList& rowMajor) { m_tooltips.clear(); int rowMajorSize = rowMajor.size(); if (rowMajorSize == 0) return QString(); QString table; table = ""; table += ""; table += " "; int prevLevel = 0; for (int i = 0; i < rowMajorSize; i++) { Cell* currCell = rowMajor[i]; if (currCell->level != prevLevel) { table += " "; table += " "; prevLevel = currCell->level; } QString cellStartTag = ""; table += "
isHeader) { cellStartTag = "" + i18n("%1", currCell->data) + cellEndTag; if (!currCell->tooltip.isEmpty()) m_tooltips.insert(currCell->data, currCell->tooltip); } table += "
"; return table; } QString GeneralTest::getLine(const QString& msg, const QString& color) { return "

" + i18n("%1", msg) + "

"; } void GeneralTest::printLine(const int& index, const QString& msg, const QString& color) { if (index < 0 || index >= 10) return; m_resultLine[index]->setText(getLine(msg, color)); return; } void GeneralTest::printTooltip(const int &index, const QString &msg) { if (index < 0 || index >= 10) return; m_resultLine[index]->setToolTip(i18n("%1", msg)); } void GeneralTest::printError(const QString& errorMsg) { printLine(0, errorMsg, "red"); } /******************************************************************************************************************** * virtual functions implementations ********************************************************************************************************************/ /*! Saves as XML. */ void GeneralTest::save(QXmlStreamWriter* writer) const { writer->writeStartElement("GeneralTest"); writeBasicAttributes(writer); writeCommentElement(writer); writer->writeEndElement(); } /*! Loads from XML. */ bool GeneralTest::load(XmlStreamReader* reader, bool preview) { Q_UNUSED(preview); if (!readBasicAttributes(reader)) return false; return !reader->hasError(); } Spreadsheet *GeneralTest::dataSourceSpreadsheet() const { return m_dataSourceSpreadsheet; } bool GeneralTest::exportView() const { return true; } bool GeneralTest::printView() { return true; } bool GeneralTest::printPreview() const { return true; } /*! Constructs a primary view on me. This method may be called multiple times during the life time of an Aspect, or it might not get called at all. Aspects must not depend on the existence of a view for their operation. */ //QWidget* GeneralTest::view() const { // if (!m_partView) { // m_view = new HypothesisTestView(const_cast(this)); // m_partView = m_view; // } // return m_partView; //} /*! Returns a new context menu. The caller takes ownership of the menu. */ QMenu* GeneralTest::createContextMenu() { QMenu* menu = AbstractPart::createContextMenu(); // Q_ASSERT(menu); // emit requestProjectContextMenu(menu); return menu; } diff --git a/src/backend/generalTest/GeneralTest.h b/src/backend/generalTest/GeneralTest.h index 6e313ad73..6f59e2c70 100644 --- a/src/backend/generalTest/GeneralTest.h +++ b/src/backend/generalTest/GeneralTest.h @@ -1,146 +1,146 @@ /*************************************************************************** File : GeneralTest.h Project : LabPlot Description : Doing Hypothesis-Test on data provided -------------------------------------------------------------------- Copyright : (C) 2019 Devanshu Agarwal(agarwaldevanshu8@gmail.com) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #ifndef GENERALTEST_H #define GENERALTEST_H #include "backend/core/AbstractPart.h" #include "backend/lib/macros.h" +#include "kdefrontend/generalTest/GeneralTestView.h" -class GeneralTestView; class Spreadsheet; class QString; class Column; class QVBoxLayout; class QLabel; class GeneralTest : public AbstractPart { Q_OBJECT public: explicit GeneralTest(const QString& name, const AspectType& type); ~GeneralTest() override; enum DataSourceType {DataSourceSpreadsheet, DataSourceDatabase}; struct Cell { QString data; int level; bool isHeader; QString tooltip; int rowSpanCount; int columnSpanCount; Cell(QVariant data = "", int level = 0, bool isHeader = false, QString tooltip = "", int rowSpanCount = 1, int columnSpanCount = 1) { this->data = data.toString(); this->level = level; this->isHeader = isHeader; this->tooltip = tooltip; this->rowSpanCount = rowSpanCount; this->columnSpanCount = columnSpanCount; } }; enum ErrorType {ErrorUnqualSize, ErrorEmptyColumn, NoError}; void setDataSourceType(DataSourceType type); DataSourceType dataSourceType() const; void setDataSourceSpreadsheet(Spreadsheet* spreadsheet); Spreadsheet* dataSourceSpreadsheet() const; void setColumns(const QVector& cols); void setColumns(QStringList cols); QStringList allColumns(); QString testName(); QString statsTable(); QMap tooltips(); QVBoxLayout* summaryLayout(); //virtual methods // QIcon icon() const override; QMenu* createContextMenu() override; // QWidget* view() const override; bool exportView() const override; bool printView() override; bool printPreview() const override; void save(QXmlStreamWriter*) const override; bool load(XmlStreamReader*, bool preview) override; signals: void changed(); void requestProjectContextMenu(QMenu*); void dataSourceTypeChanged(GeneralTest::DataSourceType); void dataSourceSpreadsheetChanged(Spreadsheet*); protected: DataSourceType m_dataSourceType{GeneralTest::DataSourceSpreadsheet}; Spreadsheet* m_dataSourceSpreadsheet{nullptr}; QVector m_columns; QStringList m_allColumns; - QString m_currTestName{"Result Table"}; + QString m_currTestName; QString m_statsTable; QVBoxLayout* m_summaryLayout{nullptr}; - QLabel* m_resultLine[10]; + QLabel* m_resultLine[RESULTLINESCOUNT]; QMap m_tooltips; bool isNumericOrInteger(const Column* column); QString round(QVariant number, int precision = 3); int findCount(const Column* column); double findSum(const Column* column, int N = -1); double findSumSq(const Column* column, int N = -1); double findMean(const Column* column, int N = -1); double findStd(const Column* column, int N, double mean); double findStd(const Column* column, int N = -1); void countPartitions(Column* column, int& np, int& totalRows); // double findSumProducts(const Column* columns[], int N = -1); 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(Column* column1, Column* column2, int n[], double sum[], double mean[], double std[], QMap& colName, const int& np, const int& totalRows); QString getHtmlTable(int row, int column, QVariant* rowMajor); QString getHtmlTable3(const QList& rowMajor); QString getLine(const QString& msg, const QString& color = "black"); void printLine(const int& index, const QString& msg, const QString& color = "black"); void printTooltip(const int& index, const QString& msg); void printError(const QString& errorMsg); bool m_dbCreated{false}; mutable GeneralTestView* m_view{nullptr}; }; #endif // GeneralTest_H diff --git a/src/kdefrontend/generalTest/GeneralTestView.cpp b/src/kdefrontend/generalTest/GeneralTestView.cpp index fb98793e8..958952a7d 100644 --- a/src/kdefrontend/generalTest/GeneralTestView.cpp +++ b/src/kdefrontend/generalTest/GeneralTestView.cpp @@ -1,197 +1,202 @@ /*************************************************************************** File : GeneralTestView.cpp Project : LabPlot Description : View class for Hypothesis Tests' Table -------------------------------------------------------------------- Copyright : (C) 2019 Devanshu Agarwal(agarwaldevanshu8@gmail.com) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "GeneralTestView.h" #include "backend/generalTest/GeneralTest.h" #include "backend/lib/macros.h" #include "backend/lib/trace.h" #include #include #include #include #include #include #include #include #include #include #include /*! \class GeneralTestView \brief View class for Hypothesis Test \ingroup kdefrontend */ GeneralTestView::GeneralTestView(GeneralTest* GeneralTest) : QWidget(), m_generalTest(GeneralTest), m_testName(new QLabel()), m_statsTable(new QTextEdit()), m_summaryResults(new QWidget()) { m_statsTable->setReadOnly(true); auto* layout = new QVBoxLayout(this); layout->addWidget(m_testName); layout->addWidget(m_statsTable); layout->addWidget(m_summaryResults); layout->addStretch(1); init(); } -GeneralTestView::~GeneralTestView() = default; +GeneralTestView::~GeneralTestView() { + delete m_generalTest; + delete m_testName; + delete m_statsTable; + delete m_summaryResults; +} void GeneralTestView::init() { initActions(); initMenus(); m_statsTable->setMouseTracking(true); // m_summaryResults->setStyleSheet("background-color:white; border: 0px; margin: 0px; padding 0px;qproperty-frame: false;"); connect(m_generalTest, &GeneralTest::changed, this, &GeneralTestView::changed); connect(m_statsTable, &QTextEdit::cursorPositionChanged, this, &GeneralTestView::cursorPositionChanged); } void GeneralTestView::initActions() { } void GeneralTestView::initMenus() { } void GeneralTestView::clearResult() { for (int i = 0; i < RESULTLINESCOUNT; i++) m_resultLine[i]->clear(); } void GeneralTestView::connectActions() { } void GeneralTestView::fillToolBar(QToolBar* toolBar) { Q_UNUSED(toolBar); } /*! * Populates the menu \c menu with the pivot table and pivot table view relevant actions. * The menu is used * - as the context menu in PivotTableView * - as the "pivot table menu" in the main menu-bar (called form MainWin) * - as a part of the pivot table context menu in project explorer */ void GeneralTestView::createContextMenu(QMenu* menu) { Q_ASSERT(menu); } bool GeneralTestView::exportView() { return true; } bool GeneralTestView::printView() { QPrinter printer; auto* dlg = new QPrintDialog(&printer, this); dlg->setWindowTitle(i18nc("@title:window", "Print Spreadsheet")); bool ret; if ((ret = dlg->exec()) == QDialog::Accepted) { print(&printer); } delete dlg; return ret; } bool GeneralTestView::printPreview() { QPrintPreviewDialog* dlg = new QPrintPreviewDialog(this); connect(dlg, &QPrintPreviewDialog::paintRequested, this, &GeneralTestView::print); return dlg->exec(); } /*! prints the complete spreadsheet to \c printer. */ void GeneralTestView::print(QPrinter* printer) const { WAIT_CURSOR; QPainter painter (printer); RESET_CURSOR; } void GeneralTestView::changed() { m_testName->setText(m_generalTest->testName()); if (m_generalTest->statsTable().isEmpty()) m_statsTable->hide(); else { m_statsTable->setHtml(m_generalTest->statsTable()); m_statsTable->show(); } m_summaryResults->setLayout(m_generalTest->summaryLayout()); } void GeneralTestView::cursorPositionChanged() { QTextCursor cursor = m_statsTable->textCursor(); cursor.select(QTextCursor::LineUnderCursor); QMap tooltips = m_generalTest->tooltips(); if (!cursor.selectedText().isEmpty()) QToolTip::showText(QCursor::pos(), QString("%1") .arg(tooltips.value(cursor.selectedText()))); else QToolTip::hideText(); } void GeneralTestView::exportToFile(const QString& path, const bool exportHeader, const QString& separator, QLocale::Language language) const { Q_UNUSED(exportHeader); Q_UNUSED(separator); Q_UNUSED(language); QFile file(path); if (!file.open(QFile::WriteOnly | QFile::Truncate)) return; PERFTRACE("export pivot table to file"); } void GeneralTestView::exportToLaTeX(const QString & path, const bool exportHeaders, const bool gridLines, const bool captions, const bool latexHeaders, const bool skipEmptyRows, const bool exportEntire) const { Q_UNUSED(exportHeaders); Q_UNUSED(gridLines); Q_UNUSED(captions); Q_UNUSED(latexHeaders); Q_UNUSED(skipEmptyRows); Q_UNUSED(exportEntire); QFile file(path); if (!file.open(QFile::WriteOnly | QFile::Truncate)) return; PERFTRACE("export pivot table to latex"); }