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 }