4

I have written a PyQGIS script that loops through an attribute table and finds duplicated values in a target attribute field.

My technique requires that the attribute table is first sorted by the target field.

Is that possible with PyQGIS? I've only been able to have the table sorted by the row number.

  • Why you need to sort first? What exactly you do with the duplicated values? Perhaps there could be easier ways of achieving what you want. – Germán Carrillo Mar 13 '15 at 21:35
  • @gcarrillo - I need to identify polygons which are part of a multi-polygon feature. Each feature has a unique ID, I sort by ID, polygons with a duplicate ID are part of a multi-polygon feature. Perhaps not the most elegant method, happy to hear of any suggestion to improve it. – Ben Mayo Mar 16 '15 at 9:15
5

Using a similar approach to the solution presented by xunilk:

Assuming you have vector layer as the active layer in QGIS with a name column which you want to sort on, you should be able to run the following in the QGIS Python Console:

# Get a reference to the active layer (the layer
# selected in the Layers Panel)
layer = iface.activeLayer()

# Define a custom `key` function for use with `sorted`
# which is passed a feature `f` and returns the value
# of it's `name` attribute
def get_name(f):
    return f['name']

# Create a sorted list of features. The `sorted` function
# will read all features into a list and return a new list
# sorted in this case by the features name value returned
# by the `get_name` function
features = sorted(layer.getFeatures(), key=get_name)

# Loop through the sorted list and print out the name value
# of each just to prove it's now sorted.
for feature in features:
    print feature['name']

References:

1

Assuming that I have understood your question correctly… I will provide a purely Python-based solution. There might be a more elegant way to do this, using the QGIS API directly, but I am not aware of it at this point.

Let’s say I have a country shapefile, where the first column contains names. Now, the countries are not alphabetically ordered, but I would like to still print them in alphabetical order (that is how I understand the question):

lyr = iface.activeLayer()

features = lyr.getFeatures()

featList = []

for feat in features:
    featList.append(feat.attributes())

featList.sort()

for f in featList:
    print f

My solution requires looping through the features once to push them into a list, and then, in a second step, you would order that list alphabetically, using .sort().

Of course, to make this work, your field that is used to sort needs to be the first field in your table. If that is not the case you can easily achieve that manually by using the Table Manager plugin.

The previous example would then return a table like this:

enter image description here

in alphabetical order:

enter image description here
[...]

So, basically, instead of working with the features themselves you would be working with a list of the features.

1

I used world_borders.shp to test this.

To get features and print a slice of first 6 elements. The shapefile has 3784 records in the attributes table (many of them duplicates).

wb=iface.activeLayer()
iter = wb.getFeatures()
attr=[]
for feature in iter:
    attr.append(feature.attributes())
print len(attr)
3784
print attr[0:5]
[[1.0, u'AA', u'Aruba', 193.0, 71218.0],
[2.0, u'AC', u'Antigua and Barbuda', 443.0, 68320.0],
[2.0, u'AC', u'Antigua and Barbuda', 443.0, 68320.0],
[4.0, u'AG', u'Algeria', 2381740.0, 32129324.0],
[5.0, u'AJ', u'Azerbaijan', 86600.0, 7868385.0]]

To eliminate duplicate records. The attributes list has now 251 records. However, country names (index 2) are not sorted (printed 11 first elements).

attr2=[]
for attribute in attr:
    if attribute not in attr2:
    attr2.append(attribute)
print len(attr2)
251
print attr2[0:10]
[[1.0, u'AA', u'Aruba', 193.0, 71218.0],
[2.0, u'AC', u'Antigua and Barbuda', 443.0, 68320.0],
[4.0, u'AG', u'Algeria', 2381740.0, 32129324.0],
[5.0, u'AJ', u'Azerbaijan', 86600.0, 7868385.0],
[6.0, u'AL', u'Albania', 28748.0, 3544808.0],
[7.0, u'AM', u'Armenia', 29800.0, 2991360.0],
[9.0, u'AO', u'Angola', 1246700.0, 10978552.0],
[10.0, u'AQ', u'American Samoa', 199.0, 57902.0],
[11.0, u'AR', u'Argentina', 2766890.0, 39144753.0],
[12.0, u'AS', u'Australia', 7686850.0, 19913144.0]]

To sort by field (index 2) is necessary to use one anonymous function with lambda in 'sort'. Now, country names are sorted.

attr2.sort(key=lambda x: x[2])
print attr2[0:10]
[[3.0, u'AF', u'Afghanistan', 647500.0, 28513677.0],
[6.0, u'AL', u'Albania', 28748.0, 3544808.0],
[4.0, u'AG', u'Algeria', 2381740.0, 32129324.0],
[10.0, u'AQ', u'American Samoa', 199.0, 57902.0],
[8.0, u'AN', u'Andorra', 468.0, 69865.0],
[9.0, u'AO', u'Angola', 1246700.0, 10978552.0],
[14.0, u'AV', u'Anguilla', 102.0, 13008.0],
[15.0, u'AY', u'Antarctica', 1.0, 1.0],
[2.0, u'AC', u'Antigua and Barbuda', 443.0, 68320.0],
[11.0, u'AR', u'Argentina', 2766890.0, 39144753.0]]

If you want to sort, for example, by 'AREA' (index 3):

attr_id = wb.pendingAllAttributesList()
for i in attr_id: #print index and field names
    print i,wb.attributeDisplayName(i)
0 CAT
1 FIPS_CNTRY
2 CNTRY_NAME
3 AREA
4 POP_CNTRY
attr2.sort(key=lambda x: x[3])
1

This code will sort getFeatures() results by field in descending order. Tested with QGIS 3.6.1:

request = qgis.core.QgsFeatureRequest()

# set order by field
clause = qgis.core.QgsFeatureRequest.OrderByClause('field_name', ascending=False)
orderby = qgis.core.QgsFeatureRequest.OrderBy([clause])
request.setOrderBy(orderby)

layer = qgis.core.QgsVectorLayer('layer_path', 'layer_name', "provider") # add some layer
features = layer.getFeatures(request)

for feature in features:
    attrs = feature.attributes()
    print(attrs)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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