Вычисление последовательных чисел в отсортированную таблицу с помощью ArcGIS Desktop?

11

Есть ли способ рассчитать отсортированное поле с последовательными числами? Я видел класс объектов Сортировка для вычисления последовательного идентификатора поля с помощью ArcGIS Field Calculator? в нем описано, как рассчитывать последовательные числа, но это всегда рассчитывается по порядку 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поле не в порядке, поэтому приведенный выше код не будет работать.

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

Можно ли это сделать либо в Калькуляторе поля, либо с помощью курсора обновления в arcpy?

Мидавало
источник
В ArcObjects с ITableSort вы должны быть в состоянии сделать это ... не так много в python. Как сортируется таблица? Вы можете прочитать его до словаря с OID и отсортировать поле, отсортировать словарь, создать другой словарь с OID и значением, выполнить итерацию отсортированного первого словаря, чтобы присвоить значение второму, а затем переместить курсор через присвоение со вторым словарем ... 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 минут

FelixIP
источник
Я считаю, что первые четыре строки этого кода выполняются для каждой записи. Это не должно быть разрешено, так как слой должен быть отсортирован только один раз за весь расчет. Попробуйте использовать трюк, который я покажу в своем посте, или продемонстрируйте, что слой читается только один раз, чтобы определить порядок сортировки записей только для первой записи.
Ричард Фэйрхерст
@RichardFairhurst Я протестировал свое оригинальное выражение на 10 тысячах записей, его завершение заняло 2 минуты 06 секунд, изменение привело к улучшению в течение 5 секунд. Кажется, что первые строки не повторяются на каждой записи. Да, полевой калькулятор намного медленнее, чем скрипт, но удобно
FelixIP
Проверьте ту же таблицу против моего расчета. Если для выполнения расчета потребуется практически одинаковое время, я приму ваше предположение, что оно обрабатывается только один раз. 2 минуты и 6 секунд довольно медленно.
Ричард Фэйрхерст
ОК. Похоже, что через 1,5 секунды первые 4 строки не обрабатываются для каждой записи. В любом случае, словарь - это путь в любом случае. Однако что вы делаете, когда я хочу, чтобы номер Seq не был уникальным для каждой записи, если значения в других полях совпадают? Это было бы то, что я хотел бы для связанной таблицы в отношении 1: M.
Ричард Фэйрхерст
+1 @RichardFairhurst для словаря. Переполнение списка было медленной частью моего оригинала. Не быть уникальным, это большой вариант OP
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!)

Я также не рекомендовал бы использовать имена полей Год, Месяц и День, поскольку они работают только в шейп-файлах и не допускаются в базах геоданных. База геоданных изменит имена на Year_1, Month_1, Day_1, если вы попытаетесь добавить их в список полей в свойствах таблицы.

Если цель этой таблицы состоит в том, чтобы связать ее с другим классом таблиц / пространственных объектов по многопольному ключу, рассмотрите возможность использования инструмента, созданного в моем блоге, под названием « Ключ от нескольких полей к ключу одного поля» - связать два слоя на основе более чем одного поле

Ричард Фэйрхерст
источник
Как он обрабатывает дубликаты?
FelixIP
Добавьте OID в список полей. Я добавил OID в список полей, чтобы убедиться, что он уникален.
Ричард Фэйрхерст
В качестве альтернативы, если есть дубликаты, и пользователь хочет, чтобы все дубликаты имели одинаковое значение SEQ, оставьте ObjectID и используйте set () в списке перед запуском цикла for и добавьте его в словарь.
Ричард Фэйрхерст
+1 Спасибо @RichardFairhurst, почти так же, как моя попытка написать в arcpy, хотя я не понимал, что большую часть этого вы можете вызвать из Калькулятора поля
Midavalo
2

У меня был тот же вопрос, но для более простой задачи, основанной на сортировке только одного поля. Я был успешным с помощью следующего сценария:

# 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!)
user122347
источник