Select to view content in your preferred language

Calculate Percentage of Field Value in a Row

170
2
2 weeks ago
haitemar
New Contributor

Hi all, I've been trying to automate the calculation of some land cover percentages per area, but I'm not a programmer so I'm not sure how to fix where I've gotten stuck. So far, I have been able to create and calculate a field with the sum of all of the area values, and new fields for the percentage calculations to be in. 

import arcpy

#Set path to the feature class or the table put name within the quotations

table = r"" 

 

# Add a new field to the table

arcpy.management.AddField(table, "SumField", "DOUBLE")

 

# Use UpdateCursor to iterate through the rows of the table

with arcpy.da.UpdateCursor(table, "*") as cursor:

    for row in cursor:

        # Initialize a variable to store the sum

        total = 0

        # Iterate through each field in the row

        for i in range(len(row)):

            # Check if the field is numeric and not OBJECTID, Shape_Length, and Shape_Area

            if isinstance(row[i], (int, float, complex)) and cursor.fields[i] not in ['OBJECTID', 'Shape_Length', 'Shape_Area']:

                # Add the field's value to the total

                total += row[i]

        # Update the new field with the total

        row[cursor.fields.index('SumField')] = total

        cursor.updateRow(row)

#make fields into a list

fields = arcpy.ListFields("")

for field in fields:

    #exclude fields with these names

    if field.name not in ['OBJECTID', 'Shape_Length', 'Shape_Area', 'SumField', 'Spatial_ID']:

        #add copies of all not excluded fields adding _perc after the name

        arcpy.management.AddField(table, field.name + "_perc", "DOUBLE")

 

Screenshot 2024-07-01 135755.png

 

I've tried a few things to calculate these new fields, including using CalculateField() in the place of AddField(), but I wasn't able to get the expression to work because referencing a variable which is a field name breaks it and makes it think they're both strings instead of using the values within the fields. 

fields = arcpy.ListFields("perc_test_table")
for field in fields:
    if field.name not in ['OBJECTID', 'Shape_Length', 'Shape_Area', 'SumField', 'Spatial_ID']:
        field_name = "'!"+ field.name+ "!'"
        arcpy.management.CalculateField(table, field.name + "_perc", field_name/'!SumField!', "PYTHON3",field_type="DOUBLE")

 

Thank you!

0 Kudos
2 Replies
BlakeTerhune
MVP Regular Contributor

You can use an f-string to dynamically insert the field name into the calculate field expression.

fields = arcpy.ListFields("perc_test_table")
for field in fields:
    if field.name not in ['OBJECTID', 'Shape_Length', 'Shape_Area', 'SumField', 'Spatial_ID']:
        arcpy.management.CalculateField(table, f"{field.name}_perc", f"!{field.name}!/!SumField!", "PYTHON3", field_type="DOUBLE")

 

HaydenWelch
Occasional Contributor II

You're doing a lot of work inside the update cursor, see if this works for you:

 

 

import arcpy
from arcpy.da import SearchCursor, UpdateCursor, InsertCursor, Editor

table = r"path/to/your/table"

fields = arcpy.Describe(table).fields
for idx, field in enumerate(fields):
    # Notify User of progress
    print(f"Calculating percentage for field: {field.name} {idx}/{len(fields)}")
    # Skip not numeric fields
    if field.type not in ['Double', 'Integer', 'BigInteger', 'Single', 'SmallInteger',]: continue
    # Skip system fields
    if field.name in ['OBJECTID', 'Shape_Length', 'Shape_Area', 'SumField']: continue
    # Calculate the sum of the field values
    total = sum(val[0] for val in arcpy.da.SearchCursor(table, [field.name]) if val[0])
    # Skip if no values
    if not total: continue
    # Add _perc field
    arcpy.management.AddField(table, field.name + "_perc", "DOUBLE")
    # Start update cursor
    with UpdateCursor(table, [field.name, field.name + "_perc"]) as cursor:
        for row in cursor:
            row = dict(zip(cursor.fields, row))
            # Calculate the percentage of the field value (rounded to 2 decimal places)
            row[f"{field.name}_perc"] = round((row[field.name] / total) * 100, 2)
            cursor.updateRow(list(row.values()))

 

 

 

Notice that there's barely anything happening in the cursor

0 Kudos