У меня есть таблица с 8 столбцами и ~ 16,7 миллионов записей. Мне нужно запустить набор уравнений if-else для столбцов. Я написал скрипт с использованием модуля UpdateCursor, но после нескольких миллионов записей ему не хватает памяти. Мне было интересно, есть ли лучший способ обработать эти 16,7 миллиона записей.
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"
ОБНОВЛЕНИЕ № 1
Я запустил тот же сценарий на компьютере с 40 ГБ ОЗУ (исходный компьютер имел только 12 ГБ ОЗУ). Успешно завершено через ~ 16 часов. Я чувствую, что 16 часов - это слишком долго, но я никогда не работал с таким большим набором данных, поэтому не знаю, чего ожидать. Единственное новое дополнение к этому скрипту arcpy.env.parallelProcessingFactor = "100%"
. Я пробую два предложенных метода (1) сделать миллион записей в пакетном режиме и (2) использовать SearchCursor и записать результаты в csv. Я сообщу о прогрессе в ближайшее время.
ОБНОВЛЕНИЕ № 2
Обновление SearchCursor и CSV сработало великолепно! У меня нет точного времени выполнения, я обновлю сообщение, когда завтра нахожусь в офисе, но я бы сказал, что приблизительное время выполнения составляет ~ 5-6 минут, что довольно впечатляет. Я не ожидал этого. Я делюсь своим неполированным кодом, любые комментарии и улучшения приветствуются:
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"
ОБНОВЛЕНИЕ № 3 Окончательное обновление. Общее время выполнения сценария составляет ~ 199,6 секунд / 3,2 минуты.
источник
Ответы:
Вы можете записать Objectid и результат расчета (cate_2) в файл CSV. Затем присоедините CSV к исходному файлу, заполните поле, чтобы сохранить результат. Таким образом, вы не обновляете таблицу с помощью курсора DA. Вы можете использовать курсор поиска.
источник
Извините, если я продолжу возрождать эту старую ветку. Идея заключалась в том, чтобы выполнить операторы if-else для объединенного растра, а затем использовать новое поле в Lookup для создания нового растра. Я усложнил проблему, экспортировав данные в виде таблицы, и представил неэффективный рабочий процесс, на который обратился @Alex Tereshenkov. Поняв очевидное, я разбил данные на 17 запросов (по 1 миллиону каждый), как это было предложено @FelixIP. В среднем на каждую партию ушло ~ 1,5 минуты, а общее время работы составило ~ 23,3 минуты. Этот метод устраняет необходимость в объединениях, и я думаю, что этот метод лучше всего решает задачу. Вот пересмотренный скрипт для дальнейшего использования:
источник
Lookup
и экспортировать растр с новыми определенными категориями.arcpy.env.parallelProcessingFactor = "100%"
которое не влияет на ваш сценарий. Я не вижу никаких инструментов, которые могли бы использовать эту среду.Вы можете попробовать перейти на использование CalculateField_management . Это позволяет избежать циклического использования курсоров, и, судя по вашим параметрам для значения категории, вы можете настроить это как четыре подпроцесса, порожденных последовательно. По завершении каждого подпроцесса его память освобождается перед запуском следующего. Вы выполняете небольшое (миллисекунды) попадание, порождая каждый подпроцесс.
Или, если вы хотите сохранить свой текущий подход, используйте подпроцесс, который принимает по x строк одновременно. Иметь основной процесс для управления им, и, как и прежде, вы продолжаете ломать свою память каждый раз, когда она заканчивается. Преимущество такого подхода (особенно через автономный процесс Python) заключается в том, что вы можете более активно использовать все свои ядра в качестве порождающих подпроцессов в многопоточности Python, которую вы получаете вокруг GIL. Это возможно с ArcPy и подходом, который я использовал в прошлом, чтобы делать большие объемы данных. Очевидно, что ваши куски данных будут неактивными, иначе у вас закончится нехватка памяти быстрее!
источник
Логика манипулирования данными может быть записана в виде оператора SQL UPDATE с использованием выражения CASE, которое можно выполнить с помощью GDAL / OGR, например, через OSGeo4W с
gdal-filegdb
установленным.Вот рабочий процесс, который использует
osgeo.ogr
вместоarcpy
:Для аналогичной таблицы, содержащей чуть более 1 миллиона записей, этот запрос занял 18 минут. Таким образом, обработка 16 миллионов записей может занять от 4 до 5 часов.
источник
arcpy
но я ценю ответ. Я медленно пытаюсь использовать GDAL больше.Обновление кода в разделе № 2 по вашему вопросу не показывает, как вы присоединяете
.csv
файл обратно к исходной таблице в вашей файловой базе геоданных. Вы говорите, что запуск вашего сценария занял ~ 5 минут. Это звучит справедливо, если вы только экспортировали.csv
файл без каких-либо соединений. Когда вы попытаетесь вернуть.csv
файл в ArcGIS, у вас возникнут проблемы с производительностью.1) Вы не можете делать соединения напрямую с
.csv
таблицей базы геоданных, потому что.csv
файл не имеет OID (поле, рассчитанное с уникальными значениями, не поможет, так как вам все равно потребуется преобразовать ваш.csv
файл в таблицу базы геоданных). Итак, несколько минут дляTable To Table
инструмента GP (вы можете использоватьin_memory
рабочее пространство для создания там временной таблицы, будет немного быстрее).2) После загрузки
.csv
в таблицу базы геоданных вы захотите построить индекс для поля, в котором вы будете выполнять соединение (в вашем случае исходное значениеobjectid
vaue из.csv
файла. Это займет несколько минут для таблицы с 16 млн строк).3) Тогда вам нужно будет использовать
Add Join
либоJoin Field
инструменты GP, либо Ни один из них не будет хорошо работать на ваших больших столах.4) После этого вам нужно использовать
Calculate Field
инструмент GP для вычисления вновь соединенных полей. Много минут здесь; Более того, вычисление полей занимает больше времени, когда поля, которые участвуют в вычислении, поступают из объединенной таблицы.Одним словом, вы не получите ничего близкого к 5 минутам, которые вы упомянули. Если вы сделаете это через час, я был бы впечатлен.
Чтобы избежать обработки больших наборов данных в ArcGIS, я предлагаю перенести ваши данные вне ArcGIS во
pandas
фрейм данных и выполнить все ваши расчеты там. Когда вы закончите, просто запишите строки фрейма данных обратно в новую таблицу базы геоданных с помощьюda.InsertCursor
(или вы можете усечь существующую таблицу и записать свои строки в исходную).Полный код, который я написал для тестирования, приведен ниже:
Ниже приведены выходные данные отладочного ввода-вывода (количество сообщений - это количество строк в используемой таблице) с информацией о времени выполнения для отдельных функций:
Вставка строки с помощью
da.InsertCursor
занимает постоянное время, то есть если для вставки 1 строки требуется, скажем, 0,1 секунды, для вставки 100 строк потребуется 10 секунд. К сожалению, 95% + общего времени выполнения тратится на чтение таблицы базы геоданных и последующую вставку строк обратно в базу геоданных.То же самое применимо к созданию
pandas
кадра данных изda.SearchCursor
генератора и к вычислению поля (полей). Поскольку число строк в вашей исходной таблице базы геоданных удваивается, увеличивается и время выполнения приведенного выше сценария. Конечно, вам все равно нужно использовать 64-битный Python, так как во время выполнения некоторые большие структуры данных будут обрабатываться в памяти.источник
Lookup
создание растра на основе значений в новом столбце. В моем методе было много ненужных шагов и неэффективный рабочий процесс, я должен был упомянуть об этом в своем первоначальном вопросе. Живи и учись. Я попробую ваш сценарий позже на этой неделе.