Berechnen eines neuen Attributs basierend auf Änderungen in einem anderen Attribut mithilfe von ArcGIS Desktop mit Python?

11

Ich versuche, eine Reihe von zeitcodierten GPS-Punktdaten in Verhaltensweisen zu klassifizieren, die auf verschiedenen Attributen basieren.

Ich habe ein Attribut erstellt, das je nach Standort 0 für Zuhause und 1 für Auswärts ist, und möchte nun die Reisen von zu Hause aus nummerieren (eine Reihe von Punkten 01111111111110wäre eine Reise, da sie zu Hause begann und endete). Ich habe das Attributfeld hinzugefügt, das die Reisenummern enthält, weiß aber nicht, wie das Feld berechnet werden soll, damit es auf dem Heim- / Auswärtsfeld basiert.

Hier ist ein Beispiel für die GPS-Daten (mit "*", um irrelevante Informationen anzuzeigen und einfach die Zeiten als 1, 2 usw. zu indizieren), die oben beschriebene Anzeige "Home / Away" und die gewünschte Auslöseanzeige "Trip". was ich berechnen muss:

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

Mein Datensatz ist zu groß, um jede Fahrt manuell in der Attributtabelle zu durchlaufen und zu nummerieren. Gibt es also eine Möglichkeit, das Feld basierend auf der Reihenfolge des Home / Away-Attributs zu berechnen und jeden "Klumpen" von Auswärtspunkten als zu kennzeichnen Ausflug?

Dies sind die Grundlagen dafür, wie Python-Code aussehen könnte (ich habe keine Erfahrung mit Code).

Ausdruck:

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  

Nachdem ich das von matt wilkie empfohlene Skript verwendet habe, habe ich einige Änderungen vorgenommen, sodass meine erste Reise die Nummer 1, meine zweite die Nummer 2 usw. ist.

Hier ist der von matt modifizierte Code:

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

Dann wähle ich einfach für home = 0 und berechne mein Reisefeld zurück auf 0. Ordentlich geordnete Reisen.

AlmaThom
quelle

Antworten:

12

Dazu können Sie UpdateCursor verwenden , der die Feature-Class oder Tabelle öffnet und schrittweise durch jeden Datensatz (jede Zeile) geht.

Das folgende Skript arbeitet mit diesen Testdaten

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

Das nachfolgende Ende des Auslöseblocks wird tatsächlich auch für den Beginn einer Auslösung ausgeführt, aber da der Auslösezähler korrekt ist, spielt die Doppelberechnung in der Startauslösereihe keine Rolle. Kommentieren Sie die print-Anweisung in diesem Block aus, um zu sehen, was ich meine.

Python fügt rows.next()am Ende automatisch ein implizites Element für den for row in rowsBlock hinzu.

Dies setzt Datenintegrität voraus. Es wird durcheinander bringen, wenn es jemals eine ungerade Anzahl von Null Heim- / Auswärtsdatensätzen in einer Reihe ( 000oder 00000) gibt. Eine Reise, die nur aus Start und Stopp besteht, sollte in Ordnung sein, z. B. eine 3-Trip-Sequenz von 01..10 00 01..10, wobei die Leerzeichen die Lücken zwischen den Fahrten bezeichnen. Mit anderen Worten, validieren Sie die Ergebnisse!

matt wilkie
quelle
2
+1, Sie MÜSSEN dies in einem Aktualisierungscursor tun. Das CalculateField-Tool garantiert nicht, dass der Codeblock nur einmal ausgeführt wird, sodass die tripVariable beliebig oft neu initialisiert werden kann.
Jason Scheirer
Dies funktioniert insofern hervorragend, als allen meinen Reisen eine Nummer für alle Punkte der Reise zugewiesen wird, jedoch allen Punkten zu Hause eine neue Nummer zugewiesen wird (dh meine Daten beginnen mit Punkten zu Hause, die jetzt mit 1, 2, 3, ... nummeriert sind. ... 136 und dann ist meine erste Reise alle mit 137) gekennzeichnet. Es ist keine große Sache, weil ich alle "Heim" -Punkte auf 0 zurücksetzen kann, aber es wäre schön, wenn meine Reisen bei 1 beginnen und danach gleichmäßig nummeriert wären. Irgendein Rat?
AlmaThom
@ Alice, ich habe nicht getestet, aber alles, was Sie tun müssen, ist die row.TRIP = tripZeile in jedem der beiden Blöcke, die den Beginn und das Ende der Reise behandeln, zu kommentieren oder zu löschen . (Und denken Sie darüber nach, rows.updateRow(row)welche folgen, da es dort nichts mehr zu retten gibt.)
Matt Wilkie
Die Panne aussortiert! Mein Skript besteht jetzt aus drei Teilen:
AlmaThom
5

Die ArcGIS 10-Hilfe unter "Feldbeispiele berechnen" zeigt Ihnen, wie Sie den Akkumulationswert eines numerischen Felds berechnen. Dies reicht aus, vorausgesetzt, die Daten befinden sich physisch in der beabsichtigten zeitlichen Reihenfolge.

Um es direkt anzuwenden, invertieren Sie Ihren [Home / Away] -Indikator (subtrahieren Sie ihn von 1), sodass "0" "weg" und "1" "zu Hause" bedeutet. Ich nenne dies im folgenden Beispiel [Auswärts / Heim].

Berechnen Sie den kumulativen Wert - [kumulativ] im Beispiel.

Addiere eins und dividiere durch zwei - [Trip] im Beispiel (fast).

Setzen Sie schließlich [Trip] für alle "Home" -Datensätze auf Null. Nun stimmen die Ergebnisse mit dem Beispiel überein:

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

Im Folgenden finden Sie den Code aus der ArcGIS 10-Hilfe. Ich habe es leicht modifiziert, damit es jeden Schritt auf einmal macht: Jetzt müssen Sie es nur noch ausführen. Es sollte klar sein, wo [Heim / Auswärts] invertiert wird und wo der Schritt "1 addieren, durch 2 teilen" auftritt.

Ausdruck:

acc(!Home/Away!)

Ausdruckstyp:

PYTHON_9.3

Codeblock:

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
quelle
3
Bei einer großen Anzahl von Datensätzen funktioniert dies nicht. Der Codeblock wird alle paar hunderttausend Zeilen erneut ausgeführt (zusammen mit einem vollständigen tSpeicherbereinigungszyklus ), sodass er an scheinbar zufälligen Stellen auf 0 zurückgesetzt wird.
Jason Scheirer
2
Danke, @Jason: Ich war mir dieses Fehlers nicht bewusst. Das ist ein echter Show-Stopper. <rant> Ich dachte, ArcGIS sollte skaliert werden, damit es mehr als nur kleine Spielzeugprobleme
löst
1
Kein Fehler, sondern ein Implementierungsdetail, das von der VBScript-Implementierung geerbt wurde, um Speicherverluste zu minimieren (Benutzer, die für jeden Datensatz eine Liste anhängen, die Liste jedoch nie für irgendetwas verwenden). Ich bin mir ziemlich sicher, dass ich die Aktualisierung in 11 losgeworden bin, weil es nicht offensichtliches Verhalten ist, aber ich erinnere mich nicht.
Jason Scheirer
1
@ Jason Das ist ein neuer Euphemismus für mich: "Implementierungsdetail". Andere Euphemismen sind "Merkmal" und "undokumentiertes Verhalten". Eine Rose mit einem anderen Namen ...
whuber
2
So sehe ich das, @Jason: Die Hilfeseite selbst enthält den von mir präsentierten Code. Es gibt daher eine implizite Behauptung von ESRI, dass der Code funktioniert. Ihrer Meinung nach nicht; In der Tat kann es unter Ihrer Charakterisierung erheblich, lautlos, ohne Vorwarnung und unvorhersehbar scheitern. Das ist nicht nur ein Fehler, es ist die schlimmste Form von Fehler, die möglich ist. Ein "periodisches Zurücksetzen" ist kein "Fix", sondern ein Kluge, der die Situation meiner Meinung nach nur verschlimmert.
whuber