Obliczanie kolejnych liczb w posortowanej tabeli za pomocą ArcGIS Desktop?

11

Czy istnieje sposób na obliczenie posortowanego pola z kolejnymi liczbami? Widziałem Sortowanie klasy obiektów do obliczania sekwencyjnego pola identyfikatora za pomocą kalkulatora pola ArcGIS? to opisuje, jak obliczyć kolejne numery, ale zawsze jest to obliczane według kolejności FID, a nie według kolejności posortowanej.

#Pre-logic Script Code:
rec=0
def autoIncrement(): 
    global rec 
    pStart = 1  
    pInterval = 1 
    if (rec == 0):  
        rec = pStart  
    else:  
        rec += pInterval  
    return rec

#Expression:
autoIncrement()

Przykładem tego, co próbuję zrobić. Użyłem zaawansowanego sortowania do sortowania według roku, miesiąca, dnia, a teraz chcę mieć kolejne numery w Seqterenie. Zobaczysz, że moje OBJECTIDpole nie jest w porządku, więc powyższy kod nie będzie działać.

wprowadź opis zdjęcia tutaj

Czy można tego dokonać w Kalkulatorze polowym lub za pomocą kursora aktualizacji w trybie Arcpy?

Midavalo
źródło
W ArcObjects z ITableSort powinieneś być w stanie to zrobić ... nie tyle w pythonie. Jak sortowany jest stół? możesz przeczytać do słownika z OID i polem sortowania, posortować słownik, stworzyć inny słownik z OID i wartością, powtórzyć posortowany pierwszy słownik, aby przypisać wartość do drugiego, a następnie kursor poprzez przypisanie z drugim słownikiem ... a trochę rozmyślania, ale to wszystko, o czym mogę myśleć bez użycia ArcObjects.
Michael Stimson
@ MichaelMiles-Stimson, co nie jest złym pomysłem, prawdopodobnie mógłbym załadować go do słowników w celu ustalenia kolejności sortowania, a następnie zapisać te wartości w Seq.
Midavalo
Tak już wcześniej to robiłem i działa dobrze. Nie mogę teraz znaleźć mojego kodu; Było to jednorazowe, więc prawdopodobnie znajduje się na jednym z moich zapasowych dysków ... Jeśli się z tym spotkam, opublikuję odpowiedź - pod warunkiem, że nie ma jeszcze dobrej odpowiedzi na to pytanie.
Michael Stimson
Zawsze denerwowałem się, że nie można tego łatwo zrobić w ArcGIS. W MapInfo jest to banalne. Najłatwiejszym sposobem, z jakim się zetknąłem, jest skorzystanie z Narzędzia sortowania, ale tworzy ono inny zestaw danych, do którego musiałbyś się przyłączyć.
Fezter
Twoja składnia w Pythonie działa idealnie, dzięki za to. Zastanawiam się tylko, czy można rozpocząć pierwszy wiersz od 1, a nie od 0. Jeśli to możliwe, możesz podać mi kod. Miłego weekendu Fred
Fred

Odpowiedzi:

13

„Rozwiązanie” z 2 posortowanymi polami (rosnąco):

mxd = arcpy.mapping.MapDocument("CURRENT")
lr=arcpy.mapping.ListLayers(mxd)[0]
tbl=arcpy.da.TableToNumPyArray(lr,("oid","A","B"))
bs=sorted(tbl, key=lambda x: (x[1], x[2]))
def sortSeq(fid,a,b):
 for i,ent in enumerate(bs):
   if ent[0]==fid: return i

--------------------------------------

sortSeq( !OID!, !A!, !B! )

wprowadź opis zdjęcia tutaj

ZAKTUALIZOWANA WERSJA:

mxd = arcpy.mapping.MapDocument("CURRENT")
lr=arcpy.mapping.ListLayers(mxd)[0]
tbl=arcpy.da.TableToNumPyArray(lr,("oid","A","B"))
bs=sorted(tbl, key=lambda x: (x[1], x[2]))
aDict={}
for i,row in enumerate(bs):
 aDict[row[0]]=i
def sortSeq(fid):
 return aDict[fid]

-----------------------

sortSeq( !OID!)

wykonanie zadania na 10000 rekordach zajmuje 1,5 sekundy. Oryginał zajmuje nieco ponad 2 minuty

FelixIP
źródło
Uważam, że pierwsze cztery wiersze tego kodu są uruchamiane dla każdego rekordu. To nie może być dozwolone, ponieważ warstwa musi być posortowana tylko raz dla całego obliczenia. Zastanów się nad użyciem sztuczki, którą pokazuję w swoim poście, lub zademonstruj, że warstwa jest odczytywana tylko raz, aby ustalić kolejność sortowania rekordów tylko dla pierwszego rekordu.
Richard Fairhurst
@RichardFairhurst Przetestowałem moje oryginalne wyrażenie na 10 tysiącach rekordów, wykonanie zajęło 2 minuty 06 sekund, modyfikacja spowodowała poprawę w 5 sekund. Wygląda na to, że pierwsze wiersze nie są powtarzane na każdej płycie. Tak, kalkulator polowy jest znacznie wolniejszy niż skrypt, wygodny
FelixIP
Przetestuj tę samą tabelę na podstawie moich obliczeń. Jeśli obliczenia zajmą praktycznie tyle samo czasu, zaakceptuję twoje założenie, że jest ono przetwarzane tylko raz. 2 minuty i 6 sekund jest dość wolny.
Richard Fairhurst
OK 1,5 sekundy wydaje się wskazywać, że pierwsze 4 wiersze nie są przetwarzane dla każdego rekordu. W każdym razie słownik jest właściwą drogą. Co jednak robisz, gdy chcę, aby numer Seq nie był unikalny w każdym rekordzie, gdy wartości w innych polach są takie same? Tego właśnie chciałbym dla powiązanej tabeli w relacji 1: M.
Richard Fairhurst
+1 @RichardFairhurst dla słownika. Przejrzenie listy było powolną częścią mojego oryginału. Nie jest wyjątkowy, jest to duża odmiana OP
FelixIP
6

Jest to proces dwuetapowy, w wyniku czego Kalkulator polowy nie jest do niego dobrze dostosowany. Lepiej jest uruchomić to w samodzielnym skrypcie. Można to jednak zrobić w kalkulatorze polowym, pod warunkiem, że użyjesz podstępu. Musisz użyć kursora, aby załadować wszystkie wartości do słownika globalnego z posortowanej listy, ale tylko podczas obliczania pierwszego rekordu. W przypadku wszystkich innych rekordów musisz pominąć tworzenie słownika, aby uniknąć ciągłego ponownego czytania całej tabeli dla każdego wiersza.

Trzy wartości pól muszą być umieszczone w krotce, aby działały jak klucz, który będzie poprawnie sortowany. Zakładam, że wszystkie wartości kombinacji 3 pól są unikalne w tabeli SamplePoint, ale dodałem ObjectID, aby upewnić się, że jest unikalny. Musisz podać ścieżkę i nazwę pliku kształtu w wierszu 8 (lub mógłbym użyć techniki, której używa FelixIP, gdy używana jest pierwsza warstwa na bieżącej mapie). Jeśli chcesz użyć różnych pól dla klucza, musisz zmienić listę pól w linii 10 i dopasować je do pól wejściowych w linii 3 i 15.

#Pre-logic Script Code:
relateDict = {}
def autoIncrement(myYear, myMonth, myDay, OID): 
    global relateDict  
    # only populate the dictionary if it has no keys  
    if len(relateDict) == 0:  
        # Provide the path to the relate feature class/table  
        relateFC = r"C:\Users\OWNER\Documents\ArcGIS\SamplePoints.shp"  
        # create a field list with the relate fields in sort order  
        relateFieldsList = ["Year", "Month", "Day", "OID@"]  
        # process a da search cursor to transfer the data to the dictionary  
        relateList = sorted([(r[0:]) for r in arcpy.da.SearchCursor(relateFC, relateFieldsList)])
        for relateSort in range(0, len(relateList)):
            relateDict[relateList[relateSort]] = relateSort + 1
    return relateDict[(myYear,myMonth,myDay,OID)]    

#Expression:
autoIncrement(!Year!, !Month!, !Day!, !OBJECTID!)

Nie polecałbym także używania nazw pól Rok, Miesiąc i Dzień, ponieważ działają one tylko w plikach kształtów i nie są dozwolone w geobazach. Geobaza danych zmieni nazwy na Rok_1, Miesiąc_1, Dzień_1, jeśli spróbujesz dodać je do listy pól we właściwościach tabeli.

Jeśli celem tej tabeli jest powiązanie jej z inną tabelą / klasą funkcji na kluczu z wieloma polami, rozważ użycie narzędzia, które utworzyłem na swoim blogu o nazwie Klucz z wieloma polami do klucza z jednym polem - Powiązanie dwóch warstw na podstawie więcej niż jednej Pole

Richard Fairhurst
źródło
Jak radzi sobie z duplikatami?
FelixIP
Dodaj OID do listy pól. Dodałem OID do listy pól, aby upewnić się, że jest unikalny.
Richard Fairhurst
Alternatywnie, jeśli istnieją duplikaty, a użytkownik chce, aby wszystkie duplikaty miały tę samą wartość SEQ, pomiń ObjectID i użyj set () na liście przed uruchomieniem pętli for i dodaniem jej do słownika.
Richard Fairhurst
+1 Dzięki @RichardFairhurst, podobnie jak moja próba pisania w arkadach, chociaż nie zdawałem sobie sprawy, że większość z tego można
sprawdzić w
2

Miałem to samo pytanie, ale prostszy problem polegał na tym, że mam tylko jedno pole do sortowania. Udało mi się wykonać następujący skrypt:

# Pre-Logic Script Code:
# Specify that the target Map Document is the current one
mxd = arcpy.mapping.MapDocument("CURRENT")
# Specify that the target layer is the first layer in the table of 
# content
lr=arcpy.mapping.ListLayers(mxd)[0]

tbl=arcpy.da.TableToNumPyArray(lr,("fid","Name_of_sorted_Field"))
bs=sorted(tbl,key=lambda x: x[1])
aDict={}
for i,row in enumerate(bs):
 aDict[row[0]]=i
def sortSeq(fid):
 return aDict[fid]

---------------------------------------------------------------
# to run the code, the following goes in the expression window
sortSeq(!FID!)
użytkownik122347
źródło