Mam tabelę z 8 kolumnami i ~ 16,7 miliona rekordów. Muszę uruchomić zestaw równań if-else na kolumnach. Napisałem skrypt za pomocą modułu UpdateCursor, ale po kilku milionach rekordów zabrakło mu pamięci. Zastanawiałem się, czy istnieje lepszy sposób na przetworzenie tych 16,7 miliona rekordów.
import arcpy
arcpy.TableToTable_conversion("combine_2013", "D:/mosaic.gdb", "combo_table")
c_table = "D:/mosaic.gdb/combo_table"
fields = ['dev_agg', 'herb_agg','forest_agg','wat_agg', 'cate_2']
start_time = time.time()
print "Script Started"
with arcpy.da.UpdateCursor(c_table, fields) as cursor:
for row in cursor:
# row's 0,1,2,3,4 = dev, herb, forest, water, category
#classficiation water = 1; herb = 2; dev = 3; forest = 4
if (row[3] >= 0 and row[3] > row[2]):
row[4] = 1
elif (row[2] >= 0 and row[2] > row[3]):
row[4] = 4
elif (row[1] > 180):
row[4] = 2
elif (row[0] > 1):
row[4] = 3
cursor.updateRow(row)
end_time = time.time() - start_time
print "Script Complete - " + str(end_time) + " seconds"
AKTUALIZACJA # 1
Uruchomiłem ten sam skrypt na komputerze z 40 GB pamięci RAM (oryginalny komputer miał tylko 12 GB pamięci RAM). Pomyślnie ukończono po ~ 16 godzinach. Wydaje mi się, że 16 godzin to za długo, ale nigdy nie pracowałem z tak dużym zestawem danych, więc nie wiem, czego się spodziewać. Jedynym nowym dodatkiem do tego skryptu jest arcpy.env.parallelProcessingFactor = "100%"
. Próbuję dwóch sugerowanych metod (1) zrobienia 1 miliona rekordów partiami i (2) przy użyciu SearchCursor i zapisywania wyników do csv. Niedługo przedstawię sprawozdanie z postępów.
AKTUALIZACJA # 2
SearchCursor i aktualizacja CSV działały doskonale! Nie mam dokładnych czasów uruchomienia, zaktualizuję pocztę, gdy jutro będę w biurze, ale powiedziałbym, że przybliżony czas pracy wynosi ~ 5-6 minut, co jest dość imponujące. Nie spodziewałem się tego. Udostępniam mój nieoczyszczony kod wszelkie uwagi i ulepszenia są mile widziane:
import arcpy, csv, time
from arcpy import env
arcpy.env.parallelProcessingFactor = "100%"
arcpy.TableToTable_conversion("D:/mosaic.gdb/combine_2013", "D:/mosaic.gdb", "combo_table")
arcpy.AddField_management("D:/mosaic.gdb/combo_table","category","SHORT")
# Table
c_table = "D:/mosaic.gdb/combo_table"
fields = ['wat_agg', 'dev_agg', 'herb_agg','forest_agg','category', 'OBJECTID']
# CSV
c_csv = open("D:/combine.csv", "w")
c_writer = csv.writer(c_csv, delimiter= ';',lineterminator='\n')
c_writer.writerow (['OID', 'CATEGORY'])
c_reader = csv.reader(c_csv)
start_time = time.time()
with arcpy.da.SearchCursor(c_table, fields) as cursor:
for row in cursor:
#skip file headers
if c_reader.line_num == 1:
continue
# row's 0,1,2,3,4,5 = water, dev, herb, forest, category, oid
#classficiation water = 1; dev = 2; herb = 3; ; forest = 4
if (row[0] >= 0 and row[0] > row[3]):
c_writer.writerow([row[5], 1])
elif (row[1] > 1):
c_writer.writerow([row[5], 2])
elif (row[2] > 180):
c_writer.writerow([row[5], 3])
elif (row[3] >= 0 and row[3] > row[0]):
c_writer.writerow([row[5], 4])
c_csv.close()
end_time = time.time() - start_time
print str(end_time) + " - Seconds"
AKTUALIZACJA # 3 Ostatnia aktualizacja. Całkowity czas działania skryptu wynosi ~ 199,6 sekundy / 3,2 minuty.
źródło
Odpowiedzi:
Możesz zapisać Objectid i wynik obliczeń (cate_2) do pliku csv. Następnie dołącz plik csv do oryginalnego pliku, wypełnij pole, aby zachować wynik. W ten sposób nie aktualizujesz tabeli za pomocą kursora DA. Możesz użyć kursora wyszukiwania.
źródło
Przepraszam, jeśli wciąż ożywiam ten stary wątek. Pomysł polegał na wykonaniu instrukcji if-else na rastrze kombajnu, a następnie użyciu nowego pola w odnośniku w celu utworzenia nowego rastra. Skomplikowałem problem, eksportując dane jako tabelę i wprowadziłem nieefektywny przepływ pracy, którym zajął się @Alex Tereshenkov. Po zrozumieniu tego, co oczywiste, podzieliłem dane na 17 zapytań (po 1 milion), zgodnie z sugestią @FelixIP. Każda partia trwała średnio ~ 1,5 minuty, a całkowity czas pracy wynosił ~ 23,3 minuty. Ta metoda eliminuje potrzebę łączenia i myślę, że ta metoda najlepiej spełnia zadanie. Oto poprawiony skrypt do wykorzystania w przyszłości:
źródło
Lookup
i wyeksportowanie rastra z nowo zdefiniowanymi kategoriami.arcpy.env.parallelProcessingFactor = "100%"
nie ma wpływu na twój skrypt. Nie widzę tam żadnych narzędzi, które mogłyby wykorzystać to środowisko.Możesz spróbować zmienić na użycie CalculateField_management . Zapobiega to zapętlaniu się za pomocą kursorów, a po wyglądzie opcji dla wartości kategorii można ustawić to jako cztery podprocesy odradzające się sekwencyjnie. Po zakończeniu każdego podprocesu pamięć jest zwalniana przed rozpoczęciem następnego. Oddajesz małe (milisekundy) trafienie spawnujące każdy podproces.
Lub, jeśli chcesz zachować obecne podejście, skorzystaj z podprocesu, który pobiera x wierszy naraz. Przeprowadź główny proces, aby go poprowadzić i, jak poprzednio, za każdym razem, gdy się skończy, wciąż przeszukujesz swoją pamięć. Dodatkową zaletą robienia tego w ten sposób (szczególnie poprzez samodzielny proces pythona) jest to, że możesz lepiej wykorzystać wszystkie swoje rdzenie jako spawnujące podprocesy w wielowątkowości Pythona wokół GIL. Jest to możliwe dzięki ArcPy i podejściu, które stosowałem w przeszłości do masowej utraty danych. Oczywiście zmniejsz ilość danych, w przeciwnym razie skończy Ci się pamięć szybciej!
źródło
Logikę manipulacji danymi można zapisać jako instrukcję SQL UPDATE przy użyciu wyrażenia CASE, którą można wykonać za pomocą GDAL / OGR, np. Przez OSGeo4W z
gdal-filegdb
zainstalowanym programem .Oto przepływ pracy, który wykorzystuje
osgeo.ogr
zamiastarcpy
:Na podobnej tabeli z nieco ponad 1 milionem rekordów zapytanie zajęło 18 minut. Przetwarzanie 16 milionów rekordów może potrwać około 4–5 godzin.
źródło
arcpy
ale doceniam odpowiedź. Powoli próbuję więcej używać GDAL.Aktualizacja kodu w sekcji 2 w twoim pytaniu nie pokazuje, w jaki sposób łączysz
.csv
plik z powrotem do oryginalnej tabeli w geobazie plików. Mówisz, że uruchomienie skryptu zajęło ~ 5 minut. Brzmi to uczciwie, jeśli.csv
plik został wyeksportowany tylko bez wykonywania połączeń. Gdy spróbujesz przywrócić.csv
plik do ArcGIS, napotkasz problemy z wydajnością.1) Nie można wykonywać połączeń bezpośrednio z
.csv
tabeli geobazy, ponieważ.csv
plik nie ma identyfikatora OID (pole obliczone przy użyciu unikalnych wartości nie pomoże, ponieważ nadal trzeba będzie przekonwertować.csv
plik na tabelę geobazy). Tak więc kilka minut naTable To Table
narzędzie GP (możesz użyćin_memory
obszaru roboczego do utworzenia tabeli tymczasowej, będzie nieco szybsze).2) Po załadowaniu
.csv
do tabeli geobazy, chciałbyś zbudować indeks na polu, w którym wykonasz łączenie (w twoim przypadku, wartość źródłowaobjectid
z.csv
pliku. Zajmie to kilka minut na tabeli wierszy 16mln.3) Następnie musisz użyć narzędzi
Add Join
lubJoin Field
GP. Żadne z nich nie będzie dobrze działać na dużych stołach.4) Następnie musisz wykonać
Calculate Field
narzędzie GP, aby obliczyć nowo połączone pola (pola). Wiele minut tutaj; co więcej, obliczanie pól zajmuje więcej czasu, gdy pola biorące udział w obliczeniach pochodzą z połączonej tabeli.Jednym słowem, nie dostaniesz niczego, co wspominasz o 5 minutach. Jeśli zrobisz to za godzinę, będę pod wrażeniem.
Aby uniknąć przetwarzania dużych zestawów danych w ArcGIS, sugeruję przeniesienie danych poza ArcGIS do
pandas
ramki danych i wykonanie tam wszystkich obliczeń. Kiedy skończysz, po prostu zapisz wiersze ramki danych z powrotem w nowej tabeli geobazy za pomocąda.InsertCursor
(lub możesz obciąć istniejącą tabelę i zapisać wiersze w źródłowej).Pełny kod, który napisałem w celu przeprowadzenia tego testu, znajduje się poniżej:
Poniżej przedstawiono dane wyjściowe debugowania we / wy (podana liczba to liczba użytych wierszy w tabeli) z informacją o czasie wykonania poszczególnych funkcji:
Wstawienie wiersza z
da.InsertCursor
zajmuje stały czas, to znaczy, jeśli wstawienie 1 rzędu zajmie, powiedzmy, 0,1 sekundy, wstawienie 100 rzędów zajmie 10 sekund. Niestety, ponad 95% całkowitego czasu wykonania spędza na czytaniu tabeli geobazy, a następnie wstawianiu wierszy z powrotem do geobazy.To samo dotyczy tworzenia
pandas
ramki danych zda.SearchCursor
generatora i obliczania pola (pól). Wraz ze podwojeniem liczby wierszy w źródłowej tabeli geobazy, czas wykonania powyższego skryptu również się wydłuża. Oczywiście nadal musisz używać 64-bitowego Pythona, ponieważ podczas wykonywania niektóre większe struktury danych będą obsługiwane w pamięci.źródło
Lookup
aby utworzyć raster na podstawie wartości w nowej kolumnie. Moja metoda miała wiele niepotrzebnych kroków i nieefektywnego przepływu pracy, powinienem był o tym wspomnieć w moim pierwotnym pytaniu. Żyj i ucz się. Wypróbuję twój skrypt później w tym tygodniu.