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