Skip to content

Commit 2d2dff9

Browse files
committed
Restore basic stats alg
1 parent 32ecbcf commit 2d2dff9

6 files changed

Lines changed: 245 additions & 196 deletions

File tree

‎python/core/processing/qgsprocessingoutputs.sip‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ class QgsProcessingOutputDefinition
3434
sipType = sipType_QgsProcessingOutputRasterLayer;
3535
else if ( sipCpp->type() == "outputHtml" )
3636
sipType = sipType_QgsProcessingOutputHtml;
37+
else if ( sipCpp->type() == "outputNumber" )
38+
sipType = sipType_QgsProcessingOutputNumber;
3739
%End
3840
public:
3941

@@ -162,6 +164,26 @@ class QgsProcessingOutputHtml : QgsProcessingOutputDefinition
162164
virtual QString type() const;
163165
};
164166

167+
class QgsProcessingOutputNumber : QgsProcessingOutputDefinition
168+
{
169+
%Docstring
170+
A numeric output for processing algorithms.
171+
.. versionadded:: 3.0
172+
%End
173+
174+
%TypeHeaderCode
175+
#include "qgsprocessingoutputs.h"
176+
%End
177+
public:
178+
179+
QgsProcessingOutputNumber( const QString &name, const QString &description = QString() );
180+
%Docstring
181+
Constructor for QgsProcessingOutputNumber.
182+
%End
183+
184+
virtual QString type() const;
185+
};
186+
165187

166188

167189
/************************************************************************

‎python/plugins/processing/algs/qgis/BasicStatistics.py‎

Lines changed: 89 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,14 @@
3535
QgsStringStatisticalSummary,
3636
QgsDateTimeStatisticalSummary,
3737
QgsFeatureRequest,
38-
QgsProcessingUtils)
38+
QgsProcessingUtils,
39+
QgsProcessingParameterFeatureSource,
40+
QgsProcessingParameterTableField,
41+
QgsProcessingParameterFileOutput,
42+
QgsProcessingOutputHtml,
43+
QgsProcessingOutputNumber)
3944

4045
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
41-
from processing.core.parameters import ParameterTable
42-
from processing.core.parameters import ParameterTableField
43-
from processing.core.outputs import OutputHTML
44-
from processing.core.outputs import OutputNumber
4546

4647
pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]
4748

@@ -85,35 +86,37 @@ def group(self):
8586

8687
def __init__(self):
8788
super().__init__()
88-
self.addParameter(ParameterTable(self.INPUT_LAYER,
89-
self.tr('Input table')))
90-
self.addParameter(ParameterTableField(self.FIELD_NAME,
91-
self.tr('Field to calculate statistics on'),
92-
self.INPUT_LAYER))
93-
94-
self.addOutput(OutputHTML(self.OUTPUT_HTML_FILE,
95-
self.tr('Statistics')))
96-
97-
self.addOutput(OutputNumber(self.COUNT, self.tr('Count')))
98-
self.addOutput(OutputNumber(self.UNIQUE, self.tr('Number of unique values')))
99-
self.addOutput(OutputNumber(self.EMPTY, self.tr('Number of empty (null) values')))
100-
self.addOutput(OutputNumber(self.FILLED, self.tr('Number of non-empty values')))
101-
self.addOutput(OutputNumber(self.MIN, self.tr('Minimum value')))
102-
self.addOutput(OutputNumber(self.MAX, self.tr('Maximum value')))
103-
self.addOutput(OutputNumber(self.MIN_LENGTH, self.tr('Minimum length')))
104-
self.addOutput(OutputNumber(self.MAX_LENGTH, self.tr('Maximum length')))
105-
self.addOutput(OutputNumber(self.MEAN_LENGTH, self.tr('Mean length')))
106-
self.addOutput(OutputNumber(self.CV, self.tr('Coefficient of Variation')))
107-
self.addOutput(OutputNumber(self.SUM, self.tr('Sum')))
108-
self.addOutput(OutputNumber(self.MEAN, self.tr('Mean value')))
109-
self.addOutput(OutputNumber(self.STD_DEV, self.tr('Standard deviation')))
110-
self.addOutput(OutputNumber(self.RANGE, self.tr('Range')))
111-
self.addOutput(OutputNumber(self.MEDIAN, self.tr('Median')))
112-
self.addOutput(OutputNumber(self.MINORITY, self.tr('Minority (rarest occurring value)')))
113-
self.addOutput(OutputNumber(self.MAJORITY, self.tr('Majority (most frequently occurring value)')))
114-
self.addOutput(OutputNumber(self.FIRSTQUARTILE, self.tr('First quartile')))
115-
self.addOutput(OutputNumber(self.THIRDQUARTILE, self.tr('Third quartile')))
116-
self.addOutput(OutputNumber(self.IQR, self.tr('Interquartile Range (IQR)')))
89+
90+
self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT_LAYER,
91+
self.tr('Input layer')))
92+
93+
self.addParameter(QgsProcessingParameterTableField(self.FIELD_NAME,
94+
self.tr('Field to calculate statistics on'),
95+
None, self.INPUT_LAYER, QgsProcessingParameterTableField.Any))
96+
97+
self.addParameter(QgsProcessingParameterFileOutput(self.OUTPUT_HTML_FILE, self.tr('Statistics'), self.tr('HTML files (*.html)')))
98+
self.addOutput(QgsProcessingOutputHtml(self.OUTPUT_HTML_FILE, self.tr('Statistics')))
99+
100+
self.addOutput(QgsProcessingOutputNumber(self.COUNT, self.tr('Count')))
101+
self.addOutput(QgsProcessingOutputNumber(self.UNIQUE, self.tr('Number of unique values')))
102+
self.addOutput(QgsProcessingOutputNumber(self.EMPTY, self.tr('Number of empty (null) values')))
103+
self.addOutput(QgsProcessingOutputNumber(self.FILLED, self.tr('Number of non-empty values')))
104+
self.addOutput(QgsProcessingOutputNumber(self.MIN, self.tr('Minimum value')))
105+
self.addOutput(QgsProcessingOutputNumber(self.MAX, self.tr('Maximum value')))
106+
self.addOutput(QgsProcessingOutputNumber(self.MIN_LENGTH, self.tr('Minimum length')))
107+
self.addOutput(QgsProcessingOutputNumber(self.MAX_LENGTH, self.tr('Maximum length')))
108+
self.addOutput(QgsProcessingOutputNumber(self.MEAN_LENGTH, self.tr('Mean length')))
109+
self.addOutput(QgsProcessingOutputNumber(self.CV, self.tr('Coefficient of Variation')))
110+
self.addOutput(QgsProcessingOutputNumber(self.SUM, self.tr('Sum')))
111+
self.addOutput(QgsProcessingOutputNumber(self.MEAN, self.tr('Mean value')))
112+
self.addOutput(QgsProcessingOutputNumber(self.STD_DEV, self.tr('Standard deviation')))
113+
self.addOutput(QgsProcessingOutputNumber(self.RANGE, self.tr('Range')))
114+
self.addOutput(QgsProcessingOutputNumber(self.MEDIAN, self.tr('Median')))
115+
self.addOutput(QgsProcessingOutputNumber(self.MINORITY, self.tr('Minority (rarest occurring value)')))
116+
self.addOutput(QgsProcessingOutputNumber(self.MAJORITY, self.tr('Majority (most frequently occurring value)')))
117+
self.addOutput(QgsProcessingOutputNumber(self.FIRSTQUARTILE, self.tr('First quartile')))
118+
self.addOutput(QgsProcessingOutputNumber(self.THIRDQUARTILE, self.tr('Third quartile')))
119+
self.addOutput(QgsProcessingOutputNumber(self.IQR, self.tr('Interquartile Range (IQR)')))
117120

118121
def name(self):
119122
return 'basicstatisticsforfields'
@@ -122,29 +125,35 @@ def displayName(self):
122125
return self.tr('Basic statistics for fields')
123126

124127
def processAlgorithm(self, parameters, context, feedback):
125-
layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_LAYER), context)
126-
field_name = self.getParameterValue(self.FIELD_NAME)
127-
field = layer.fields().at(layer.fields().lookupField(field_name))
128+
source = self.parameterAsSource(parameters, self.INPUT_LAYER, context)
129+
field_name = self.parameterAsString(parameters, self.FIELD_NAME, context)
130+
field = source.fields().at(source.fields().lookupField(field_name))
128131

129-
output_file = self.getOutputValue(self.OUTPUT_HTML_FILE)
132+
output_file = self.parameterAsFileOutput(parameters, self.OUTPUT_HTML_FILE, context)
130133

131-
request = QgsFeatureRequest().setFlags(QgsFeatureRequest.NoGeometry).setSubsetOfAttributes([field_name], layer.fields())
132-
features = QgsProcessingUtils.getFeatures(layer, context, request)
133-
count = QgsProcessingUtils.featureCount(layer, context)
134+
request = QgsFeatureRequest().setFlags(QgsFeatureRequest.NoGeometry).setSubsetOfAttributes([field_name], source.fields())
135+
features = source.getFeatures(request)
136+
count = source.featureCount()
134137

135138
data = []
136-
data.append(self.tr('Analyzed layer: {}').format(layer.name()))
137139
data.append(self.tr('Analyzed field: {}').format(field_name))
140+
results = {}
138141

139142
if field.isNumeric():
140-
data.extend(self.calcNumericStats(features, feedback, field, count))
143+
d, results = self.calcNumericStats(features, feedback, field, count)
144+
data.extend(d)
141145
elif field.type() in (QVariant.Date, QVariant.Time, QVariant.DateTime):
142-
data.extend(self.calcDateTimeStats(features, feedback, field, count))
146+
d, results = self.calcDateTimeStats(features, feedback, field, count)
147+
data.extend(d)
143148
else:
144-
data.extend(self.calcStringStats(features, feedback, field, count))
149+
d, results = self.calcStringStats(features, feedback, field, count)
150+
data.extend(d)
145151

146152
self.createHTML(output_file, data)
147153

154+
results[self.OUTPUT_HTML_FILE] = output_file
155+
return results
156+
148157
def calcNumericStats(self, features, feedback, field, count):
149158
total = 100.0 / float(count)
150159
stat = QgsStatisticalSummary()
@@ -155,23 +164,23 @@ def calcNumericStats(self, features, feedback, field, count):
155164

156165
cv = stat.stDev() / stat.mean() if stat.mean() != 0 else 0
157166

158-
self.setOutputValue(self.COUNT, stat.count())
159-
self.setOutputValue(self.UNIQUE, stat.variety())
160-
self.setOutputValue(self.EMPTY, stat.countMissing())
161-
self.setOutputValue(self.FILLED, count - stat.countMissing())
162-
self.setOutputValue(self.MIN, stat.min())
163-
self.setOutputValue(self.MAX, stat.max())
164-
self.setOutputValue(self.RANGE, stat.range())
165-
self.setOutputValue(self.SUM, stat.sum())
166-
self.setOutputValue(self.MEAN, stat.mean())
167-
self.setOutputValue(self.MEDIAN, stat.median())
168-
self.setOutputValue(self.STD_DEV, stat.stDev())
169-
self.setOutputValue(self.CV, cv)
170-
self.setOutputValue(self.MINORITY, stat.minority())
171-
self.setOutputValue(self.MAJORITY, stat.majority())
172-
self.setOutputValue(self.FIRSTQUARTILE, stat.firstQuartile())
173-
self.setOutputValue(self.THIRDQUARTILE, stat.thirdQuartile())
174-
self.setOutputValue(self.IQR, stat.interQuartileRange())
167+
results = {self.COUNT: stat.count(),
168+
self.UNIQUE: stat.variety(),
169+
self.EMPTY: stat.countMissing(),
170+
self.FILLED: count - stat.countMissing(),
171+
self.MIN: stat.min(),
172+
self.MAX: stat.max(),
173+
self.RANGE: stat.range(),
174+
self.SUM: stat.sum(),
175+
self.MEAN: stat.mean(),
176+
self.MEDIAN: stat.median(),
177+
self.STD_DEV: stat.stDev(),
178+
self.CV: cv,
179+
self.MINORITY: stat.minority(),
180+
self.MAJORITY: stat.majority(),
181+
self.FIRSTQUARTILE: stat.firstQuartile(),
182+
self.THIRDQUARTILE: stat.thirdQuartile(),
183+
self.IQR: stat.interQuartileRange()}
175184

176185
data = []
177186
data.append(self.tr('Count: {}').format(stat.count()))
@@ -190,7 +199,7 @@ def calcNumericStats(self, features, feedback, field, count):
190199
data.append(self.tr('First quartile: {}').format(stat.firstQuartile()))
191200
data.append(self.tr('Third quartile: {}').format(stat.thirdQuartile()))
192201
data.append(self.tr('Interquartile Range (IQR): {}').format(stat.interQuartileRange()))
193-
return data
202+
return data, results
194203

195204
def calcStringStats(self, features, feedback, field, count):
196205
total = 100.0 / float(count)
@@ -200,15 +209,15 @@ def calcStringStats(self, features, feedback, field, count):
200209
feedback.setProgress(int(current * total))
201210
stat.finalize()
202211

203-
self.setOutputValue(self.COUNT, stat.count())
204-
self.setOutputValue(self.UNIQUE, stat.countDistinct())
205-
self.setOutputValue(self.EMPTY, stat.countMissing())
206-
self.setOutputValue(self.FILLED, stat.count() - stat.countMissing())
207-
self.setOutputValue(self.MIN, stat.min())
208-
self.setOutputValue(self.MAX, stat.max())
209-
self.setOutputValue(self.MIN_LENGTH, stat.minLength())
210-
self.setOutputValue(self.MAX_LENGTH, stat.maxLength())
211-
self.setOutputValue(self.MEAN_LENGTH, stat.meanLength())
212+
results = {self.COUNT: stat.count(),
213+
self.UNIQUE: stat.countDistinct(),
214+
self.EMPTY: stat.countMissing(),
215+
self.FILLED: stat.count() - stat.countMissing(),
216+
self.MIN: stat.min(),
217+
self.MAX: stat.max(),
218+
self.MIN_LENGTH: stat.minLength(),
219+
self.MAX_LENGTH: stat.maxLength(),
220+
self.MEAN_LENGTH: stat.meanLength()}
212221

213222
data = []
214223
data.append(self.tr('Count: {}').format(count))
@@ -220,7 +229,7 @@ def calcStringStats(self, features, feedback, field, count):
220229
data.append(self.tr('Maximum length: {}').format(stat.maxLength()))
221230
data.append(self.tr('Mean length: {}').format(stat.meanLength()))
222231

223-
return data
232+
return data, results
224233

225234
def calcDateTimeStats(self, features, feedback, field, count):
226235
total = 100.0 / float(count)
@@ -230,12 +239,12 @@ def calcDateTimeStats(self, features, feedback, field, count):
230239
feedback.setProgress(int(current * total))
231240
stat.finalize()
232241

233-
self.setOutputValue(self.COUNT, stat.count())
234-
self.setOutputValue(self.UNIQUE, stat.countDistinct())
235-
self.setOutputValue(self.EMPTY, stat.countMissing())
236-
self.setOutputValue(self.FILLED, stat.count() - stat.countMissing())
237-
self.setOutputValue(self.MIN, stat.statistic(QgsDateTimeStatisticalSummary.Min))
238-
self.setOutputValue(self.MAX, stat.statistic(QgsDateTimeStatisticalSummary.Max))
242+
results = {self.COUNT: stat.count(),
243+
self.UNIQUE: stat.countDistinct(),
244+
self.EMPTY: stat.countMissing(),
245+
self.FILLED: stat.count() - stat.countMissing(),
246+
self.MIN: stat.statistic(QgsDateTimeStatisticalSummary.Min),
247+
self.MAX: stat.statistic(QgsDateTimeStatisticalSummary.Max)}
239248

240249
data = []
241250
data.append(self.tr('Count: {}').format(count))
@@ -244,7 +253,7 @@ def calcDateTimeStats(self, features, feedback, field, count):
244253
data.append(self.tr('Minimum value: {}').format(field.displayString(stat.statistic(QgsDateTimeStatisticalSummary.Min))))
245254
data.append(self.tr('Maximum value: {}').format(field.displayString(stat.statistic(QgsDateTimeStatisticalSummary.Max))))
246255

247-
return data
256+
return data, results
248257

249258
def createHTML(self, outputFile, algData):
250259
with codecs.open(outputFile, 'w', encoding='utf-8') as f:

‎python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@
170170
# from .RasterCalculator import RasterCalculator
171171
# from .CreateAttributeIndex import CreateAttributeIndex
172172
# from .DropGeometry import DropGeometry
173-
# from .BasicStatistics import BasicStatisticsForField
173+
from .BasicStatistics import BasicStatisticsForField
174174
# from .Heatmap import Heatmap
175175
# from .Orthogonalize import Orthogonalize
176176
# from .ShortestPathPointToPoint import ShortestPathPointToPoint
@@ -251,7 +251,7 @@ def getAlgs(self):
251251
# ExtendLines(), ExtractSpecificNodes(),
252252
# GeometryByExpression(), SnapGeometriesToLayer(),
253253
# PoleOfInaccessibility(), CreateAttributeIndex(),
254-
# DropGeometry(), BasicStatisticsForField(),
254+
# DropGeometry(),
255255
# RasterCalculator(), Heatmap(), Orthogonalize(),
256256
# ShortestPathPointToPoint(), ShortestPathPointToLayer(),
257257
# ShortestPathLayerToPoint(), ServiceAreaFromPoint(),
@@ -262,6 +262,7 @@ def getAlgs(self):
262262
algs = [AddTableField(),
263263
Aspect(),
264264
AutoincrementalField(),
265+
BasicStatisticsForField(),
265266
Boundary(),
266267
BoundingBox(),
267268
Clip(),

0 commit comments

Comments
 (0)