# 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.

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)
``````