Calcolare un nuovo attributo basato sulle modifiche in un altro attributo utilizzando ArcGIS Desktop con Python?


11

Sto tentando di classificare un insieme di dati punto codificati nel tempo gps in comportamenti basati su attributi diversi.

Ho creato un attributo 0 per la casa e 1 per la trasferta in base alla posizione e ora voglio numerare i viaggi lontano da casa (una serie di punti 01111111111110sarebbe un viaggio perché è iniziato e terminato a casa). Ho aggiunto il campo attributo che avrà i numeri di viaggio, ma non so come calcolare il campo in modo che sia basato sul campo casa / fuori.

Ecco un esempio dei dati GPS (utilizzando "*" per indicare informazioni irrilevanti e semplicemente indicizzare i tempi come 1, 2, ecc.), L'indicatore "Casa / Ospite" sopra descritto e l'indicatore di viaggio desiderato, "Viaggio", che devo calcolare:

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

Il mio set di dati è troppo grande per passare manualmente e numerare ogni viaggio nella tabella degli attributi, quindi esiste un modo per calcolare il campo in base a come viene ordinato l'attributo home / away e ogni "gruppo" di punti away è designato come viaggio?

Queste sono le ossa nude di come potrebbe apparire il codice Python (non ho esperienza con il codice).

Espressione:

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  

Dopo aver utilizzato lo script consigliato da Matt Wilson ho apportato alcune modifiche in modo che il mio primo viaggio sia il numero 1, il secondo sia il 2, ecc. Ecc.

Ecco il codice modificato da Matt:

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

Quindi seleziono solo home = 0 e calcolo il campo del mio viaggio a 0. Viaggi ordinatamente ordinati.

Risposte:


12

Per questo è possibile utilizzare UpdateCursor , che apre la classe di funzionalità o la tabella e passa attraverso ciascun record (riga) in modo incrementale.

Lo script seguente funziona su questi dati di test

+-----------------------+
| 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

La fine finale del blocco di viaggio viene effettivamente eseguita anche per l'inizio di un viaggio, ma poiché il contatore di viaggi è corretto, il doppio calcolo sulla riga di inizio-viaggio non ha importanza. Scomponi l'istruzione print in quel blocco per vedere cosa intendo.

Python aggiunge automaticamente un implicito rows.next()alla fine per il for row in rowsblocco.

Ciò presuppone l'integrità dei dati. Si incasinerà se ci sarà mai un numero dispari di zero record Casa / Ospite in una riga ( 000o 00000). Un viaggio che consiste solo in start e stop dovrebbe andare bene, ad esempio una sequenza di 3 trip di 01..10 00 01..10, in cui gli spazi indicano gli spazi tra i viaggi. In altre parole, convalida i risultati!


2
+1, DEVI farlo in un cursore di aggiornamento. Lo strumento CalculateField non garantisce che il blocco di codice verrà eseguito una sola volta, pertanto la tripvariabile può essere inizializzata nuovamente un numero arbitrario di volte.
Jason Scheirer,

Funziona alla grande in quanto a tutti i miei viaggi viene assegnato un numero per tutti i punti del viaggio, tuttavia a tutti i punti a casa viene assegnato un nuovo numero (ovvero i miei dati iniziano con i punti a casa ora numerati 1, 2, 3, .. ... 136 e quindi il mio primo viaggio è tutto etichettato 137). Non è un grosso problema perché posso riportare tutti i punti "home" su 0, ma sarebbe bello se i miei viaggi iniziassero a 1 e fossero in numero uniforme dopo. Qualche consiglio?
AlmaThom,

@Alice, non ho testato, ma tutto ciò che dovresti fare è commentare o eliminare la row.TRIP = triplinea in ciascuno dei due blocchi che gestiscono l'inizio e la fine del viaggio. (e, vieni a pensarci, quello rows.updateRow(row)che segue, poiché non c'è più nulla da salvare lì.)
Matt Wilson

Risolto il problema tecnico! la mia sceneggiatura ora ha tre parti:
AlmaThom,

5

La guida di ArcGIS 10 in "calcola esempi di campi" mostra come "Calcolare il valore cumulativo di un campo numerico". Questo funzionerà, a condizione che i dati siano fisicamente nell'ordine temporale previsto.

Per applicarlo direttamente, inverti l'indicatore [Home / Away] (sottrai da 1) in modo che "0" significhi "away" e "1" significhi "home". Lo chiamo [Fuori casa] nell'esempio di seguito.

Calcola il suo valore cumulativo - [Cumulativo] nell'esempio.

Aggiungi uno e dividi per due - [Trip] nell'esempio (quasi).

Infine, imposta [Trip] su zero per tutti i record "home". Ora i risultati concordano con l'esempio:

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

Per la cronaca, ecco il codice preso dalla guida di ArcGIS 10. L'ho modificato leggermente in modo da fare tutti i passaggi contemporaneamente: ora devi solo eseguirlo. Dovrebbe essere chiaro dove [Casa / Ospite] viene invertito e dove si verifica il passaggio "aggiungi 1, dividi per 2".

Espressione:

acc(!Home/Away!)

Tipo di espressione:

PYTHON_9.3

Blocco di codice:

t=0
def acc(i):
  global t
  if t:
    t += (1-i)
  else:
    t = 1
  if i:
    return (t+1)/2
  else:
    return 0

3
Per un gran numero di record questo non funzionerà. Il codeblock si ripete ogni poche centinaia di migliaia di righe (insieme a un ciclo completo di garbage collection), quindi tverrà reimpostato su 0 in punti apparentemente casuali.
Jason Scheirer,

2
Grazie, @Jason: non ero a conoscenza di quel bug. È un vero spettacolo. <rant> Pensavo che ArcGIS avrebbe dovuto ridimensionarsi in modo che sia buono per i piccoli problemi del giocattolo? </rant>
whuber

1
Non un bug, in realtà è un dettaglio dell'implementazione ereditato dall'implementazione di VBScript per cercare di ridurre al minimo le perdite di memoria (gli utenti accedono a un elenco per ogni record ma non utilizzano mai l'elenco per nulla, ad esempio). Sono abbastanza sicuro di essermi liberato dell'aggiornamento in 11 perché è un comportamento non ovvio, ma non ricordo.
Jason Scheirer,

1
@Jason Questo è un nuovo eufemismo per me: "dettaglio di implementazione". Altri eufemismi sono "caratteristica" e "comportamento non documentato". Una rosa con qualsiasi altro nome ...
whuber

2
Ecco come lo vedo, @Jason: la pagina di aiuto stessa fornisce il codice che ho presentato. Esiste quindi un'affermazione implicita da parte dell'ESRI secondo cui il codice funziona. Secondo te, non lo fa; infatti, sotto la tua caratterizzazione, può fallire in modo significativo, silenzioso, senza preavviso e imprevedibile. Non è solo un bug, è la forma più cattiva di bug possibile. Un "reset periodico" non è una "correzione", è un kluge che non fa che peggiorare la situazione IMHO.
whuber
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.