I have a set of string values in a shapefile's attribute column and want to assign a number to them in a way that each unique string gets an integer number, like this:

strings        numbers
-------        -------
Cloud          1
Water          2
Water          2
Vegetation     3
Water          2
Cloud          1
Cloud          1
Vegetation     3

The order does not matter (but of course, if it's possible, alphabetically would be perfect).

Is there a way to achieve this without knowing the actual strings or the count of unique strings (assuming I have 20-30 different strings)?

I am using Python in QGIS 3.


Using the suggestion of BERA, I was able to run the Python script from the console. Then I tried to implement it as an expression:

from qgis.core import *
from qgis.gui import *
from qgis.utils import iface

@qgsfunction(args='auto', group='Custom')
def numberUniqueStrings(layer_name, src_column, dst_column, feature, parent):
    layer_list = QgsProject.instance().layerTreeRoot().children()
    layer = [lyr.layer() for lyr in layer_list if lyr.name() == layer_name][0]
    idx = layer.getFeature(1).fieldNameIndex(dst_column)
    unique = []
    with edit(layer):
        for feature in layer.getFeatures():
            value = str(feature[src_column]).strip()
            if value not in unique:
                unique.append(value)
            counter = unique.index(value) + 1
            feature.setAttribute(idx, counter)
            layer.updateFeature(feature)

and called it using the "Field Calculator" (created the field "numbers" as integer before):

numberUniqueStrings(@layer_name, "strings", "numbers")

Unfortunately nothing happens, all the fields stay empty.

  • in reply to your edit: that sounds like you're iterating over -and editing - the whole layer once for each feature. I think you want to call this as a function from the console, but only once, rather than making it into an expression function – Steven Kay Aug 16 at 9:57
  • Actually, I don't want to call it from the console every time I need to do this. Instead, I'd rather like to use it either as an expression or a script tool (where I did not dive into yet). – s6hebern Aug 16 at 10:56

Select your layer in the layer tree and:

layer = iface.activeLayer() #Select layer in tree
unique = []
with edit(layer):
    for feature in layer.getFeatures():
        if feature['strings'] not in unique:
            unique.append(feature['strings'])
        new_value = unique.index(feature['strings'])
        feature.setAttribute(feature.fieldNameIndex('numbers'), new_value)
        layer.updateFeature(feature)
  • This is great, but it only seems to work in the console and not as an expression. See the edit on how I implemented and called it. – s6hebern Aug 16 at 9:48
  • @s6hebern sorry cant answer that – BERA Aug 16 at 10:01
  • I will leave the question unanswered for now hoping that someone else has an idea, although your suggested code works perfectly from the console. Thanks, @BERA – s6hebern Aug 16 at 15:05

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.