Cálculo de números secuenciales en una tabla ordenada usando ArcGIS Desktop?

11

¿Hay alguna manera de calcular un campo ordenado con números secuenciales? ¿He visto la clase de entidad de clasificación para calcular el campo de ID secuencial usando ArcGIS Field Calculator? que describe cómo calcular números secuenciales, pero esto siempre se calcula en orden FID, no en orden ordenado.

#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 ejemplo de lo que estoy tratando de hacer. He usado una ordenación avanzada para ordenar por año, mes, día y ahora quiero tener números secuenciales en el Seqcampo. Verás que mi OBJECTIDcampo no está en orden, por lo que el código anterior no funcionará.

ingrese la descripción de la imagen aquí

¿Se puede hacer esto en la Calculadora de campo o usando un cursor de actualización en arcpy?

Midavalo
fuente
En ArcObjects con un ITableSort deberías poder hacerlo ... no tanto en Python. ¿Cómo se ordena la mesa? puede leerlo en un diccionario con OID y ordenar el campo, ordenar el diccionario, crear otro diccionario con OID y Valor, iterar el primer diccionario ordenado para asignar el valor al segundo y luego pasar el cursor a través de la asignación con el segundo diccionario ... a un poco de broma pero eso es todo lo que puedo pensar sin usar ArcObjects.
Michael Stimson
@ MichaelMiles-Stimson no es una mala idea, probablemente podría cargarlo en los diccionarios para determinar un orden de clasificación y luego escribir esos valores en la Seq.
Midavalo
Así es como lo hice antes y funcionó bien. No puedo encontrar mi código en este momento; Fue excepcional, por lo que probablemente esté en uno de mis discos de respaldo ... Si lo encuentro, lo publicaré como respuesta, siempre que no haya una buena respuesta a esta pregunta.
Michael Stimson
Siempre me ha molestado que esto no se pueda hacer fácilmente en ArcGIS. Mientras que, es trivial en MapInfo. La forma más fácil con la que me he encontrado es usar la herramienta de clasificación, pero eso crea otro conjunto de datos al que tendrías que volver a unir.
Fezter
Su sintaxis de Python funciona perfectamente, gracias por eso. Me pregunto si es posible comenzar la primera fila con 1 en lugar de 0. Si es posible, ¿puede darme el código? Que tengas un buen fin de semana Fred
Fred

Respuestas:

13

"Solución" con 2 campos ordenados (ascendente):

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

ingrese la descripción de la imagen aquí

VERSIÓN ACTUALIZADA:

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

Toma 1.5 segundos completar la tarea en 10000 registros. El original tarda un poco más de 2 minutos

FelixIP
fuente
Creo que las primeras cuatro líneas de este código se están ejecutando para todos y cada uno de los registros. Eso no debe permitirse, ya que la capa solo necesita ser ordenada una vez para todo el cálculo. Considere usar el truco que muestro en mi publicación o demuestre que la capa solo se lee una vez para determinar el orden de clasificación de registros solo para el primer registro.
Richard Fairhurst
@ RichardFairhurst Probé mi expresión original en 10 mil registros, tardó 2 minutos 06 segundos en completarse, la modificación resultó en una mejora de 5 segundos. Parece que las primeras líneas no se repiten en todos los registros. Sí, la calculadora de campo es mucho más lenta que la secuencia de comandos, aunque conveniente
FelixIP
Probar la misma tabla contra mi cálculo. Si tardan prácticamente el mismo tiempo en hacer el cálculo, aceptaré su suposición de que solo se está procesando una vez. 2 min y 6 segundos es bastante lento.
Richard Fairhurst
OK 1.5 segundos parecería indicar que las primeras 4 líneas no se están procesando para cada registro. De todos modos, el diccionario es el camino a seguir en cualquier caso. Sin embargo, ¿qué haces cuando quiero que el número Seq no sea único en cada registro cuando los valores en los otros campos son los mismos? Eso sería lo que desearía para la tabla relacionada en una relación 1: M.
Richard Fairhurst
+1 @RichardFairhurst para el diccionario. Repasar la lista fue una parte lenta en mi original. Re no ser único, es una gran variación de OP
FelixIP
6

Este es un proceso de dos pasos y, como resultado, la Calculadora de campo no es adecuada para ello. Es mejor ejecutar esto en un script independiente. Sin embargo, se puede hacer en la calculadora de campo, siempre que use un truco. Debe usar un cursor para cargar todos los valores en un diccionario global de una lista ordenada, pero solo durante el cálculo del primer registro. Para todos los demás registros, debe omitir la creación del diccionario para evitar volver a leer constantemente la tabla completa para cada fila.

Los tres valores de campo deben colocarse en una tupla para actuar como una clave que se clasificará correctamente. Asumiré que todos los valores de combinación de 3 campos son únicos en la tabla SamplePoint, pero agregué el ObjectID para asegurar que sea único. Debe proporcionar la ruta y el nombre del archivo de forma en la línea 8 (o podría usar la técnica que FelixIP usa cuando se usa la primera capa en el mapa actual). Si desea utilizar diferentes campos para una clave, debe cambiar la lista de campos en la línea 10 y hacerlos coincidir con los campos de entrada en la línea 3 y la línea 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!)

Tampoco recomendaría usar los nombres de campo de Año, Mes y Día, ya que solo funcionan en archivos shape y no están permitidos en geodatabases. Una geodatabase cambiará los nombres a Año_1, Mes_1, Día_1 si intenta agregarlos a la lista de campos en las propiedades de la tabla.

Si el propósito de esta tabla es relacionarla con otra tabla / clase de entidad en una clave de campo múltiple, considere usar la herramienta que creé en mi blog llamada Herramienta de clave de campo múltiple a clave de campo único - Relacionar dos capas en base a más de una Campo

Richard Fairhurst
fuente
¿Cómo maneja los duplicados?
FelixIP
Agregue el OID a la lista de campos. He agregado el OID a la lista de campos para asegurarme de que sea único.
Richard Fairhurst
Alternativamente, si hay duplicados y el usuario desea que todos los duplicados tengan el mismo valor SEQ, omita el ObjectID y use set () en la lista antes de ejecutar el bucle for y agregarlo al diccionario.
Richard Fairhurst
+1 Gracias @RichardFairhurst, muy parecido a mi intento de escribir en arco, aunque no me di cuenta de que se podía llamar a eso desde la Calculadora de campo
Midavalo
2

Tenía la misma pregunta pero para un problema más simple, basado en tener solo un campo para ordenar. Tuve éxito con el siguiente 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!)
usuario122347
fuente