3

I'm using QGIS 3.4 (Madeira) and I am struggling to use python to calculate attributes of a new field in the attribute table.

I have already joined a csv and a shapefile. I use the code below to create a new column in the attribute table called 'PatCNT' with type as 'Int'

join_layer = QgsVectorLayer('/Users/ep9k/Desktop/SandraMonson/cb_2017_us_zcta510_500k/cb_2017_us_zcta510_500k.shp', 'US Zip Codes', 'ogr')

caps = join_layer.dataProvider().capabilities()
if caps & QgsVectorDataProvider.AddAttributes:
    join_layer.dataProvider().addAttributes([QgsField('PatCNT', QVariant.Double)])

Now, I have another column in my attribute table called Patient_Data_PatientCount (which is what I joined from the csv file). I want to copy that column as an integer. When imported from a csv, it's type is 'String'

I've made some attempts based on other Stack Overflow threads like:

-Is it possible to programmatically add calculated fields?

-How to use QGIS field calculator in python?

-Cannot calculate new values for an empty attribute using existing attributes in QGIS with python However these are all for QGIS 2.18 and there are some differences (pendingFields doesn't exist anymore)

I've also been looking at the documentation and in the PyQGIS developers cookbook with no success

-https://qgis.org/pyqgis/master/core/Expression/QgsExpression.html

-https://docs.qgis.org/testing/en/docs/pyqgis_developer_cookbook/vector.html

2 Answers 2

2

If you want to add an integer field, you should replace:

QVariant.Double

with

QVariant.Int

If your field from the csv is already joined to your shapefile, you could try using:

with edit(join_layer):
    for feature in join_layer.getFeatures():
        feature.setAttribute(feature.fieldNameIndex('PatCNT'), feature['Patient_Data_PatientCount'])
        join_layer.updateFeature(feature)
4
  • 1
    QVariant.Double was a typo, thanks for catching that. This solution is so simple and easy to follow. After hours of looking through confusing threads. Thank you! Commented Nov 20, 2018 at 16:13
  • @ErichPurpur - Most welcome! Glad it helped :)
    – Joseph
    Commented Nov 20, 2018 at 16:15
  • A follow up comment...I'm playing with the expression to learn it better. Say I wanted to add 1 to Patient_Data_PatientCount feature['Patient_Data_PatientCount'] is my expression to calculate this, but how? feature['Patient_Data_PatientCount + 1'] doesn't work. I feel like I can't even experiment with it since I can't find a usable (for my small brain) resource as an example Commented Nov 20, 2018 at 16:40
  • 1
    feature['Patient_Data_PatientCount'] basically means that you are calling the value of the feature inside the field 'Patient_Data_PatientCount'. Notice that the field name is inside quotes. So if you want add 1 to this, you could use feature['Patient_Data_PatientCount'] + 1. This could output an error though if the value is a string so it may be safer to use something like int(feature['Patient_Data_PatientCount']) + 1 to ensure the value is converted to integer before doing any arithmetic. Can't test this at the moment but hopefully it will help.
    – Joseph
    Commented Nov 21, 2018 at 10:45
2

I'm adding another answer as I was struggling to do anything but copy @Joseph code (which does exactly what I asked for). But I wanted to expound a little bit on how to write an expression using the "field calculator", in python.

My function now looks like this:

def calculate_attributes():
"""Copies attributes from Patient_Data_PatientCount and adds them to 'PatCNT' column in US Zip Codes table"""

    with edit(join_layer):
        for feature in join_layer.getFeatures():
            feature.setAttribute(feature.fieldNameIndex('PatCNT'), feature['Patient_Data_PatientCount'])
            join_layer.updateFeature(feature)
    print(f"Attribute calculated for {target_field} field")

The key here is this line:

feature.setAttribute(feature.fieldNameIndex('PatCNT'), feature['Patient_Data_PatientCount'])

The setAttribute() function takes two arguments. The first argument is the column which I'll be writing to ('PatCNT'), the second is the expression you want to evaluate. If I were to use the GUI field calculator to do so it would look like this: enter image description here

So the second argument in setAttribute() is the expression. In this case, I am just recreating the 'Patient_Data_PatientCount' field. I'm doing this because I previously converted the field type from a string to an integer.

Anyway, this is a pretty plain expression, but I could do more exciting things. For example, if I want to add 10 to the 'Patient_Data_PatientCount' field, my expression would be:

    feature.setAttribute(feature.fieldNameIndex('PatCNT'), feature['Patient_Data_PatientCount'] + 10)

Also, just like in the GUI field calculator, I can use the values in other fields to write my expression. Another example:

    feature.setAttribute(feature.fieldNameIndex('PatCNT'), feature['Patient_Data_PatientCount'] * feature['AWATER10'])

This is really nonsense in my context, but you can use it as an example. I have another field in the attribute table called 'AWATER10'. This is the amount of water area in each zip code. My expression here multiplies 'Patient_Data_PatientCount' by 'AWATER' for each zip code.

One last note, you must include the 'feature' part of that expression because remember, you are iterating over each feature in the layer. In this case each feature is a zip code.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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