Puedo ver varias cosas que pueden estar causando que su script sea lento. Lo que probablemente sea muy lento es la arcpy.CalculateField_management()
función. Debe usar un cursor, lo hará varias magnitudes más rápido. Además, dijo que está utilizando ArcGIS Desktop 10.3.1, pero está utilizando los antiguos cursores de estilo ArcGIS 10.0, que también son mucho más lentos.
La operación min () incluso en una lista de 200K será bastante rápida. Puede verificar esto ejecutando este pequeño fragmento; sucede en un abrir y cerrar de ojos:
>>> min(range(200000)) # will return 0, but is still checking a list of 200,000 values very quickly
Vea si esto es más rápido:
import arcpy
fc = arcpy.env.workspace = arcpy.GetParameterAsText(0)
Xfield = "XKoordInt"
with arcpy.da.SearchCursor(fc, [Xfield]) as rows:
ListVal = [r[0] for r in rows]
value = min(ListVal) - 20
print value
# now update
with arcpy.da.UpdateCursor(fc, [Xfield, 'Matrix_Z']) as rows:
for r in rows:
if r[0] is not None:
r[1] = (r[0] - value) / 20.0
rows.updateRow(r)
EDITAR:
Realicé algunas pruebas de tiempo y, como sospechaba, la calculadora de campo tardó casi el doble de tiempo que el nuevo cursor de estilo. Curiosamente, el cursor de estilo antiguo era ~ 3 veces más lento que la calculadora de campo. Creé 200,000 puntos aleatorios y usé los mismos nombres de campo.
Se usó una función de decorador para cronometrar cada función (puede haber un poco de sobrecarga en la configuración y desmantelamiento de las funciones, por lo que tal vez el módulo timeit sería un poco más preciso para probar fragmentos).
Aquí están los resultados:
Getting the values with the old style cursor: 0:00:19.23
Getting values with the new style cursor: 0:00:02.50
Getting values with the new style cursor + an order by sql statement: 0:00:00.02
And the calculations:
field calculator: 0:00:14.21
old style update cursor: 0:00:42.47
new style cursor: 0:00:08.71
Y aquí está el código que usé (desglosé todo en funciones individuales para usar el timeit
decorador):
import arcpy
import datetime
import sys
import os
def timeit(function):
"""will time a function's execution time
Required:
function -- full namespace for a function
Optional:
args -- list of arguments for function
kwargs -- keyword arguments for function
"""
def wrapper(*args, **kwargs):
st = datetime.datetime.now()
output = function(*args, **kwargs)
elapsed = str(datetime.datetime.now()-st)[:-4]
if hasattr(function, 'im_class'):
fname = '.'.join([function.im_class.__name__, function.__name__])
else:
fname = function.__name__
print'"{0}" from {1} Complete - Elapsed time: {2}'.format(fname, sys.modules[function.__module__], elapsed)
return output
return wrapper
@timeit
def get_value_min_old_cur(fc, field):
rows = arcpy.SearchCursor(fc)
return min([r.getValue(field) for r in rows])
@timeit
def get_value_min_new_cur(fc, field):
with arcpy.da.SearchCursor(fc, [field]) as rows:
return min([r[0] for r in rows])
@timeit
def get_value_sql(fc, field):
"""good suggestion to use sql order by by dslamb :) """
wc = "%s IS NOT NULL"%field
sc = (None,'Order By %s'%field)
with arcpy.da.SearchCursor(fc, [field]) as rows:
for r in rows:
# should give us the min on the first record
return r[0]
@timeit
def test_field_calc(fc, field, expression):
arcpy.management.CalculateField(fc, field, expression, 'PYTHON')
@timeit
def old_cursor_calc(fc, xfield, matrix_field, value):
wc = "%s IS NOT NULL"%xfield
rows = arcpy.UpdateCursor(fc, where_clause=wc)
for row in rows:
if row.getValue(xfield) is not None:
row.setValue(matrix_field, (row.getValue(xfield) - value) / 20)
rows.updateRow(row)
@timeit
def new_cursor_calc(fc, xfield, matrix_field, value):
wc = "%s IS NOT NULL"%xfield
with arcpy.da.UpdateCursor(fc, [xfield, matrix_field], where_clause=wc) as rows:
for r in rows:
r[1] = (r[0] - value) / 20
rows.updateRow(r)
if __name__ == '__main__':
Xfield = "XKoordInt"
Mfield = 'Matrix_Z'
fc = r'C:\Users\calebma\Documents\ArcGIS\Default.gdb\Random_Points'
# first test the speed of getting the value
print 'getting value tests...'
value = get_value_min_old_cur(fc, Xfield)
value = get_value_min_new_cur(fc, Xfield)
value = get_value_sql(fc, Xfield)
print '\n\nmin value is {}\n\n'.format(value)
# now test field calculations
expression = "(!XKoordInt!-{0})/20".format(value)
test_field_calc(fc, Xfield, expression)
old_cursor_calc(fc, Xfield, Mfield, value)
new_cursor_calc(fc, Xfield, Mfield, value)
Y finalmente, esta es la impresión real de mi consola.
>>>
getting value tests...
"get_value_min_old_cur" from <module '__main__' from 'C:/Users/calebma/Desktop/speed_test2.py'> Complete - Elapsed time: 0:00:19.23
"get_value_min_new_cur" from <module '__main__' from 'C:/Users/calebma/Desktop/speed_test2.py'> Complete - Elapsed time: 0:00:02.50
"get_value_sql" from <module '__main__' from 'C:/Users/calebma/Desktop/speed_test2.py'> Complete - Elapsed time: 0:00:00.02
min value is 5393879
"test_field_calc" from <module '__main__' from 'C:/Users/calebma/Desktop/speed_test2.py'> Complete - Elapsed time: 0:00:14.21
"old_cursor_calc" from <module '__main__' from 'C:/Users/calebma/Desktop/speed_test2.py'> Complete - Elapsed time: 0:00:42.47
"new_cursor_calc" from <module '__main__' from 'C:/Users/calebma/Desktop/speed_test2.py'> Complete - Elapsed time: 0:00:08.71
>>>
Edición 2: Acabo de publicar algunas pruebas actualizadas, encontré un pequeño defecto con mi timeit
función.