Perché lo script ArcPy è lento?


12

Ho un semplice script arcpy per aggiornare un campo in un file di forma punto con informazioni dalla funzione poligono in cui si trova. Ci vogliono 9 minuti per fare 100 punti in arcpy ma un'unione spaziale in arcmap è istantanea. Sono sicuro che esiste un modo rapido per risolvere questo problema. Qualcuno può indicarmi la giusta direzione?

import took 0:00:07.085000
extent took 0:00:05.991000
one pt loop took 0:00:03.780000
one pt loop took 0:00:03.850000
one pt loop took 0:00:03.791000


import datetime
t1 = datetime.datetime.now()
import arcpy
t2 = datetime.datetime.now()
print "import took %s" %  ( t2-t1)
#set up environment
arcpy.env.workspace = "data\\"
arcpy.env.overwriteOutput = True

desc = arcpy.Describe("parcels.shp")
ext = desc.Extent
extent = (ext.XMin,ext.XMax,ext.YMin,ext.YMax)
t3 = datetime.datetime.now()
print "extent took %s" %  (t3 -t2)
fc = arcpy.CreateRandomPoints_management("", "malls.shp", "", ext, 100, "", "POINT", "")
arcpy.AddField_management("malls.shp", 'ParcelID', 'LONG')

rows = arcpy.UpdateCursor('malls.shp',"","",'ParcelID')
for row in rows:
    t4 = datetime.datetime.now()
    pt = row.Shape.getPart()
    for polyrow in arcpy.SearchCursor('parcels.shp'):
        t6 = datetime.datetime.now()
        poly = polyrow.getValue('Shape')
        if extent[0]<pt.X<extent[1] and extent[2]<pt.Y<extent[3]:
            if poly.contains(pt):
                print "works"
                row.ParcelID = polyrow.Parcels_ID
                rows.updateRow(row)
                break #we can stop looking for matches since
        t7 = datetime.datetime.now()
        "a full poly loop took %s" % (t7-t6)
    t5 = datetime.datetime.now()
    print "one pt loop took %s" % (t5-t4)


print datetime.datetime.now() -t1

4
A quale versione di ArcGIS sei? 10.1 ha aggiunto il arcpy.damodulo (Accesso ai dati) con versioni (molto) più veloci dei cursori.
blah238

Risposte:


20

Se è necessario creare un secondo cursore per parcels.shp, farlo all'esterno del ciclo per il primo cursore. Allo stato attuale, il tuo script sta creando un nuovo oggetto cursore per ogni riga in malls.shpcui ti costa tutto quel tempo di elaborazione.

...
rows = arcpy.UpdateCursor('malls.shp',"","",'ParcelID')
polyrows = arcpy.SearchCursor('parcels.shp')
for row in rows:
    t4 = datetime.datetime.now()
    pt = row.Shape.getPart()
    for polyrow in polyrows:
...

Questo era esattamente. Grazie. e poi uso .reset () sul mio secondo cursore per ogni volta che voglio attraversarlo? Sembra che passi solo attraverso il cursore 1 volta.
EmdyP

Hmm, non dovresti aver bisogno di resettare le righe. Assicurati di eliminare sia gli oggetti riga che gli oggetti cursore alla fine dello script. Se non lo fai, possono succedere cose divertenti.
Jason

Credo che il cursore del ciclo interno non necessità di essere azzerato ogni volta se seguire questa strada. Vedi la mia risposta per un'alternativa.
blah238

10

Il problema con la risposta di @ Jason (e il tuo approccio originale) è che non sfrutta l'indice spaziale e richiede un ciclo annidato a due cursori che diventerà esponenzialmente più lento all'aumentare del numero di punti.

Un flusso di lavoro alternativo che potrebbe essere più veloce mentre ti consente comunque di aggiornare la classe di funzionalità punto sul posto (Join spaziale genera solo una nuova classe di funzionalità, non aggiorna una esistente) potrebbe essere:

  1. Utilizzare Join spaziale per creare una classe caratteristica intermedia (forse in memoria)
  2. Utilizzare Aggiungi join per unire la classe di caratteristiche intermedie alla classe di caratteristiche del punto esistente
  3. Utilizzare Calcola campo o un UpdateCursor per copiare i valori nel campo unito nel campo nella classe caratteristica punti esistente.

2
Mi piace questo flusso di lavoro alternativo: adoro il fatto che, anche se non ho posto la domanda, sto ancora imparando nuovi modi di fare le cose qui.
Jason,
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.