Effizientes Auswählen verwandter Datensätze mit ArcPy?

14

Im Folgenden finden Sie den Code, mit dem ich die Schaltfläche "Verwandte Tabellen" in ArcMap repliziere. In ArcMap werden mit dieser Schaltfläche Features in einer Feature-Class oder Tabelle basierend auf der Auswahl von Features in einer anderen verwandten Feature-Class oder Tabelle ausgewählt.

In ArcMap kann ich diese Schaltfläche verwenden, um meine Auswahl in Sekundenschnelle in die zugehörige Tabelle zu verschieben. Ich konnte nichts in arcpy eingebautes finden, das die Schaltfläche repliziert, daher habe ich einige verschachtelte Schleifen verwendet, um dieselbe Aufgabe auszuführen.

Der folgende Code durchläuft eine Tabelle mit "Behandlungen". Für jede Behandlung durchläuft es eine Liste von "Bäumen". Wenn eine Übereinstimmung zwischen den ID-Feldern der Behandlung und den Bäumen gefunden wird, erfolgt eine Auswahl in der Baumebene. Sobald eine Übereinstimmung für eine Behandlung gefunden wurde, durchsucht der Code die Baumebene nicht mehr nach zusätzlichen Übereinstimmungen. Es kehrt zur Behandlungstabelle zurück, wählt die nächste Behandlung aus und durchsucht erneut die Tree-Feature-Class.

Der Code selbst funktioniert gut, ist aber quälend langsam. Die "Behandlungstabelle" hat in diesem Fall 16.000 Datensätze. Die Feature-Class "tree" enthält 60.000 Datensätze.

Gibt es eine andere effizientere Methode, um das, was ESRI tut, neu zu erstellen, wenn die Auswahl von einer Tabelle in eine andere verschoben wird? Soll ich einen Index für die Tabellen erstellen? HINWEIS: Diese Daten werden in einer SDE gespeichert.

 # Create search cursor to loop through the treatments
treatments = arcpy.SearchCursor(treatment_tv)
treatment_field = "Facility_ID"

for treatment in treatments:

    #Get ID of treatment
    treatment_ID = treatment.getValue(treatment_field)

    # Create search cursor for looping through the trees
    trees = arcpy.SearchCursor(tree_fl)
    tree_field = "FACILITYID"

    for tree in trees:

        # Get FID of tree
        tree_FID = tree.getValue(tree_field)

        if tree_FID == treatment_FID:
            query = "FACILITYID = " + str(tree_FID)
            arcpy.SelectLayerByAttribute_management(tree_fl, "REMOVE_FROM_SELECTION", query)
            break
WatsonP
quelle
2
Verwenden Sie ArcGIS 10.1? In diesem Fall ist arcpy.da.SearchCursor wahrscheinlich viel schneller (möglicherweise 10x) als arcpy.SearchCursor. Möglicherweise möchten Sie auch die Verwendung eines Python-Wörterbuchs in Betracht ziehen. Ich vermute, dass eine "Schlüsseldateiauswahl" wie diese stark von dem hier
PolyGeo
Ist Ihre SDE-Datenbank zufällig auf Oracle?
blah238

Antworten:

12

Zuallererst sollten Sie auf jeden Fall sicherstellen, dass Ihre Primär- und Fremdschlüsselfelder in beiden Tabellen indiziert sind. Dadurch kann das DBMS Abfragen für diese Felder wesentlich effizienter planen und ausführen.

Zweitens rufen Sie SelectLayerByAttribute_managementeine enge, verschachtelte Schleife auf (einmal pro Baum und Behandlung). Dies ist aus mehreren Gründen äußerst ineffizient:

  • Sie brauchen dafür keine zwei ineinander verschachtelten Schleifen, soweit ich das beurteilen kann. Einer wird ausreichen.
  • Geoverarbeitungsfunktionen sind "klobig" und benötigen im Vergleich zu den typischen integrierten Python-Funktionen viel Zeit zum Aufrufen. Sie sollten vermeiden, sie in einer engen Schleife anzurufen.
  • Das gleichzeitige Abfragen eines Datensatzes / einer ID führt zu erheblich mehr Roundtrips in der Datenbank.

SelectLayerByAttribute_managementKorrigieren Sie stattdessen Ihren Code so, dass Sie ihn nur einmal aufrufen und eine Where-Klausel verwenden, mit der alle zugehörigen Datensätze ausgewählt werden.

Wenn ich eine Funktion aus einer anderen Antwort für die Whereclause-Konstruktionslogik entliehe, würde sie ungefähr so ​​aussehen:

def selectRelatedRecords(sourceLayer, targetLayer, sourceField, targetField):
    sourceIDs = set([row[0] for row in arcpy.da.SearchCursor(sourceLayer, sourceField)])
    whereClause = buildWhereClauseFromList(targetLayer, targetField, sourceIDs)
    arcpy.AddMessage("Selecting related records using WhereClause: {0}".format(whereClause))
    arcpy.SelectLayerByAttribute_management(targetLayer, "NEW_SELECTION", whereClause)

Man könnte es so nennen: selectRelatedRecords(treatment_tv, tree_fl, "Facility_ID", "FACILITYID")

Anmerkungen:

  • Dies verwendet eine arcpy.da.SearchCursor, nur verfügbar bei 10.1. Wie @PolyGeo bereits erwähnte, sind diese Cursor viel schneller als ihre Vorgänger ( arcpy.SearchCursor). Es könnte leicht modifiziert werden, um den alten SearchCursor zu verwenden:

    sourceIDs = set([row.getValue(sourceField) for row in arcpy.SearchCursor(sourceLayer, "", "", sourceField)])
  • Wenn sich Ihre SDE-Geodatabase in Oracle befindet, müssen Sie darauf hinweisen, dass die INin der Funktion aus der verknüpften Antwort verwendete Anweisung auf 1000 Elemente beschränkt ist. Eine mögliche Lösung wird in dieser Antwort beschrieben , aber Sie müssen die Funktion ändern, um sie in mehrere INAnweisungen mit einer Länge von 1000 anstelle von einer aufzuteilen .

blah238
quelle
5

Die obige Lösung funktioniert hervorragend und war sehr schnell. Unter Verwendung des obigen Codes und des referenzierten Codes aus dem anderen Beitrag habe ich Folgendes erstellt:

# Local Variables
OriginTable = "This must be a Table View or Feature Layer"
DestinationTable = "This must be a Table View or Feature Layer"
PrimaryKeyField = "Matching Origin Table Field"
ForiegnKeyField = "Matching Destination Table Field"

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

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

    # Determine field type
    fieldType = arcpy.ListFields(OriginTable, PrimaryKeyField)[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

def selectRelatedRecords(OriginTable, DestinationTable, PrimaryKeyField, ForiegnKeyField):
    """Defines the record selection from the record selection of the OriginTable
      and applys it to the DestinationTable using a SQL WHERE clause built
      in the previous defintion"""

    # Set the SearchCursor to look through the selection of the OriginTable
    sourceIDs = set([row[0] for row in arcpy.da.SearchCursor(OriginTable, PrimaryKeyField)])

    # Establishes the where clause used to select records from DestinationTable
    whereClause = buildWhereClauseFromList(DestinationTable, ForiegnKeyField, sourceIDs)

    # Process: Select Layer By Attribute
    arcpy.SelectLayerByAttribute_management(DestinationTable, "NEW_SELECTION", whereClause)

# Process: Select related records between OriginTable and DestinationTable
selectRelatedRecords(OriginTable, DestinationTable, PrimaryKeyField, ForiegnKeyField)
user1714326
quelle