Обчислення послідовних чисел у відсортованій таблиці за допомогою ArcGIS Desktop?


11

Чи є спосіб обчислити відсортоване поле із послідовними числами? Я бачив клас сортування сортування для обчислення послідовного поля ідентифікатора за допомогою калькулятора поля ArcGIS? це визначає, як обчислювати послідовні числа, але це завжди розраховується за порядком FID, а не за відсортованим порядком.

#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()

Приклад того , що я намагаюся зробити. Я використовував розширене сортування для сортування за роком, місяцем, днем, і тепер хочу мати в Seqполі послідовні номери . Ви побачите, що в моєму OBJECTIDполі не в порядку, тому наведений вище код не працює.

введіть тут опис зображення

Це можна зробити в полевому калькуляторі або за допомогою курсору оновлення в аркпії?


В ArcObjects з ITableSort ви повинні зробити це .. не стільки в python. Як сортується таблиця? Ви можете прочитати його до словника з OID та сортувати поле, сортувати словник, створити інший словник з OID та Value, відібрати відсортований перший словник, щоб призначити значення другому, а потім курсором, призначивши з другим словником ... a трохи роздумуючи навколо, але це все, що я можу думати, не використовуючи ArcObjects.
Майкл Стімсон

@ MichaelMiles-Stimson це не погана ідея, я, мабуть, міг би завантажити його в словники для визначення порядку сортування, а потім записати ці значення у Seq.
Мідавало

Ось як я це робив раніше, і це справно працює. Я не можу зараз знайти свій код; Це було одноразове, так що, мабуть, на одному з резервних дисків ... Якщо я натраплю на нього, я опублікую як відповідь - за умови, що на це питання ще немає гарної відповіді.
Майкл Стімсон

Мене завжди дратувало, що цього не можна зробити легко в ArcGIS. Тоді як у MapInfo це банально. Найпростіший спосіб, з яким я зіткнувся, - це використовувати інструмент сортування, але це створює інший набір даних, до якого вам доведеться приєднатися.
Фестер

Ваш синтаксис python прекрасно працює, за це дякую. Мені просто цікаво, чи можна починати перший рядок з 1, а не з 0. Якщо це можливо, ви можете дати мені код для нього. Приємного закінчення тижня Фред
Фред

Відповіді:


13

"Рішення" з 2 відсортованими полями (у зростанні):

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! )

введіть тут опис зображення

ОНОВЛЕНА ВЕРСІЯ:

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!)

займає 1,5 секунди для виконання завдання на 10000 записів. Оригінал займає трохи більше 2 хвилин


Я вважаю, що перші чотири рядки цього коду запускаються для кожного запису. Це не повинно бути дозволеним, оскільки шар потрібно сортувати лише один раз за весь розрахунок. Подумайте про використання трюку, який я показую у своєму дописі, або продемонструйте, що шар читається лише один раз для визначення порядку сортування записів лише для першого запису.
Річард Ферхерст

@RichardFairhurst Я перевірив свій оригінальний вираз на 10 тисячах записів, на це пішло 2 хвилини 06 сек. Здається, перші рядки повторюються не в кожному записі. Так,
полевий

Тестуйте ту саму таблицю проти мого розрахунку. Якщо для розрахунку знадобиться практично той самий час, то я прийму ваше припущення, що воно обробляється лише один раз. 2 хв і 6 секунд досить повільно.
Річард Ферхерст

Гаразд через 1,5 секунди вказується, що перші 4 рядки не обробляються для кожного запису. У будь-якому випадку, словник - це спосіб пройти в будь-якому випадку. Однак що ви робите, коли я хочу, щоб число Seq не було унікальним для кожного запису, коли значення в інших полях однакові? Це було б те, що я хотів би для відповідної таблиці у відносинах 1: M.
Річард Ферхерст

+1 @RichardFairhurst для словника. Переміщення списку було повільною частиною мого оригіналу. Щоб не бути унікальним, це велика варіація ОП
FelixIP

6

Це двоетапний процес, і як результат, калькулятор поля не дуже підходить до нього. Краще запустити це в автономному сценарії. Однак це можна зробити в польовому калькуляторі за умови використання хитрості. Вам потрібно використовувати курсор для завантаження всіх значень у глобальний словник із відсортованого списку, але лише під час обчислення першого запису. Для всіх інших записів вам потрібно пропустити створення словника, щоб уникнути постійного перечитування всієї таблиці для кожного рядка.

Три значення поля повинні бути розміщені в кордоні, щоб діяти як ключ, який буде сортувати належним чином. Я буду вважати, що всі значення комбінацій 3-х полів є унікальними в таблиці SamplePoint, але я додав ObjectID, щоб переконатися, що він унікальний. Ви повинні надати шлях і ім’я файлу форми у рядку 8 (або я міг би використовувати техніку, яку використовує FelixIP, коли використовується перший шар у поточній карті). Якщо ви хочете використовувати різні поля для ключа, вам слід змінити список полів у рядку 10 та зіставити їх із полями введення у рядку 3 та рядку 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!)

Я також не рекомендував би використовувати назви полів "Рік", "Місяць" та "День", оскільки вони працюють лише у форматі файлів і не дозволяються в базах даних про геодані. База геоданих змінить імена на Рік_1, Місяць_1, День_1, якщо ви спробуєте додати їх до списку полів у властивостях таблиці.

Якщо мета цієї таблиці полягає в тому, щоб пов’язати її з іншою таблицею / класом функцій на клавіші з декількома полями, розгляньте можливість використання інструменту, створеного в моєму блозі під назвою Кілька польових ключів, до інструмента «Клавіша одного поля» - відношення двох шарів на основі більше одного Поле


Як він обробляє дублікати?
FelixIP

Додайте OID до списку полів. Я додав OID до списку полів, щоб переконатися, що він унікальний.
Річард Ферхерст

Альтернативно, якщо є дублікати і користувач хоче, щоб усі дублікати мали одне і те ж значення SEQ, тоді залиште ObjectID і використовуйте set () у списку перед запуском циклу for і додавши його до словника.
Річард Ферхерст

+1 Дякую @RichardFairhurst, майже так само, як і моя спроба писати в архпію, хоча я не розумів, що ти можеш зателефонувати більшу частину цього з поля Field Calculator
Midavalo

3

У мене було те саме запитання, але щодо більш простої проблеми, заснованої на тому, щоб мати лише одне поле для сортування. Я мав успіх із наступним сценарієм:

# 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!)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.