Python - Как создать и получить доступ к большому набору данных, не увеличивая объем памяти и не останавливая обработку - PullRequest
1 голос
/ 20 октября 2011

Я пишу скрипт на Python для данных контроля качества в собственной таблице базы данных ESRI. Цель сценария не в том, чтобы изменить недопустимые данные, а просто в том, чтобы сообщить пользователю о недопустимых данных через файл csv. Я использую пакет ESRI ArcPy для доступа к каждой отдельной записи с помощью arcpy.SearchCursor. SearchCursor - единственный способ получить доступ к каждой отдельной записи в форматах ESRI.

Когда я прокручиваю каждую запись таблиц, я делаю несколько проверок QC для проверки конкретной бизнес-логики. Одна из этих проверок ищет дубликаты данных в определенных полях. Одним из этих полей может быть геометрия. Я сделал это, создав пустой контейнерный объект для каждого из этих полей, и при проверке каждой записи я использую следующую логику.

for field in dupCheckFields:
    if row.getValue(field) in fieldValues[field]: dupValues.add(row.getValue(idField))
    else: fieldValues[field].append(row.getValue(field))

Приведенный выше код является примером базовой логики, которую я использую. Проблема в том, что каждая из этих таблиц может содержать от 5000 до 10 миллионов записей. У меня либо не хватает памяти, либо спектакль останавливается.

Я пробовал следующие типы контейнеров: наборы, списки, словари, ZODB + BList и Shelve.

С типами в памяти (наборы, списки, словари) процесс запускается очень быстро в начале, но по мере продвижения он становится намного медленнее. Мы эти типы, если у меня будет много записей в таблице, мне не хватит памяти. С постоянными типами данных у меня не хватает памяти, но обработка занимает очень много времени.

Мне нужны только данные во время работы скрипта, и все постоянные файлы данных будут удалены после завершения.

Вопрос. Существует ли лучший тип контейнера для хранения большого объема данных с малым объемом памяти без значительного снижения производительности при доступе к данным?

Система: Win7 64-разрядная, Python 2.6.5 32-разрядная, 4 ГБ ОЗУ

Заранее спасибо за помощь.

EDIT:

Пример кода SQLite:

import sqlite3, os, arcpy, timeit

fc = r"path\to\feature\class"

# test feature class was in ESRI ArcSDE format and contained "." characters separating database name, owner, and feature class name
fcName = fc.split(".")[-1]

# convert ESRI data types to SQLite data types
dataTypes = {"String":"text","Guid":"text","Double":"real","SmallInteger":"integer"}

fields = [(field.name,dataTypes[field.type]) for field in arcpy.ListFields(fc) if field.name != arcpy.Describe(fc).OIDFieldName]

# SQL string to create table in SQLite with same schema as feature class
createTableString = """create table %s(%s,primary key(%s))""" % (fcName,",\n".join('%s %s' % field for field in fields),fields[0][0])

# SQL string to insert data into SQLite table
insertString = """insert into %s values(%s)""" % (fcName, ",".join(["?" for i in xrange(len(fields))]))

# location to save SQLite database
loc = r'C:\TEMPORARY_QC_DATA'

def createDB():
    conn = sqlite3.connect(os.path.join(loc,'database.db'))
    cur = conn.cursor()

    cur.execute(createTableString)

    conn.commit()

    rows = arcpy.SearchCursor(fc)

    i = 0
    for row in rows:
        try:
            cur.execute(insertString, [row.getValue(field[0]) for field in fields])
            if i % 10000 == 0:
                print i, "records"
                conn.commit()
            i += 1
        except sqlite3.IntegrityError: pass
    print i, "records"

t1 = timeit.Timer("createDB()","from __main__ import createDB")

print t1.timeit(1)

К сожалению, я не могу поделиться тестовыми данными, которые я использовал с этим кодом, однако это была таблица базы геоданных ESRI ArcSDE, содержащая ок. 10 полей и ок. 7 миллионов записей.

Я пытался использовать timeit, чтобы определить, сколько времени занял этот процесс, однако после 2 часов обработки было завершено только 120 000 записей.

Ответы [ 2 ]

0 голосов
/ 20 октября 2011

Я думаю, что я бы оценил хранение постоянных данных (таких как известные значения полей и счетчики) в базе данных SQLite. Это, конечно, компромисс между использованием памяти и производительностью.

Если вы используете механизм персистентности, который поддерживает параллельный доступ, вы, вероятно, можете распараллелить обработку ваших данных, используя multiprocessing . После завершения из базы данных можно сгенерировать сводку ошибок.

0 голосов
/ 20 октября 2011

Если вы храните хэши в (сжатых) файлах, вы можете просматривать их в потоковом режиме, сравнивать хэши и искать дубликаты.Потоковая передача обычно имеет очень низкие требования к памяти - вы можете установить желаемый буфер, скажем, одну строку для каждой хешированной записи.Как правило, компромисс - это время, особенно если вы добавляете сжатие, но если вы упорядочиваете файлы по некоторым критериям, то вы можете пройтись по несжатым потокам, чтобы быстрее сравнить записи.

...