Field calculator: generate n fields reporting n-largest values

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"]

QgsProject.instance().addMapLayer(input_layer)

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.dataProvider().addAttributes(nf)
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)