Dlaczego skrypt ArcPy działa wolno?

12

Mam prosty skrypt arkadowy, aby zaktualizować pole w pliku kształtu punktowego o informacje z funkcji wielokąta, w której on jest. Zebranie 100 punktów w arcpy zajmuje 9 minut, ale połączenie przestrzenne w arcmap jest natychmiastowe. Jestem pewien, że istnieje szybko ustalony sposób rozwiązania tego problemu. Czy ktoś może skierować mnie we właściwym kierunku?

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
EmdyP
źródło
4
W której wersji ArcGIS jesteś? 10.1 dodał arcpy.damoduł (Data Access) z (znacznie) szybszymi wersjami kursorów.
blah238

Odpowiedzi:

20

Jeśli chcesz utworzyć drugi kursor parcels.shp, zrób to poza pętlą dla pierwszego kursora. W tej chwili skrypt tworzy nowy obiekt kursora dla każdego wiersza, w malls.shpktórym kosztuje Cię cały ten czas przetwarzania.

...
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:
...
Jason
źródło
To było dokładnie to. Dziękuję Ci. a następnie używam .reset () na moim drugim kursorze za każdym razem, gdy chcę go przechodzić? Wygląda na to, że teraz kursor przechodzi tylko raz.
EmdyP,
Hmm, nie powinieneś resetować wierszy. Upewnij się, że usuwasz zarówno obiekty wierszy, jak i obiekty kursora na końcu skryptu. Zabawne rzeczy mogą się zdarzyć, jeśli tego nie zrobisz.
Jason
Myślę kursor pętli wewnętrznej jest nie wymagać wyzerowania za każdym razem, jeśli tę drogę. Zobacz moją odpowiedź na alternatywę.
blah238
10

Problem z odpowiedzią @ Jasona (i twoim oryginalnym podejściem) polega na tym, że nie wykorzystuje on wskaźnika przestrzennego i wymaga zagnieżdżonej pętli z dwoma kursorami, która będzie wykładniczo wolniejsza wraz ze wzrostem liczby punktów.

Alternatywnym przepływem pracy, który może być szybszy, a jednocześnie pozwala aktualizować punktową klasę obiektów w miejscu (sprzężenie przestrzenne generuje tylko nową klasę elementów, a nie aktualizuje istniejącą), może być:

  1. Użyj łączenia przestrzennego, aby utworzyć pośrednią (być może w pamięci) klasę obiektów
  2. Użyj polecenia Dodaj dołącz, aby dołączyć pośrednią klasę obiektów do istniejącej klasy obiektów punktowych
  3. Użyj narzędzia Calculate Field lub UpdateCursor, aby skopiować wartości z połączonego pola do pola w istniejącej klasie obiektów punktowych.
blah238
źródło
2
Podoba mi się ten alternatywny przepływ pracy - uwielbiam to, że chociaż nie zadawałem pytania, wciąż uczę się nowych sposobów robienia tego tutaj.
Jason