2 votos

Llamar campo desde zip() a expresión de calculadora de campo ArcGIS

He estado tratando de encontrar una manera de llamar a los nombres de campo de un zip(). Básicamente tengo 12 campos llamados "Flw_days1", "Flw_days2" etc y los correspondientes campos "Depth" que necesitan ser calculados usando las entradas de los campos "Flw_days". Es decir, "Profundidad1" se calcula utilizando "Flw_Days1" y así sucesivamente.

He utilizado la función zip() para combinarlos y luego he intentado incluirlos en un bloque de código y una expresión para utilizarlos en la calculadora de campos.

Véase el ejemplo siguiente:

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)

Como puede ver, he intentado llamar al campo "Flw_Days" en la expresión y al campo "Depth" en la calculadora de campos.

El error que obtengo es:

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).

Así que parece ser la llamada a la entrada [0] en la expresión que está causando el problema.

Utilizo ArcGIS Desktop 10.

1voto

alasdairg Puntos 1518

El uso de un UpdateCursor le permite evitar el bloqueo de código y trabajar directamente con los datos de la fila, en lugar de tratar con la Calculadora de Campo. Por ejemplo, en 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)

Para 10.0, que no tiene cursores DA, puede utilizar un Cursor de Actualización normal. La idea básica es la misma, pero necesita llamar a los campos específicamente ya que no está limitando su consulta inicial:

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

1voto

Örjan Jämte Puntos 3127

Aunque esto se puede lograr con un solo cursor de actualización, me parece más fácil separar los dos. Aquí está el código algo probado para empezar con los cursores. He creado una clase de característica simulada con 26 campos rellenados con valores aleatorios y he elegido dos de ellos para compararlos con la calculadora de campos y parece que es correcto. Siéntase libre de pedir aclaraciones.

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)

Definitivamente hay un método más eficiente, ya que está comprobando K_FC y Ret_Fact para cada campo en lugar de una vez por función. Pero a menos que ejecute este código a menudo o en conjuntos de datos masivos, debería funcionar lo suficientemente bien. Podría funcionar para llevar el if antes de la segunda for bucle....

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X