Calcul d'un nouvel attribut basé sur les modifications d'un autre attribut à l'aide d'ArcGIS Desktop avec Python?

11

J'essaie de classer un ensemble de données ponctuelles gps encodées dans le temps en comportements basés sur différents attributs.

J'ai créé un attribut qui est 0 pour la maison et 1 pour l'extérieur en fonction de l'emplacement, et je veux maintenant numéroter les voyages loin de chez eux (un ensemble de points 01111111111110serait un voyage car il a commencé et s'est terminé à la maison). J'ai ajouté le champ d'attribut qui contiendra les numéros de trajet, mais je ne sais pas comment calculer le champ afin qu'il soit basé sur le champ domicile / extérieur.

Voici un exemple des données GPS (en utilisant "*" pour indiquer des informations non pertinentes et simplement l'indexation des heures comme 1, 2, etc.), l'indicateur "Domicile / Absent" décrit ci-dessus, et l'indicateur de trajet souhaité, "Trip", dont j'ai besoin pour calculer:

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
....

Mon ensemble de données est trop volumineux pour être parcouru manuellement et numéroter chaque trajet dans la table attributaire. voyage?

Ce sont les bases de ce à quoi pourrait ressembler le code Python (je ne suis pas expérimenté avec le code).

Expression:

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  

Après avoir utilisé le script recommandé de matt wilkie, j'ai apporté quelques modifications pour que mon premier voyage soit le numéro 1, mon deuxième soit 2, etc.

Voici le code modifié de Matt:

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

Ensuite, je sélectionne simplement home = 0 et je calcule mon champ de voyage à 0. Voyages soigneusement ordonnés.

AlmaThom
la source

Réponses:

12

Pour cela, vous pouvez utiliser UpdateCursor , qui ouvre la classe d' entités ou la table et parcourt progressivement chaque enregistrement (ligne).

Le script ci-dessous fonctionne sur ces données de test

+-----------------------+
| 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

La fin de fin du bloc de déclenchement est en fait exécutée pour le début d'un trajet également, mais comme le compteur de trajet est correct, le double calcul sur la ligne de début de trajet n'a pas d'importance. Décommentez l'instruction d'impression dans ce bloc pour voir ce que je veux dire.

Python ajoute automatiquement un implicite rows.next()à la fin du for row in rowsbloc.

Cela suppose l'intégrité des données. Cela gâchera s'il y a un nombre impair d'enregistrements Domicile / Absent dans une rangée ( 000ou 00000). Un trajet qui ne comprend que le démarrage et l'arrêt devrait être correct, par exemple une séquence de 3 voyages de 01..10 00 01..10, où les espaces indiquent les écarts entre les voyages. En d'autres termes, validez les résultats!

Matt Wilkie
la source
2
+1, vous DEVEZ le faire dans un curseur de mise à jour. L'outil CalculateField ne garantit pas que le bloc de code ne sera exécuté qu'une seule fois, de sorte que la tripvariable peut être réinitialisée un nombre arbitraire de fois.
Jason Scheirer
Cela fonctionne très bien dans la mesure où tous mes voyages se voient attribuer un numéro pour tous les points du voyage, mais tous les points à la maison reçoivent un nouveau numéro (c'est-à-dire que mes données commencent avec les points à la maison maintenant numérotés 1, 2, 3, .. ... 136 puis mon premier voyage est tout étiqueté 137). Ce n'est pas un gros problème parce que je peux ramener tous les points "à la maison" à 0, mais ce serait bien si mes voyages commençaient à 1 et étaient également en nombre après cela. Aucun conseil?
AlmaThom
@Alice, je n'ai pas testé, mais tout ce que vous devez faire est de commenter ou de supprimer la row.TRIP = tripligne dans chacun des deux blocs qui gèrent le début et la fin du voyage. (et, à bien y penser, ce rows.updateRow(row)qui suit, car il n'y a plus rien à y sauver.)
matt wilkie
A réglé le problème! mon script a maintenant trois parties:
AlmaThom
5

L'aide d'ArcGIS 10 sous "calculer des exemples de champs" vous montre comment "calculer la valeur cumulée d'un champ numérique". Cela fera l'affaire, à condition que les données soient physiquement dans l'ordre temporel prévu.

Pour l'appliquer directement, inversez votre indicateur [Domicile / Absent] (soustrayez-le de 1) de sorte que "0" signifie "loin" et "1" signifie "maison". J'appelle cela [Absent / Domicile] dans l'exemple ci-dessous.

Calculez sa valeur cumulée - [Cumulative] dans l'exemple.

Ajoutez un et divisez par deux - [Trip] dans l'exemple (presque).

Enfin, définissez [Trip] sur zéro pour tous les enregistrements "home". Maintenant, les résultats correspondent à l'exemple:

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
....

Pour mémoire, voici le code extrait de l'aide d'ArcGIS 10. Je l'ai légèrement modifié pour qu'il fasse chaque étape à la fois: maintenant il vous suffit de l'exécuter. Il doit être clair où [Domicile / Absent] est inversé et où l'étape "ajouter 1, diviser par 2" se produit.

Expression:

acc(!Home/Away!)

Type d'expression:

PYTHON_9.3

Bloc de code:

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
la source
3
Pour un grand nombre d'enregistrements, cela ne fonctionnera pas. Le bloc de code réexécute toutes les quelques centaines de milliers de lignes (avec un cycle complet de collecte des ordures) et tsera donc réinitialisé à 0 dans des endroits apparemment aléatoires.
Jason Scheirer
2
Merci, @Jason: Je n'étais pas au courant de ce bogue. C'est un vrai spectacle. <rant> Je pensais qu'ArcGIS était censé
évoluer
1
Ce n'est pas un bug, c'est en fait un détail d'implémentation hérité de l'implémentation VBScript pour essayer de minimiser les fuites de mémoire (les utilisateurs s'ajoutant à une liste pour chaque enregistrement mais n'utilisant jamais la liste pour rien, par exemple). Je suis presque sûr que je me suis débarrassé du rafraîchissement dans 11 parce que c'est un comportement non évident, mais je ne me souviens pas.
Jason Scheirer
1
@Jason C'est un nouvel euphémisme pour moi: "détail d'implémentation". D'autres euphémismes sont «caractéristique» et «comportement non documenté». Une rose sous un autre nom ...
whuber
2
Voici comment je le vois, @Jason: la page d'aide elle-même fournit le code que j'ai présenté. Il y a donc une affirmation implicite de la part d'ESRI que le code fonctionne. Selon vous, ce n'est pas le cas; en effet, selon votre caractérisation, il peut échouer de manière significative, silencieuse, sans avertissement et de façon imprévisible. Ce n'est pas seulement un bug, c'est la forme de bug la plus méchante possible. Une «réinitialisation périodique» n'est pas un «correctif», c'est un kluge qui ne fait qu'empirer la situation à mon humble avis.
whuber