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