QGIS API Documentation 3.29.0-Master (ade4f0cf0f)
qgsstatisticalsummary.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsstatisticalsummary.cpp
3 --------------------------------------
4 Date : May 2015
5 Copyright : (C) 2015 by Nyall Dawson
6 Email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17#include "qgsvariantutils.h"
18#include <limits>
19#include <QString>
20#include <QObject>
21
22/***************************************************************************
23 * This class is considered CRITICAL and any change MUST be accompanied with
24 * full unit tests in testqgsstatisticalsummary.cpp.
25 * See details in QEP #17
26 ****************************************************************************/
27
29 : mStatistics( stats )
30{
31 reset();
32}
33
34void QgsStatisticalSummary::setStatistics( QgsStatisticalSummary::Statistics stats )
35{
36 mStatistics = stats;
37 reset();
38}
39
41{
42 mFirst = std::numeric_limits<double>::quiet_NaN();
43 mLast = std::numeric_limits<double>::quiet_NaN();
44 mCount = 0;
45 mMissing = 0;
46 mSum = 0;
47 mMean = 0;
48 mMedian = 0;
49 mMin = std::numeric_limits<double>::max();
50 mMax = -std::numeric_limits<double>::max();
51 mStdev = 0;
52 mSampleStdev = 0;
53 mMinority = 0;
54 mMajority = 0;
55 mFirstQuartile = 0;
56 mThirdQuartile = 0;
57 mValueCount.clear();
58 mValues.clear();
59
60 mRequiresHisto = mStatistics & QgsStatisticalSummary::Majority || mStatistics & QgsStatisticalSummary::Minority || mStatistics & QgsStatisticalSummary::Variety;
61
62 mRequiresAllValueStorage = mStatistics & QgsStatisticalSummary::StDev || mStatistics & QgsStatisticalSummary::StDevSample ||
65}
66
67/***************************************************************************
68 * This class is considered CRITICAL and any change MUST be accompanied with
69 * full unit tests in testqgsstatisticalsummary.cpp.
70 * See details in QEP #17
71 ****************************************************************************/
72
73void QgsStatisticalSummary::calculate( const QList<double> &values )
74{
75 reset();
76
77 for ( const double value : values )
78 {
79 addValue( value );
80 }
81
82 finalize();
83}
84
86{
87 if ( mCount == 0 )
88 mFirst = value;
89 mCount++;
90 mSum += value;
91 mMin = std::min( mMin, value );
92 mMax = std::max( mMax, value );
93 mLast = value;
94
95 if ( mRequiresHisto )
96 mValueCount.insert( value, mValueCount.value( value, 0 ) + 1 );
97
98 if ( mRequiresAllValueStorage )
99 mValues << value;
100}
101
102void QgsStatisticalSummary::addVariant( const QVariant &value )
103{
104 bool convertOk = false;
105 if ( QgsVariantUtils::isNull( value ) )
106 mMissing++;
107 else
108 {
109 const double val = value.toDouble( &convertOk );
110 if ( convertOk )
111 addValue( val );
112 else
113 mMissing++;
114 }
115}
116
118{
119 if ( mCount == 0 )
120 {
121 mFirst = std::numeric_limits<double>::quiet_NaN();
122 mLast = std::numeric_limits<double>::quiet_NaN();
123 mMin = std::numeric_limits<double>::quiet_NaN();
124 mMax = std::numeric_limits<double>::quiet_NaN();
125 mMean = std::numeric_limits<double>::quiet_NaN();
126 mMedian = std::numeric_limits<double>::quiet_NaN();
127 mStdev = std::numeric_limits<double>::quiet_NaN();
128 mSampleStdev = std::numeric_limits<double>::quiet_NaN();
129 mMinority = std::numeric_limits<double>::quiet_NaN();
130 mMajority = std::numeric_limits<double>::quiet_NaN();
131 mFirstQuartile = std::numeric_limits<double>::quiet_NaN();
132 mThirdQuartile = std::numeric_limits<double>::quiet_NaN();
133 return;
134 }
135
136 mMean = mSum / mCount;
137
138 if ( mStatistics & QgsStatisticalSummary::StDev || mStatistics & QgsStatisticalSummary::StDevSample )
139 {
140 double sumSquared = 0;
141 const auto constMValues = mValues;
142 for ( const double value : constMValues )
143 {
144 const double diff = value - mMean;
145 sumSquared += diff * diff;
146 }
147 mStdev = std::pow( sumSquared / mValues.count(), 0.5 );
148 mSampleStdev = std::pow( sumSquared / ( mValues.count() - 1 ), 0.5 );
149 }
150
151 if ( mStatistics & QgsStatisticalSummary::Median
155 {
156 std::sort( mValues.begin(), mValues.end() );
157 const bool even = ( mCount % 2 ) < 1;
158 if ( even )
159 {
160 mMedian = ( mValues[mCount / 2 - 1] + mValues[mCount / 2] ) / 2.0;
161 }
162 else //odd
163 {
164 mMedian = mValues[( mCount + 1 ) / 2 - 1];
165 }
166 }
167
168 if ( mStatistics & QgsStatisticalSummary::FirstQuartile
170 {
171 if ( ( mCount % 2 ) < 1 )
172 {
173 const int halfCount = mCount / 2;
174 const bool even = ( halfCount % 2 ) < 1;
175 if ( even )
176 {
177 mFirstQuartile = ( mValues[halfCount / 2 - 1] + mValues[halfCount / 2] ) / 2.0;
178 }
179 else //odd
180 {
181 mFirstQuartile = mValues[( halfCount + 1 ) / 2 - 1];
182 }
183 }
184 else
185 {
186 const int halfCount = mCount / 2 + 1;
187 const bool even = ( halfCount % 2 ) < 1;
188 if ( even )
189 {
190 mFirstQuartile = ( mValues[halfCount / 2 - 1] + mValues[halfCount / 2] ) / 2.0;
191 }
192 else //odd
193 {
194 mFirstQuartile = mValues[( halfCount + 1 ) / 2 - 1];
195 }
196 }
197 }
198
199 if ( mStatistics & QgsStatisticalSummary::ThirdQuartile
201 {
202 if ( ( mCount % 2 ) < 1 )
203 {
204 const int halfCount = mCount / 2;
205 const bool even = ( halfCount % 2 ) < 1;
206 if ( even )
207 {
208 mThirdQuartile = ( mValues[ halfCount + halfCount / 2 - 1] + mValues[ halfCount + halfCount / 2] ) / 2.0;
209 }
210 else //odd
211 {
212 mThirdQuartile = mValues[( halfCount + 1 ) / 2 - 1 + halfCount ];
213 }
214 }
215 else
216 {
217 const int halfCount = mCount / 2 + 1;
218 const bool even = ( halfCount % 2 ) < 1;
219 if ( even )
220 {
221 mThirdQuartile = ( mValues[ halfCount + halfCount / 2 - 2 ] + mValues[ halfCount + halfCount / 2 - 1 ] ) / 2.0;
222 }
223 else //odd
224 {
225 mThirdQuartile = mValues[( halfCount + 1 ) / 2 - 2 + halfCount ];
226 }
227 }
228 }
229
230 if ( mStatistics & QgsStatisticalSummary::Minority || mStatistics & QgsStatisticalSummary::Majority )
231 {
232 QList<int> valueCounts = mValueCount.values();
233
234 if ( mStatistics & QgsStatisticalSummary::Minority )
235 {
236 mMinority = mValueCount.key( *std::min_element( valueCounts.begin(), valueCounts.end() ) );
237 }
238 if ( mStatistics & QgsStatisticalSummary::Majority )
239 {
240 mMajority = mValueCount.key( *std::max_element( valueCounts.begin(), valueCounts.end() ) );
241 }
242 }
243
244}
245
246/***************************************************************************
247 * This class is considered CRITICAL and any change MUST be accompanied with
248 * full unit tests in testqgsstatisticalsummary.cpp.
249 * See details in QEP #17
250 ****************************************************************************/
251
253{
254 switch ( stat )
255 {
256 case Count:
257 return mCount;
258 case CountMissing:
259 return mMissing;
260 case Sum:
261 return mSum;
262 case Mean:
263 return mMean;
264 case Median:
265 return mMedian;
266 case StDev:
267 return mStdev;
268 case StDevSample:
269 return mSampleStdev;
270 case Min:
271 return mMin;
272 case Max:
273 return mMax;
274 case Range:
275 return mMax - mMin;
276 case Minority:
277 return mMinority;
278 case Majority:
279 return mMajority;
280 case Variety:
281 return mValueCount.count();
282 case FirstQuartile:
283 return mFirstQuartile;
284 case ThirdQuartile:
285 return mThirdQuartile;
287 return mThirdQuartile - mFirstQuartile;
288 case First:
289 return mFirst;
290 case Last:
291 return mLast;
292 case All:
293 return 0;
294 }
295 return 0;
296}
297
299{
300 switch ( statistic )
301 {
302 case Count:
303 return QObject::tr( "Count" );
304 case CountMissing:
305 return QObject::tr( "Count (missing)" );
306 case Sum:
307 return QObject::tr( "Sum" );
308 case Mean:
309 return QObject::tr( "Mean" );
310 case Median:
311 return QObject::tr( "Median" );
312 case StDev:
313 return QObject::tr( "St dev (pop)" );
314 case StDevSample:
315 return QObject::tr( "St dev (sample)" );
316 case Min:
317 return QObject::tr( "Minimum" );
318 case Max:
319 return QObject::tr( "Maximum" );
320 case Range:
321 return QObject::tr( "Range" );
322 case Minority:
323 return QObject::tr( "Minority" );
324 case Majority:
325 return QObject::tr( "Majority" );
326 case Variety:
327 return QObject::tr( "Variety" );
328 case FirstQuartile:
329 return QObject::tr( "Q1" );
330 case ThirdQuartile:
331 return QObject::tr( "Q3" );
333 return QObject::tr( "IQR" );
334 case First:
335 return QObject::tr( "First" );
336 case Last:
337 return QObject::tr( "Last" );
338 case All:
339 return QString();
340 }
341 return QString();
342}
343
345{
346 switch ( statistic )
347 {
348 case Count:
349 return QStringLiteral( "count" );
350 case CountMissing:
351 return QStringLiteral( "countmissing" );
352 case Sum:
353 return QStringLiteral( "sum" );
354 case Mean:
355 return QStringLiteral( "mean" );
356 case Median:
357 return QStringLiteral( "median" );
358 case StDev:
359 return QStringLiteral( "stdev" );
360 case StDevSample:
361 return QStringLiteral( "stdevsample" );
362 case Min:
363 return QStringLiteral( "min" );
364 case Max:
365 return QStringLiteral( "max" );
366 case Range:
367 return QStringLiteral( "range" );
368 case Minority:
369 return QStringLiteral( "minority" );
370 case Majority:
371 return QStringLiteral( "majority" );
372 case Variety:
373 return QStringLiteral( "variety" );
374 case FirstQuartile:
375 return QStringLiteral( "q1" );
376 case ThirdQuartile:
377 return QStringLiteral( "q3" );
379 return QStringLiteral( "iqr" );
380 case First:
381 return QStringLiteral( "first" );
382 case Last:
383 return QStringLiteral( "last" );
384 case All:
385 return QString();
386 }
387 return QString();
388}
389
void addVariant(const QVariant &value)
Adds a single value to the statistics calculation.
void calculate(const QList< double > &values)
Calculates summary statistics for a list of values.
Statistic
Enumeration of flags that specify statistics to be calculated.
@ InterQuartileRange
Inter quartile range (IQR)
@ Median
Median of values.
@ Variety
Variety (count of distinct) values.
@ First
First value (since QGIS 3.6)
@ Last
Last value (since QGIS 3.6)
@ Minority
Minority of values.
@ ThirdQuartile
Third quartile.
@ Majority
Majority of values.
@ CountMissing
Number of missing (null) values.
@ Range
Range of values (max - min)
@ StDevSample
Sample standard deviation of values.
@ FirstQuartile
First quartile.
@ StDev
Standard deviation of values.
void addValue(double value)
Adds a single value to the statistics calculation.
static QString shortName(QgsStatisticalSummary::Statistic statistic)
Returns a short, friendly display name for a statistic, suitable for use in a field name.
double statistic(QgsStatisticalSummary::Statistic stat) const
Returns the value of a specified statistic.
QgsStatisticalSummary(QgsStatisticalSummary::Statistics stats=QgsStatisticalSummary::All)
Constructor for QgsStatisticalSummary.
void reset()
Resets the calculated values.
void setStatistics(QgsStatisticalSummary::Statistics stats)
Sets flags which specify which statistics will be calculated.
void finalize()
Must be called after adding all values with addValues() and before retrieving any calculated statisti...
static QString displayName(QgsStatisticalSummary::Statistic statistic)
Returns the friendly display name for a statistic.
static bool isNull(const QVariant &variant)
Returns true if the specified variant should be considered a NULL value.