Computando novo atributo com base em alterações em outro atributo usando o ArcGIS Desktop com Python?

11

Estou tentando classificar um conjunto de dados de ponto codificado em tempo de gps em comportamentos com base em diferentes atributos.

Criei um atributo que é 0 para casa e 1 para fora com base na localização e agora quero numerar as viagens fora de casa (um conjunto de pontos 01111111111110seria uma viagem porque começou e terminou em casa). Eu adicionei o campo de atributo que terá os números de viagem, mas não sei como calcular o campo para que ele se baseie no campo de casa / fora.

Aqui está um exemplo dos dados do GPS (usando "*" para indicar informações irrelevantes e simplesmente indexar os tempos como 1, 2 etc.), o indicador "Casa / Fora" descrito acima e o indicador de viagem desejado, "Viagem", que eu preciso calcular:

Time Lat Lon Home/Away Trip
   1   *   *         0    0
   2   *   *         1    1
   3   *   *         1    1
....
  12   *   *         1    1
  13   *   *         0    0
  14   *   *         0    0
  15   *   *         1    2
  16   *   *         1    2
.... 
  34   *   *         1    2
  35   *   *         0    0
  36   *   *         0    0
  37   *   *         1    3
....

Meu conjunto de dados é muito grande para percorrer manualmente e numerar cada viagem na tabela de atributos; portanto, existe alguma maneira de calcular o campo com base em como o atributo home / away é solicitado e cada "grupo" de pontos ausentes é designado como um viagem?

Estes são os ossos básicos da aparência do código Python (não tenho experiência com código).

Expressão:

trip = Reclass(!home!)

Codeblock:

def Reclass(home):  
  if (home = 0):  
    return 0   
  elif (home = 1 and lastValue = 0):  
    return _(incremental numbering?)_  
  elif (home = 1 and lastValue = 1):  
    return lastValue  

Depois de usar o script recomendado de matt wilkie, fiz algumas alterações para que minha primeira viagem seja o número 1, a segunda seja 2, etc.

Aqui está o código modificado de matt's:

import arcpy
rows = arcpy.UpdateCursor("test2")

trip = 0
for row in rows:
    if row.home == 0:
        prev = row.home
        row.TRIP = trip
        rows.updateRow(row)

    elif row.home == 1 and prev == 0:
        trip += 1
        prev = row.home
        row.TRIP = trip
        rows.updateRow(row)
        rows.next()

    elif row.home == 1 and prev == 1:
        prev = row.home
        row.TRIP = trip
        rows.updateRow(row)
        rows.next()

    row.TRIP = trip
    rows.updateRow(row)


del row, rows

Depois, basta selecionar para casa = 0 e calcular meu campo de viagem de volta a 0. Viagens ordenadas ordenadamente.

AlmaThom
fonte

Respostas:

12

Para isso, você pode usar o UpdateCursor , que abre a classe ou a tabela de recursos e percorre cada registro (linha) de forma incremental.

O script abaixo funciona nesses dados de teste

+-----------------------+
| Time| Home_Away|Trip  |
+-----|----------|------+
|  1  |  0       | <nul>|
|  2  |  1       | <nul>|
|  4  |  1       | <nul>|
|  5  |  0       | <nul>|
|  6  |  0       | <nul>|
|  7  |  1       | <nul>|
|  9  |  1       | <nul>|
| 12  |  1       | <nul>|
| 13  |  0       | <nul>|
+-----------------------+

.

import arcpy
fc = r'D:\s\py\pyscratch.gdb\gps_points'

# open the feature class and create the cursor
rows = arcpy.UpdateCursor(fc)

trip = 0
for row in rows:
    if row.HOME_AWAY == 0:
        trip += 1           # start of new trip, increment counter
        row.TRIP = trip     # calc the TRIP field to be current trip#
        rows.updateRow(row) # save
        print "Trip %s started at %s" % (trip, row.TIME)

    # keep cycling through records until HOME_AWAY is not 1
    while row.HOME_AWAY == 1:
        row.TRIP = trip
        rows.updateRow(row)
        rows.next() # move to next record

    # this is for the trailing end of a trip, the second 0
    # print "     %s ended at %s" % (trip, row.TIME)
    row.TRIP = trip
    rows.updateRow(row)

# remove programming objects and data locks
# the data itself is left alone
del row, rows

O final do bloco de viagem também é executado no início de uma viagem, mas como o contador de viagens está correto, o cálculo duplo na linha de início da viagem não importa. Remova o comentário da declaração de impressão desse bloco para ver o que quero dizer.

O Python adiciona automaticamente um implícito rows.next()no final do for row in rowsbloco.

Isso pressupõe a integridade dos dados. Isso atrapalhará se houver um número ímpar de zero registros Casa / Fora consecutivos ( 000ou 00000). Uma viagem que consiste apenas de partida e parada deve ser boa, por exemplo, uma sequência de três viagens 01..10 00 01..10, em que os espaços denotam as lacunas entre as viagens. Em outras palavras, valide os resultados!

Matt Wilson
fonte
2
+1, você DEVE fazer isso em um cursor de atualização. A ferramenta CalculateField não garante que o bloco de código seja executado apenas uma vez; portanto, a tripvariável pode ser reinicializada qualquer número arbitrário de vezes.
Jason Scheirer
Isso funciona muito bem, pois todas as minhas viagens recebem um número para todos os pontos da viagem; no entanto, todos os pontos em casa recebem um novo número (ou seja, meus dados começam com pontos em casa agora numerados 1, 2, 3, .. ... 136 e então minha primeira viagem é toda rotulada como 137). Não é grande coisa, porque eu posso reverter todos os pontos "domésticos" para 0, mas seria bom se minhas viagens começassem com 1 e fossem o mesmo número depois disso. Algum conselho?
AlmaThom
@ Alice, eu não testei, mas tudo que você precisa fazer é comentar ou excluir a row.TRIP = triplinha em cada um dos dois blocos que lidam com o início e o fim da viagem. (e, venha para pensar sobre isso, o rows.updateRow(row)que se seguem, como não há nada para salvar mais lá.)
Matt Wilkie
Resolvido a falha! meu script agora tem três partes:
AlmaThom 18/06/12
5

A ajuda do ArcGIS 10 em "calcular exemplos de campo" mostra como "Calcular o valor acumulativo de um campo numérico". Isso funcionará, desde que os dados estejam fisicamente na ordem temporal pretendida.

Para aplicá-lo diretamente, inverta o indicador [Casa / Fora] (subtraia-o de 1) para que "0" signifique "ausente" e "1" signifique "casa". Eu chamo isso de [Fora / Casa] no exemplo abaixo.

Calcule seu valor cumulativo - [Cumulativo] no exemplo.

Adicione um e divida por dois - [Trip] no exemplo (quase).

Por fim, defina [Trip] para zero para todos os registros "residenciais". Agora os resultados concordam com o exemplo:

Time Lat Lon Home/Away Trip Away/Home Cumulative 
   1   *   *         0    0         1          1
   2   *   *         1    1         0          1
   3   *   *         1    1         0          1
.... 
  12   *   *         1    1         0          1
  13   *   *         0    0         1          2
  14   *   *         0    0         1          3
  15   *   *         1    2         0          3
  16   *   *         1    2         0          3
.... 
  34   *   *         1    2         0          3
  35   *   *         0    0         1          4
  36   *   *         0    0         1          5
  37   *   *         1    3         0          5
....

Para o registro, aqui está o código retirado da ajuda do ArcGIS 10. Eu o modifiquei um pouco para executar cada etapa de uma só vez: agora você só precisa executá-lo. Deve ficar claro onde [Casa / Fora] fica invertido e onde ocorre a etapa "adicionar 1, dividir por 2".

Expressão:

acc(!Home/Away!)

Tipo de expressão:

PYTHON_9.3

Bloco de código:

t=0
def acc(i):
  global t
  if t:
    t += (1-i)
  else:
    t = 1
  if i:
    return (t+1)/2
  else:
    return 0
whuber
fonte
3
Para qualquer grande número de registros, isso não funcionará. O codeblock é executado novamente a cada poucas centenas de milhares de linhas (junto com um ciclo completo de coleta de lixo) t, sendo redefinido para 0 em locais aparentemente aleatórios.
Jason Scheirer
2
Obrigado, @ Jason: Eu não estava ciente desse bug. Isso é um verdadeiro show-stopper. <rant> Eu pensei que o ArcGIS deveria aumentar de
tamanho
1
Não é um bug, na verdade é um detalhe de implementação herdado da implementação do VBScript para tentar minimizar vazamentos de memória (usuários anexando uma lista a cada registro, mas nunca usando a lista para nada, por exemplo). Tenho certeza de que me livrei da atualização no 11 porque é um comportamento não óbvio, mas não me lembro.
Jason Scheirer
1
@ Jason Esse é um novo eufemismo para mim: "detalhes de implementação". Outros eufemismos são "característica" e "comportamento não documentado". Uma rosa com qualquer outro nome ...
whuber
2
Eis como eu o vejo, @ Jason: a própria página de ajuda fornece o código que apresentei. Existe, portanto, uma afirmação implícita da parte da ESRI de que o código funciona. Segundo você, isso não acontece; de fato, sob a sua caracterização, ele pode falhar significativamente, silenciosamente, sem aviso e imprevisivelmente. Isso não é apenas um bug, é a forma mais desagradável de bug possível. Uma "redefinição periódica" não é uma "correção", é um cluge que só piora a situação no IMHO.
whuber