¿Calculando un nuevo atributo basado en cambios en otro atributo usando ArcGIS Desktop con Python?

11

Estoy tratando de clasificar un conjunto de datos de puntos codificados por tiempo gps en comportamientos basados ​​en diferentes atributos.

He creado un atributo que es 0 para el hogar y 1 para el visitante según la ubicación, y ahora quiero numerar los viajes fuera de casa (un conjunto de puntos 01111111111110sería un viaje porque comenzó y terminó en casa). He agregado el campo de atributo que tendrá los números de viaje, pero no sé cómo calcular el campo, por lo que se basa en el campo local / visitante.

Aquí hay un ejemplo de los datos del GPS (usando "*" para indicar información irrelevante y simplemente indexando los tiempos como 1, 2, etc.), el indicador "Local / Ausente" descrito anteriormente y el indicador de viaje deseado, "Viaje", que necesito calcular:

Time Lat Lon Home/Away Trip
   1   *   *         0    0
   2   *   *         1    1
   3   *   *         1    1
....
  12   *   *         1    1
  13   *   *         0    0
  14   *   *         0    0
  15   *   *         1    2
  16   *   *         1    2
.... 
  34   *   *         1    2
  35   *   *         0    0
  36   *   *         0    0
  37   *   *         1    3
....

Mi conjunto de datos es demasiado grande para pasar manualmente y numerar cada viaje en la tabla de atributos, por lo tanto, ¿hay alguna manera de calcular el campo en función de cómo se ordena el atributo local / visitante y cada "grupo" de puntos de distancia se designa como un ¿viaje?

Estos son los huesos básicos del aspecto que podría tener el código Python (no tengo experiencia con el código).

Expresión:

trip = Reclass(!home!)

Codeblock:

def Reclass(home):  
  if (home = 0):  
    return 0   
  elif (home = 1 and lastValue = 0):  
    return _(incremental numbering?)_  
  elif (home = 1 and lastValue = 1):  
    return lastValue  

Después de usar el script recomendado de matt wilkie, hice algunos cambios para que mi primer viaje sea el número 1, el segundo sea el 2, etc.

Aquí está el código modificado de Matt's:

import arcpy
rows = arcpy.UpdateCursor("test2")

trip = 0
for row in rows:
    if row.home == 0:
        prev = row.home
        row.TRIP = trip
        rows.updateRow(row)

    elif row.home == 1 and prev == 0:
        trip += 1
        prev = row.home
        row.TRIP = trip
        rows.updateRow(row)
        rows.next()

    elif row.home == 1 and prev == 1:
        prev = row.home
        row.TRIP = trip
        rows.updateRow(row)
        rows.next()

    row.TRIP = trip
    rows.updateRow(row)


del row, rows

Luego solo selecciono para inicio = 0 y calculo mi campo de viaje nuevamente a 0. Viajes ordenados cuidadosamente.

AlmaThom
fuente

Respuestas:

12

Para esto, puede usar UpdateCursor , que abre la clase de entidad o tabla y los pasos a través de cada registro (fila) de forma incremental.

El siguiente script funciona con estos datos de prueba

+-----------------------+
| Time| Home_Away|Trip  |
+-----|----------|------+
|  1  |  0       | <nul>|
|  2  |  1       | <nul>|
|  4  |  1       | <nul>|
|  5  |  0       | <nul>|
|  6  |  0       | <nul>|
|  7  |  1       | <nul>|
|  9  |  1       | <nul>|
| 12  |  1       | <nul>|
| 13  |  0       | <nul>|
+-----------------------+

.

import arcpy
fc = r'D:\s\py\pyscratch.gdb\gps_points'

# open the feature class and create the cursor
rows = arcpy.UpdateCursor(fc)

trip = 0
for row in rows:
    if row.HOME_AWAY == 0:
        trip += 1           # start of new trip, increment counter
        row.TRIP = trip     # calc the TRIP field to be current trip#
        rows.updateRow(row) # save
        print "Trip %s started at %s" % (trip, row.TIME)

    # keep cycling through records until HOME_AWAY is not 1
    while row.HOME_AWAY == 1:
        row.TRIP = trip
        rows.updateRow(row)
        rows.next() # move to next record

    # this is for the trailing end of a trip, the second 0
    # print "     %s ended at %s" % (trip, row.TIME)
    row.TRIP = trip
    rows.updateRow(row)

# remove programming objects and data locks
# the data itself is left alone
del row, rows

El final final del bloque de viaje también se ejecuta para el comienzo de un viaje, pero dado que el contador de viajes es correcto, el doble cálculo en la fila de inicio de viaje no importa. Descomente la declaración impresa en ese bloque para ver a qué me refiero.

Python agrega automáticamente un implícito rows.next()al final del for row in rowsbloque.

Esto supone la integridad de los datos. Se estropeará si alguna vez hay un número impar de cero registros Local / Visitante seguidos ( 000o 00000). Un viaje que solo consta de inicio y parada debería estar bien, por ejemplo, una secuencia de 3 viajes 01..10 00 01..10, donde los espacios denotan los espacios entre viajes. En otras palabras, ¡valide los resultados!

wilkie mate
fuente
2
+1, DEBE hacer esto en un cursor de actualización. La herramienta CalculateField no garantiza que el bloque de código se ejecutará solo una vez, por lo que la tripvariable puede reiniciarse cualquier número arbitrario de veces.
Jason Scheirer
Esto funciona muy bien porque a todos mis viajes se les asigna un número para todos los puntos en el viaje, sin embargo, a todos los puntos en casa se les asigna un nuevo número (es decir, mis datos comienzan con puntos en casa ahora numerados 1, 2, 3, .. ... 136 y luego mi primer viaje está etiquetado como 137). No es un gran problema porque puedo revertir todos los puntos de "casa" a 0, pero sería bueno si mis viajes comenzaran en 1 y fueran iguales después de eso. ¿Algún consejo?
AlmaThom
@ Alice, no probé, pero todo lo que debes hacer es comentar o eliminar la row.TRIP = triplínea en cada uno de los dos bloques que manejan el inicio y el final del viaje. (y, ahora que lo pienso, lo rows.updateRow(row)que sigue, ya que ya no hay nada que salvar allí.)
Matt wilkie
Solucionado el problema técnico! mi guión ahora tiene tres partes:
AlmaThom
5

La ayuda de ArcGIS 10 en "Calcular ejemplos de campo" le muestra cómo "Calcular el valor acumulativo de un campo numérico". Esto hará el truco, siempre que los datos estén físicamente en el orden temporal previsto.

Para aplicarlo directamente, invierta su indicador [Local / Ausente] (reste de 1) para que "0" signifique "ausente" y "1" significa "local". Llamo a esto [Ausente / Inicio] en el ejemplo a continuación.

Calcule su valor acumulativo - [Acumulativo] en el ejemplo.

Agregue uno y divida por dos - [Disparo] en el ejemplo (casi).

Finalmente, establezca [Viaje] en cero para todos los registros de "inicio". Ahora los resultados concuerdan con el ejemplo:

Time Lat Lon Home/Away Trip Away/Home Cumulative 
   1   *   *         0    0         1          1
   2   *   *         1    1         0          1
   3   *   *         1    1         0          1
.... 
  12   *   *         1    1         0          1
  13   *   *         0    0         1          2
  14   *   *         0    0         1          3
  15   *   *         1    2         0          3
  16   *   *         1    2         0          3
.... 
  34   *   *         1    2         0          3
  35   *   *         0    0         1          4
  36   *   *         0    0         1          5
  37   *   *         1    3         0          5
....

Para el registro, aquí está el código tomado de la ayuda de ArcGIS 10. Lo modifiqué un poco para que hiciera todos los pasos a la vez: ahora solo necesitas ejecutarlo. Debe quedar claro dónde se invierte [Inicio / Visitante] y dónde se produce el paso "sumar 1, dividir por 2".

Expresión:

acc(!Home/Away!)

Tipo de expresión:

PYTHON_9.3

Bloque de código:

t=0
def acc(i):
  global t
  if t:
    t += (1-i)
  else:
    t = 1
  if i:
    return (t+1)/2
  else:
    return 0
whuber
fuente
3
Para cualquier gran cantidad de registros, esto no funcionará. El bloque de código se vuelve a ejecutar cada cientos de miles de filas (junto con un ciclo completo de recolección de basura), por tlo que se restablecerá a 0 en lugares aparentemente aleatorios.
Jason Scheirer
2
Gracias, @ Jason: no estaba al tanto de ese error. Eso es un verdadero show-stopper. <rant> ¿Pensé que ArcGIS se suponía que iba a escalar para que sea bueno para más que pequeños problemas con los juguetes? </rant>
whuber
1
No es un error, en realidad es un detalle de implementación heredado de la implementación de VBScript para tratar de minimizar las pérdidas de memoria (los usuarios agregan a una lista para cada registro pero nunca usan la lista para nada, por ejemplo). Estoy bastante seguro de que me deshice de la actualización en 11 porque es un comportamiento no obvio, pero no lo recuerdo.
Jason Scheirer
1
@ Jason: Es un nuevo eufemismo para mí: "detalles de implementación". Otros eufemismos son "características" y "comportamiento indocumentado". Una rosa con cualquier otro nombre ...
whuber
2
Así es como lo veo, @Jason: la página de ayuda proporciona el código que presenté. Por lo tanto, existe una afirmación implícita por parte de ESRI de que el código funciona. Según usted, no lo hace; de hecho, bajo su caracterización, puede fallar de manera significativa, silenciosa, sin advertencia e impredecible. Eso no es solo un error, es la forma más desagradable de error posible. Un "reinicio periódico" no es una "solución", es un problema que solo empeora la situación en mi humilde opinión.
whuber