Skip to content

Commit 31e8611

Browse files
committedMay 3, 2015
Add quartiles and IQR to QgsStatisticalSummary calculations
1 parent 154468b commit 31e8611

File tree

4 files changed

+154
-3
lines changed

4 files changed

+154
-3
lines changed
 

‎python/core/qgsstatisticalsummary.sip

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ class QgsStatisticalSummary
3333
Minority, //!< Minority of values
3434
Majority, //!< Majority of values
3535
Variety, //!< Variety (count of distinct) values
36+
FirstQuartile, //!< First quartile
37+
ThirdQuartile, //!< Third quartile
38+
InterQuartileRange, //!< Inter quartile range (IQR)
3639
All
3740
};
3841

@@ -127,6 +130,27 @@ class QgsStatisticalSummary
127130
* @see minority
128131
*/
129132
double majority() const;
133+
134+
/** Returns the first quartile of the values. The quartile is calculated using the
135+
* "Tukey's hinges" method.
136+
* @see thirdQuartile
137+
* @see interQuartileRange
138+
*/
139+
double firstQuartile() const;
140+
141+
/** Returns the third quartile of the values. The quartile is calculated using the
142+
* "Tukey's hinges" method.
143+
* @see firstQuartile
144+
* @see interQuartileRange
145+
*/
146+
double thirdQuartile() const;
147+
148+
/** Returns the inter quartile range of the values. The quartiles are calculated using the
149+
* "Tukey's hinges" method.
150+
* @see firstQuartile
151+
* @see thirdQuartile
152+
*/
153+
double interQuartileRange() const;
130154

131155
};
132156

‎src/core/qgsstatisticalsummary.cpp

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ void QgsStatisticalSummary::reset()
4141
mSampleStdev = 0;
4242
mMinority = 0;
4343
mMajority = 0;
44+
mFirstQuartile = 0;
45+
mThirdQuartile = 0;
4446
mValueCount.clear();
4547
}
4648

@@ -76,9 +78,13 @@ void QgsStatisticalSummary::calculate( const QList<double> &values )
7678
mSampleStdev = qPow( sumSquared / ( values.count() - 1 ), 0.5 );
7779
}
7880

79-
if ( mStatistics & QgsStatisticalSummary::Median )
81+
QList<double> sorted;
82+
if ( mStatistics & QgsStatisticalSummary::Median
83+
|| mStatistics & QgsStatisticalSummary::FirstQuartile
84+
|| mStatistics & QgsStatisticalSummary::ThirdQuartile
85+
|| mStatistics & QgsStatisticalSummary::InterQuartileRange )
8086
{
81-
QList<double> sorted = values;
87+
sorted = values;
8288
qSort( sorted.begin(), sorted.end() );
8389
bool even = ( mCount % 2 ) < 1;
8490
if ( even )
@@ -91,6 +97,68 @@ void QgsStatisticalSummary::calculate( const QList<double> &values )
9197
}
9298
}
9399

100+
if ( mStatistics & QgsStatisticalSummary::FirstQuartile
101+
|| mStatistics & QgsStatisticalSummary::InterQuartileRange )
102+
{
103+
if (( mCount % 2 ) < 1 )
104+
{
105+
int halfCount = mCount / 2;
106+
bool even = ( halfCount % 2 ) < 1;
107+
if ( even )
108+
{
109+
mFirstQuartile = ( sorted[halfCount / 2 - 1] + sorted[halfCount / 2] ) / 2.0;
110+
}
111+
else //odd
112+
{
113+
mFirstQuartile = sorted[( halfCount + 1 ) / 2 - 1];
114+
}
115+
}
116+
else
117+
{
118+
int halfCount = mCount / 2 + 1;
119+
bool even = ( halfCount % 2 ) < 1;
120+
if ( even )
121+
{
122+
mFirstQuartile = ( sorted[halfCount / 2 - 1] + sorted[halfCount / 2] ) / 2.0;
123+
}
124+
else //odd
125+
{
126+
mFirstQuartile = sorted[( halfCount + 1 ) / 2 - 1];
127+
}
128+
}
129+
}
130+
131+
if ( mStatistics & QgsStatisticalSummary::ThirdQuartile
132+
|| mStatistics & QgsStatisticalSummary::InterQuartileRange )
133+
{
134+
if (( mCount % 2 ) < 1 )
135+
{
136+
int halfCount = mCount / 2;
137+
bool even = ( halfCount % 2 ) < 1;
138+
if ( even )
139+
{
140+
mThirdQuartile = ( sorted[ halfCount + halfCount / 2 - 1] + sorted[ halfCount + halfCount / 2] ) / 2.0;
141+
}
142+
else //odd
143+
{
144+
mThirdQuartile = sorted[( halfCount + 1 ) / 2 - 1 + halfCount ];
145+
}
146+
}
147+
else
148+
{
149+
int halfCount = mCount / 2 + 1;
150+
bool even = ( halfCount % 2 ) < 1;
151+
if ( even )
152+
{
153+
mThirdQuartile = ( sorted[ halfCount + halfCount / 2 - 2 ] + sorted[ halfCount + halfCount / 2 - 1 ] ) / 2.0;
154+
}
155+
else //odd
156+
{
157+
mThirdQuartile = sorted[( halfCount + 1 ) / 2 - 2 + halfCount ];
158+
}
159+
}
160+
}
161+
94162
if ( mStatistics & QgsStatisticalSummary::Minority || mStatistics & QgsStatisticalSummary::Majority )
95163
{
96164
QList<int> valueCounts = mValueCount.values();

‎src/core/qgsstatisticalsummary.h

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,10 @@ class CORE_EXPORT QgsStatisticalSummary
4949
Minority = 512, //!< Minority of values
5050
Majority = 1024, //!< Majority of values
5151
Variety = 2048, //!< Variety (count of distinct) values
52-
All = Count | Sum | Mean | Median | StDev | Max | Min | Range | Minority | Majority | Variety
52+
FirstQuartile = 4096, //!< First quartile
53+
ThirdQuartile = 8192, //!< Third quartile
54+
InterQuartileRange = 16384, //!< Inter quartile range (IQR)
55+
All = Count | Sum | Mean | Median | StDev | Max | Min | Range | Minority | Majority | Variety | FirstQuartile | ThirdQuartile | InterQuartileRange
5356
};
5457
Q_DECLARE_FLAGS( Statistics, Statistic )
5558

@@ -143,6 +146,27 @@ class CORE_EXPORT QgsStatisticalSummary
143146
*/
144147
double majority() const { return mMajority; }
145148

149+
/** Returns the first quartile of the values. The quartile is calculated using the
150+
* "Tukey's hinges" method.
151+
* @see thirdQuartile
152+
* @see interQuartileRange
153+
*/
154+
double firstQuartile() const { return mFirstQuartile; }
155+
156+
/** Returns the third quartile of the values. The quartile is calculated using the
157+
* "Tukey's hinges" method.
158+
* @see firstQuartile
159+
* @see interQuartileRange
160+
*/
161+
double thirdQuartile() const { return mThirdQuartile; }
162+
163+
/** Returns the inter quartile range of the values. The quartiles are calculated using the
164+
* "Tukey's hinges" method.
165+
* @see firstQuartile
166+
* @see thirdQuartile
167+
*/
168+
double interQuartileRange() const { return mThirdQuartile - mFirstQuartile; }
169+
146170
private:
147171

148172
Statistics mStatistics;
@@ -157,6 +181,8 @@ class CORE_EXPORT QgsStatisticalSummary
157181
double mSampleStdev;
158182
double mMinority;
159183
double mMajority;
184+
double mFirstQuartile;
185+
double mThirdQuartile;
160186
QMap< double, int > mValueCount;
161187
};
162188

‎tests/src/core/testqgsstatisticalsummary.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,39 @@ void TestQgsStatisticSummary::stats()
8484
QCOMPARE( s.variety(), 7 );
8585
QCOMPARE( s.minority(), 3.0 );
8686
QCOMPARE( s.majority(), 12.0 );
87+
88+
//test quartiles. lots of possibilities here, involving odd/even/divisible by 4 counts
89+
values.clear();
90+
values << 7 << 15 << 36 << 39 << 40 << 41;
91+
s.calculate( values );
92+
QCOMPARE( s.median(), 37.5 );
93+
QCOMPARE( s.firstQuartile(), 15.0 );
94+
QCOMPARE( s.thirdQuartile(), 40.0 );
95+
QCOMPARE( s.interQuartileRange(), 25.0 );
96+
97+
values.clear();
98+
values << 7 << 15 << 36 << 39 << 40 << 41 << 43 << 49;
99+
s.calculate( values );
100+
QCOMPARE( s.median(), 39.5 );
101+
QCOMPARE( s.firstQuartile(), 25.5 );
102+
QCOMPARE( s.thirdQuartile(), 42.0 );
103+
QCOMPARE( s.interQuartileRange(), 16.5 );
104+
105+
values.clear();
106+
values << 6 << 7 << 15 << 36 << 39 << 40 << 41 << 42 << 43 << 47 << 49;
107+
s.calculate( values );
108+
QCOMPARE( s.median(), 40.0 );
109+
QCOMPARE( s.firstQuartile(), 25.5 );
110+
QCOMPARE( s.thirdQuartile(), 42.5 );
111+
QCOMPARE( s.interQuartileRange(), 17.0 );
112+
113+
values.clear();
114+
values << 6 << 7 << 15 << 36 << 39 << 40 << 41 << 42 << 43 << 47 << 49 << 50 << 58;
115+
s.calculate( values );
116+
QCOMPARE( s.median(), 41.0 );
117+
QCOMPARE( s.firstQuartile(), 36.0 );
118+
QCOMPARE( s.thirdQuartile(), 47.0 );
119+
QCOMPARE( s.interQuartileRange(), 11.0 );
87120
}
88121

89122
QTEST_MAIN( TestQgsStatisticSummary )

0 commit comments

Comments
 (0)
Please sign in to comment.