diff --git a/python/core/auto_generated/qgsstatisticalsummary.sip.in b/python/core/auto_generated/qgsstatisticalsummary.sip.in index 2e989229312..ea01691269b 100644 --- a/python/core/auto_generated/qgsstatisticalsummary.sip.in +++ b/python/core/auto_generated/qgsstatisticalsummary.sip.in @@ -46,6 +46,8 @@ specifying the statistic in the constructor or via setStatistics(). FirstQuartile, ThirdQuartile, InterQuartileRange, + First, + Last, All }; typedef QFlags Statistics; @@ -214,6 +216,24 @@ be calculated. %Docstring Returns calculated range (difference between maximum and minimum values). A NaN value may be returned if the range cannot be calculated. +%End + + double first() const; +%Docstring +Returns the first value obtained. A NaN value may be returned if no values were encountered. + +.. seealso:: :py:func:`last` + +.. versionadded:: 3.6 +%End + + double last() const; +%Docstring +Returns the last value obtained. A NaN value may be returned if no values were encountered. + +.. seealso:: :py:func:`first` + +.. versionadded:: 3.6 %End double stDev() const; diff --git a/src/core/qgsstatisticalsummary.cpp b/src/core/qgsstatisticalsummary.cpp index 3ebb77f5c37..70995206c25 100644 --- a/src/core/qgsstatisticalsummary.cpp +++ b/src/core/qgsstatisticalsummary.cpp @@ -32,6 +32,8 @@ QgsStatisticalSummary::QgsStatisticalSummary( Statistics stats ) void QgsStatisticalSummary::reset() { + mFirst = std::numeric_limits::quiet_NaN(); + mLast = std::numeric_limits::quiet_NaN(); mCount = 0; mMissing = 0; mSum = 0; @@ -59,7 +61,7 @@ void QgsStatisticalSummary::calculate( const QList &values ) { reset(); - Q_FOREACH ( double value, values ) + for ( double value : values ) { addValue( value ); } @@ -69,10 +71,13 @@ void QgsStatisticalSummary::calculate( const QList &values ) void QgsStatisticalSummary::addValue( double value ) { + if ( mCount == 0 ) + mFirst = value; mCount++; mSum += value; mMin = std::min( mMin, value ); mMax = std::max( mMax, value ); + mLast = value; if ( mStatistics & QgsStatisticalSummary::Majority || mStatistics & QgsStatisticalSummary::Minority || mStatistics & QgsStatisticalSummary::Variety ) mValueCount.insert( value, mValueCount.value( value, 0 ) + 1 ); @@ -102,6 +107,8 @@ void QgsStatisticalSummary::finalize() { if ( mCount == 0 ) { + mFirst = std::numeric_limits::quiet_NaN(); + mLast = std::numeric_limits::quiet_NaN(); mMin = std::numeric_limits::quiet_NaN(); mMax = std::numeric_limits::quiet_NaN(); mMean = std::numeric_limits::quiet_NaN(); @@ -266,6 +273,10 @@ double QgsStatisticalSummary::statistic( QgsStatisticalSummary::Statistic stat ) return mThirdQuartile; case InterQuartileRange: return mThirdQuartile - mFirstQuartile; + case First: + return mFirst; + case Last: + return mLast; case All: return 0; } @@ -308,6 +319,10 @@ QString QgsStatisticalSummary::displayName( QgsStatisticalSummary::Statistic sta return QObject::tr( "Q3" ); case InterQuartileRange: return QObject::tr( "IQR" ); + case First: + return QObject::tr( "First" ); + case Last: + return QObject::tr( "Last" ); case All: return QString(); } diff --git a/src/core/qgsstatisticalsummary.h b/src/core/qgsstatisticalsummary.h index 26974ab8e0d..c9a6a3a7031 100644 --- a/src/core/qgsstatisticalsummary.h +++ b/src/core/qgsstatisticalsummary.h @@ -47,23 +47,25 @@ class CORE_EXPORT QgsStatisticalSummary //! Enumeration of flags that specify statistics to be calculated enum Statistic { - Count = 1, //!< Count - CountMissing = 32770, //!< Number of missing (null) values - Sum = 2, //!< Sum of values - Mean = 4, //!< Mean of values - Median = 8, //!< Median of values - StDev = 16, //!< Standard deviation of values - StDevSample = 32, //!< Sample standard deviation of values - Min = 64, //!< Min of values - Max = 128, //!< Max of values - Range = 256, //!< Range of values (max - min) - Minority = 512, //!< Minority of values - Majority = 1024, //!< Majority of values - Variety = 2048, //!< Variety (count of distinct) values - FirstQuartile = 4096, //!< First quartile - ThirdQuartile = 8192, //!< Third quartile - InterQuartileRange = 16384, //!< Inter quartile range (IQR) - All = Count | CountMissing | Sum | Mean | Median | StDev | Max | Min | Range | Minority | Majority | Variety | FirstQuartile | ThirdQuartile | InterQuartileRange + Count = 1 << 0, //!< Count + CountMissing = 1 << 15, //!< Number of missing (null) values + Sum = 1 << 1, //!< Sum of values + Mean = 1 << 2, //!< Mean of values + Median = 1 << 3, //!< Median of values + StDev = 1 << 4, //!< Standard deviation of values + StDevSample = 1 << 5, //!< Sample standard deviation of values + Min = 1 << 6, //!< Min of values + Max = 1 << 7, //!< Max of values + Range = 1 << 8, //!< Range of values (max - min) + Minority = 1 << 9, //!< Minority of values + Majority = 1 << 10, //!< Majority of values + Variety = 1 << 11, //!< Variety (count of distinct) values + FirstQuartile = 1 << 12, //!< First quartile + ThirdQuartile = 1 << 13, //!< Third quartile + InterQuartileRange = 1 << 14, //!< Inter quartile range (IQR) + First = 1 << 16, //!< First value (since QGIS 3.6) + Last = 1 << 17, //!< Last value (since QGIS 3.6) + All = Count | CountMissing | Sum | Mean | Median | StDev | Max | Min | Range | Minority | Majority | Variety | FirstQuartile | ThirdQuartile | InterQuartileRange | First | Last }; Q_DECLARE_FLAGS( Statistics, Statistic ) @@ -199,6 +201,22 @@ class CORE_EXPORT QgsStatisticalSummary */ double range() const { return std::isnan( mMax ) || std::isnan( mMin ) ? std::numeric_limits::quiet_NaN() : mMax - mMin; } + /** + * Returns the first value obtained. A NaN value may be returned if no values were encountered. + * + * \see last() + * \since QGIS 3.6 + */ + double first() const { return mFirst; } + + /** + * Returns the last value obtained. A NaN value may be returned if no values were encountered. + * + * \see first() + * \since QGIS 3.6 + */ + double last() const { return mLast; } + /** * Returns population standard deviation. This is only calculated if Statistic::StDev has * been specified in the constructor or via setStatistics. A NaN value may be returned if the standard deviation cannot @@ -290,6 +308,8 @@ class CORE_EXPORT QgsStatisticalSummary double mMajority; double mFirstQuartile; double mThirdQuartile; + double mFirst; + double mLast; QMap< double, int > mValueCount; QList< double > mValues; }; diff --git a/tests/src/core/testqgsstatisticalsummary.cpp b/tests/src/core/testqgsstatisticalsummary.cpp index 34191135b59..4da66b2b055 100644 --- a/tests/src/core/testqgsstatisticalsummary.cpp +++ b/tests/src/core/testqgsstatisticalsummary.cpp @@ -85,6 +85,10 @@ void TestQgsStatisticSummary::stats() QCOMPARE( s2.sum(), 24.0 ); QCOMPARE( s.mean(), 4.0 ); QCOMPARE( s2.mean(), 4.0 ); + QCOMPARE( s.first(), 4.0 ); + QCOMPARE( s2.first(), 4.0 ); + QCOMPARE( s.last(), 8.0 ); + QCOMPARE( s2.last(), 8.0 ); QGSCOMPARENEAR( s.stDev(), 2.0816, 0.0001 ); QGSCOMPARENEAR( s2.stDev(), 2.0816, 0.0001 ); QGSCOMPARENEAR( s.sampleStDev(), 2.2803, 0.0001 ); @@ -227,6 +231,8 @@ void TestQgsStatisticSummary::individualStatCalculations_data() QTest::newRow( "third_quartile" ) << ( int )QgsStatisticalSummary::ThirdQuartile << 5.0; QTest::newRow( "iqr" ) << ( int )QgsStatisticalSummary::InterQuartileRange << 2.0; QTest::newRow( "missing" ) << ( int )QgsStatisticalSummary::CountMissing << 0.0; + QTest::newRow( "first" ) << static_cast< int >( QgsStatisticalSummary::First ) << 4.0; + QTest::newRow( "last" ) << static_cast< int >( QgsStatisticalSummary::Last ) << 8.0; } void TestQgsStatisticSummary::individualStatCalculations() @@ -311,6 +317,10 @@ void TestQgsStatisticSummary::noValues() QCOMPARE( s.statistic( QgsStatisticalSummary::CountMissing ), 0.0 ); QCOMPARE( s.sum(), 0.0 ); QCOMPARE( s.statistic( QgsStatisticalSummary::Sum ), 0.0 ); + QVERIFY( std::isnan( s.first() ) ); + QVERIFY( std::isnan( s.statistic( QgsStatisticalSummary::First ) ) ); + QVERIFY( std::isnan( s.last() ) ); + QVERIFY( std::isnan( s.statistic( QgsStatisticalSummary::Last ) ) ); QVERIFY( std::isnan( s.mean() ) ); QVERIFY( std::isnan( s.statistic( QgsStatisticalSummary::Mean ) ) ); QVERIFY( std::isnan( s.median() ) );