Вычисление нового атрибута на основе изменений в другом атрибуте с использованием ArcGIS Desktop с Python?

11

Я пытаюсь классифицировать набор данных точек, закодированных по времени в gps, в поведение на основе различных атрибутов.

Я создал атрибут, который равен 0 для дома и 1 для выезда, в зависимости от местоположения, и теперь хочу подсчитать количество поездок вне дома (набор точек 01111111111110будет одной поездкой, потому что она началась и закончилась дома). Я добавил поле атрибута, в котором будут номера поездок, но не знаю, как рассчитать поле, поэтому оно основано на поле дома / на выезде.

Ниже приведен пример данных GPS (с использованием «*» для обозначения нерелевантной информации и простого индексации времени как 1, 2 и т. Д.), Индикатора «Home / Away», описанного выше, и индикатора требуемой поездки «Поездка», который мне нужно вычислить:

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

Мой набор данных слишком велик для того, чтобы вручную проходить и пронумеровывать каждую поездку в таблице атрибутов, поэтому есть ли способ рассчитать поле на основе того, как упорядочен атрибут home / away, и каждый «скопление» точек выезда обозначается как поездка?

Это голая основа того, как может выглядеть код Python (я не разбираюсь в коде).

Выражение:

trip = Reclass(!home!)

кодоблок:

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  

После использования рекомендованного сценария Мэтта Уилки я внес некоторые изменения, так что моя первая поездка - номер 1, моя вторая - 2, и т. Д. И т. Д.

Вот код, модифицированный из 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

Затем я просто выбираю home = 0 и вычисляю поле моего путешествия обратно до 0. Точно упорядоченные поездки.

AlmaThom
источник

Ответы:

12

Для этого вы можете использовать UpdateCursor , который открывает класс пространственных объектов или таблицу и пошагово просматривает каждую запись (строку).

Скрипт ниже работает с этими данными теста

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

Задний конец блока отключения фактически запускается и для начала отключения, но, поскольку счетчик отключений правильный, двойной расчет в строке начала-отключения не имеет значения. Раскомментируйте оператор print в этом блоке, чтобы понять, что я имею в виду.

Python автоматически добавляет неявное rows.next()в конце для for row in rowsблока.

Это предполагает целостность данных. Это испортится, если в строке ( 000или 00000) будет нечетное количество нулевых записей Home / Away . Поездка, которая состоит только из запуска и остановки, должна быть в порядке, например, последовательность из трех поездок 01..10 00 01..10, где пробелы обозначают промежутки между поездками. Другими словами, подтвердите результаты!

Мэтт Уилки
источник
2
+1, вы ДОЛЖНЫ сделать это в курсоре обновления. Инструмент CalculateField не гарантирует, что блок кода будет запущен только один раз, поэтому tripпеременная может быть повторно инициализирована любое произвольное количество раз.
Джейсон Шейрер,
Это прекрасно работает, поскольку всем моим поездкам присваивается один номер для всех точек в поездке, однако всем точкам дома присваивается новый номер (т.е. мои данные начинаются с точек дома, которые теперь имеют номера 1, 2, 3, .. ... 136 и тогда моя первая поездка помечена 137). Это не имеет большого значения, потому что я могу вернуть все «домашние» баллы на 0, но было бы неплохо, если бы мои поездки начинались с 1 и после этого были равномерным числом. Любой совет?
AlmaThom
@ Алиса, я не проверял, но все, что тебе нужно сделать, это закомментировать или удалить row.TRIP = tripстроку в каждом из двух блоков, которые обрабатывают начало и конец поездки. (и, если подумать, то, rows.updateRow(row)что следует, так как больше там нечего спасать.)
Matt Wilkie
Разобрался с глюком! мой сценарий теперь состоит из трех частей:
AlmaThom
5

Справка ArcGIS 10 в разделе «Рассчитать примеры полей» показывает, как «Рассчитать накопительное значение числового поля». Это поможет, если данные физически находятся в заданном временном порядке.

Чтобы применить его напрямую, инвертируйте индикатор [Home / Away] (вычтите его из 1), чтобы «0» означало «отсутствующий», а «1» означало «домашний». Я называю это [Away / Home] в примере ниже.

Вычислите его совокупную стоимость - [Накопительная] в примере.

Добавьте один и разделите на два - [Trip] в примере (почти).

Наконец, установите [Trip] на ноль для всех «домашних» записей. Теперь результаты согласуются с примером:

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

Для справки вот код, взятый из справки ArcGIS 10. Я немного изменил его, чтобы он выполнял каждый шаг сразу: теперь вам нужно всего лишь запустить его. Должно быть ясно, где инвертируется [Home / Away] и где происходит шаг «добавить 1, разделить на 2».

Выражение:

acc(!Home/Away!)

Тип выражения:

PYTHON_9.3

Блок кода:

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
источник
3
Для любого большого количества записей это не будет работать. Кодовый блок перезапускается каждые несколько сотен тысяч строк (вместе с полным циклом сбора мусора), поэтому tон сбрасывается до 0 в, казалось бы, случайных местах.
Джейсон Шайрер,
2
Спасибо, @Jason: я не знал об этой ошибке. Это настоящий шоу-стоппер. <rant> Я думал, что ArcGIS должен был масштабироваться так, чтобы он был полезен не только для маленьких игрушечных проблем? </ rant>
whuber
1
Не ошибка, это на самом деле деталь реализации, унаследованная от реализации VBScript, чтобы попытаться минимизировать утечки памяти (например, пользователи добавляют к списку для каждой записи, но никогда не используют этот список для чего-либо). Я почти уверен, что избавился от обновления в 11, потому что это неочевидное поведение, но я не помню.
Джейсон Шайрер,
1
@ Джейсон Для меня это новый эвфемизм: «детали реализации». Другими эвфемизмами являются «особенность» и «недокументированное поведение». Роза под любым другим именем ...
whuber
2
Вот как я это вижу, @Jason: сама страница справки содержит код, который я представил. Поэтому со стороны ESRI существует неявное утверждение о том, что код работает. По вашему мнению, это не так; действительно, по вашей характеристике он может потерпеть неудачу значительно, тихо, без предупреждения и непредсказуемо. Это не просто ошибка, это самая отвратительная из возможных ошибок. «Периодический сброс» - это не «исправление», это «клюж», который только ухудшает ситуацию ИМХО.
whuber