I metodi più veloci per modificare le tabelle degli attributi con Python?


12

Qualche tempo fa, ho scritto una rapida funzione Python per convertire una tabella di attributi in un dizionario python, in cui la chiave è presa da un campo ID univoco specificato dall'utente (in genere il campo OID). Inoltre, per impostazione predefinita tutti i campi vengono copiati nel dizionario, ma ho incluso un parametro che consente di specificare solo un sottoinsieme.

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

Funziona benissimo per set di dati relativamente piccoli, ma l'ho appena eseguito su una tabella contenente circa 750.000 righe e 15 campi - circa 100 MB in un geodatabase di file. Su questi, la funzione funziona molto più lentamente di quanto mi aspettassi: circa 5-6 minuti (e questo dopo aver copiato la tabella in_memorynell'area di lavoro). Mi piacerebbe davvero trovare un modo per accelerare la conversione in dizionario o ottenere informazioni su una strategia migliore per la manipolazione di grandi quantità di dati di attributi utilizzando Python.

UpdateCursors non funzionerà bene per me, perché quando cambia una riga, ha il potenziale per innescare cambiamenti in molti altri. Effettuare il ciclo e processarli uno alla volta è troppo ingombrante per quello di cui ho bisogno.


2
Il fattore limitante in quanto è possibile ottimizzare lo script potrebbe essere il tempo necessario per scorrere il cursore. Hai confrontato il tempo necessario per scorrere il cursore senza creare dizionari?
Jason,

2
@Jason commentando le righe da subdict = {}attraverso del subdictproduce un tempo di elaborazione di circa 10 secondi.
nmpeterson

Probabilmente ne sai più di me, ma l'unica altra cosa che vorrei offrire in termini di ottimizzazione è vedere se chiamare subdict[field] = row[cursor_fields.index(field)]è più veloce di chiamare subdict[field] = row.getValue(field). In quest'ultimo scenario eseguiresti un passo ... anche se la differenza di prestazioni tra l'indicizzazione di due elenchi ( cursor_fieldse row) e l'utilizzo di un singolo processo ESRI potrebbe non essere molto migliore e potrebbe anche peggiorare!
Jason,

Risposte:


16

Penso che il problema sia probabilmente le tue due righe in cui stai andando oltre i campi e aggiungendo ogni campo singolarmente al tuo subdictdizionario.

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

Il tuo rowoggetto è già una tupla nello stesso ordine dei campi, approfittane e usa la zipfunzione.

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

Ciò ha funzionato attraverso una classe di funzionalità di geodatabase di file di campo 16 record 218k in 8 secondi sul mio sistema.

Modifica: ho provato un test più rigoroso. 518k record su una connessione SDE remota con 16 campi tra cui OBJECTID e Shape, eseguiti a 32 bit. 11 secondi :)


1
Si noti che ho creato key_fieldil primo campo in modo da poter fare affidamento sull'utilizzo row[0]per fare riferimento al valore di key_field. Ho anche dovuto cambiare la tua variabile dictin attdict. dict è una parola chiave e senza quella parola chiave non potrei usaredict(zip())
blord-castillo

6
Intelligente. Questo è esattamente il tipo di dolce pitone idiomatico che arcpy.dadovrebbe abilitare.
Jason Scheirer,

Ottima intuizione. Adoro il metodo e mi ha davvero aiutato.
nmpeterson
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.