Самые быстрые методы для модификации таблиц атрибутов с помощью Python?

12

Некоторое время назад я написал быструю функцию Python для преобразования таблицы атрибутов в словарь Python, где ключ берется из указанного пользователем поля уникального идентификатора (обычно это поле OID). Кроме того, по умолчанию все поля копируются в словарь, но я включил параметр, позволяющий указать только подмножество.

def make_attribute_dict(fc, key_field, attr_list=['*']):
    dict = {}
    fc_field_objects = arcpy.ListFields(fc)
    fc_fields = [field.name for field in fc_field_objects if field.type != 'Geometry']
    if attr_list == ['*']:
        valid_fields = fc_fields
    else:
        valid_fields = [field for field in attr_list if field in fc_fields]
    if key_field not in valid_fields:
        cursor_fields = valid_fields + [key_field]
    else:
        cursor_fields = valid_fields
    with arcpy.da.SearchCursor(fc, cursor_fields) as cursor:
        for row in cursor:
            key = row[cursor_fields.index(key_field)]
            subdict = {}
            for field in valid_fields:
                subdict[field] = row[cursor_fields.index(field)]
            dict[key] = subdict
            del subdict
    return dict

Это прекрасно работает для сравнительно небольших наборов данных, но я просто запустил его на таблице, содержащей около 750 000 строк и 15 полей - около 100 МБ в файловой базе геоданных. На них функция работает намного медленнее, чем я ожидал: около 5-6 минут (и это после копирования таблицы в in_memoryрабочую область). Я действительно хотел бы найти способ ускорить преобразование в словарь или получить представление о лучшей стратегии для манипулирования большими объемами данных атрибутов с использованием Python.

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

nmpeterson
источник
2
Ограничивающим фактором в том, насколько вы можете оптимизировать ваш скрипт, может быть количество времени, необходимое для итерации вашего курсора. Вы сравнили время, необходимое для перебора курсора без создания словарей?
Джейсон
2
@ Джейсон, закомментировавший строки из subdict = {}сквозного, del subdictдает время обработки около 10 секунд.
nmpeterson
Вы, вероятно, знаете об этом больше, чем я, но единственное, что я хотел бы предложить в плане оптимизации, это посмотреть, subdict[field] = row[cursor_fields.index(field)]быстрее ли вызов, чем вызов subdict[field] = row.getValue(field). В последнем сценарии вы выполняете один шаг ... хотя разница в производительности между индексированием двух списков ( cursor_fieldsи row) и использованием одного процесса ESRI может быть не намного лучше, а может быть, даже хуже!
Джейсон

Ответы:

16

Я думаю, что проблема, скорее всего, в ваших двух строках, где вы перебираете поля и добавляете каждое поле отдельно в свой subdictсловарь.

for field in valid_fields:
    subdict[field] = row[cursor_fields.index(field)]

Ваш rowобъект уже является кортежем в том же порядке, что и ваши поля, воспользуйтесь этим и используйте zipфункцию.

def make_attribute_dict(fc, key_field, attr_list=['*']):
    attdict = {}
    fc_field_objects = arcpy.ListFields(fc)
    fc_fields = [field.name for field in fc_field_objects if field.type != 'Geometry']
    if attr_list == ['*']:
        valid_fields = fc_fields
    else:
        valid_fields = [field for field in attr_list if field in fc_fields]
    #Ensure that key_field is always the first field in the field list
    cursor_fields = [key_field] + list(set(valid_fields) - set([key_field]))
    with arcpy.da.SearchCursor(fc, cursor_fields) as cursor:
        for row in cursor:
            attdict[row[0]] = dict(zip(cursor.fields,row))
    return attdict

Это провалилось через 88 секунд в моей системе через 218 тыс. Записей файлового класса базы геоданных.

Изменить: попробовал более строгий тест. 518k записей по удаленному соединению SDE с 16 полями, включая OBJECTID и Shape, работающими в 32-битном режиме. 11 секунд :)

blord-Castillo
источник
1
Обратите внимание, что я сделал key_fieldпервое поле, чтобы можно было использовать row[0]для ссылки на значение key_field. Я также должен был изменить вашу переменную dictна attdict. dict - это ключевое слово, и без этого ключевого слова я бы не смог воспользоватьсяdict(zip())
blord-castillo
6
Умная. Это именно тот сладкий идиоматический Python, который arcpy.daпредназначен для включения.
Джейсон Шейрер
Отличное понимание. Люблю метод, и он действительно помог.
nmpeterson