File indexing completed on 2024-05-12 03:47:56

0001 /*
0002     File                 : StatisticsSpreadsheet.cpp
0003     Project              : LabPlot
0004     Description          : Aspect providing a spreadsheet table with column logic
0005     --------------------------------------------------------------------
0006     SPDX-FileCopyrightText: 2023 Alexander Semke <alexander.semke@web.de>
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 #include "StatisticsSpreadsheet.h"
0010 #include "SpreadsheetModel.h"
0011 #include "backend/lib/XmlStreamReader.h"
0012 #include "backend/lib/macros.h"
0013 
0014 #include <QIcon>
0015 #include <QXmlStreamWriter>
0016 
0017 #include <KConfigGroup>
0018 #include <KLocalizedString>
0019 #include <KSharedConfig>
0020 
0021 /*!
0022   \class StatisticsSpreadsheet
0023   \brief Aspect providing a spreadsheet table with column logic.
0024   \ingroup backend
0025 */
0026 
0027 StatisticsSpreadsheet::StatisticsSpreadsheet(Spreadsheet* spreadsheet, bool loading, AspectType type)
0028     : Spreadsheet(i18n("Column Statistics"), loading, type)
0029     , m_spreadsheet(spreadsheet) {
0030     m_metricNames = {
0031         {StatisticsSpreadsheet::Metric::Count, i18n("Count")},
0032         {StatisticsSpreadsheet::Metric::Minimum, i18n("Minimum")},
0033         {StatisticsSpreadsheet::Metric::Maximum, i18n("Maximum")},
0034         {StatisticsSpreadsheet::Metric::ArithmeticMean, i18n("ArithmeticMean")},
0035         {StatisticsSpreadsheet::Metric::GeometricMean, i18n("GeometricMean")},
0036         {StatisticsSpreadsheet::Metric::HarmonicMean, i18n("HarmonicMean")},
0037         {StatisticsSpreadsheet::Metric::ContraharmonicMean, i18n("ContraharmonicMean")},
0038         {StatisticsSpreadsheet::Metric::Mode, i18n("Mode")},
0039         {StatisticsSpreadsheet::Metric::FirstQuartile, i18n("FirstQuartile")},
0040         {StatisticsSpreadsheet::Metric::Median, i18n("Median")},
0041         {StatisticsSpreadsheet::Metric::ThirdQuartile, i18n("ThirdQuartile")},
0042         {StatisticsSpreadsheet::Metric::IQR, i18n("Interquartile Range")},
0043         {StatisticsSpreadsheet::Metric::Percentile1, i18n("Percentile1")},
0044         {StatisticsSpreadsheet::Metric::Percentile5, i18n("Percentile5")},
0045         {StatisticsSpreadsheet::Metric::Percentile10, i18n("Percentile10")},
0046         {StatisticsSpreadsheet::Metric::Percentile90, i18n("Percentile90")},
0047         {StatisticsSpreadsheet::Metric::Percentile95, i18n("Percentile95")},
0048         {StatisticsSpreadsheet::Metric::Percentile99, i18n("Percentile99")},
0049         {StatisticsSpreadsheet::Metric::Trimean, i18n("Trimean")},
0050         {StatisticsSpreadsheet::Metric::Variance, i18n("Variance")},
0051         {StatisticsSpreadsheet::Metric::StandardDeviation, i18n("StandardDeviation")},
0052         {StatisticsSpreadsheet::Metric::MeanDeviation, i18n("MeanDeviation")},
0053         {StatisticsSpreadsheet::Metric::MeanDeviationAroundMedian, i18n("MeanDeviationAroundMedian")},
0054         {StatisticsSpreadsheet::Metric::MedianDeviation, i18n("MedianDeviation")},
0055         {StatisticsSpreadsheet::Metric::Skewness, i18n("Skewness")},
0056         {StatisticsSpreadsheet::Metric::Kurtosis, i18n("Kurtosis")},
0057         {StatisticsSpreadsheet::Metric::Entropy, i18n("Entropy")},
0058     };
0059 
0060     auto* model = m_spreadsheet->model();
0061     connect(model, &SpreadsheetModel::dataChanged, this, &StatisticsSpreadsheet::update);
0062     connect(model, &SpreadsheetModel::rowsRemoved, this, &StatisticsSpreadsheet::update);
0063     connect(model, &SpreadsheetModel::rowsInserted, this, &StatisticsSpreadsheet::update);
0064     connect(model, &SpreadsheetModel::columnsRemoved, this, &StatisticsSpreadsheet::update);
0065     connect(model, &SpreadsheetModel::columnsInserted, this, &StatisticsSpreadsheet::update);
0066 
0067     setUndoAware(false);
0068     setFixed(true);
0069 
0070     if (!loading)
0071         init();
0072 }
0073 
0074 StatisticsSpreadsheet::~StatisticsSpreadsheet() {
0075 }
0076 
0077 /*!
0078   Returns an icon to be used for decorating my views.
0079   */
0080 QIcon StatisticsSpreadsheet::icon() const {
0081     return QIcon::fromTheme(QStringLiteral("view-statistics"));
0082 }
0083 
0084 StatisticsSpreadsheet::Metrics StatisticsSpreadsheet::metrics() const {
0085     return m_metrics;
0086 }
0087 
0088 void StatisticsSpreadsheet::setMetrics(Metrics metrics) {
0089     m_metrics = metrics;
0090     update();
0091 }
0092 
0093 /*!
0094     initializes the spreadsheet with the default number of columns and rows
0095 */
0096 void StatisticsSpreadsheet::init() {
0097     // measures that are shown on default
0098     KConfig config;
0099     KConfigGroup group = config.group(QLatin1String("StatisticsSpreadsheet"));
0100 
0101     Metrics defaultMetrics;
0102     defaultMetrics.setFlag(StatisticsSpreadsheet::Metric::Count);
0103     defaultMetrics.setFlag(StatisticsSpreadsheet::Metric::Minimum);
0104     defaultMetrics.setFlag(StatisticsSpreadsheet::Metric::Maximum);
0105     defaultMetrics.setFlag(StatisticsSpreadsheet::Metric::ArithmeticMean);
0106     defaultMetrics.setFlag(StatisticsSpreadsheet::Metric::Variance);
0107     defaultMetrics.setFlag(StatisticsSpreadsheet::Metric::StandardDeviation);
0108 
0109     m_metrics = static_cast<Metrics>(group.readEntry(QStringLiteral("Metrics"), static_cast<int>(defaultMetrics)));
0110 
0111     update();
0112 }
0113 
0114 void StatisticsSpreadsheet::update() {
0115     // determine the number of activated metrics and properly resize the spreadsheet
0116     int colCount = 1; // first column for "column name"
0117     auto it = m_metricNames.constBegin();
0118     while (it != m_metricNames.constEnd()) {
0119         if (m_metrics.testFlag(it.key()))
0120             ++colCount;
0121         ++it;
0122     }
0123 
0124     setUndoAware(false);
0125     setRowCount(m_spreadsheet->columnCount());
0126     setColumnCount(colCount);
0127     setUndoAware(true);
0128 
0129     // make all columns in this statistics spreadsheet undo unaware
0130     const auto& statisticsColumns = children<Column>();
0131     for (auto* col : statisticsColumns) {
0132         col->setUndoAware(false);
0133         col->setFixed(true);
0134     }
0135 
0136     // rename the columns
0137     statisticsColumns.at(0)->setName(i18n("Column"));
0138     statisticsColumns.at(0)->setColumnMode(AbstractColumn::ColumnMode::Text);
0139 
0140     int colIndex = 1;
0141     it = m_metricNames.constBegin();
0142     while (it != m_metricNames.constEnd()) {
0143         if (m_metrics.testFlag(it.key())) {
0144             statisticsColumns.at(colIndex)->setName(it.value());
0145 
0146             if (it.key() == StatisticsSpreadsheet::Metric::Count)
0147                 statisticsColumns.at(colIndex)->setColumnMode(AbstractColumn::ColumnMode::Integer);
0148 
0149             ++colIndex;
0150         }
0151         ++it;
0152     }
0153 
0154     // show the column names in the first column of the statistics spreadsheet
0155     const auto& columns = m_spreadsheet->children<Column>();
0156     auto* statisticsColumn = statisticsColumns.at(0);
0157     for (int i = 0; i < columns.count(); ++i)
0158         statisticsColumn->setTextAt(i, columns.at(i)->name());
0159 
0160     // show other statistics metrics that were activated
0161     colIndex = 1;
0162     it = m_metricNames.constBegin();
0163     while (it != m_metricNames.constEnd()) {
0164         if (m_metrics.testFlag(it.key())) {
0165             statisticsColumn = statisticsColumns.at(colIndex);
0166             for (int i = 0; i < columns.count(); ++i) {
0167                 const auto* column = columns.at(i);
0168                 const auto& statistics = column->statistics();
0169 
0170                 switch (it.key()) {
0171                 case Metric::Count:
0172                     statisticsColumn->setIntegerAt(i, statistics.size);
0173                     break;
0174                 case Metric::Minimum:
0175                     statisticsColumn->setValueAt(i, statistics.minimum);
0176                     break;
0177                 case Metric::Maximum:
0178                     statisticsColumn->setValueAt(i, statistics.maximum);
0179                     break;
0180                 case Metric::ArithmeticMean:
0181                     statisticsColumn->setValueAt(i, statistics.arithmeticMean);
0182                     break;
0183                 case Metric::GeometricMean:
0184                     statisticsColumn->setValueAt(i, statistics.geometricMean);
0185                     break;
0186                 case Metric::HarmonicMean:
0187                     statisticsColumn->setValueAt(i, statistics.harmonicMean);
0188                     break;
0189                 case Metric::ContraharmonicMean:
0190                     statisticsColumn->setValueAt(i, statistics.contraharmonicMean);
0191                     break;
0192                 case Metric::Mode:
0193                     statisticsColumn->setValueAt(i, statistics.mode);
0194                     break;
0195                 case Metric::FirstQuartile:
0196                     statisticsColumn->setValueAt(i, statistics.firstQuartile);
0197                     break;
0198                 case Metric::Median:
0199                     statisticsColumn->setValueAt(i, statistics.median);
0200                     break;
0201                 case Metric::ThirdQuartile:
0202                     statisticsColumn->setValueAt(i, statistics.thirdQuartile);
0203                     break;
0204                 case Metric::IQR:
0205                     statisticsColumn->setValueAt(i, statistics.iqr);
0206                     break;
0207                 case Metric::Percentile1:
0208                     statisticsColumn->setValueAt(i, statistics.percentile_1);
0209                     break;
0210                 case Metric::Percentile5:
0211                     statisticsColumn->setValueAt(i, statistics.percentile_5);
0212                     break;
0213                 case Metric::Percentile10:
0214                     statisticsColumn->setValueAt(i, statistics.percentile_10);
0215                     break;
0216                 case Metric::Percentile90:
0217                     statisticsColumn->setValueAt(i, statistics.percentile_90);
0218                     break;
0219                 case Metric::Percentile95:
0220                     statisticsColumn->setValueAt(i, statistics.percentile_95);
0221                     break;
0222                 case Metric::Percentile99:
0223                     statisticsColumn->setValueAt(i, statistics.percentile_99);
0224                     break;
0225                 case Metric::Trimean:
0226                     statisticsColumn->setValueAt(i, statistics.trimean);
0227                     break;
0228                 case Metric::Variance:
0229                     statisticsColumn->setValueAt(i, statistics.variance);
0230                     break;
0231                 case Metric::StandardDeviation:
0232                     statisticsColumn->setValueAt(i, statistics.standardDeviation);
0233                     break;
0234                 case Metric::MeanDeviation:
0235                     statisticsColumn->setValueAt(i, statistics.meanDeviation);
0236                     break;
0237                 case Metric::MeanDeviationAroundMedian:
0238                     statisticsColumn->setValueAt(i, statistics.meanDeviationAroundMedian);
0239                     break;
0240                 case Metric::MedianDeviation:
0241                     statisticsColumn->setValueAt(i, statistics.medianDeviation);
0242                     break;
0243                 case Metric::Skewness:
0244                     statisticsColumn->setValueAt(i, statistics.skewness);
0245                     break;
0246                 case Metric::Kurtosis:
0247                     statisticsColumn->setValueAt(i, statistics.kurtosis);
0248                     break;
0249                 case Metric::Entropy:
0250                     statisticsColumn->setValueAt(i, statistics.entropy);
0251                     break;
0252                 }
0253             }
0254 
0255             ++colIndex;
0256         }
0257 
0258         ++it;
0259     }
0260 }
0261 
0262 // ##############################################################################
0263 // ##################  Serialization/Deserialization  ###########################
0264 // ##############################################################################
0265 /*!
0266   Saves as XML.
0267  */
0268 void StatisticsSpreadsheet::save(QXmlStreamWriter* writer) const {
0269     writer->writeStartElement(QStringLiteral("statisticsSpreadsheet"));
0270     writeBasicAttributes(writer);
0271     writer->writeAttribute(QStringLiteral("metrics"), QString::number(m_metrics));
0272 
0273     // columns
0274     const auto& columns = children<Column>(ChildIndexFlag::IncludeHidden);
0275     for (auto* column : columns)
0276         column->save(writer);
0277 
0278     writer->writeEndElement(); // "statisticsSpreadsheet"
0279 }
0280 
0281 /*!
0282   Loads from XML.
0283 */
0284 bool StatisticsSpreadsheet::load(XmlStreamReader* reader, bool preview) {
0285     if (preview)
0286         return true;
0287 
0288     if (!readBasicAttributes(reader))
0289         return false;
0290 
0291     const auto& attribs = reader->attributes();
0292     const auto& str = attribs.value(QStringLiteral("metrics")).toString();
0293     if (str.isEmpty())
0294         reader->raiseMissingAttributeWarning(QStringLiteral("metrics"));
0295     else
0296         m_metrics = static_cast<StatisticsSpreadsheet::Metric>(str.toInt());
0297 
0298     // read child elements
0299     while (!reader->atEnd()) {
0300         reader->readNext();
0301 
0302         if (reader->isEndElement() && reader->name() == QStringLiteral("statisticsSpreadsheet"))
0303             break;
0304 
0305         if (reader->isStartElement()) {
0306             if (reader->name() == QStringLiteral("column")) {
0307                 Column* column = new Column(QString());
0308                 column->setIsLoading(true);
0309                 if (!column->load(reader, preview)) {
0310                     delete column;
0311                     setColumnCount(0);
0312                     return false;
0313                 }
0314                 addChildFast(column);
0315             } else { // unknown element
0316                 reader->raiseUnknownElementWarning();
0317                 if (!reader->skipToEndElement())
0318                     return false;
0319             }
0320         }
0321     }
0322 
0323     return !reader->hasError();
0324 }