Python, PyTables - использование поиска в ядре

У меня есть файлы HDF5 с несколькими группами, где каждая группа содержит набор данных с>= 25 миллионами строк. На каждом временном шаге моделирования каждый агент выводит других агентов, которые он / она обнаружил на этом временном шаге. В сценарии ~2000 агентов и тысячи временных шагов; O(n^2) характер вывода объясняет огромное количество строк.

Что меня интересует в подсчете, так это количество уникальных наблюдений по категориям. Например, агенты принадлежат стороне, красный, синий или зеленый. Я хочу сделать двумерную таблицу, где строка i, столбец j это количество агентов в категории j которые были обнаружены по крайней мере одним агентом в категории i. (Я использую Стороны в этом примере кода, но мы могли бы классифицировать агентов и другими способами, такими как оружие, которое у них есть, или датчики, которые они несут.)

Вот пример выходной таблицы; обратите внимание, что симуляция не дает сине-голубых ощущений, потому что занимает тонну места, и мы не заинтересованы в них. То же самое для зеленого, зеленого)

      blue     green      red
blue  0      492       186
green 1075    0     186
red   451    498      26

Столбцы

  1. тик - шаг по времени
  2. sensingAgentId - идентификатор агента, выполняющего распознавание
  3. sensedAgentId - идентификатор распознаваемого агента
  4. detRange - расстояние в метрах между двумя агентами
  5. senseType - перечислимый тип для того, какой тип определения был выполнен

Вот код, который я сейчас использую для этого:

def createHeatmap():
  h5file = openFile("someFile.h5")
  run0 = h5file.root.run0.detections

  # A dictionary of dictionaries, {'blue': {'blue':0, 'red':0, ...}
  classHeat = emptyDict(sides)

  # Interested in Per Category Unique Detections
  seenClass = {}

  # Initially each side has seen no one    
  for theSide in sides:
    seenClass[theSide] = []

  # In-kernel search filtering out many rows in file; in this instance 25,789,825 rows
  # are filtered to 4,409,176  
  classifications = run0.where('senseType == 3')

  # Iterate and filter 
  for row in classifications:
    sensedId = row['sensedAgentId']
    # side is a function that returns the string representation of the side of agent
    # with that id.
    sensedSide = side(sensedId)
    sensingSide = side(row['sensingAgentId'])

    # The side has already seen this agent before; ignore it
    if sensedId in seenClass[sensingSide]:
      continue
    else:
      classHeat[sensingSide][sensedSide] += 1
      seenClass[sensingSide].append(sensedId)


  return classHeat

Примечание: у меня есть фон Java, поэтому я прошу прощения, если это не Pythonic. Пожалуйста, укажите это и предложите способы улучшить этот код, я хотел бы стать более опытным с Python.

Теперь, это очень медленно: для выполнения этой итерации и проверки членства требуется приблизительно 50 секунд, и это с наиболее ограниченным набором критериев членства (другие типы обнаружения имеют намного больше строк для итерации).

Мой вопрос: возможно ли перенести работу из Python в поисковый запрос в ядре? Если так, то как? Есть ли какое-то явно очевидное ускорение, которое мне не хватает? Мне нужно иметь возможность запускать эту функцию для каждого прогона в наборе прогонов (~30) и для нескольких наборов критериев (~5), поэтому было бы здорово, если бы это можно было ускорить.

Последнее замечание: я пытался использовать psyco, но это едва имело значение.

1 ответ

Если у вас N=~2 тыс. Агентов, я предлагаю поместить все наблюдения в массив размером NxN. Это легко помещается в памяти (около 16 мегабайт для целых чисел). Просто храните 1 там, где произошло прицеливание.

Предположим, что у вас есть массив sightings, Первая координата - Sensing, вторая - Sensed. Предположим, у вас также есть массивы 1-го индекса, в которых перечислены агенты, с какой стороны. Вы можете получить количество наблюдений стороны B стороной A таким образом:

sideAseesB = sightings[sideAindices, sideBindices]
sideAseesBcount = numpy.logical_or.reduce(sideAseesB, axis=0).sum()

Возможно, вам нужно использовать sightings.take(sideAindices, axis=0).take(sideBindices, axis=1) на первом этапе, но я сомневаюсь в этом.

Другие вопросы по тегам