Calcolo dei numeri sequenziali nella tabella ordinata usando ArcGIS Desktop?


11

C'è un modo per calcolare un campo ordinato con numeri sequenziali? Ho visto l' ordinamento della classe di caratteristiche per calcolare il campo ID sequenziale usando ArcGIS Field Calculator? che illustra come calcolare i numeri sequenziali, ma questo viene sempre calcolato sull'ordine FID, non sull'ordine ordinato.

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

Un esempio di ciò che sto cercando di fare. Ho usato un ordinamento avanzato per ordinare per anno, mese, giorno e ora voglio avere numeri sequenziali nel Seqcampo. Vedrai che il mio OBJECTIDcampo non è in ordine, quindi il codice sopra non funzionerà.

inserisci qui la descrizione dell'immagine

Questo può essere fatto nel Field Calculator o usando un cursore di aggiornamento in arcpy?


In ArcObjects con un ITableSort dovresti essere in grado di farlo .. non tanto in Python. Come viene ordinata la tabella? potresti leggerlo in un dizionario con OID e campo di ordinamento, ordinare il dizionario, creare un altro dizionario con OID e valore, ripetere il primo dizionario ordinato per assegnare il valore al secondo, quindi il cursore assegnandolo con il secondo dizionario ... a un po 'di confusione, ma è tutto ciò a cui riesco a pensare senza usare ArcObjects.
Michael Stimson,

@ MichaelMiles-Stimson non è una cattiva idea, probabilmente potrei caricarlo nei dizionari per determinare un ordinamento e poi scrivere quei valori nel Seq.
Midavalo

È così che l'ho fatto prima e ha funzionato bene. Non riesco a trovare il mio codice in questo momento; Era una tantum, quindi è probabilmente su uno dei miei dischi di backup ... Se lo trovo, inserirò una risposta, a condizione che non ci sia già una buona risposta a questa domanda.
Michael Stimson,

Sono sempre stato infastidito dal fatto che non è possibile farlo facilmente in ArcGIS. Considerando che è banale in MapInfo. Il modo più semplice in cui mi sono imbattuto è usare lo strumento di ordinamento, ma crea un altro set di dati a cui dovresti riconnetterti.
Fezter

La sintassi di Python funziona perfettamente, grazie per quello. Mi chiedo solo se è possibile iniziare la prima riga con 1 anziché con 0. Se è possibile, puoi darmi il codice. Buon fine settimana Fred
Fred

Risposte:


13

"Soluzione" con 2 campi ordinati (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! )

inserisci qui la descrizione dell'immagine

VERSIONE AGGIORNATA:

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

richiede 1,5 secondi per completare l'attività su 10000 record. L'originale impiega poco più di 2 minuti


Credo che le prime quattro righe di questo codice vengano eseguite per ogni singolo record. Ciò non deve essere consentito, poiché il livello deve essere ordinato solo una volta per l'intero calcolo. Prendi in considerazione l'utilizzo del trucco che mostro nel mio post o dimostra che il layer viene letto solo una volta per determinare l'ordinamento dei record solo per il primo record.
Richard Fairhurst,

@RichardFairhurst Ho testato la mia espressione originale su 10 mila dischi, ci sono voluti 2 minuti e 06 secondi per completare, la modifica ha portato a un miglioramento di 5 secondi. Sembra che le prime righe non vengano ripetute su ogni disco. Sì, il calcolatore di campo è molto più lento dello script, anche se è conveniente
FelixIP

Prova la stessa tabella rispetto al mio calcolo. Se impiegano praticamente lo stesso tempo per fare il calcolo, accetterò la tua ipotesi che sia stata elaborata una sola volta. 2 minuti e 6 secondi è abbastanza lento.
Richard Fairhurst,

OK 1,5 secondi sembrerebbe indicare che le prime 4 righe non vengono elaborate per ciascun record. In ogni caso, il dizionario è la strada da percorrere in entrambi i casi. Tuttavia, cosa fai quando voglio che il numero Seq non sia univoco su ciascun record quando i valori negli altri campi sono uguali? Sarebbe quello che vorrei per la tabella correlata in una relazione 1: M.
Richard Fairhurst,

+1 @RichardFairhurst per il dizionario. Mescolare l'elenco era una parte lenta del mio originale. Per non essere unico, è una grande variazione di OP
FelixIP,

6

Questo è un processo in due passaggi e, di conseguenza, il calcolatore di campo non è adatto a questo. È meglio eseguirlo in uno script autonomo. Tuttavia, può essere fatto nel calcolatore di campo, a patto che tu usi un trucco. È necessario utilizzare un cursore per caricare tutti i valori in un dizionario globale da un elenco ordinato, ma solo durante il calcolo del primo record. Per tutti gli altri record devi saltare la creazione del dizionario per evitare di rileggere costantemente l'intera tabella per ogni riga.

I tre valori di campo devono essere inseriti in una tupla per fungere da chiave che verrà ordinata correttamente. Presumo che tutti i valori della combinazione a 3 campi siano univoci nella tabella SamplePoint, ma ho aggiunto l'ObjectID per assicurarmi che sia univoco. Devi fornire il percorso e il nome dello shapefile nella riga 8 (o potrei usare la tecnica che FelixIP usa dove viene usato il primo layer nella mappa corrente). Se si desidera utilizzare campi diversi per una chiave, è necessario modificare l'elenco dei campi nella riga 10 e abbinarli ai campi di input nella riga 3 e nella riga 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!)

Inoltre, non consiglierei di utilizzare i nomi dei campi di Anno, Mese e Giorno, poiché funzionano solo nei file di forma e non sono consentiti nei database geografici. Un geodatabase cambierà i nomi in Year_1, Month_1, Day_1 se si tenta di aggiungerli all'elenco dei campi nelle proprietà della tabella.

Se lo scopo di questa tabella è di metterlo in relazione con un'altra tabella / classe di funzionalità su una chiave multi-campo, prendere in considerazione l'utilizzo dello strumento che ho creato nel mio blog denominato Chiave da campo multiplo a Strumento chiave da campo singolo - Correlare due livelli basati su più di uno Campo


Come gestisce i duplicati?
FelixIP

Aggiungi l'OID all'elenco dei campi. Ho aggiunto l'OID all'elenco dei campi per assicurarmi che sia unico.
Richard Fairhurst,

In alternativa, se ci sono duplicati e l'utente desidera che tutti i duplicati abbiano lo stesso valore SEQ, lascia fuori l'ObjectID e usa set () nell'elenco prima di eseguire il ciclo for e aggiungerlo al dizionario.
Richard Fairhurst,

+1 Grazie @RichardFairhurst, più o meno lo stesso del mio tentativo di scrivere in arcpy, anche se non mi rendevo conto che potresti chiamarlo per lo più dal Field Calculator
Midavalo

2

Ho avuto la stessa domanda, ma per un problema più semplice, basato sull'avere un solo campo da ordinare. Ho avuto successo con il seguente 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!)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.