Calculando números sequenciais em uma tabela classificada usando o ArcGIS Desktop?

11

Existe uma maneira de calcular um campo classificado com números sequenciais? Eu vi a classe de recurso de classificação para calcular o campo de identificação sequencial usando o ArcGIS Field Calculator? que descreve como calcular números sequenciais, mas isso é sempre calculado na ordem do FID, não na ordem classificada.

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

Um exemplo do que estou tentando fazer. Usei uma classificação avançada para classificar por ano, mês, dia e agora quero ter números sequenciais no Seqcampo. Você verá que meu OBJECTIDcampo não está em ordem, portanto o código acima não funcionará.

insira a descrição da imagem aqui

Isso pode ser feito na calculadora de campo ou usando um cursor de atualização no arcpy?

Midavalo
fonte
No ArcObjects com um ITableSort, você deve ser capaz de fazê-lo .. não tanto em python. Como a tabela é classificada? você pode lê-lo em um dicionário com OID e campo de classificação, classificar o dicionário, criar outro dicionário com OID e Valor, iterar o primeiro dicionário classificado para atribuir o valor ao segundo e, em seguida, cursor através da atribuição com o segundo dicionário ... a mexendo um pouco, mas é tudo que consigo pensar sem usar o ArcObjects.
Michael Stimson
@ MichaelMiles-Stimson que não é uma má idéia, eu provavelmente poderia carregá-lo em dicionários para determinar uma ordem de classificação e depois escrever esses valores na Seq.
Midavalo
Foi assim que eu fiz antes e funcionou bem. Não consigo encontrar meu código agora; Foi um caso único, portanto provavelmente está em um dos meus discos de backup ... Se eu me deparar, postarei como resposta - desde que ainda não haja uma boa resposta para esta pergunta.
Michael Stimson 15/05
Sempre me irritei que isso não possa ser feito facilmente no ArcGIS. Enquanto isso, é trivial no MapInfo. A maneira mais fácil que me deparei é usando a ferramenta de classificação, mas isso cria outro conjunto de dados no qual você teria que se juntar novamente.
Fezter
Sua sintaxe python funciona perfeitamente, obrigado por isso. Eu apenas me pergunto se é possível iniciar a primeira linha com 1 em vez de 0. Se possível, você pode me dar o código para isso. Tenha um fim boa semana Fred
Fred

Respostas:

13

"Solução" com 2 campos classificados (crescente):

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

insira a descrição da imagem aqui

VERSÃO ATUALIZADA:

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

leva 1,5 segundos para concluir a tarefa em 10000 registros. O original leva um pouco mais de 2 minutos

FelixIP
fonte
Acredito que as quatro primeiras linhas desse código estejam sendo executadas para cada registro. Isso não deve ser permitido, uma vez que a camada precisa ser classificada apenas uma vez para todo o cálculo. Considere usar o truque que mostro na minha postagem ou demonstrar que a camada está sendo lida apenas uma vez para determinar a ordem de classificação do registro apenas para o primeiro registro.
Richard Fairhurst
@RichardFairhurst Testei minha expressão original em 10 mil registros. Demorou 2 minutos e 06 segundos para concluir. A modificação resultou em uma melhoria de 5 segundos. Parece que as primeiras linhas não são repetidas em todos os registros. Sim calculadora campo é muito mais lento do que o roteiro, conveniente embora
FelixIP
Teste a mesma tabela em relação ao meu cálculo. Se eles demorarem praticamente o mesmo tempo para fazer o cálculo, aceitarei sua suposição de que ele está sendo processado apenas uma vez. 2 min e 6 segundos é bastante lento.
Richard Fairhurst
OK 1,5 segundos parece indicar que as 4 primeiras linhas não estão sendo processadas para cada registro. De qualquer forma, o dicionário é o caminho a seguir em ambos os casos. No entanto, o que você faz quando eu quero que o número Seq não seja exclusivo em cada registro quando os valores nos outros campos são os mesmos? Seria o que eu gostaria para a tabela relacionada em um relacionamento 1: M.
Richard Fairhurst
+1 em RichardFairhurst para o dicionário. Navegar pela lista foi uma parte lenta do meu original. Re não ser único, é uma grande variação de OP
FelixIP
6

Este é um processo de duas etapas e, como resultado, a Calculadora de Campo não é adequada. É melhor executar isso em um script independente. No entanto, isso pode ser feito na calculadora de campo, desde que você use um truque. Você precisa usar um cursor para carregar todos os valores em um dicionário global de uma lista classificada, mas apenas durante o cálculo do primeiro registro. Para todos os outros registros, você deve pular a criação do dicionário para evitar reler constantemente a tabela inteira para cada linha.

Os três valores do campo devem ser colocados em uma tupla para atuar como uma chave que será classificada corretamente. Assumirei que todos os valores da combinação de 3 campos são exclusivos na tabela SamplePoint, mas adicionei o ObjectID para garantir que ele seja exclusivo. Você deve fornecer o nome do caminho e do arquivo shapefile na linha 8 (ou eu poderia usar a técnica que o FelixIP usa onde a primeira camada no mapa atual é usada). Se você deseja usar campos diferentes para uma chave, é necessário alterar a lista de campos na linha 10 e combiná-los com os campos de entrada nas linhas 3 e 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!)

Eu também não recomendaria o uso de nomes de campos Ano, Mês e Dia, pois eles funcionam apenas em shapefiles e não são permitidos em bancos de dados geográficos. Um geodatabase mudará os nomes para Year_1, Month_1, Day_1 se você tentar adicioná-los à lista de campos nas propriedades da tabela.

Se o objetivo desta tabela é relacioná-la a outra classe de tabela / recurso em uma chave de vários campos, considere usar a ferramenta que criei no meu blog chamada Chave de Campo Múltiplo a Ferramenta Chave de Campo Único - Relacione Duas Camadas com Base em Mais de Um Campo

Richard Fairhurst
fonte
Como ele lida com duplicatas?
FelixIP 16/05
Adicione o OID à lista de campos. Eu adicionei o OID à lista de campos para garantir que ele seja exclusivo.
Richard Fairhurst
Como alternativa, se houver duplicatas e o usuário desejar que todas as duplicatas tenham o mesmo valor SEQ, deixe de fora o ObjectID e use set () na lista antes de executar o loop for e adicioná-lo ao dicionário.
Richard Fairhurst
+1 Obrigado @ RichardFairhurst, da mesma forma que minha tentativa de escrever em arcpy, embora eu não tenha percebido que você poderia chamar a maior parte disso na Calculadora de Campo
Midavalo
2

Eu tinha a mesma pergunta, mas para um problema mais simples, baseado em ter apenas um campo para classificar. Fui bem-sucedido com o seguinte script:

# 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
fonte