Allow QgsStatisticalSummary to calculate first/last value

Trivial, but simplifies other code which desires to expose
a choice between first/last/min/max/mean/etc...
This commit is contained in:
Nyall Dawson 2019-01-01 09:27:25 +10:00
parent 5586352a13
commit 5f817b4db3
4 changed files with 83 additions and 18 deletions

View File

@ -46,6 +46,8 @@ specifying the statistic in the constructor or via setStatistics().
FirstQuartile,
ThirdQuartile,
InterQuartileRange,
First,
Last,
All
};
typedef QFlags<QgsStatisticalSummary::Statistic> 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;

View File

@ -32,6 +32,8 @@ QgsStatisticalSummary::QgsStatisticalSummary( Statistics stats )
void QgsStatisticalSummary::reset()
{
mFirst = std::numeric_limits<double>::quiet_NaN();
mLast = std::numeric_limits<double>::quiet_NaN();
mCount = 0;
mMissing = 0;
mSum = 0;
@ -59,7 +61,7 @@ void QgsStatisticalSummary::calculate( const QList<double> &values )
{
reset();
Q_FOREACH ( double value, values )
for ( double value : values )
{
addValue( value );
}
@ -69,10 +71,13 @@ void QgsStatisticalSummary::calculate( const QList<double> &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<double>::quiet_NaN();
mLast = std::numeric_limits<double>::quiet_NaN();
mMin = std::numeric_limits<double>::quiet_NaN();
mMax = std::numeric_limits<double>::quiet_NaN();
mMean = std::numeric_limits<double>::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();
}

View File

@ -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<double>::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;
};

View File

@ -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() ) );