5

I have 5 fields of different landcover types with areas of each and found below:

> PRC_Water| PRC_Bare| PRC_Urban| PRC_LVeg| PRC_LVeg|
>     5        20         40        20         15

I have 5 more fields of dominance [Dom1, Dom2, Dom3, Dom4, Dom5] which I would like to populate with the field_name of the landcover type. I would like to know if it is possible to write a script/conditional statement in the field calculator to return the field name of the '#th' largest area, and if there is a way to return both names if the areas are equal??

enter image description here

I have limited knowledge of python, but am puzzled how I can rank these areas by returning a field name?

6

If you were to use a script it could calculate all the fields at once. If you were to use the field calculator you'd have to run it on each field you want to update (5x).

The following script will update your Dom fields in order of the values of your landcover fields. If several fields have the same value, each field name will be appended as per your example. I tested the script on the dumby data, and it works for me. That said, it might not be the most efficient method.

EDIT: The script now dynamically pulls the field names from your dataset. It looks for "PRC" in the field name to denote a landcover type and "Dom" for a dominant field. Only fields with values greater than zero are written with the remaining Dom fields left blank.

There are quite a few decimal places so the only repeated values (according to the dummy data you dropboxed) are zeros. Maybe this is different on the whole dataset so I couldn't test the section that writes multiple field names to a column.

import arcpy

# Put the absolute path to your table / feature class
theData = r"C:YourPathHere" 

# Create empty Python list, and a list of field names
values = []
fields = []
domFields = []

# Get all landcover and Dom field names dynamically
for field in arcpy.ListFields(theData):
    if 'PRC' in field.name: 
        fields.append(field.name) # Grab all fields names that contain "PRC"
    elif 'Dom' in field.name:
        domFields.append(field.name)

# Arcpy Update cursor
rows = arcpy.UpdateCursor(theData)

# Loop through each row in your attribute table
for row in rows:
    i = 0
    while i < len(fields):
        # Store the fieldname and value in a tuple, add to list ex. [("PRC_Water", 5), ("PRC_Bare", 10), etc.]
        values.append((fields[i], row.getValue(fields[i])))
        i += 1 # iterate counter
    # Sort the list of tuples based on the integer value in descending order
    values =  sorted(values, key=lambda value: value[1], reverse=True)
    sortedFields = [x[0] for x in values] # Grab the ordered field names
    sortedValues = [x[1] for x in values] # Grab the ordered field values

    # Set the values of Dominance fields based on the order of the list
    j = 0
    while j < len(domFields):
        if sortedValues[j] > 0:
            row.setValue(domFields[j], sortedFields[j])
        else: # if the next greatest value is zero, leave the field blank
            row.setValue(domFields[j], "")
        j += 1 # iterate counter
    rows.updateRow(row)

    # This section handles cases where multiple landuse values are equal
    k=0
    while k < len(fields):
        fieldsEqual = ""
        startRepeat = 0
        numRepeat = 0
        # If two fields contain the same value...
        if sortedValues.count(sortedValues[k]) > 1:
            # Find the position in the list where they begin to repeat (repeated values will occur back-to-back)
            startRepeat = sortedValues.index(sortedValues[k])
            # Find the number of times the value is repeated
            numRepeat = sortedValues.count(sortedValues[k])
            l = 0
            # While the counter is less than the number of repeated values, append the field name to a string
            while l < numRepeat:
                fieldsEqual += sortedFields[startRepeat + l]
                # Add a comma and space to each value except the last one
                if l < numRepeat - 1:
                    fieldsEqual += ", "
                l += 1 # iterate counter

            # Write the fields with equal values into the Dom fields
            m = 0
            while l < numRepeat:
                row.setValue(domFields[startRepeat + m] , fieldsEqual)
                rows.updateRow(row)
                m += 1 # iterate counter
        k += 1 # iterate counter

    # Clear the list for the next row of data
    values = []

# Delete cursor objs
del row, rows
  • 1
    +1 - good point about 5 x field calculations vs single run of Python script and nice code. – PolyGeo Sep 11 '13 at 21:24
0

I tried @Roy's answer on the dummy data and it worked perfectly!

However, the real data has 11 fields, and 5 dominant fields. This creates a Runtime error and say "list index out of range".

Here is the adjusted script (from my python window) for the real data and the error that follows, I am not quite sure where the script could be adjusted to include more fields and still only have 5 dominant classes? Please have a look at the script and advise how to adjust it for the real data. Thank you so much for your time!

>>> import arcpy
>>> theData = r"J:\Jens\Leeuwfontein (Ben Breedlove)\Fixed_LC and Seg\Pythontest\test2.dbf"
>>> values = []
... fields = ["PRC_Water", "PRC_Bare", "PRC_Urban", "PRC_Lveg", "PRC_Hveg", "PRC_Trans", "PRC_BLSVeg", "PRC_Wetlnd", "PRC_ImpGrd", "PRC_Cloud", "PRC_Cldshd"]
... domFields = ["Dom1", "Dom2", "Dom3", "Dom4", "Dom5"]
... 
>>> rows = arcpy.UpdateCursor(theData)
>>> for row in rows:
...     i = 0
...     while i < len(fields):
...         values.append((fields[i], row.getValue(fields[i])))
...         i += 1
...     values =  sorted(values, key=lambda value: value[1], reverse=True)
...     sortedFields = [x[0] for x in values]
...     sortedValues = [x[1] for x in values]
...     j = 0
...     while j < len(fields):
...         row.setValue(domFields[j], sortedFields[j])
...         j += 1
...     rows.updateRow(row)
...     k=0
...     while k < len(fields):
...         fieldsEqual = ""
...         startRepeat = 0
...         numRepeat = 0
...         if sortedValues.count(sortedValues[k]) > 1:
...             startRepeat = sortedValues.index(sortedValues[k])
...             numRepeat = sortedValues.count(sortedValues[k])
...             l = 0
...             while l < numRepeat:
...                 fieldsEqual += sortedFields[startRepeat + l]
...                 if l < numRepeat - 1:
...                     fieldsEqual += ", "
...                 l += 1
...             l = 0
...             while l < numRepeat:
...                 row.setValue(domFields[startRepeat + l] , fieldsEqual)
...                 rows.updateRow(row)
...                 l += 1
...         k += 1
...     values = []
...     
Runtime error <type 'exceptions.IndexError'>: list index out of range
>>>
  • One more comment or request Roy; is it possible to only return a fieldname if its value is greater than zero? So that if the value is zero then return a blank! Example: If values in ID 1 = 0,20,40,20,0 for PRC_Water| PRC_Bare| PRC_Urban| PRC_LVeg| PRC_LVeg| respectively I would like it to return "Dom1=PRC_Urban|Dom2=PRC_Bare,PRC_LVeg|Dom3=PRC_Bare,PRC_LVeg|Dom4= |Dom5= | Would that be possible?? I cant thank you enough for your assistance, this is helping me a great deal! – Jens Hiestermann Sep 12 '13 at 9:16
  • Perhaps you could post your .dbf to dropbox or pastebin so I can take a closer look. – Roy Sep 12 '13 at 11:19
  • I'm also curious where this errored out. I suspect it was on line 29 row.setValue(domFields[j], sortedFields[j]). Can you provide the line number? – Roy Sep 12 '13 at 11:55
  • Through trial and error I worked out it is important that the number of "fields" matches the number of "domfields": I tried adding a LandCover field, and no extra Dominance field, and it returned that runtime error. Then I added the extra dominance field to that, and it ran perfectly! – Jens Hiestermann Sep 12 '13 at 12:11
  • I will clip out 100 rows from my .dbf (too many rows to paste all) and will drop it in the paste bin for you to look at. I appreciate your help Roy! – Jens Hiestermann Sep 12 '13 at 12:15

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.