# Field calculator: generate n fields reporting n-largest values

I have a polygon shapefile and I need to generate n new fields reporting the n largest values across k variables. In this particular case n = 3, so that I would like to create three new fields: `"max_area_1"`, `"max_area_2"`, `"max_area_3"`, with the highest, the second and third largest, values across variables `x1, x2, ..., xk`.

To make things clearer, consider this simpliefied version of my data:

``````x1  x2  x3  x4
10  31  73  14
7  69  55   2
91   9  87  13
``````

What I would like to get is:

``````x1  x2  x3  x4  max_area_1  max_area_2  max_area_3
10  31  73  14      73          31          14
7  69  55   2      69          55           7
91   9  87  13      91          87          13
``````

Using the field calculator in Python console of QGIS, I can compute the highest value using the max function. My code is:

``````crops = [x1, ..., xk]
processing.run("qgis:fieldcalculator", {
"INPUT": input_layer,
"FIELD_NAME": "max_area_1",
"FIELD_TYPE": 0,
"NEW_FIELD": True,
"FORMULA": 'max(crops)',
"OUTPUT":  output_layer})
``````

Any idea on how I can compute, in PyQGIS, three new fields reporting the three largest values across the relevant variables?

You can use this script. First, select a the layer, then run the script. It adds the result as a memory layer. You should save the result as a new file manually or using a code.

``````input_layer = iface.activeLayer()
max_area_x = ["max_area_1", "max_area_2", "max_area_3"]

for i in range(len(max_area_x)): # OR for i in [0, 1, 2]
input_layer = processing.run("qgis:fieldcalculator",
{"INPUT": input_layer,
"FIELD_NAME": max_area_x[i],
"FIELD_TYPE": 0,
"NEW_FIELD": True,
"FORMULA": 'array_sort(array("x1", "x2", "x3", "x4"), False)[' + str(i) + ']',
"OUTPUT": "TEMPORARY_OUTPUT"})["OUTPUT"]

``````

It works in QGIS 3.8. I have not tested in other versions.

• I need to compute maximum values across fields, not within them. I've edited the question to make it clearer Mar 20, 2020 at 10:14
• @Matteo I have changed the answer. Please review. Mar 20, 2020 at 11:46

You can also use the following script without any Processing tool. The script doesn't create any layer, it adds fields to the active layer and assigns values.

``````from PyQt5.QtCore import QVariant

lyr = iface.activeLayer()

# crops fields
cfs = ["x1","x2","x3","x4"]

# new fields list
nf = [QgsField("max_area_1", QVariant.Int),
QgsField("max_area_2", QVariant.Int),
QgsField("max_area_3", QVariant.Int)]

# add fields to layer
lyr.updateFields()

# new field indices
idx = [lyr.fields().indexOf(f.name()) for f in nf]

attr_map = {} # {id: {index: value}, id: {index: value}, ...}
for f in lyr.getFeatures():
vals = sorted([f[c] for c in cfs], reverse=True)
attr_map[f.id()] = {idx[0]: vals[0],
idx[1]: vals[1],
idx[2]: vals[2]}

lyr.dataProvider().changeAttributeValues(attr_map)
``````