2

I've been trying to find a way to call in field names from a zip(). Basically I have 12 fields called "Flw_days1", "Flw_days2" etc and corresponding "Depth" fields that need to be calculated using the inputs of the "Flw_days" fields. I.e. "Depth1" is calculated using "Flw_Days1" and so on.

I've used the zip() function to match these together and then tried to call these into a codeblock and expression for use in the field calculator.

See example below:

depth_fields = ["Depth1", "Depth2", "Depth3", "Depth4", "Depth5", "Depth6", "Depth7", "Depth8", "Depth9", "Depth10", "Depth11", "Depth12"]
flow_fields = ["!Flw_Day1!", "!Flw_Day2!", "!Flw_Day3!", "!Flw_Day4!", "!Flw_Day5!", "!Flw_Day6!", "!Flw_Day7!", "!Flw_Day8!", "!Flw_Day9!", "!Flw_Day10!", "!Flw_Day11!", "!Flw_Day12!"]

zipped = zip(sorted(depth_fields), sorted(flow_fields))

for input in zipped:
  codeblock = """
def depth(flow, kfc, rf):
  if kfc == 0 or rf == 0:
    return ""
  else:
    return (flow * (kfc / rf) + 2)
"""
  expression = "depth(input[0], !K_FC!, !Ret_Fact!)"
  arcpy.CalculateField_management(CMPP_fc, input[1], expression, "PYTHON_9.3", codeblock)

As you can see I have tried to call the "Flw_Days" field into the expression and the "Depth" field into the field calculator.

The error I get is:

Runtime error <class 'arcgisscripting.ExecuteError'>: ERROR 000539: Error running expression: depth(input[0], 0, 0) <type 'exceptions.TypeError'>: 'builtin_function_or_method' object is unsubscriptable Failed to execute (CalculateField).

So it seems to be calling the input[0] into the expression that is causing the issue.

I am using ArcGIS Desktop 10.

4
  • Maybe use an UpdateCursor instead of trying to call Field Calculator -- this would let you iterate through the rows one at a time, avoiding the need for the codeblock.
    – Erica
    Jul 2, 2014 at 16:37
  • Instead of using field calculator to do something this complicated, it would be a lot simpler to use cursors.
    – Paul
    Jul 2, 2014 at 16:37
  • Also, side note (still think UpdateCursor would be more straightforward) -- do you need exclamation points around the depth_fields, e.g. "!Depth1!"?
    – Erica
    Jul 2, 2014 at 16:50
  • And finally: in the above code, zip(sorted(flow_fields), sorted(depth_fields)) -- depth_fields should be first, flow_fields should be second.
    – Erica
    Jul 3, 2014 at 14:01

2 Answers 2

1

Using an UpdateCursor lets you avoid the codeblock and directly work with the row data, instead of dealing with the Field Calculator. For example, in 10.1:

depth_fields = # list of depth fields
flow_fields = # list of flow fields

zipped = zip(sorted(depth_fields), sorted(flow_fields))

for input in zipped:
    fields = (input[0], input[1], 'K_FC', 'Ret_Fact')
    with arcpy.da.UpdateCursor(CMPP_fc, fields) as cursor:
        for row in cursor:
            if row[2] == 0 or row[3] == 0:
                # error message?
            else:
                row[0] = row[1] * (row[2] / row[3]) + 2
                cursor.updateRow(row)

For 10.0, which doesn't have DA cursors, you can use a regular Update Cursor. The basic idea is the same, but it needs to call the fields specifically since it's not limiting its initial query:

depth_fields = # list of depth fields
flow_fields = # list of flow fields

zipped = zip(sorted(depth_fields), sorted(flow_fields))

for input in zipped:
    fields = (input[0], input[1], 'K_FC', 'Ret_Fact')
    cursor = arcpy.UpdateCursor(CMPP_fc)
    for row in cursor:
        flow = row.getValue(fields[1])
        kfc = row.getValue(fields[2])
        rf = row.getValue(fields[3])
        # pulled values from rows into variables to simplify following code; not required
        if kfc == 0 or rf == 0:
            row.setValue(fields[0],'')
        else:
            depth = flow * (kfc / rf) + 2
            row.setValue(fields[0], depth)
            cursor.updateRow(row)
    del row, cursor
3
  • Thanks for replying. The code above doesn't loop through the 12 Flw_Day and Depth fields. Even so I tried it to see if it worked with just the single fields and I got the error "Runtime error <type 'exceptions.AttributeError'>: Object: Error in parsing arguments for UpdateCursor". It's worth noting that I am working in ArcGIS 10 and therefore had to modify the code so that it used arcpy.UpdateCursor rather than arcpy.da.UpdateCursor, could this be causing the issue? Jul 3, 2014 at 8:28
  • I've updated the script so it can fully loop through; I wasn't sure how much help you'd need replacing the Field Calculator part, but erred on the side of less and gave an example rather than a working script. Hope the new version helps.
    – Erica
    Jul 3, 2014 at 12:19
  • Ah I almost got to the same script myself today but kept getting a few different errors. Your updated code works perfectly, thank you! Jul 3, 2014 at 14:56
1

Although this can be accomplished with one update cursor, I find it easier to separate the two. Here's the somewhat tested code to get you started with cursors. I created a mock feature class with 26 fields filled with random values and I picked two of them to compare with field calculator and it seemed to be correct. Feel free to ask for clarification.

import arcpy
#Create two lists of input and output fields.    
flow_fields, depth_fields = zip(*(("Flw_Day{}".format(i),
                                "Depth{}".format(i)) for i in xrange(1,13)))

#Create list of lists containing each record's attributes for each field.
flow_data = [[r for r in row] for row in arcpy.da.SearchCursor(CMPP_fc, flow_fields)]

other_data = [[r for r in row] for row in arcpy.da.SearchCursor(CMPP_fc, ['K_FC', 'Ret_Fact'])]

#Bulk update each field
with arcpy.da.UpdateCursor(CMPP_fc, depth_fields) as rows:
    #The first counter loops over each feature
    for j,row in enumerate(rows):
        #The second counter loops over each field for every feature
        for k,_ in enumerate(depth_fields):
            if other_data[j][0] == 0 or other_data[j][1] == 0:
                row[k] = ""
            else:
                row[k] = flow_data[j][k] * other_data[j][0] / other_data[j][1] + 2
            rows.updateRow(row)

There's definitely a more efficient method, since you're checking K_FC and Ret_Fact for each field instead of once per feature. But unless you're running this code often or on massive datasets, it should work well enough. It might work to bring the if statement before the second for loop....

1
  • I perhaps should have said that I am working with ArcGIS 10 which as far as I am aware doesn't have the arcpy.da.UpdateCursor function? I tried modifying your code with the arcpy.UpdateCursor but got the error message - "Runtime error <type 'exceptions.AttributeError'>: Object: Error in parsing arguments for UpdateCursor". Sorry for not specifying this and thank you for your help. Jul 3, 2014 at 8:12

Your Answer

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

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