У меня есть большое количество независимых параллельных горизонтальных сегментов линии в массиве 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 в диапазоне, в отличие от вышеприведенного примера. Оба являются точными (и эквивалентными).
Вы можете использовать логический массив, чтобы определить, находится ли данная точка в пространстве в данном сегменте линии. Этот логический массив может быть умножен на значения сегмента для генерации массива, где каждая точка на линии имеет вектор значений сегмента, а если сегмент не включает точку, значение этого сегмента обнуляется. Оттуда метод массива 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]]
space
займет ~ 47 ГБ памяти, что не подходит. Но, возможно, я могу как-то разбить его на части, а затем применить исправление, чтобы справиться с переходами чанков.
Вы можете использовать массивы 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
ranges
, которые вы жестко закодировали :)