¿Seleccionar características por atributo si está en la lista de Python?

14

Estoy tratando de completar una selección por atributo en Python, pero en función de la consulta de si un atributo está presente en una lista.

Tal consulta en su forma más simple debería ser algo como esto:

qry = " \"OBJECTID\" in oid_list"
arcpy.SelectLayersByAttribute_management(inft, "NEW_SELECTION", qry)

pero ese enfoque devuelve un error de expresión no válida.

En el pasado, tuve que usar una sintaxis más complicada para este tipo de consulta, como por ejemplo:

sqlQuery2 = "nid in (" + ','.join(["'"+x+"'" for x in delta_list]) +")"

pero una adaptación de este fragmento tampoco parece funcionar para mí, es decir:

 "OBJECTID_1 in (" + ','.join(["'"+str(x)+"'" for x in oid_list]) +")"

¿Que me estoy perdiendo aqui?

jsnider
fuente

Respuestas:

16

Su consulta original podría haberse modificado para obtener una lista de enteros:

'"OBJECTID_1" IN ' + str(tuple(oid_list))

entonces si oid_list = [7, 9, 4, 8], entonces el resultado es:

"OBJECTID_1" IN (7, 9, 4, 8)

Tenga en cuenta que este "truco" funciona si oid_listsiempre tiene dos o más elementos, ya que otras tuplas válidas, como ()o (7,), generarán un error de sintaxis SQL.

Una expresión más genérica que también manejaría cero o un oid_listelemento sería:

'"OBJECTID_1" IN ({0})'.format(', '.join(map(str, oid_list)) or 'NULL')
Mike T
fuente
No me di cuenta de que la interfaz de selección ArcGIS admitía 'IN'. Esto es probablemente más eficiente que mi solución.
AHigh
1
Solo tenga cuidado, hay un límite superior que es compatible con la consulta IN Creo que son 2000 registros
Tristan Forward
9

Aquí hay una versión ligeramente modificada de la función en esta respuesta , para aceptar una lista de Python en lugar de una cadena delimitada por punto y coma:

def buildWhereClauseFromList(table, field, valueList):
    """Takes a list of values and constructs a SQL WHERE
    clause to select those values within a given field and table."""

    # Add DBMS-specific field delimiters
    fieldDelimited = arcpy.AddFieldDelimiters(arcpy.Describe(table).path, field)

    # Determine field type
    fieldType = arcpy.ListFields(table, field)[0].type

    # Add single-quotes for string field values
    if str(fieldType) == 'String':
        valueList = ["'%s'" % value for value in valueList]

    # Format WHERE clause in the form of an IN statement
    whereClause = "%s IN(%s)" % (fieldDelimited, ', '.join(map(str, valueList)))
    return whereClause
blah238
fuente
6

Creo que el enfoque más directo para esto es iterar a través de los valores en su lista de manera singular y agregarlos a la selección (para que pueda cambiar su consulta con cada valor en la lista). Algo como esto:

oidList = [1,2,3,4]
arcpy.management.MakeFeatureLayer(thisFC,thisLyr)
for values in oidList:
    query = "\"OBJECTID\"="+str(values)
    arcpy.management.SelectLayerByAttribute(thisLyr,"ADD_TO_SELECTION",query)

Puede usar ADD_TO_SELECTION incluso si no hay características seleccionadas, creará una nueva selección en la primera iteración.

Editar:

Si cree que el costo de hacer SelectLayerByAttribute individual será demasiado alto, podría usar un enfoque como este en el que cree una cláusula de selección bastante grande dependiendo de la longitud de su lista:

oidList = [1,2,3,4]
arcpy.management.MakeFeatureLayer(thisFC,thisLyr)
query=""
q=""
oidList.sort()
for x in oidList:
    query="\"OBJECTID\"="+str(x)+" OR "+q
    q=query
q=q[1:-4]
arcpy.management.SelectLayerByAttribute(thisLyr,"NEW_SELECTION",q)
Un alto
fuente
Una idea interesante para recorrer los valores y realizar una selección por atributo para cada iteración. Probaré esto, pero estoy bastante seguro de que esto debería funcionar. Gracias.
jsnider
Esto parece estar funcionando, pero ciertamente tomará tiempo para procesar cada selección individual para listas más largas.
jsnider
2
Se actualizó la respuesta con un enfoque diferente.
AHigh
Buena idea con la respuesta actualizada. Elegí usar este enfoque ya que es mucho más rápido para procesar listas más grandes. Ligeramente modificado: q = "" para x en oid_set: query = '"OBJECTID_1" =' + str (x) + 'OR' q = query q = q [1: -4] y luego selectbyattribute. ¡Parece funcionar!
jsnider
Actualizaré mi respuesta con el enfoque elegido para que se analice y sea más fácil de leer. Me alegro de que funcionó.
AHigh