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.

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!


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

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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.