Wie greife ich mit dem Cursor auf benachbarte Zeilen zu?

8

Im angehängten Screenshot enthalten die Attribute zwei interessierende Felder "a" und "b". Ich möchte ein Skript schreiben, um auf die benachbarten Zeilen zuzugreifen und einige Berechnungen durchzuführen. Um auf eine einzelne Zeile zuzugreifen, würde ich den folgenden UpdateCursor verwenden:

fc = r'C:\path\to\fc'

with arcpy.da.UpdateCursor(fc, ["a", "b"]) as cursor:
     for row in cursor:
          # Do something

Zum Beispiel möchte ich mit OBJECTID 4 die Summe der Zeilenwerte im Feld "a" neben der Zeile OBJECTID 4 (dh 1 + 3) berechnen und diesen Wert zur Zeile OBJECTID 4 im Feld "b" hinzufügen. Wie kann ich mit dem Cursor auf benachbarte Zeilen zugreifen, um diese Art von Berechnungen durchzuführen?

Geben Sie hier die Bildbeschreibung ein

Aaron
quelle

Antworten:

7

Wenn ich es wäre, würde ich die Tabelle durchlaufen, sobald ich ein Wörterbuch erstellt habe, in dem der Schlüssel OBJECTID und das Element der Wert im Feld "a" ist. Ich würde dann mit einem Aktualisierungscursor durch die Tabelle gehen und die OBJECTID erhalten, und daraus könnte ich die benachbarten Werte aus dem Wörterbuch abrufen, sie summieren und in das Feld "b" zurückschreiben.

In Ihrem Screenshot haben Sie jedoch Sonderfälle für die Zeilen 3 und 8, da sie nur eine benachbarte Zeile haben. Was würden Sie für diese beabsichtigen?

Hornbydd
quelle
+1 Dies ist eine gute Lösung, da sie so verwendet werden kann, dass das relationale Datenmodell berücksichtigt wird. In diesem Modell ist die Reihenfolge, in der Zeilen verarbeitet werden, undefiniert. Daher sollte jede Prozedur, die auf der Annahme beruht, dass sie in derselben Reihenfolge iteriert werden, die in einer Tabellenansicht angezeigt wird, als unzuverlässig betrachtet werden. Durch die Verwendung eines beliebigen Schlüssels - nicht nur OBJECTID- kann diese Lösung Nachbarn anhand der Werte dieses Schlüssels zuverlässig identifizieren. Wörterbücher unterstützen jedoch normalerweise keine "nächste" oder "vorherige" Suche. Du brauchst so etwas wie einen Trie .
whuber
4

Während Sie die Zeilen durchlaufen, müssen Sie die vorherigen Werte verfolgen. Dies ist eine Möglichkeit, dies zu tun:

 previous_value = None
 cursor = arcpy.UpdateCursor(fc, fields, sort_fields)
 for i, in_row in enumerate(cursor):
      current_value = in_row.getValue("ColName")
      if not previous_value: 
           previous_value = current_value 
           continue
       #
       # here you have access to the current and previous value
       #

       previous_value = current_value

oder, wenn die Tabelle nicht riesig ist, würde ich wahrscheinlich ein Wörterbuch wie d = {a: b} erstellen und dann im Update-Cursor auf Daten aus dem Wörterbuch zugreifen: d.get (a + 1) oder d.get (a -1) um zu rechnen ..

Matej
quelle
3

Ich habe die Antwort von @Hornbydd akzeptiert, weil sie mich zu einer Wörterbuchlösung geführt hat. Das angehängte Skript führt die folgenden Aktionen aus:

  1. Durchlaufen Sie eine FC und füllen Sie ein Wörterbuch mit key = OID und value = "MyField" mit einem SearchCursor.
  2. Starten Sie einen UpdateCursor
  3. Erstellen Sie eine Logik für die erste und letzte Zeile (dh wenn keine vorherige oder aufeinanderfolgende Zeile vorhanden ist).
  4. Verknüpfen Sie die UpdateCursor-Zeile mit der Wörterbuch-OID und dem Feldwert
  5. Mach die Verarbeitung ...

import arcpy, collections

fc = r'C:\path\to\fc'

# Populate a dictionary with key = OID and value = "a"
names = collections.defaultdict(list)

for name1, name2 in arcpy.da.SearchCursor(fc, ("OBJECTID", "a")):
    names[name1].append(name2)

# Use .values() class to access adjacent rows

with arcpy.da.UpdateCursor(fc, ["OBJECTID", "a", "b"]) as cursor:
    for row in cursor:
        # Get first and last rows for special processing
        if row[0] == names.keys()[0] or row[0] == names.keys()[-1]:
            """Note that the first and last row will need special rules because
             they cannot reference either the previous or next row.  In this case
             the features are skipped"""
            pass

        else:
            """This needs to be corrected because OID = (row[0] - 1)"""
            # Now link the dictionary names with row[0] (i.e. OBJECTID)
            x = names.values()[row[0] - 2]  # e.g. OID 2 (pre)
            y = names.values()[row[0] - 1]  # e.g. OID 3 (current row)
            z = names.values()[row[0]]      # e.g. OID 4 (post)

            # Now do the calculation
            row[2] = x + z
            cursor.updateRow(row)
Aaron
quelle
Ich verstehe nicht wie: wenn Zeile [0] == names.keys () [0] oder Zeile [0] == names.keys () [- 1]: funktionieren würde? Die Werte im defaultdict sind nicht geordnet, oder?
Ianbroad
2

Das Datenzugriffsmodul ist recht schnell und Sie können ein erstellen SearchCursor, um alle Werte von 'a' in einer Liste zu speichern. Erstellen Sie dann ein UpdateCursor, um jede Zeile zu durchlaufen, und wählen Sie aus der Liste aus, um die erforderlichen 'b'-Zeilen zu aktualisieren. Auf diese Weise müssen Sie sich keine Gedanken über das Speichern von Daten zwischen Zeilen machen =)

Also so etwas wie das:

fc = r'C:\path\to\fc'
fields = ["a","b"]
aList = []
index = 0

with arcpy.da.SearchCursor(fc,fields) as cursor:
     for row in cursor:
         aList.append(row[0])    # This saves each value of 'a'
with arcpy.da.UpdateCursor(fc,fields) as cursor:
     for row in cursor:
         if index-1 > 0 AND index+1 <= len(aList):     # Keeps 'b' null if out of list range 
                                                       # or at end of list
             row[1] = aList[index-1]+aList[index+1]
         index += 1

Dies ist eine ziemlich grobe Lösung, aber ich habe sie erst kürzlich verwendet, um ein sehr ähnliches Problem zu umgehen. Wenn der Code hoffentlich nicht funktioniert, sind Sie auf dem richtigen Weg!

Edit: Letzte if-Anweisung von AND nach OR geändert. Edit2: Zurück geändert. Ahh der Druck von meinem ersten StackExchange-Beitrag!

römisch
quelle
Anstatt meine Antwort ein anderes Mal zu bearbeiten, gebe ich meine $ 0,02 an, warum ich denke, dass dies eine gute Lösung für das Problem ist. Das Durchlaufen von Zeilen macht es wirklich einfach, Listen auch mit Index + = 1 zu durchlaufen. Obwohl ich nicht viel über die Mechanik des Codes nachgedacht habe, sollte es den Punkt vermitteln, den ich Ihnen machen wollte. Viel Glück!
Roman
1
Einige Anmerkungen: 1) Bei großen Datenmengen kann es schneller sein, wenn Sie ein Listenverständnis zum Füllen verwenden, aListanstatt jeden Eintrag anzuhängen. 2) Verwenden Sie enumerate()anstelle eines separaten Zählers für den Index.
Paul
-1

Zuerst benötigen Sie einen Suchcursor. Ich glaube nicht, dass Sie mit einem Update-Cursor Werte erhalten können. Verwenden Sie dann in jeder Iteration aNext = row.next (). GetValue ('a'), um den Wert der nächsten Zeile abzurufen.

Um den Wert der vorherigen Zeile zu erhalten, würde ich eine Variable außerhalb der for-Schleife gleich Null einrichten. Dies wird aktualisiert, um dem aktuellen Zeilenwert von 'a' zu entsprechen. Sie können dann in der nächsten Iteration auf diese Variable zugreifen.

Dies würde dann Ihre Gleichung von B = A (Zeilen-ID-1) + A (Zeilen-ID + 1) erfüllen.

user25074
quelle
Sie müssen nicht zu einem Suchcursor wechseln, um getValue zu verwenden. Es ist eine Methode des ROW-Objekts, nicht des CURSOR-Objekts.
Msayler
Eigentlich verwenden sie DA-Cursor, daher glaube ich nicht, dass getValue überhaupt zutrifft. Ich denke, DA-Cursor verwenden stattdessen Indizes.
Msayler
1
Update-Cursor können Daten lesen und schreiben. (Nützlich zum Beispiel, um einen Wert aus zu lesen fieldAund einen neuen Wert für zu berechnen fieldB.)
Erica