¿Cómo acceder a las filas adyacentes con el cursor?

8

En la captura de pantalla adjunta, los atributos contienen dos campos de interés "a" y "b". Quiero escribir un script para acceder a las filas adyacentes para hacer algunos cálculos. Para acceder a una sola fila, usaría el siguiente UpdateCursor:

fc = r'C:\path\to\fc'

with arcpy.da.UpdateCursor(fc, ["a", "b"]) as cursor:
     for row in cursor:
          # Do something

Por ejemplo, con OBJECTID 4, estoy interesado en calcular la suma de los valores de fila en el campo "a" adyacente a la fila de OBJECTID 4 (es decir, 1 + 3) y agregar ese valor a la fila de OBJECTID 4 en el campo "b". ¿Cómo puedo acceder a las filas adyacentes con el cursor para hacer este tipo de cálculos?

ingrese la descripción de la imagen aquí

Aaron
fuente

Respuestas:

7

Si fuera yo, pasaría por la tabla una vez que creara un diccionario donde la clave sea ​​OBJECTID y el elemento sea ​​el valor en el campo "a". Luego pasaría a través de la tabla con un cursor de actualización obteniendo el OBJECTID y de allí podría obtener los valores adyacentes del diccionario, sumarlos y escribirlos de nuevo en el campo "b".

Pero en su captura de pantalla tiene casos especiales para las filas 3 y 8, ya que solo tienen una fila adyacente. ¿Qué pretendes para estos?

Hornbydd
fuente
+1 Esta es una buena solución porque puede usarse de una manera que respete el modelo de datos relacionales. En este modelo, la secuencia en la que se procesan las filas no está definida, por lo que cualquier procedimiento que se base en el supuesto de que se repetirá en el mismo orden que se muestra en una vista de tabla debería considerarse poco confiable. Al usar cualquier clave, no solo, OBJECTIDesta solución puede identificar vecinos de manera confiable de acuerdo con los valores de esa clave. Sin embargo, los diccionarios no suelen admitir una búsqueda "siguiente" o "anterior". Necesitas algo como un Trie .
whuber
4

Mientras recorre las filas, debe realizar un seguimiento de los valores anteriores. Esta es un forma de hacerlo:

 previous_value = None
 cursor = arcpy.UpdateCursor(fc, fields, sort_fields)
 for i, in_row in enumerate(cursor):
      current_value = in_row.getValue("ColName")
      if not previous_value: 
           previous_value = current_value 
           continue
       #
       # here you have access to the current and previous value
       #

       previous_value = current_value

o, si la tabla no es enorme, probablemente construiría un diccionario, como d = {a: b} y luego, en el cursor de actualización, acceda a los datos del diccionario: d.get (a + 1) o d.get (a -1) para hacer los cálculos ..

Matej
fuente
3

Acepté la respuesta de @Hornbydd por guiarme hacia una solución de diccionario. El script adjunto realiza las siguientes acciones:

  1. Recorra un FC y complete un diccionario con key = OID y value = "MyField" usando un SearchCursor.
  2. Iniciar un cursor de actualización
  3. Crear lógica para manejar la primera y la última fila (es decir, donde no hay una fila previa o consecutiva)
  4. Enlace la fila del cursor de actualización al OID del diccionario y al valor del campo
  5. Haz el procesamiento ...

import arcpy, collections

fc = r'C:\path\to\fc'

# Populate a dictionary with key = OID and value = "a"
names = collections.defaultdict(list)

for name1, name2 in arcpy.da.SearchCursor(fc, ("OBJECTID", "a")):
    names[name1].append(name2)

# Use .values() class to access adjacent rows

with arcpy.da.UpdateCursor(fc, ["OBJECTID", "a", "b"]) as cursor:
    for row in cursor:
        # Get first and last rows for special processing
        if row[0] == names.keys()[0] or row[0] == names.keys()[-1]:
            """Note that the first and last row will need special rules because
             they cannot reference either the previous or next row.  In this case
             the features are skipped"""
            pass

        else:
            """This needs to be corrected because OID = (row[0] - 1)"""
            # Now link the dictionary names with row[0] (i.e. OBJECTID)
            x = names.values()[row[0] - 2]  # e.g. OID 2 (pre)
            y = names.values()[row[0] - 1]  # e.g. OID 3 (current row)
            z = names.values()[row[0]]      # e.g. OID 4 (post)

            # Now do the calculation
            row[2] = x + z
            cursor.updateRow(row)
Aaron
fuente
No entiendo cómo: si la fila [0] == names.keys () [0] o la fila [0] == names.keys () [- 1]: ¿funcionaría? Los valores en el defaultdict no están ordenados, ¿verdad?
ianbroad
2

El módulo de acceso a datos es bastante rápido y puede crear un SearchCursorpara guardar todos los valores de 'a' en una lista y luego crear un UpdateCursorpara recorrer cada fila y seleccionar de la lista para actualizar las filas 'b' necesarias. De esta manera, no necesita preocuparse por guardar datos entre filas =)

Entonces algo como esto:

fc = r'C:\path\to\fc'
fields = ["a","b"]
aList = []
index = 0

with arcpy.da.SearchCursor(fc,fields) as cursor:
     for row in cursor:
         aList.append(row[0])    # This saves each value of 'a'
with arcpy.da.UpdateCursor(fc,fields) as cursor:
     for row in cursor:
         if index-1 > 0 AND index+1 <= len(aList):     # Keeps 'b' null if out of list range 
                                                       # or at end of list
             row[1] = aList[index-1]+aList[index+1]
         index += 1

Esta es una solución bastante cruda, pero la utilicé recientemente para solucionar un problema muy similar. Si el código no funciona con suerte, ¡te pone en el camino correcto!

Editar: Modificado en último lugar si la declaración de AND a OR Edit2: Modificado de nuevo. ¡Ah, la presión de mi primera publicación de StackExchange!

romano
fuente
En lugar de editar mi respuesta en otra ocasión, le daré $ 0.02 por qué creo que esta es una buena solución al problema. La iteración a través de las filas hace que sea muy fácil iterar a través de las listas también con index + = 1. Aunque no pensé mucho en la mecánica del código, debería transmitir el punto que quería hacer. ¡Buena suerte!
Roman
1
Algunas notas: 1) Puede ser más rápido para grandes conjuntos de datos si utiliza una comprensión de la lista para completar en aListlugar de agregar cada entrada. 2) Use en enumerate()lugar de tener un contador separado para el índice.
Paul
-1

Primero necesitas un cursor de búsqueda; No creo que pueda obtener valores con un cursor de actualización. Luego, en cada iteración, use aNext = row.next (). GetValue ('a') para obtener el valor de la siguiente fila.

Para obtener el valor de la fila anterior, establecería una variable fuera del ciclo for igual a cero. Esto se actualiza para igualar el valor actual de las filas de 'a'. Luego puede acceder a esta variable en la próxima iteración.

Esto satisfaría su ecuación de B = A (rowid-1) + A (rowid + 1)

user25074
fuente
No necesita cambiar a un cursor de búsqueda para usar getValue. Es un método del objeto ROW, no el objeto CURSOR.
msayler
En realidad, están usando cursores DA, así que no creo que getValue se aplique. Creo que los cursores DA usan índices en su lugar.
msayler
1
Los cursores de actualización pueden leer y escribir datos. (Útil, por ejemplo, para leer un valor fieldAy usarlo para calcular un nuevo valor para fieldB.)
Erica