Skip to content

Commit 1a67b91

Browse files
committedOct 4, 2012
refactor basic statistics tool
1 parent ad0ff93 commit 1a67b91

File tree

5 files changed

+375
-195
lines changed

5 files changed

+375
-195
lines changed
 

‎python/plugins/sextante/ftools/BasicStatistics.py

Lines changed: 0 additions & 190 deletions
This file was deleted.
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
import os.path
2+
import math
3+
4+
from PyQt4 import QtGui
5+
from PyQt4.QtCore import *
6+
7+
from qgis.core import *
8+
9+
from sextante.core.GeoAlgorithm import GeoAlgorithm
10+
from sextante.core.QGisLayers import QGisLayers
11+
12+
from sextante.parameters.ParameterVector import ParameterVector
13+
from sextante.parameters.ParameterTableField import ParameterTableField
14+
from sextante.parameters.ParameterBoolean import ParameterBoolean
15+
16+
from sextante.outputs.OutputHTML import OutputHTML
17+
from sextante.outputs.OutputNumber import OutputNumber
18+
19+
from sextante.ftools import FToolsUtils as utils
20+
21+
class BasicStatisticsNumbers(GeoAlgorithm):
22+
23+
INPUT_LAYER = "INPUT_LAYER"
24+
FIELD_NAME = "FIELD_NAME"
25+
USE_SELECTION = "USE_SELECTION"
26+
OUTPUT_HTML_FILE = "OUTPUT_HTML_FILE"
27+
28+
CV = "CV"
29+
MIN = "MIN"
30+
MAX = "MAX"
31+
SUM = "SUM"
32+
MEAN = "MEAN"
33+
COUNT = "COUNT"
34+
RANGE = "RANGE"
35+
MEDIAN = "MEDIAN"
36+
UNIQUE = "UNIQUE"
37+
STD_DEV = "STD_DEV"
38+
39+
def getIcon(self):
40+
return QtGui.QIcon(os.path.dirname(__file__) + "/icons/basic_statistics.png")
41+
42+
def defineCharacteristics(self):
43+
self.name = "Basic statistics for numeric fields"
44+
self.group = "Analysis tools"
45+
46+
self.addParameter(ParameterVector(self.INPUT_LAYER, "Input vector layer", ParameterVector.VECTOR_TYPE_ANY, False))
47+
self.addParameter(ParameterTableField(self.FIELD_NAME, "Field to calculate statistics on", self.INPUT_LAYER, ParameterTableField.DATA_TYPE_NUMBER))
48+
self.addParameter(ParameterBoolean(self.USE_SELECTION, "Use selection", False))
49+
50+
self.addOutput(OutputHTML(self.OUTPUT_HTML_FILE, "Statistics for numeric field"))
51+
52+
self.addOutput(OutputNumber(self.CV, "Coefficient of Variation"))
53+
self.addOutput(OutputNumber(self.MIN, "Minimum value"))
54+
self.addOutput(OutputNumber(self.MAX, "Maximum value"))
55+
self.addOutput(OutputNumber(self.SUM, "Sum"))
56+
self.addOutput(OutputNumber(self.MEAN, "Mean value"))
57+
self.addOutput(OutputNumber(self.COUNT, "Count"))
58+
self.addOutput(OutputNumber(self.RANGE, "Range"))
59+
self.addOutput(OutputNumber(self.MEDIAN, "Median"))
60+
self.addOutput(OutputNumber(self.UNIQUE, "Number of unique values"))
61+
self.addOutput(OutputNumber(self.STD_DEV, "Standard deviation"))
62+
63+
def processAlgorithm(self, progress):
64+
layer = QGisLayers.getObjectFromUri(self.getParameterValue(self.INPUT_LAYER))
65+
fieldName = self.getParameterValue(self.FIELD_NAME)
66+
useSelection = self.getParameterValue(self.USE_SELECTION)
67+
68+
outputFile = self.getOutputValue(self.OUTPUT_HTML_FILE)
69+
70+
index = layer.fieldNameIndex(fieldName)
71+
layer.select([index], QgsRectangle(), False)
72+
73+
count = 0
74+
rValue = 0
75+
cvValue = 0
76+
minValue = 0
77+
maxValue = 0
78+
sumValue = 0
79+
meanValue = 0
80+
medianValue = 0
81+
stdDevValue = 0
82+
uniqueValue = 0
83+
84+
isFirst = True
85+
values = []
86+
87+
if useSelection:
88+
selection = layer.selectedFeatures()
89+
count = layer.selectedFeatureCount()
90+
total = 100.0 / float(count)
91+
current = 0
92+
93+
for f in selection:
94+
value = float(f.attributeMap()[index].toDouble()[0])
95+
96+
if isFirst:
97+
minValue = value
98+
maxValue = value
99+
isFirst = False
100+
else:
101+
if value < minValue:
102+
minValue = value
103+
if value > maxValue:
104+
maxValue = value
105+
106+
values.append(value)
107+
sumValue += value
108+
109+
current += 1
110+
progress.setPercentage(int(current * total))
111+
else:
112+
count = layer.featureCount()
113+
total = 100.0 / float(count)
114+
current = 0
115+
116+
ft = QgsFeature()
117+
while layer.nextFeature(ft):
118+
value = float(ft.attributeMap()[index].toDouble()[0])
119+
120+
if isFirst:
121+
minValue = value
122+
maxValue = value
123+
isFirst = False
124+
else:
125+
if value < minValue:
126+
minValue = value
127+
if value > maxValue:
128+
maxValue = value
129+
130+
values.append( value )
131+
sumValue += value
132+
133+
current += 1
134+
progress.setPercentage(int(current * total))
135+
136+
# calculate additional values
137+
rValue = maxValue - minValue
138+
uniqueValue = utils.getUniqueValuesCount(layer, index, useSelection)
139+
140+
if count > 0:
141+
meanValue = sumValue / count
142+
if meanValue != 0.00:
143+
for v in values:
144+
stdDevValue += ((v - meanValue) * (v - meanValue))
145+
stdDevValue = math.sqrt(stdDevValue / count)
146+
cvValue = stdDevValue / meanValue
147+
148+
if count > 1:
149+
tmp = values
150+
tmp.sort()
151+
# calculate median
152+
if (count % 2) == 0:
153+
medianValue = 0.5 * (tmp[(count - 1) / 2] + tmp[count / 2])
154+
else:
155+
medianValue = tmp[(count + 1) / 2 - 1]
156+
157+
data = []
158+
data.append("Count: " + unicode(count))
159+
data.append("Unique values: " + unicode(uniqueValue))
160+
data.append("Minimum value: " + unicode(minValue))
161+
data.append("Maximum value: " + unicode(maxValue))
162+
data.append("Range: " + unicode(rValue))
163+
data.append("Sum: " + unicode(sumValue))
164+
data.append("Mean value: " + unicode(meanValue))
165+
data.append("Median value: " + unicode(medianValue))
166+
data.append("Standard deviation: " + unicode(stdDevValue))
167+
data.append("Coefficient of Variation: " + unicode(cvValue))
168+
169+
self.createHTML(outputFile, data)
170+
171+
self.setOutputValue(self.COUNT, count)
172+
self.setOutputValue(self.UNIQUE, uniqueValue)
173+
self.setOutputValue(self.MIN, minValue)
174+
self.setOutputValue(self.MAX, maxValue)
175+
self.setOutputValue(self.RANGE, rValue)
176+
self.setOutputValue(self.SUM, sumValue)
177+
self.setOutputValue(self.MEAN, meanValue)
178+
self.setOutputValue(self.MEDIAN, medianValue)
179+
self.setOutputValue(self.STD_DEV, stdDevValue)
180+
self.setOutputValue(self.CV, cvValue)
181+
182+
def createHTML(self, outputFile, algData):
183+
f = open(outputFile, "w")
184+
for s in algData:
185+
f.write("<p>" + str(s) + "</p>")
186+
f.close()
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
import os.path
2+
3+
from PyQt4 import QtGui
4+
from PyQt4.QtCore import *
5+
6+
from qgis.core import *
7+
8+
from sextante.core.GeoAlgorithm import GeoAlgorithm
9+
from sextante.core.QGisLayers import QGisLayers
10+
11+
from sextante.parameters.ParameterVector import ParameterVector
12+
from sextante.parameters.ParameterTableField import ParameterTableField
13+
from sextante.parameters.ParameterBoolean import ParameterBoolean
14+
15+
from sextante.outputs.OutputHTML import OutputHTML
16+
from sextante.outputs.OutputNumber import OutputNumber
17+
18+
from sextante.ftools import FToolsUtils as utils
19+
20+
class BasicStatisticsStrings(GeoAlgorithm):
21+
22+
INPUT_LAYER = "INPUT_LAYER"
23+
FIELD_NAME = "FIELD_NAME"
24+
USE_SELECTION = "USE_SELECTION"
25+
OUTPUT_HTML_FILE = "OUTPUT_HTML_FILE"
26+
27+
MIN_LEN = "MIN_LEN"
28+
MAX_LEN = "MAX_LEN"
29+
MEAN_LEN = "MEAN_LEN"
30+
COUNT = "COUNT"
31+
EMPTY = "EMPTY"
32+
FILLED = "FILLED"
33+
UNIQUE = "UNIQUE"
34+
35+
def getIcon(self):
36+
return QtGui.QIcon(os.path.dirname(__file__) + "/icons/basic_statistics.png")
37+
38+
def defineCharacteristics(self):
39+
self.name = "Basic statistics for text fields"
40+
self.group = "Analysis tools"
41+
42+
self.addParameter(ParameterVector(self.INPUT_LAYER, "Input vector layer", ParameterVector.VECTOR_TYPE_ANY, False))
43+
self.addParameter(ParameterTableField(self.FIELD_NAME, "Field to calculate statistics on", self.INPUT_LAYER, ParameterTableField.DATA_TYPE_STRING))
44+
self.addParameter(ParameterBoolean(self.USE_SELECTION, "Use selection", False))
45+
46+
self.addOutput(OutputHTML(self.OUTPUT_HTML_FILE, "Statistics for text field"))
47+
48+
self.addOutput(OutputNumber(self.MIN_LEN, "Minimum length"))
49+
self.addOutput(OutputNumber(self.MAX_LEN, "Maximum length"))
50+
self.addOutput(OutputNumber(self.MEAN_LEN, "Mean length"))
51+
self.addOutput(OutputNumber(self.COUNT, "Count"))
52+
self.addOutput(OutputNumber(self.EMPTY, "Number of empty values"))
53+
self.addOutput(OutputNumber(self.FILLED, "Number of non-empty values"))
54+
self.addOutput(OutputNumber(self.UNIQUE, "Number of unique values"))
55+
56+
def processAlgorithm(self, progress):
57+
layer = QGisLayers.getObjectFromUri(self.getParameterValue(self.INPUT_LAYER))
58+
fieldName = self.getParameterValue(self.FIELD_NAME)
59+
useSelection = self.getParameterValue(self.USE_SELECTION)
60+
61+
outputFile = self.getOutputValue(self.OUTPUT_HTML_FILE)
62+
63+
index = layer.fieldNameIndex(fieldName)
64+
layer.select([index], QgsRectangle(), False)
65+
66+
count = 0
67+
sumValue = 0
68+
minValue = 0
69+
maxValue = 0
70+
meanValue = 0
71+
countEmpty = 0
72+
countFilled = 0
73+
74+
isFirst = True
75+
values = []
76+
77+
if useSelection:
78+
selection = layer.selectedFeatures()
79+
count = layer.selectedFeatureCount()
80+
total = 100.0 / float(count)
81+
current = 0
82+
83+
for f in selection:
84+
length = float(len(f.attributeMap()[index].toString()))
85+
86+
if isFirst:
87+
minValue = length
88+
maxValue = length
89+
isFirst = False
90+
else:
91+
if length < minValue:
92+
minValue = length
93+
if length > maxValue:
94+
maxValue = length
95+
96+
if length != 0.00:
97+
countFilled += 1
98+
else:
99+
countEmpty += 1
100+
101+
values.append(length)
102+
sumValue += length
103+
104+
current += 1
105+
progress.setPercentage(int(current * total))
106+
else:
107+
count = layer.featureCount()
108+
total = 100.0 / float(count)
109+
current = 0
110+
111+
ft = QgsFeature()
112+
while layer.nextFeature(ft):
113+
length = float(len(ft.attributeMap()[index].toString()))
114+
115+
if isFirst:
116+
minValue = length
117+
maxValue = length
118+
isFirst = False
119+
else:
120+
if length < minValue:
121+
minValue = length
122+
if length > maxValue:
123+
maxValue = length
124+
125+
if length != 0.00:
126+
countFilled += 1
127+
else:
128+
countEmpty += 1
129+
130+
values.append(length)
131+
sumValue += length
132+
133+
current += 1
134+
progress.setPercentage(int(current * total))
135+
136+
n = float(len(values))
137+
if n > 0:
138+
meanValue = sumValue / n
139+
140+
uniqueValues = utils.getUniqueValuesCount(layer, index, useSelection)
141+
142+
data = []
143+
data.append("Minimum length: " + unicode(minValue))
144+
data.append("Maximum length: " + unicode(maxValue))
145+
data.append("Mean length: " + unicode(meanValue))
146+
data.append("Filled: " + unicode(countFilled))
147+
data.append("Empty: " + unicode(countEmpty))
148+
data.append("Count: " + unicode(count))
149+
data.append("Unique: " + unicode(uniqueValues))
150+
151+
self.createHTML(outputFile, data)
152+
153+
self.setOutputValue(self.MIN_LEN, minValue)
154+
self.setOutputValue(self.MAX_LEN, maxValue)
155+
self.setOutputValue(self.MEAN_LEN, meanValue)
156+
self.setOutputValue(self.FILLED, countFilled)
157+
self.setOutputValue(self.EMPTY, countEmpty)
158+
self.setOutputValue(self.COUNT, count)
159+
self.setOutputValue(self.UNIQUE, uniqueValues)
160+
161+
def createHTML(self, outputFile, algData):
162+
f = open(outputFile, "w")
163+
for s in algData:
164+
f.write("<p>" + str(s) + "</p>")
165+
f.close()

‎python/plugins/sextante/ftools/FToolsAlgorithmProvider.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
from sextante.ftools.MeanCoords import MeanCoords
1010
from sextante.ftools.UniqueValues import UniqueValues
1111
from sextante.ftools.PointDistance import PointDistance
12-
from sextante.ftools.BasicStatistics import BasicStatistics
12+
from sextante.ftools.BasicStatisticsStrings import BasicStatisticsStrings
13+
from sextante.ftools.BasicStatisticsNumbers import BasicStatisticsNumbers
1314
from sextante.ftools.PointsInPolygon import PointsInPolygon
1415
from sextante.ftools.LinesIntersection import LinesIntersection
1516
from sextante.ftools.NearestNeighbourAnalysis import NearestNeighbourAnalysis
@@ -48,9 +49,9 @@ class FToolsAlgorithmProvider(AlgorithmProvider):
4849

4950
def __init__(self):
5051
AlgorithmProvider.__init__(self)
51-
self.alglist = [SumLines(), PointsInPolygon(), BasicStatistics(),
52-
NearestNeighbourAnalysis(), MeanCoords(), LinesIntersection(),
53-
UniqueValues(), PointDistance(),
52+
self.alglist = [SumLines(), PointsInPolygon(), BasicStatisticsStrings(),
53+
BasicStatisticsNumbers(), NearestNeighbourAnalysis(),
54+
MeanCoords(), LinesIntersection(), UniqueValues(), PointDistance(),
5455
# data management
5556
# geometry
5657
ExportGeometryInfo(), Centroids(), Delaunay(), VoronoiPolygons(),
@@ -80,4 +81,4 @@ def getSupportedOutputTableExtensions(self):
8081
return ["csv"]
8182

8283
def supportsNonFileBasedOutput(self):
83-
return True
84+
return True

‎python/plugins/sextante/ftools/FToolsUtils.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,21 @@ def extractPoints( geom ):
7474
points.extend(line)
7575

7676
return points
77+
78+
def getUniqueValuesCount(layer, fieldIndex, useSelection):
79+
count = 0
80+
values = []
81+
layer.select([fieldIndex], QgsRectangle(), False)
82+
if useSelection:
83+
selection = layer.selectedFeatures()
84+
for f in selection:
85+
if f.attributeMap()[fieldIndex].toString() not in values:
86+
values.append(f.attributeMap()[fieldIndex].toString())
87+
count += 1
88+
else:
89+
feat = QgsFeature()
90+
while layer.nextFeature(feat):
91+
if feat.attributeMap()[fieldIndex].toString() not in values:
92+
values.append(feat.attributeMap()[fieldIndex].toString())
93+
count += 1
94+
return count

0 commit comments

Comments
 (0)
Please sign in to comment.