Python векторизация работает максимум параллельных отрезков

1

У меня есть большое количество независимых параллельных горизонтальных сегментов линии в массиве numpy. Каждый сегмент имеет начало и конец (x-координаты) и значение (y-координата). Сегменты не обязательно имеют одинаковую длину (длина = конец - старт).

Примерная матрица, определяющая сегменты, по одному сегменту на строку, может выглядеть следующим образом:

Start End Value
0     10  4
5     19  3
6     25  2
7     16  1
12    21  5

В коде

A = np.array([[0,10,4],
[5,19,3],
[6,25,2],
[7,16,1],
[12,21,5]])

Я хочу выяснить, работает ли max над сегментами линии. То есть, в приведенном выше примере, для x в диапазоне [0,25), я хочу соответствующий max y. Пример вывода, соответствующего этому примеру, будет

Start End Max
0     10  4
10    12  3
12    21  5
21    25  2

Я могу сделать это в цикле for, но это медленно, так как у меня есть десятки тысяч сегментов. Кажется, я не думаю о способе векторизации этого. Может кто-нибудь?

Пример для кода цикла:

x = np.arange(np.min(A[:,0]), np.max(A[:,1]))
maxes = np.zeros((x.shape[0], 2))
maxes[:,0] = x
maxes[:,1] = -np.inf

for a in A:
    ix = (x >= a[0]) & (x < a[1]) & (maxes[:,1] < a[2])
    maxes[ix,1] = a[2]

Этот код выводит массив со строкой для каждого x в диапазоне, в отличие от вышеприведенного примера. Оба являются точными (и эквивалентными).

  • 1
    Добавить пример для массива numpy? Кроме того, могут ли быть пересечения между сегментами?
  • 0
    @Divakar: спасибо за вопросы. Если вы имеете в виду массив примеров, посмотрите пример, который я представил в первом блоке кода. Да, могут быть совпадения; см. пример в первом блоке кода.
Показать ещё 5 комментариев
Теги:
numpy

2 ответа

1
Лучший ответ

Вы можете использовать логический массив, чтобы определить, находится ли данная точка в пространстве в данном сегменте линии. Этот логический массив может быть умножен на значения сегмента для генерации массива, где каждая точка на линии имеет вектор значений сегмента, а если сегмент не включает точку, значение этого сегмента обнуляется. Оттуда метод массива max может применяться вдоль одной оси.

import numpy as np

A = np.array([[0,10,4],
[5,19,3],
[6,25,2],
[7,16,1],
[12,21,5]])

# get the dimension of the space
seg_left = A[:, 0, None]
seg_right = A[:, 1, None]
seg_val = A[:, 2, None]

# set the left edge of the space and reset the axes
left_edge = seg_left.min()
seg_left -= left_edge
seg_right -= left_edge
right_edge = seg_right.max()


# generate an array of coordinates and repeat it for each defined segment. This 
# can then be used to determine what segments are on for each point
space = np.tile(np.arange(right_edge+1), (seg_val.size, 1))
space_bool = np.logical_and(space >= seg_left,
                            space < seg_right)

# find the maximum of the on segments
seg_max = (seg_val * space_bool).max(axis=0)

# determine the continuous segments. The +1 ensures that the correct value is
# selected
steps = np.r_[0, np.where(np.diff(seg_max))[0]+1]
seg_val = seg_max[steps[:-1]]

# reset the left edge to the original left edge
steps += left_edge

print(np.c_[steps[:-1], steps[1:], seg_val])

# [[ 0 10  4]
#  [10 12  3]
#  [12 21  5]
#  [21 25  2]]
  • 0
    Большой! Благодарю. Реквизит для создания общего решения для любого диапазона x-координат. Единственная проблема, которую я имею, состоит в том, что, поскольку у меня есть десятки тысяч сегментов, массив space займет ~ 47 ГБ памяти, что не подходит. Но, возможно, я могу как-то разбить его на части, а затем применить исправление, чтобы справиться с переходами чанков.
  • 0
    Блок должен работать. Другая вещь, на которую вы можете посмотреть - это разреженные матрицы. docs.scipy.org/doc/scipy/reference/sparse.html Если ваши сегменты относительно короткие, это освободит много места. Я не совсем уверен, как построить разреженную матрицу без цикла for, но это все равно может сэкономить кучу времени.
Показать ещё 1 комментарий
1

Вы можете использовать массивы booleans для индексирования массивов. Это означает, что вы можете сразу проверить все свои координаты на свои условия, а затем индексировать столбец значений (A[2]) с результатом. Из результатов вашего примера я полагаю, что конечные точки сегментов линии не должны включаться, следовательно, следующий код:

import numpy as np

A = np.array(
    [[0,10,4],
     [5,19,3],
     [6,25,2],
     [7,16,1],
     [12,21,5]]
)

ranges = np.array([
    [0,10], [10,12], [12,21], [21,25]
])

for xmin,xmax in ranges:
    print(xmin,xmax, np.max(A[~np.logical_or(A[:,1]<=xmin, A[:,0]>=xmax),2]))

воспроизводит желаемый результат:

0 10 4
10 12 3
12 21 5
21 25 2
  • 0
    Спасибо за ваш ответ, но один из главных компонентов проблемы - это определение ranges , которые вы жестко закодировали :)
  • 0
    @ Мэтт, тогда я неправильно понял твой вопрос. Есть ли смысл думать об этом дальше, или вы довольны другим ответом?
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню