Ich habe eine Tabelle mit 8 Spalten und ~ 16,7 Millionen Datensätzen. Ich muss eine Reihe von if-else-Gleichungen für die Spalten ausführen. Ich habe ein Skript mit dem UpdateCursor-Modul geschrieben, aber nach einigen Millionen Datensätzen ist der Speicher voll. Ich habe mich gefragt, ob es einen besseren Weg gibt, diese 16,7 Millionen Datensätze zu verarbeiten.
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"
UPDATE # 1
Ich habe das gleiche Skript auf einem Computer mit 40 GB RAM ausgeführt (der ursprüngliche Computer hatte nur 12 GB RAM). Es wurde nach ca. 16 Stunden erfolgreich abgeschlossen. Ich halte 16 Stunden für zu lang, aber ich habe noch nie mit einem so großen Datensatz gearbeitet, dass ich nicht weiß, was mich erwartet. Die einzige Neuerung in diesem Skript ist arcpy.env.parallelProcessingFactor = "100%"
. Ich versuche zwei vorgeschlagene Methoden (1) 1 Million Datensätze in Stapeln zu erstellen und (2) SearchCursor zu verwenden und Ausgaben in csv zu schreiben. Ich werde in Kürze über die Fortschritte berichten.
UPDATE # 2
Das SearchCursor- und CSV-Update hat hervorragend funktioniert! Ich habe nicht die genauen Laufzeiten, ich werde den Post aktualisieren, wenn ich morgen im Büro bin, aber ich würde sagen, die ungefähre Laufzeit beträgt ~ 5-6 Minuten, was ziemlich beeindruckend ist. Ich habe es nicht erwartet. Ich teile meinen unpolierten Code, Kommentare und Verbesserungen sind willkommen:
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"
UPDATE # 3 Letztes Update. Die Gesamtlaufzeit für das Skript beträgt ~ 199,6 Sekunden / 3,2 Minuten.
quelle
Antworten:
Sie können die Objectid und das Ergebnis der Berechnung (cate_2) in eine CSV-Datei schreiben. Fügen Sie dann die CSV zu Ihrer Originaldatei hinzu, füllen Sie ein Feld aus, um das Ergebnis beizubehalten. Auf diese Weise aktualisieren Sie die Tabelle nicht mit dem DA-Cursor. Sie können einen Suchcursor verwenden.
quelle
Entschuldigung, wenn ich diesen alten Thread wiederbelebe. Die Idee war, die if-else-Anweisungen für das kombinierte Raster auszuführen und dann das neue Feld in Lookup zu verwenden, um ein neues Raster zu erstellen. Ich habe das Problem dadurch erschwert, dass ich die Daten als Tabelle exportierte und einen ineffizienten Workflow einführte, der von @Alex Tereshenkov angesprochen wurde. Nachdem ich das Offensichtliche erkannt hatte, stapelte ich die Daten in 17 Abfragen (je 1 Million), wie es von @FelixIP vorgeschlagen wurde. Es dauerte durchschnittlich ~ 1,5 Minuten, bis jede Charge fertiggestellt war, und die Gesamtlaufzeit betrug ~ 23,3 Minuten. Diese Methode macht Verknüpfungen überflüssig, und ich denke, diese Methode erfüllt die Aufgabe am besten. Hier ist ein überarbeitetes Skript zum späteren Nachschlagen:
quelle
Lookup
das Raster mit neu definierten Kategorien auszuführen und zu exportieren.arcpy.env.parallelProcessingFactor = "100%"
die keinen Einfluss auf Ihr Skript hat. Ich sehe dort keine Tools, die diese Umgebung nutzen.Sie können versuchen, CalculateField_management zu verwenden . Auf diese Weise wird vermieden, dass Sie mit Cursorn durchlaufen, und anhand Ihrer Optionen für den Kategoriewert können Sie dies als vier nacheinander erzeugte Unterprozesse einrichten. Wenn jeder Unterprozess beendet ist, wird sein Speicher freigegeben, bevor der nächste gestartet wird. Sie benötigen einen kleinen Treffer (Millisekunden), um jeden Unterprozess zu starten.
Wenn Sie Ihren aktuellen Ansatz beibehalten möchten, können Sie auch einen Unterprozess verwenden, der jeweils x-Zeilen umfasst. Haben Sie einen Hauptprozess, um es zu fahren, und wie zuvor bereinigen Sie Ihr Gedächtnis jedes Mal, wenn es beendet ist. Der Vorteil dieser Vorgehensweise (vor allem durch einen eigenständigen Python-Prozess) besteht darin, dass Sie alle Ihre Kerne in Pythons Multthreading-Prozessen, die Sie in der GIL-Umgebung durchführen, besser nutzen können. Dies ist mit ArcPy und einem Ansatz möglich, mit dem ich in der Vergangenheit massive Datenabwanderungen durchgeführt habe. Offensichtlich sollten Sie Ihre Datenmengen niedrig halten, da Ihnen sonst schneller der Speicher ausgeht!
quelle
Die Datenmanipulationslogik kann als UPDATE-SQL-Anweisung mit einem CASE-Ausdruck geschrieben werden, den Sie mit GDAL / OGR ausführen können, z. B. über OSGeo4W mit
gdal-filegdb
installiertem.Hier ist der Workflow, der
osgeo.ogr
anstelle vonarcpy
:Bei einer ähnlichen Tabelle mit etwas mehr als 1 Million Datensätzen dauerte diese Abfrage 18 Minuten. Die Verarbeitung von 16 Millionen Datensätzen kann also noch ca. 4 bis 5 Stunden dauern.
quelle
arcpy
aber ich schätze die Antwort. Ich versuche langsam, GDAL mehr zu benutzen.Die Aktualisierung des Codes in Abschnitt 2 Ihrer Frage zeigt nicht, wie Sie die
.csv
Datei wieder mit der Originaltabelle in Ihrer File-Geodatabase verbinden. Sie sagen, dass das Ausführen Ihres Skripts ca. 5 Minuten gedauert hat. Dies klingt fair, wenn Sie nur die.csv
Datei exportiert haben , ohne dass Verknüpfungen vorgenommen wurden. Wenn Sie versuchen, die.csv
Datei wieder in ArcGIS zu laden, treten die Leistungsprobleme auf.1) Sie können keine direkten
.csv
Verknüpfungen mit der Geodatabase-Tabelle herstellen, da die.csv
Datei keine OID hat (ein mit eindeutigen Werten berechnetes Feld hilft nicht, da Sie Ihre.csv
Datei dennoch in eine Geodatabase-Tabelle konvertieren müssen ). Einige Minuten für dasTable To Table
GP-Tool (Sie könnten denin_memory
Arbeitsbereich verwenden, um dort eine temporäre Tabelle zu erstellen, dies ist etwas schneller).2) Nachdem Sie das
.csv
in eine Geodatabase-Tabelle geladen haben , möchten Sie einen Index für das Feld erstellen, in dem Sie den Joinobjectid
ausführen möchten (in Ihrem Fall die Quelle aus der.csv
Datei). Dies würde einige Minuten für eine Tabelle mit 16 Millionen Zeilen dauern.3) Dann müssten Sie entweder das GP-Tool
Add Join
oder dasJoin Field
GP-Tool verwenden. Beides funktioniert nicht gut an Ihren großen Tischen.4) Anschließend müssen Sie das
Calculate Field
GP-Tool ausführen, um die neu verbundenen Felder zu berechnen. Viele Minuten vergehen hier; Außerdem dauert die Feldberechnung länger, wenn die an der Berechnung beteiligten Felder aus einer verknüpften Tabelle stammen.Mit einem Wort, Sie werden in der Nähe von 5 Minuten, die Sie erwähnen, nichts bekommen. Wenn Sie es in einer Stunde schaffen, wäre ich beeindruckt.
Um zu vermeiden, dass große Datasets in ArcGIS verarbeitet werden, empfehle ich, Ihre Daten außerhalb von ArcGIS in einen
pandas
Datenrahmen zu übernehmen und alle Berechnungen dort durchzuführen. Wenn Sie fertig sind, schreiben Sie einfach die Datenrahmenzeilen zurück in eine neue Geodatabase-Tabelleda.InsertCursor
(oder Sie können Ihre vorhandene Tabelle abschneiden und Ihre Zeilen in die Quelltabelle schreiben).Der vollständige Code, den ich zum Benchmarking geschrieben habe, ist unten:
Nachfolgend finden Sie die Ausgabe des Debug-E / A (die angegebene Anzahl entspricht der Anzahl der Zeilen in einer Tabelle) mit Informationen zur Ausführungszeit für einzelne Funktionen:
Das Einfügen einer Zeile mit
da.InsertCursor
dauert konstant, dh, wenn das Einfügen einer Zeile beispielsweise 0,1 Sekunden dauert, dauert das Einfügen von 100 Zeilen 10 Sekunden. Leider werden 95% der gesamten Ausführungszeit für das Lesen der Geodatabase-Tabelle und das anschließende Einfügen der Zeilen in die Geodatabase aufgewendet.Das Gleiche gilt für die Erstellung eines
pandas
Datenrahmens aus einemda.SearchCursor
Generator und für die Berechnung der Felder. Wenn sich die Anzahl der Zeilen in Ihrer Quell-Geodatabase-Tabelle verdoppelt, verdoppelt sich auch die Ausführungszeit des obigen Skripts. Natürlich müssen Sie weiterhin 64-Bit-Python verwenden, da während der Ausführung einige größere Datenstrukturen im Arbeitsspeicher verwaltet werden.quelle
Lookup
erstelle schließlich ein Raster basierend auf den Werten in der neuen Spalte. Meine Methode hatte viele unnötige Schritte und ineffiziente Arbeitsabläufe. Ich hätte dies in meiner ursprünglichen Frage erwähnen sollen. Lebe und lerne. Ich werde Ihr Skript jedoch später in dieser Woche testen.