Add methods to stats calculators to accept values one at a time

This can be more efficient for large quantities of values, since
it avoids the need to create a list of all values in advance
This commit is contained in:
Nyall Dawson 2016-05-16 08:59:47 +10:00
parent 2bbd5ca509
commit 4dea723cae
12 changed files with 416 additions and 62 deletions

View File

@ -53,12 +53,33 @@ class QgsDateTimeStatisticalSummary
*/ */
void reset(); void reset();
/** Calculates summary statistics for a list of variants. Any non-string variants will be /** Calculates summary statistics for a list of variants. Any non-datetime variants will be
* ignored. * ignored.
* @param values list of variants * @param values list of variants
* @see addValue()
*/ */
void calculate( const QVariantList& values ); void calculate( const QVariantList& values );
/** Adds a single datetime to the statistics calculation. Calling this method
* allows datetimes to be added to the calculation one at a time. For large
* quantities of dates this may be more efficient then first adding all the
* variants to a list and calling calculate().
* @param value datetime to add. Any non-datetime variants will be ignored.
* @note call reset() before adding the first datetime using this method
* to clear the results from any previous calculations
* @note finalize() must be called after adding the final value and before
* retrieving calculated statistics.
* @see calculate()
* @see finalize()
*/
void addValue( const QVariant& value );
/** Must be called after adding all datetimes with addValue() and before retrieving
* any calculated datetime statistics.
* @see addValue()
*/
void finalize();
/** Returns the value of a specified statistic /** Returns the value of a specified statistic
* @param stat statistic to return * @param stat statistic to return
* @returns calculated value of statistic * @returns calculated value of statistic

View File

@ -69,6 +69,25 @@ class QgsStatisticalSummary
*/ */
void calculate( const QList<double>& values ); void calculate( const QList<double>& values );
/** Adds a single value to the statistics calculation. Calling this method
* allows values to be added to the calculation one at a time. For large
* quantities of values this may be more efficient then first adding all the
* values to a list and calling calculate().
* @param value value to add
* @note call reset() before adding the first value using this method
* to clear the results from any previous calculations
* @note finalize() must be called after adding the final value and before
* retrieving calculated statistics.
* @see calculate()
* @see finalize()
*/
void addValue( double value );
/** Must be called after adding all values with addValues() and before retrieving
* any calculated statistics.
* @see addValue()
*/
void finalize();
/** Returns the value of a specified statistic /** Returns the value of a specified statistic
* @param stat statistic to return * @param stat statistic to return
* @returns calculated value of statistic * @returns calculated value of statistic

View File

@ -57,19 +57,56 @@ class QgsStringStatisticalSummary
*/ */
void reset(); void reset();
/** Calculates summary statistics for a list of strings. /** Calculates summary statistics for an entire list of strings at once.
* @param values list of strings * @param values list of strings
* @see calculateFromVariants() * @see calculateFromVariants()
* @see addString()
*/ */
void calculate( const QStringList& values ); void calculate( const QStringList& values );
/** Calculates summary statistics for a list of variants. Any non-string variants will be /** Calculates summary statistics for an entire list of variants at once. Any
* ignored. * non-string variants will be ignored.
* @param values list of variants * @param values list of variants
* @see calculate() * @see calculate()
* @see addValue()
*/ */
void calculateFromVariants( const QVariantList& values ); void calculateFromVariants( const QVariantList& values );
/** Adds a single string to the statistics calculation. Calling this method
* allows strings to be added to the calculation one at a time. For large
* quantities of strings this may be more efficient then first adding all the
* strings to a list and calling calculate().
* @param string string to add
* @note call reset() before adding the first string using this method
* to clear the results from any previous calculations
* @note finalize() must be called after adding the final string and before
* retrieving calculated statistics.
* @see calculate()
* @see addValue()
* @see finalize()
*/
void addString( const QString& string );
/** Adds a single variant to the statistics calculation. Calling this method
* allows variants to be added to the calculation one at a time. For large
* quantities of variants this may be more efficient then first adding all the
* variants to a list and calling calculateFromVariants().
* @param value variant to add
* @note call reset() before adding the first string using this method
* to clear the results from any previous calculations
* @note finalize() must be called after adding the final value and before
* retrieving calculated statistics.
* @see calculateFromVariants()
* @see finalize()
*/
void addValue( const QVariant& value );
/** Must be called after adding all strings with addString() and before retrieving
* any calculated string statistics.
* @see addString()
*/
void finalize();
/** Returns the value of a specified statistic /** Returns the value of a specified statistic
* @param stat statistic to return * @param stat statistic to return
* @returns calculated value of statistic * @returns calculated value of statistic

View File

@ -49,18 +49,30 @@ void QgsDateTimeStatisticalSummary::calculate( const QVariantList& values )
Q_FOREACH ( const QVariant& variant, values ) Q_FOREACH ( const QVariant& variant, values )
{ {
if ( variant.type() == QVariant::DateTime ) addValue( variant );
{
testDateTime( variant.toDateTime() );
}
else if ( variant.type() == QVariant::Date )
{
QDate date = variant.toDate();
testDateTime( date.isValid() ? QDateTime( date, QTime( 0, 0, 0 ) )
: QDateTime() );
}
// QTime?
} }
finalize();
}
void QgsDateTimeStatisticalSummary::addValue( const QVariant& value )
{
if ( value.type() == QVariant::DateTime )
{
testDateTime( value.toDateTime() );
}
else if ( value.type() == QVariant::Date )
{
QDate date = value.toDate();
testDateTime( date.isValid() ? QDateTime( date, QTime( 0, 0, 0 ) )
: QDateTime() );
}
// QTime?
}
void QgsDateTimeStatisticalSummary::finalize()
{
//nothing to do for now - this method has been added for forward compatibility
//if statistics are implemented which require a post-calculation step
} }
void QgsDateTimeStatisticalSummary::testDateTime( const QDateTime& dateTime ) void QgsDateTimeStatisticalSummary::testDateTime( const QDateTime& dateTime )

View File

@ -79,12 +79,33 @@ class CORE_EXPORT QgsDateTimeStatisticalSummary
*/ */
void reset(); void reset();
/** Calculates summary statistics for a list of variants. Any non-string variants will be /** Calculates summary statistics for a list of variants. Any non-datetime variants will be
* ignored. * ignored.
* @param values list of variants * @param values list of variants
* @see addValue()
*/ */
void calculate( const QVariantList& values ); void calculate( const QVariantList& values );
/** Adds a single datetime to the statistics calculation. Calling this method
* allows datetimes to be added to the calculation one at a time. For large
* quantities of dates this may be more efficient then first adding all the
* variants to a list and calling calculate().
* @param value datetime to add. Any non-datetime variants will be ignored.
* @note call reset() before adding the first datetime using this method
* to clear the results from any previous calculations
* @note finalize() must be called after adding the final value and before
* retrieving calculated statistics.
* @see calculate()
* @see finalize()
*/
void addValue( const QVariant& value );
/** Must be called after adding all datetimes with addValue() and before retrieving
* any calculated datetime statistics.
* @see addValue()
*/
void finalize();
/** Returns the value of a specified statistic /** Returns the value of a specified statistic
* @param stat statistic to return * @param stat statistic to return
* @returns calculated value of statistic * @returns calculated value of statistic

View File

@ -51,6 +51,7 @@ void QgsStatisticalSummary::reset()
mFirstQuartile = 0; mFirstQuartile = 0;
mThirdQuartile = 0; mThirdQuartile = 0;
mValueCount.clear(); mValueCount.clear();
mValues.clear();
} }
/*************************************************************************** /***************************************************************************
@ -65,15 +66,30 @@ void QgsStatisticalSummary::calculate( const QList<double> &values )
Q_FOREACH ( double value, values ) Q_FOREACH ( double value, values )
{ {
mCount++; addValue( value );
mSum += value;
mMin = qMin( mMin, value );
mMax = qMax( mMax, value );
if ( mStatistics & QgsStatisticalSummary::Majority || mStatistics & QgsStatisticalSummary::Minority || mStatistics & QgsStatisticalSummary::Variety )
mValueCount.insert( value, mValueCount.value( value, 0 ) + 1 );
} }
finalize();
}
void QgsStatisticalSummary::addValue( double value )
{
mCount++;
mSum += value;
mMin = qMin( mMin, value );
mMax = qMax( mMax, value );
if ( mStatistics & QgsStatisticalSummary::Majority || mStatistics & QgsStatisticalSummary::Minority || mStatistics & QgsStatisticalSummary::Variety )
mValueCount.insert( value, mValueCount.value( value, 0 ) + 1 );
if ( mStatistics & QgsStatisticalSummary::StDev || mStatistics & QgsStatisticalSummary::StDevSample ||
mStatistics & QgsStatisticalSummary::Median || mStatistics & QgsStatisticalSummary::FirstQuartile ||
mStatistics & QgsStatisticalSummary::ThirdQuartile || mStatistics & QgsStatisticalSummary::InterQuartileRange )
mValues << value;
}
void QgsStatisticalSummary::finalize()
{
if ( mCount == 0 ) if ( mCount == 0 )
return; return;
@ -82,31 +98,29 @@ void QgsStatisticalSummary::calculate( const QList<double> &values )
if ( mStatistics & QgsStatisticalSummary::StDev || mStatistics & QgsStatisticalSummary::StDevSample ) if ( mStatistics & QgsStatisticalSummary::StDev || mStatistics & QgsStatisticalSummary::StDevSample )
{ {
double sumSquared = 0; double sumSquared = 0;
Q_FOREACH ( double value, values ) Q_FOREACH ( double value, mValues )
{ {
double diff = value - mMean; double diff = value - mMean;
sumSquared += diff * diff; sumSquared += diff * diff;
} }
mStdev = qPow( sumSquared / values.count(), 0.5 ); mStdev = qPow( sumSquared / mValues.count(), 0.5 );
mSampleStdev = qPow( sumSquared / ( values.count() - 1 ), 0.5 ); mSampleStdev = qPow( sumSquared / ( mValues.count() - 1 ), 0.5 );
} }
QList<double> sorted;
if ( mStatistics & QgsStatisticalSummary::Median if ( mStatistics & QgsStatisticalSummary::Median
|| mStatistics & QgsStatisticalSummary::FirstQuartile || mStatistics & QgsStatisticalSummary::FirstQuartile
|| mStatistics & QgsStatisticalSummary::ThirdQuartile || mStatistics & QgsStatisticalSummary::ThirdQuartile
|| mStatistics & QgsStatisticalSummary::InterQuartileRange ) || mStatistics & QgsStatisticalSummary::InterQuartileRange )
{ {
sorted = values; qSort( mValues.begin(), mValues.end() );
qSort( sorted.begin(), sorted.end() );
bool even = ( mCount % 2 ) < 1; bool even = ( mCount % 2 ) < 1;
if ( even ) if ( even )
{ {
mMedian = ( sorted[mCount / 2 - 1] + sorted[mCount / 2] ) / 2.0; mMedian = ( mValues[mCount / 2 - 1] + mValues[mCount / 2] ) / 2.0;
} }
else //odd else //odd
{ {
mMedian = sorted[( mCount + 1 ) / 2 - 1]; mMedian = mValues[( mCount + 1 ) / 2 - 1];
} }
} }
@ -119,11 +133,11 @@ void QgsStatisticalSummary::calculate( const QList<double> &values )
bool even = ( halfCount % 2 ) < 1; bool even = ( halfCount % 2 ) < 1;
if ( even ) if ( even )
{ {
mFirstQuartile = ( sorted[halfCount / 2 - 1] + sorted[halfCount / 2] ) / 2.0; mFirstQuartile = ( mValues[halfCount / 2 - 1] + mValues[halfCount / 2] ) / 2.0;
} }
else //odd else //odd
{ {
mFirstQuartile = sorted[( halfCount + 1 ) / 2 - 1]; mFirstQuartile = mValues[( halfCount + 1 ) / 2 - 1];
} }
} }
else else
@ -132,11 +146,11 @@ void QgsStatisticalSummary::calculate( const QList<double> &values )
bool even = ( halfCount % 2 ) < 1; bool even = ( halfCount % 2 ) < 1;
if ( even ) if ( even )
{ {
mFirstQuartile = ( sorted[halfCount / 2 - 1] + sorted[halfCount / 2] ) / 2.0; mFirstQuartile = ( mValues[halfCount / 2 - 1] + mValues[halfCount / 2] ) / 2.0;
} }
else //odd else //odd
{ {
mFirstQuartile = sorted[( halfCount + 1 ) / 2 - 1]; mFirstQuartile = mValues[( halfCount + 1 ) / 2 - 1];
} }
} }
} }
@ -150,11 +164,11 @@ void QgsStatisticalSummary::calculate( const QList<double> &values )
bool even = ( halfCount % 2 ) < 1; bool even = ( halfCount % 2 ) < 1;
if ( even ) if ( even )
{ {
mThirdQuartile = ( sorted[ halfCount + halfCount / 2 - 1] + sorted[ halfCount + halfCount / 2] ) / 2.0; mThirdQuartile = ( mValues[ halfCount + halfCount / 2 - 1] + mValues[ halfCount + halfCount / 2] ) / 2.0;
} }
else //odd else //odd
{ {
mThirdQuartile = sorted[( halfCount + 1 ) / 2 - 1 + halfCount ]; mThirdQuartile = mValues[( halfCount + 1 ) / 2 - 1 + halfCount ];
} }
} }
else else
@ -163,11 +177,11 @@ void QgsStatisticalSummary::calculate( const QList<double> &values )
bool even = ( halfCount % 2 ) < 1; bool even = ( halfCount % 2 ) < 1;
if ( even ) if ( even )
{ {
mThirdQuartile = ( sorted[ halfCount + halfCount / 2 - 2 ] + sorted[ halfCount + halfCount / 2 - 1 ] ) / 2.0; mThirdQuartile = ( mValues[ halfCount + halfCount / 2 - 2 ] + mValues[ halfCount + halfCount / 2 - 1 ] ) / 2.0;
} }
else //odd else //odd
{ {
mThirdQuartile = sorted[( halfCount + 1 ) / 2 - 2 + halfCount ]; mThirdQuartile = mValues[( halfCount + 1 ) / 2 - 2 + halfCount ];
} }
} }
} }

View File

@ -91,6 +91,26 @@ class CORE_EXPORT QgsStatisticalSummary
*/ */
void calculate( const QList<double>& values ); void calculate( const QList<double>& values );
/** Adds a single value to the statistics calculation. Calling this method
* allows values to be added to the calculation one at a time. For large
* quantities of values this may be more efficient then first adding all the
* values to a list and calling calculate().
* @param value value to add
* @note call reset() before adding the first value using this method
* to clear the results from any previous calculations
* @note finalize() must be called after adding the final value and before
* retrieving calculated statistics.
* @see calculate()
* @see finalize()
*/
void addValue( double value );
/** Must be called after adding all values with addValues() and before retrieving
* any calculated statistics.
* @see addValue()
*/
void finalize();
/** Returns the value of a specified statistic /** Returns the value of a specified statistic
* @param stat statistic to return * @param stat statistic to return
* @returns calculated value of statistic * @returns calculated value of statistic
@ -201,6 +221,7 @@ class CORE_EXPORT QgsStatisticalSummary
double mFirstQuartile; double mFirstQuartile;
double mThirdQuartile; double mThirdQuartile;
QMap< double, int > mValueCount; QMap< double, int > mValueCount;
QList< double > mValues;
}; };
Q_DECLARE_OPERATORS_FOR_FLAGS( QgsStatisticalSummary::Statistics ) Q_DECLARE_OPERATORS_FOR_FLAGS( QgsStatisticalSummary::Statistics )

View File

@ -52,6 +52,27 @@ void QgsStringStatisticalSummary::calculate( const QStringList& values )
{ {
testString( string ); testString( string );
} }
finalize();
}
void QgsStringStatisticalSummary::addString( const QString& string )
{
testString( string );
}
void QgsStringStatisticalSummary::addValue( const QVariant& value )
{
if ( value.type() == QVariant::String )
{
testString( value.toString() );
}
finalize();
}
void QgsStringStatisticalSummary::finalize()
{
//nothing to do for now - this method has been added for forward compatibility
//if statistics are implemented which require a post-calculation step
} }
void QgsStringStatisticalSummary::calculateFromVariants( const QVariantList& values ) void QgsStringStatisticalSummary::calculateFromVariants( const QVariantList& values )

View File

@ -77,19 +77,56 @@ class CORE_EXPORT QgsStringStatisticalSummary
*/ */
void reset(); void reset();
/** Calculates summary statistics for a list of strings. /** Calculates summary statistics for an entire list of strings at once.
* @param values list of strings * @param values list of strings
* @see calculateFromVariants() * @see calculateFromVariants()
* @see addString()
*/ */
void calculate( const QStringList& values ); void calculate( const QStringList& values );
/** Calculates summary statistics for a list of variants. Any non-string variants will be /** Calculates summary statistics for an entire list of variants at once. Any
* ignored. * non-string variants will be ignored.
* @param values list of variants * @param values list of variants
* @see calculate() * @see calculate()
* @see addValue()
*/ */
void calculateFromVariants( const QVariantList& values ); void calculateFromVariants( const QVariantList& values );
/** Adds a single string to the statistics calculation. Calling this method
* allows strings to be added to the calculation one at a time. For large
* quantities of strings this may be more efficient then first adding all the
* strings to a list and calling calculate().
* @param string string to add
* @note call reset() before adding the first string using this method
* to clear the results from any previous calculations
* @note finalize() must be called after adding the final string and before
* retrieving calculated statistics.
* @see calculate()
* @see addValue()
* @see finalize()
*/
void addString( const QString& string );
/** Adds a single variant to the statistics calculation. Calling this method
* allows variants to be added to the calculation one at a time. For large
* quantities of variants this may be more efficient then first adding all the
* variants to a list and calling calculateFromVariants().
* @param value variant to add
* @note call reset() before adding the first string using this method
* to clear the results from any previous calculations
* @note finalize() must be called after adding the final value and before
* retrieving calculated statistics.
* @see calculateFromVariants()
* @see finalize()
*/
void addValue( const QVariant& value );
/** Must be called after adding all strings with addString() and before retrieving
* any calculated string statistics.
* @see addString()
*/
void finalize();
/** Returns the value of a specified statistic /** Returns the value of a specified statistic
* @param stat statistic to return * @param stat statistic to return
* @returns calculated value of statistic * @returns calculated value of statistic

View File

@ -62,64 +62,147 @@ void TestQgsStatisticSummary::cleanup()
void TestQgsStatisticSummary::stats() void TestQgsStatisticSummary::stats()
{ {
//note - we test everything twice, once using the statistics calculated by passing
//a list of values and once using the statistics calculated by passing values
//one-at-a-time
QgsStatisticalSummary s( QgsStatisticalSummary::All ); QgsStatisticalSummary s( QgsStatisticalSummary::All );
QgsStatisticalSummary s2( QgsStatisticalSummary::All );
QList<double> values; QList<double> values;
values << 4 << 2 << 3 << 2 << 5 << 8; values << 4 << 2 << 3 << 2 << 5 << 8;
s.calculate( values ); s.calculate( values );
s2.addValue( 4 );
s2.addValue( 2 );
s2.addValue( 3 );
s2.addValue( 2 );
s2.addValue( 5 );
s2.addValue( 8 );
s2.finalize();
QCOMPARE( s.count(), 6 ); QCOMPARE( s.count(), 6 );
QCOMPARE( s2.count(), 6 );
QCOMPARE( s.sum(), 24.0 ); QCOMPARE( s.sum(), 24.0 );
QCOMPARE( s2.sum(), 24.0 );
QCOMPARE( s.mean(), 4.0 ); QCOMPARE( s.mean(), 4.0 );
QCOMPARE( s2.mean(), 4.0 );
QVERIFY( qgsDoubleNear( s.stDev(), 2.0816, 0.0001 ) ); QVERIFY( qgsDoubleNear( s.stDev(), 2.0816, 0.0001 ) );
QVERIFY( qgsDoubleNear( s2.stDev(), 2.0816, 0.0001 ) );
QVERIFY( qgsDoubleNear( s.sampleStDev(), 2.2803, 0.0001 ) ); QVERIFY( qgsDoubleNear( s.sampleStDev(), 2.2803, 0.0001 ) );
QVERIFY( qgsDoubleNear( s2.sampleStDev(), 2.2803, 0.0001 ) );
QCOMPARE( s.min(), 2.0 ); QCOMPARE( s.min(), 2.0 );
QCOMPARE( s2.min(), 2.0 );
QCOMPARE( s.max(), 8.0 ); QCOMPARE( s.max(), 8.0 );
QCOMPARE( s2.max(), 8.0 );
QCOMPARE( s.range(), 6.0 ); QCOMPARE( s.range(), 6.0 );
QCOMPARE( s2.range(), 6.0 );
QCOMPARE( s.median(), 3.5 ); QCOMPARE( s.median(), 3.5 );
QCOMPARE( s2.median(), 3.5 );
values << 9; values << 9;
s.calculate( values ); s.calculate( values );
s2.addValue( 9 );
s2.finalize();
QCOMPARE( s.median(), 4.0 ); QCOMPARE( s.median(), 4.0 );
QCOMPARE( s2.median(), 4.0 );
values << 4 << 5 << 8 << 12 << 12 << 12; values << 4 << 5 << 8 << 12 << 12 << 12;
s.calculate( values ); s.calculate( values );
s2.addValue( 4 );
s2.addValue( 5 ) ;
s2.addValue( 8 );
s2.addValue( 12 );
s2.addValue( 12 );
s2.addValue( 12 );
s2.finalize();
QCOMPARE( s.variety(), 7 ); QCOMPARE( s.variety(), 7 );
QCOMPARE( s2.variety(), 7 );
QCOMPARE( s.minority(), 3.0 ); QCOMPARE( s.minority(), 3.0 );
QCOMPARE( s2.minority(), 3.0 );
QCOMPARE( s.majority(), 12.0 ); QCOMPARE( s.majority(), 12.0 );
QCOMPARE( s2.majority(), 12.0 );
//test quartiles. lots of possibilities here, involving odd/even/divisible by 4 counts //test quartiles. lots of possibilities here, involving odd/even/divisible by 4 counts
values.clear(); values.clear();
values << 7 << 15 << 36 << 39 << 40 << 41; values << 7 << 15 << 36 << 39 << 40 << 41;
s.calculate( values ); s.calculate( values );
s2.reset();
s2.addValue( 7 );
s2.addValue( 15 );
s2.addValue( 36 );
s2.addValue( 39 );
s2.addValue( 40 );
s2.addValue( 41 );
s2.finalize();
QCOMPARE( s.median(), 37.5 ); QCOMPARE( s.median(), 37.5 );
QCOMPARE( s2.median(), 37.5 );
QCOMPARE( s.firstQuartile(), 15.0 ); QCOMPARE( s.firstQuartile(), 15.0 );
QCOMPARE( s2.firstQuartile(), 15.0 );
QCOMPARE( s.thirdQuartile(), 40.0 ); QCOMPARE( s.thirdQuartile(), 40.0 );
QCOMPARE( s2.thirdQuartile(), 40.0 );
QCOMPARE( s.interQuartileRange(), 25.0 ); QCOMPARE( s.interQuartileRange(), 25.0 );
QCOMPARE( s2.interQuartileRange(), 25.0 );
values.clear(); values.clear();
values << 7 << 15 << 36 << 39 << 40 << 41 << 43 << 49; values << 7 << 15 << 36 << 39 << 40 << 41 << 43 << 49;
s.calculate( values ); s.calculate( values );
s2.reset();
s2.addValue( 7 );
s2.addValue( 15 );
s2.addValue( 36 );
s2.addValue( 39 );
s2.addValue( 40 );
s2.addValue( 41 ) ;
s2.addValue( 43 );
s2.addValue( 49 );
s2.finalize();
QCOMPARE( s.median(), 39.5 ); QCOMPARE( s.median(), 39.5 );
QCOMPARE( s2.median(), 39.5 );
QCOMPARE( s.firstQuartile(), 25.5 ); QCOMPARE( s.firstQuartile(), 25.5 );
QCOMPARE( s2.firstQuartile(), 25.5 );
QCOMPARE( s.thirdQuartile(), 42.0 ); QCOMPARE( s.thirdQuartile(), 42.0 );
QCOMPARE( s2.thirdQuartile(), 42.0 );
QCOMPARE( s.interQuartileRange(), 16.5 ); QCOMPARE( s.interQuartileRange(), 16.5 );
QCOMPARE( s2.interQuartileRange(), 16.5 );
values.clear(); values.clear();
values << 6 << 7 << 15 << 36 << 39 << 40 << 41 << 42 << 43 << 47 << 49; values << 6 << 7 << 15 << 36 << 39 << 40 << 41 << 42 << 43 << 47 << 49;
s.calculate( values ); s.calculate( values );
s2.reset();
s2.addValue( 6 );
s2.addValue( 7 );
s2.addValue( 15 );
s2.addValue( 36 );
s2.addValue( 39 );
s2.addValue( 40 );
s2.addValue( 41 );
s2.addValue( 42 );
s2.addValue( 43 );
s2.addValue( 47 );
s2.addValue( 49 );
s2.finalize();
QCOMPARE( s.median(), 40.0 ); QCOMPARE( s.median(), 40.0 );
QCOMPARE( s2.median(), 40.0 );
QCOMPARE( s.firstQuartile(), 25.5 ); QCOMPARE( s.firstQuartile(), 25.5 );
QCOMPARE( s2.firstQuartile(), 25.5 );
QCOMPARE( s.thirdQuartile(), 42.5 ); QCOMPARE( s.thirdQuartile(), 42.5 );
QCOMPARE( s2.thirdQuartile(), 42.5 );
QCOMPARE( s.interQuartileRange(), 17.0 ); QCOMPARE( s.interQuartileRange(), 17.0 );
QCOMPARE( s2.interQuartileRange(), 17.0 );
values.clear(); values.clear();
values << 6 << 7 << 15 << 36 << 39 << 40 << 41 << 42 << 43 << 47 << 49 << 50 << 58; values << 6 << 7 << 15 << 36 << 39 << 40 << 41 << 42 << 43 << 47 << 49 << 50 << 58;
s.calculate( values ); s.calculate( values );
s2.addValue( 50 );
s2.addValue( 58 );
s2.finalize();
QCOMPARE( s.median(), 41.0 ); QCOMPARE( s.median(), 41.0 );
QCOMPARE( s2.median(), 41.0 );
QCOMPARE( s.firstQuartile(), 36.0 ); QCOMPARE( s.firstQuartile(), 36.0 );
QCOMPARE( s2.firstQuartile(), 36.0 );
QCOMPARE( s.thirdQuartile(), 47.0 ); QCOMPARE( s.thirdQuartile(), 47.0 );
QCOMPARE( s2.thirdQuartile(), 47.0 );
QCOMPARE( s.interQuartileRange(), 11.0 ); QCOMPARE( s.interQuartileRange(), 11.0 );
QCOMPARE( s2.interQuartileRange(), 11.0 );
} }
void TestQgsStatisticSummary::individualStatCalculations_data() void TestQgsStatisticSummary::individualStatCalculations_data()
@ -165,6 +248,22 @@ void TestQgsStatisticSummary::individualStatCalculations()
s.calculate( values ); s.calculate( values );
QVERIFY( qgsDoubleNear( s.statistic( stat ), expected, 0.00001 ) ); QVERIFY( qgsDoubleNear( s.statistic( stat ), expected, 0.00001 ) );
//also test using values added one-at-a-time
QgsStatisticalSummary s2( QgsStatisticalSummary::Statistics( 0 ) );
s2.setStatistics( stat );
s2.addValue( 4 );
s2.addValue( 4 );
s2.addValue( 2 );
s2.addValue( 3 );
s2.addValue( 3 );
s2.addValue( 3 ) ;
s2.addValue( 5 ) ;
s2.addValue( 5 ) ;
s2.addValue( 8 );
s2.addValue( 8 );
s2.finalize();
QCOMPARE( s2.statistics(), stat );
//make sure stat has a valid display name //make sure stat has a valid display name
QVERIFY( !QgsStatisticalSummary::displayName( stat ).isEmpty() ); QVERIFY( !QgsStatisticalSummary::displayName( stat ).isEmpty() );
} }

View File

@ -24,19 +24,29 @@ from qgis.testing import unittest
class PyQgsDateTimeStatisticalSummary(unittest.TestCase): class PyQgsDateTimeStatisticalSummary(unittest.TestCase):
def testStats(self): def testStats(self):
# we test twice, once with values added as a list and once using values
# added one-at-a-time
dates = [QDateTime(QDate(2015, 3, 4), QTime(11, 10, 54)),
QDateTime(QDate(2011, 1, 5), QTime(15, 3, 1)),
QDateTime(QDate(2015, 3, 4), QTime(11, 10, 54)),
QDateTime(QDate(2015, 3, 4), QTime(11, 10, 54)),
QDateTime(QDate(2019, 12, 28), QTime(23, 10, 1)),
QDateTime(),
QDateTime(QDate(1998, 1, 2), QTime(1, 10, 54)),
QDateTime(),
QDateTime(QDate(2011, 1, 5), QTime(11, 10, 54))]
s = QgsDateTimeStatisticalSummary() s = QgsDateTimeStatisticalSummary()
self.assertEqual(s.statistics(), QgsDateTimeStatisticalSummary.All) self.assertEqual(s.statistics(), QgsDateTimeStatisticalSummary.All)
s.calculate([QDateTime(QDate(2015, 3, 4), QTime(11, 10, 54)), s.calculate(dates)
QDateTime(QDate(2011, 1, 5), QTime(15, 3, 1)), s2 = QgsDateTimeStatisticalSummary()
QDateTime(QDate(2015, 3, 4), QTime(11, 10, 54)), for d in dates:
QDateTime(QDate(2015, 3, 4), QTime(11, 10, 54)), s2.addValue(d)
QDateTime(QDate(2019, 12, 28), QTime(23, 10, 1)), s2.finalize()
QDateTime(),
QDateTime(QDate(1998, 1, 2), QTime(1, 10, 54)),
QDateTime(),
QDateTime(QDate(2011, 1, 5), QTime(11, 10, 54))])
self.assertEqual(s.count(), 9) self.assertEqual(s.count(), 9)
self.assertEqual(s2.count(), 9)
self.assertEqual(s.countDistinct(), 6) self.assertEqual(s.countDistinct(), 6)
self.assertEqual(s2.countDistinct(), 6)
self.assertEqual(set(s.distinctValues()), self.assertEqual(set(s.distinctValues()),
set([QDateTime(QDate(2015, 3, 4), QTime(11, 10, 54)), set([QDateTime(QDate(2015, 3, 4), QTime(11, 10, 54)),
QDateTime(QDate(2011, 1, 5), QTime(15, 3, 1)), QDateTime(QDate(2011, 1, 5), QTime(15, 3, 1)),
@ -44,10 +54,15 @@ class PyQgsDateTimeStatisticalSummary(unittest.TestCase):
QDateTime(), QDateTime(),
QDateTime(QDate(1998, 1, 2), QTime(1, 10, 54)), QDateTime(QDate(1998, 1, 2), QTime(1, 10, 54)),
QDateTime(QDate(2011, 1, 5), QTime(11, 10, 54))])) QDateTime(QDate(2011, 1, 5), QTime(11, 10, 54))]))
self.assertEqual(s2.distinctValues(), s.distinctValues())
self.assertEqual(s.countMissing(), 2) self.assertEqual(s.countMissing(), 2)
self.assertEqual(s2.countMissing(), 2)
self.assertEqual(s.min(), QDateTime(QDate(1998, 1, 2), QTime(1, 10, 54))) self.assertEqual(s.min(), QDateTime(QDate(1998, 1, 2), QTime(1, 10, 54)))
self.assertEqual(s2.min(), QDateTime(QDate(1998, 1, 2), QTime(1, 10, 54)))
self.assertEqual(s.max(), QDateTime(QDate(2019, 12, 28), QTime(23, 10, 1))) self.assertEqual(s.max(), QDateTime(QDate(2019, 12, 28), QTime(23, 10, 1)))
self.assertEqual(s2.max(), QDateTime(QDate(2019, 12, 28), QTime(23, 10, 1)))
self.assertEqual(s.range(), QgsInterval(693871147)) self.assertEqual(s.range(), QgsInterval(693871147))
self.assertEqual(s2.range(), QgsInterval(693871147))
def testIndividualStats(self): def testIndividualStats(self):
# tests calculation of statistics one at a time, to make sure statistic calculations are not # tests calculation of statistics one at a time, to make sure statistic calculations are not
@ -60,7 +75,10 @@ class PyQgsDateTimeStatisticalSummary(unittest.TestCase):
{'stat': QgsDateTimeStatisticalSummary.Range, 'expected': QgsInterval(693871147)}, {'stat': QgsDateTimeStatisticalSummary.Range, 'expected': QgsInterval(693871147)},
] ]
# we test twice, once with values added as a list and once using values
# added one-at-a-time
s = QgsDateTimeStatisticalSummary() s = QgsDateTimeStatisticalSummary()
s3 = QgsDateTimeStatisticalSummary()
for t in tests: for t in tests:
# test constructor # test constructor
s2 = QgsDateTimeStatisticalSummary(t['stat']) s2 = QgsDateTimeStatisticalSummary(t['stat'])
@ -68,16 +86,25 @@ class PyQgsDateTimeStatisticalSummary(unittest.TestCase):
s.setStatistics(t['stat']) s.setStatistics(t['stat'])
self.assertEqual(s.statistics(), t['stat']) self.assertEqual(s.statistics(), t['stat'])
s.calculate([QDateTime(QDate(2015, 3, 4), QTime(11, 10, 54)), s3.setStatistics(t['stat'])
QDateTime(QDate(2011, 1, 5), QTime(15, 3, 1)),
QDateTime(QDate(2015, 3, 4), QTime(11, 10, 54)), dates = [QDateTime(QDate(2015, 3, 4), QTime(11, 10, 54)),
QDateTime(QDate(2015, 3, 4), QTime(11, 10, 54)), QDateTime(QDate(2011, 1, 5), QTime(15, 3, 1)),
QDateTime(QDate(2019, 12, 28), QTime(23, 10, 1)), QDateTime(QDate(2015, 3, 4), QTime(11, 10, 54)),
QDateTime(), QDateTime(QDate(2015, 3, 4), QTime(11, 10, 54)),
QDateTime(QDate(1998, 1, 2), QTime(1, 10, 54)), QDateTime(QDate(2019, 12, 28), QTime(23, 10, 1)),
QDateTime(), QDateTime(),
QDateTime(QDate(2011, 1, 5), QTime(11, 10, 54))]) QDateTime(QDate(1998, 1, 2), QTime(1, 10, 54)),
QDateTime(),
QDateTime(QDate(2011, 1, 5), QTime(11, 10, 54))]
s.calculate(dates)
s3.reset()
for d in dates:
s3.addValue(d)
s3.finalize()
self.assertEqual(s.statistic(t['stat']), t['expected']) self.assertEqual(s.statistic(t['stat']), t['expected'])
self.assertEqual(s3.statistic(t['stat']), t['expected'])
# display name # display name
self.assertTrue(len(QgsDateTimeStatisticalSummary.displayName(t['stat'])) > 0) self.assertTrue(len(QgsDateTimeStatisticalSummary.displayName(t['stat'])) > 0)

View File

@ -22,17 +22,32 @@ from qgis.testing import unittest
class PyQgsStringStatisticalSummary(unittest.TestCase): class PyQgsStringStatisticalSummary(unittest.TestCase):
def testStats(self): def testStats(self):
# we test twice, once with values added as a list and once using values
# added one-at-a-time
s = QgsStringStatisticalSummary() s = QgsStringStatisticalSummary()
self.assertEqual(s.statistics(), QgsStringStatisticalSummary.All) self.assertEqual(s.statistics(), QgsStringStatisticalSummary.All)
s.calculate(['cc', 'aaaa', 'bbbbbbbb', 'aaaa', 'eeee', '', 'eeee', '', 'dddd']) strings = ['cc', 'aaaa', 'bbbbbbbb', 'aaaa', 'eeee', '', 'eeee', '', 'dddd']
s.calculate(strings)
s2 = QgsStringStatisticalSummary()
for string in strings:
s2.addString(string)
s2.finalize()
self.assertEqual(s.count(), 9) self.assertEqual(s.count(), 9)
self.assertEqual(s2.count(), 9)
self.assertEqual(s.countDistinct(), 6) self.assertEqual(s.countDistinct(), 6)
self.assertEqual(s2.countDistinct(), 6)
self.assertEqual(set(s.distinctValues()), set(['cc', 'aaaa', 'bbbbbbbb', 'eeee', 'dddd', ''])) self.assertEqual(set(s.distinctValues()), set(['cc', 'aaaa', 'bbbbbbbb', 'eeee', 'dddd', '']))
self.assertEqual(s2.distinctValues(), s.distinctValues())
self.assertEqual(s.countMissing(), 2) self.assertEqual(s.countMissing(), 2)
self.assertEqual(s2.countMissing(), 2)
self.assertEqual(s.min(), 'aaaa') self.assertEqual(s.min(), 'aaaa')
self.assertEqual(s2.min(), 'aaaa')
self.assertEqual(s.max(), 'eeee') self.assertEqual(s.max(), 'eeee')
self.assertEqual(s2.max(), 'eeee')
self.assertEqual(s.minLength(), 0) self.assertEqual(s.minLength(), 0)
self.assertEqual(s2.minLength(), 0)
self.assertEqual(s.maxLength(), 8) self.assertEqual(s.maxLength(), 8)
self.assertEqual(s2.maxLength(), 8)
#extra check for minLength without empty strings #extra check for minLength without empty strings
s.calculate(['1111111', '111', '11111']) s.calculate(['1111111', '111', '11111'])
@ -51,15 +66,25 @@ class PyQgsStringStatisticalSummary(unittest.TestCase):
] ]
s = QgsStringStatisticalSummary() s = QgsStringStatisticalSummary()
s3 = QgsStringStatisticalSummary()
for t in tests: for t in tests:
# test constructor # test constructor
s2 = QgsStringStatisticalSummary(t['stat']) s2 = QgsStringStatisticalSummary(t['stat'])
self.assertEqual(s2.statistics(), t['stat']) self.assertEqual(s2.statistics(), t['stat'])
s.setStatistics(t['stat']) s.setStatistics(t['stat'])
s3.setStatistics(t['stat'])
self.assertEqual(s.statistics(), t['stat']) self.assertEqual(s.statistics(), t['stat'])
s.calculate(['cc', 'aaaa', 'bbbbbbbb', 'aaaa', 'eeee', '', 'eeee', '', 'dddd'])
strings = ['cc', 'aaaa', 'bbbbbbbb', 'aaaa', 'eeee', '', 'eeee', '', 'dddd']
s.calculate(strings)
s3.reset()
for string in strings:
s3.addString(string)
s3.finalize()
self.assertEqual(s.statistic(t['stat']), t['expected']) self.assertEqual(s.statistic(t['stat']), t['expected'])
self.assertEqual(s3.statistic(t['stat']), t['expected'])
# display name # display name
self.assertTrue(len(QgsStringStatisticalSummary.displayName(t['stat'])) > 0) self.assertTrue(len(QgsStringStatisticalSummary.displayName(t['stat'])) > 0)