Como acessar linhas adjacentes com o cursor?

8

Na captura de tela em anexo, os atributos contêm dois campos de interesse "a" e "b". Quero escrever um script para acessar as linhas adjacentes para fazer alguns cálculos. Para acessar uma única linha, eu usaria o seguinte UpdateCursor:

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

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

Por exemplo, com OBJECTID 4, estou interessado em calcular a soma dos valores da linha no campo "a" adjacente à linha OBJECTID 4 (ou seja, 1 + 3) e adicionar esse valor à linha OBJECTID 4 no campo "b". Como acessar linhas adjacentes com o cursor para fazer esse tipo de cálculo?

insira a descrição da imagem aqui

Aaron
fonte

Respostas:

7

Se fosse eu, passaria pela tabela uma vez criando um dicionário em que chave é OBJECTID e item é o valor no campo "a". Depois, percorria a tabela com um cursor de atualização, obtendo o OBJECTID e, a partir disso, conseguia obter os valores adjacentes do dicionário, somar e escrevê-los no campo "b".

Mas, na sua captura de tela, há casos especiais para as linhas 3 e 8, pois eles têm apenas uma linha adjacente. O que você pretenderia para isso?

Hornbydd
fonte
+1 Esta é uma boa solução, pois pode ser usada de uma maneira que respeite o modelo de dados relacionais. Nesse modelo, a sequência na qual as linhas são processadas é indefinida; portanto, qualquer procedimento que se baseie na suposição de que será iterado na mesma ordem exibida em uma exibição de tabela deve ser considerado não confiável. Ao usar qualquer chave - e não apenas OBJECTID-, essa solução pode identificar vizinhos de maneira confiável, de acordo com os valores dessa chave. No entanto, os dicionários normalmente não oferecem suporte a uma pesquisa "próxima" ou "anterior". Você precisa de algo como um Trie .
whuber
4

Ao percorrer as linhas, você precisa acompanhar os valores anteriores. Esta é uma maneira de fazer isso:

 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

ou, se a tabela não for grande, provavelmente criaria um dicionário, como d = {a: b} e, no cursor de atualização, acesse os dados do dicionário: d.get (a + 1) ou d.get (a -1) para fazer as contas ..

Matej
fonte
3

Aceitei a resposta de @Hornbydd por me levar a uma solução de dicionário. O script anexado executa as seguintes ações:

  1. Passe por um FC e preencha um dicionário com key = OID e value = "MyField" usando um SearchCursor.
  2. Iniciar um UpdateCursor
  3. Crie lógica para lidar com a primeira e a última linha (ou seja, onde não há linha anterior ou consecutiva)
  4. Vincular linha UpdateCursor ao OID do dicionário e valor do campo
  5. Faça o processamento ...

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
fonte
Não entendo como: se a linha [0] == names.keys () [0] ou a linha [0] == names.keys () [- 1]: funcionaria? Os valores no padrão não são ordenados, são?
Ianbroad
2

O módulo Data Access é bastante rápido e você pode criar a SearchCursorpara salvar todos os valores de 'a' em uma lista e criar um UpdateCursorpara iterar por cada linha e selecionar na lista para atualizar as linhas 'b' necessárias. Dessa forma, você não precisa se preocupar em salvar dados entre linhas =)

Então, algo como isto:

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

Essa é uma solução bastante grosseira, mas eu a usei recentemente para solucionar um problema muito semelhante. Se o código não funcionar, espero que ele o coloque no caminho certo!

Editar: Última alteração se instrução de AND para OU Editar2: Alterada novamente. Ahh a pressão do meu primeiro post no StackExchange!

romano
fonte
Em vez de editar minha resposta outra vez, darei meus US $ 0,02 a respeito de por que acho que essa é uma boa solução para o problema. A iteração pelas linhas facilita muito a iteração nas listas, bem como o índice + = 1. Embora eu não tenha pensado muito na mecânica do código, ele deve transmitir o ponto que eu queria enfatizar para você. Boa sorte!
Roman
1
Algumas notas: 1) Pode ser mais rápido para grandes conjuntos de dados se você usar uma compreensão de lista para preencher em aListvez de anexar cada entrada. 2) Use em enumerate()vez de ter um contador separado para o índice.
Paulo
-1

Primeiro você precisa de um cursor de pesquisa; Eu não acho que você pode obter valores com um cursor de atualização. Em cada iteração, use aNext = row.next (). GetValue ('a') para obter o valor da próxima linha.

Para obter o valor da linha anterior, eu configuraria uma variável fora do loop for igual a zero. Isso é atualizado para igualar o valor atual das linhas de 'a'. Você pode acessar essa variável na próxima iteração.

Isso satisfaria sua equação de B = A (rowid-1) + A (rowid + 1)

user25074
fonte
Você não precisa mudar para um cursor de Pesquisa para usar o getValue. É um método do objeto ROW, não o objeto CURSOR.
22468 msayler
Na verdade, eles estão usando cursores DA, então acho que o getValue nem se aplica. Eu acho que os cursores DA usam índices.
22464 msayler
1
Os cursores de atualização são capazes de ler e gravar dados. (Útil, por exemplo, para ler um valor de fieldAe usá-lo para calcular um novo valor para fieldB.)
Erica