QGIS – How to Sum Values of a Field Based on Unique Values in Another Field Using Python

attribute-tablepyqgispythonqgis

I have a layer made of a field with repeted string values ("building") and a field with float values ("flatssurface"). I'd like to write a script to do some arithmetic operations (like sum, max and min, etc.) on all the values in column B which are associated to same values in column A but it doesn't work.

Dataset:

BUILDING       FLATSURF    SUM (wanted result)
    1           40.0       115 (i.e. 40+45+30 m^2)
    1           45.0       115
    1           30.0       115
    2          200.0       300 (i.e. 200+100 m^2)
    2          100.0       300
    3           60.0       140 (i.e. 60+80 m^2)
    3           80.0       140

Code trial:

building=layer.dataProvider().fieldNameIndex('BUILDING')
flatssurface=layer.dataProvider().fieldNameIndex('FLATSURF')

uniquevalues=layer.uniqueValues(building,limit=10000)
for uv in uniquevalues:
    feat=layer.getFeatures()
    for f in feat:
      tot=sum([flatssurface])
      print tot

however it returns only the index of the field flats' surface (f[1]).
For simplicity, I neglected the part on adding the field but I just tried to print the results, in order to check if it worked.
If possible, I don't need answers related to GroupStats (Calculating sum of parts of column based on another column using QGIS Field Calculator?) or SQL or statistics (https://gis.stackexchange.com/editing-help) but I am looking only for solution related to Python.

Best Answer

when you do

flatssurface=layer.dataProvider().fieldNameIndex('FLATSURF')

you are indeeded getting the index (column number) of the field Flatsurf. To access the data, in your loop, you need to get the feature, its attributes, and at last restrict to the proper column using the index you have found. So, the numerical value you want to use in your loop is

f.attributes()[flatssurface]

On top of accessing the field value, you need to sum it by building. Using your sample code you are making the cross product of all buildings by all building values.

buildingidx=layer.dataProvider().fieldNameIndex('BUILDING')
flatssurfaceidx=layer.dataProvider().fieldNameIndex('FLATSURF')

uniquevalues=layer.uniqueValues(buildingidx,limit=10000)
for uv in uniquevalues:
    #re-initialise the total area for each building
    tot = 0.0        

    #statement for selecting just the proper buildings
    exp = QgsExpression('BUILDING = ' + str(uv))
    request = QgsFeatureRequest(exp)
    #Select only the buildings having the specified value
    feat=layer.getFeatures(request)
    #Do the sum
    for f in feat:
      tot+=f.attributes()[flatssurfaceidx]
    #once done, print the building value and the corresponding total surface
    print("Building:",uv, "Total surface:", tot)
Related Question