Obliczanie nowego atrybutu na podstawie zmian w innym atrybucie za pomocą ArcGIS Desktop z Pythonem?

11

Usiłuję sklasyfikować zestaw danych punktowych zakodowanych w czasie GPS na zachowania oparte na różnych atrybutach.

Stworzyłem atrybut, który wynosi 0 dla domu i 1 dla wyjazdu na podstawie lokalizacji, a teraz chcę numerować wyjazdy poza domem (zestaw punktów 01111111111110to jedna podróż, ponieważ zaczynała się i kończyła w domu). Dodałem pole atrybutu, które będzie miało numery podróży, ale nie wiem, jak obliczyć pole, więc jest ono oparte na polu domu / wyjazdu.

Oto przykład danych GPS (użycie „*” do wskazania nieistotnych informacji i po prostu indeksowanie czasów jako 1, 2 itd.), Opisanego powyżej wskaźnika „Home / Away” oraz pożądanego wskaźnika podróży „Trip”, które muszę obliczyć:

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

Mój zestaw danych jest zbyt duży, aby ręcznie przejść i numerować każdą podróż w tabeli atrybutów, więc jest jakiś sposób, aby obliczyć pole na podstawie tego, w jaki sposób uporządkowano atrybut home / away, a każda „grupa” punktów away jest oznaczana jako wyjazd?

Są to gołe kości tego, jak mógłby wyglądać kod Pythona (nie mam doświadczenia z kodem).

Wyrażenie:

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  

Po użyciu skryptu Matta Wilkiego dokonałem pewnych zmian, dzięki czemu moja pierwsza podróż to numer 1, moja druga to 2 itd.

Oto kod zmodyfikowany z 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

Następnie wybieram dla domu = 0 i przeliczam pole podróży z powrotem na 0. Starannie uporządkowane wycieczki.

AlmaThom
źródło

Odpowiedzi:

12

W tym celu można użyć UpdateCursor , który otwiera klasę obiektów lub tabelę i krok po kroku przez każdy rekord (wiersz).

Poniższy skrypt działa na tych danych testowych

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

Blok końca wyzwalacza jest właściwie uruchamiany również na początku wyłączenia, ale ponieważ licznik wyzwalaczy jest poprawny, podwójne obliczanie w wierszu początku wyzwalania nie ma znaczenia. Odkomentuj instrukcję print w tym bloku, aby zobaczyć, co mam na myśli.

Python automatycznie dodaje niejawne rows.next()na końcu for row in rowsbloku.

Zakłada to integralność danych. Będzie bałagan, jeśli kiedykolwiek będzie nieparzysta liczba rekordów Home / Away z rzędu ( 000lub 00000). Podróż składająca się tylko z startu i zatrzymania powinna być w porządku, np. 3 sekwencja podróży 01..10 00 01..10, gdzie spacje oznaczają przerwy między podróżami. Innymi słowy, sprawdź wyniki!

matowe wilkie
źródło
2
+1, MUSISZ to zrobić kursorem aktualizacji. Narzędzie CalculateField nie gwarantuje, że blok kodu zostanie uruchomiony tylko raz, więc tripzmienną można ponownie zainicjalizować dowolną liczbę razy.
Jason Scheirer
Działa to świetnie, ponieważ do wszystkich moich podróży przypisany jest jeden numer do wszystkich punktów w podróży, jednak wszystkie punkty w domu otrzymują nowy numer (tj. Moje dane zaczynają się od punktów w domu, teraz numerowanych 1, 2, 3, .. ... 136, a potem moja pierwsza podróż jest oznaczona jako 137). Nie jest to wielka sprawa, ponieważ mogę przywrócić wszystkie punkty „domowe” do zera, ale byłoby miło, gdyby moje wycieczki zaczęły się od 1, a potem były równe. Jakakolwiek rada?
AlmaThom
@Alice, nie testowałem, ale wszystko, co musisz zrobić, to skomentować lub usunąć row.TRIP = tripwiersz w każdym z dwóch bloków, które obsługują początek i koniec podróży. (i rows.updateRow(row)
przyjrzyjmy się
Rozwiązałem usterkę! mój skrypt składa się teraz z trzech części:
AlmaThom,
5

Pomoc ArcGIS 10 w sekcji „Oblicz przykłady pól” pokazuje, jak „Oblicz wartość skumulowaną pola numerycznego”. To załatwi sprawę, pod warunkiem, że dane są fizycznie w zamierzonym porządku czasowym.

Aby zastosować go bezpośrednio, odwróć wskaźnik [Home / Away] (odejmij go od 1), aby „0” oznaczało „away”, a „1” oznacza „home”. Nazywam to [Poza domem / Domem] w poniższym przykładzie.

Oblicz jego wartość skumulowaną - [Skumulowana] w przykładzie.

Dodaj jeden i podziel przez dwa - [Trip] w przykładzie (prawie).

Na koniec ustaw [Trip] na zero dla wszystkich rekordów „home”. Teraz wyniki są zgodne z przykładem:

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

Dla przypomnienia, oto kod pobrany z pomocy ArcGIS 10. Zmodyfikowałem go nieco, aby działał na każdym kroku naraz: teraz wystarczy go uruchomić. Powinno być jasne, gdzie [Home / Away] zostaje odwrócony i gdzie występuje krok „dodaj 1, podziel przez 2”.

Wyrażenie:

acc(!Home/Away!)

Typ wyrażenia:

PYTHON_9.3

Blok kodu:

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
źródło
3
W przypadku dowolnej dużej liczby rekordów to nie zadziała. Blok kodu uruchamia się ponownie co kilkaset tysięcy wierszy (wraz z pełnym cyklem usuwania śmieci), więc tzostanie zresetowany do zera w pozornie losowych miejscach.
Jason Scheirer
2
Dzięki, @Jason: Nie wiedziałem o tym błędzie. To prawdziwy przerywnik show. <rant> Myślałem, że ArcGIS powinien się skalować, aby był dobry na więcej niż małe problemy z zabawkami? </rant>
whuber
1
To nie błąd, w rzeczywistości jest to szczegół implementacji odziedziczony z implementacji VBScript, aby spróbować zminimalizować wycieki pamięci (użytkownicy dołączają się do listy dla każdego rekordu, ale na przykład nigdy nie używają tej listy do niczego). Jestem całkiem pewien, że pozbyłem się odświeżania w 11, ponieważ jest to nieoczywiste zachowanie, ale nie pamiętam.
Jason Scheirer
1
@Jason To dla mnie nowy eufemizm: „szczegóły wdrożenia”. Inne eufemizmy to „cechy” i „nieudokumentowane zachowanie”. Róża pod jakimkolwiek innym imieniem ...
whuber
2
Oto, jak to widzę, @Jason: sama strona pomocy zawiera przedstawiony kod. Istnieje zatem domniemane twierdzenie ze strony ESRI, że kod działa. Według ciebie tak nie jest; w rzeczy samej, pod twoją charakterystyką, może zawieść znacznie, cicho, bez ostrzeżenia i nieprzewidywalnie. To nie tylko błąd, to najgorsza możliwa forma błędu. „Okresowy reset” nie jest „naprawą”, to jest kluge, który tylko pogarsza sytuację IMHO.
whuber