カーソルで隣接する行にアクセスする方法は?


8

添付のスクリーンショットでは、属性に2つの対象フィールド「a」と「b」が含まれています。いくつかの計算を行うために、隣接する行にアクセスするスクリプトを記述したいと思います。単一の行にアクセスするには、次のUpdateCursorを使用します。

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

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

たとえば、OBJECTID 4の場合、OBJECTID 4行に隣接するフィールド「a」の行の値の合計(つまり、1 + 3)を計算し、その値を「b」フィールドのOBJECTID 4行に追加します。カーソルで隣接する行にアクセスして、このような計算をするにはどうすればよいですか?

ここに画像の説明を入力してください

回答:


7

これが私である場合、keyがOBJECTIDであり、itemがフィールド "a"の値であるディクショナリを作成してから、テーブルを通過します。次に、OBJECTIDを取得する更新カーソルを使用してテーブルをステップ実行し、そこから辞書から隣接する値を取得し、それらを合計してフィールド "b"に書き戻します。

ただし、スクリーンショットでは、行3と8に隣接する行が1つしかないため、特別なケースがあります。これらに対して何を意図しますか?


+1リレーショナルデータモデルを尊重する方法で使用できるため、これは優れたソリューションです。このモデルでは、行が処理される順序は定義されていないため、テーブルビューに表示されるのと同じ順序で行が反復されるという前提に依存する手順は、信頼できないと見なされます。単なるキーではなく任意のキーを使用することにより、このOBJECTIDソリューションは、そのキーの値に従って近隣を確実に識別できます。ただし、辞書は通常「次の」または「前の」ルックアップをサポートしていません。あなたのような何か必要なトライを
whuber

4

行をループしている間、以前の値を追跡する必要があります。これはそのための1つの方法です。

 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

または、テーブルが巨大でない場合、おそらくd = {a:b}のようなディクショナリを作成し、更新カーソルでディクショナリからデータにアクセスします:d.get(a + 1)またはd.get(a -1)数学をする


3

@Hornbyddの回答を受け入れて、辞書ソリューションに導いた。添付されたスクリプトは、次のアクションを実行します。

  1. FCをループして、SearchCursorを使用して、キー= OIDおよび値= "MyField"のディクショナリを入力します。
  2. UpdateCursorを開始する
  3. 最初と最後の行を処理するロジックを作成します(つまり、前の行または連続した行がない場合)
  4. UpdateCursor行をディクショナリOIDおよびフィールド値にリンク
  5. 処理を行います...

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)

方法がわかりません。row[0] == names.keys()[0]またはrow [0] == names.keys()[-1]の場合:機能しますか?defaultdictの値は順序付けされていませんか?
ianbroad 14

2

データアクセスモジュールは非常に高速で、a SearchCursorを作成してリストにすべての値を保存し、次にを作成しUpdateCursorて各行を反復処理し、リストから選択して必要な「b」行を更新できます。これにより、行間でデータを保存することを心配する必要がなくなります=)

だからこのようなもの:

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

これはかなり大雑把な解決策ですが、私は最近、非常によく似た問題を回避するために使用しました。コードがうまく行かない場合は、正しい方向に進みます。

編集:最後のifステートメントをANDからORに変更Edit2:元に戻しました。私の最初のStackExchange投稿からのプレッシャーです!


もう一度私の回答を編集する代わりに、これが問題の良い解決策であると私が思う理由について、私の$ 0.02をあげます。行を反復処理すると、インデックス+ = 1を使用してリストを反復処理するのが非常に簡単になります。コードの機構についてはあまり考えていませんでしたが、私が伝えたかった点を伝える必要があります。幸運を!
ローマ14

1
注意事項:1)aList各エントリを追加する代わりにリスト内包表記を使用して入力すると、大規模なデータセットの方が高速になる場合があります。2)enumerate()インデックス用に別のカウンターを持つ代わりに使用します。
Paul

-1

まず、検索カーソルが必要です。更新カーソルでは値を取得できないと思います。次に、各反復でaNext = row.next()。getValue( 'a')を使用して、次の行の値を取得します。

前の行の値を取得するには、forループの外側の変数をゼロに設定します。これは、現在の行の値「a」と等しくなるように更新されます。その後、次の反復でこの変数にアクセスできます。

これは、B = A(rowid-1)+ A(rowid + 1)の方程式を満たします。


getValueを使用するために検索カーソルに切り替える必要はありません。これはROWオブジェクトのメソッドであり、CURSORオブジェクトではありません。
msayler 2014

実際、DAカーソルを使用しているので、getValueが適用されるとは思いません。DAカーソルは代わりにインデックスを使用すると思います。
msayler 2014

1
更新カーソルは、データの読み取りと書き込みを行うことができます。(たとえば、から値を読み取り、fieldAそれを使用しての新しい値を計算するのに役立ちますfieldB。)
Erica
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.