Более быстрый путь к порогу четырехмерного массива - PullRequest
0 голосов
/ 06 октября 2019

У меня есть массив 4D NumPy размера (98,359,256,269), который я хочу порог. Прямо сейчас у меня есть два отдельных списка, в которых хранятся координаты первых двух измерений и последних двух измерений. (mag_ang для первых 2 измерений и индексов для последних 2).

размер индексов: (61821,2)

размер mag_ang: (35182,2)

В настоящее время мой код выглядит следующим образом:

inner_points = []

for k in indices:
    x = k[0]
    y = k[1]
    for i,ctr in enumerate(mag_ang):
        mag = ctr[0]
        ang = ctr[1]
        if X[mag][ang][x][y] > 10:
            inner_points.append((y,x))

Этот код работает, но он довольно медленный, и мне интересно, есть ли еще какой-нибудь более питонский / более быстрый способ сделать это? S

Ответы [ 4 ]

1 голос
/ 06 октября 2019

Вот несколько проверенных способов использования broadcasting -

thresh = 10
mask = X[mag_ang[:,0],mag_ang[:,1],indices[:,0,None],indices[:,1,None]]>thresh
r = np.where(mask)[0]
inner_points_out = indices[r][:,::-1]

Для больших массивов мы можем сначала сравнить, а затем индексировать, чтобы получить маску -

mask = (X>thresh)[mag_ang[:,0],mag_ang[:,1],indices[:,0,None],indices[:,1,None]]

Если вас интересуют только уникальные координаты от indices, используйте маску напрямую -

inner_points_out = indices[mask.any(1)][:,::-1]

Для больших массивов мы также можем использовать многоядерные процессоры с модулем numexpr.

Таким образом, сначала импортируйте модуль -

import numexpr as ne

Затем замените (X>thresh) на ne.evaluate('X>thresh') в вычислениях, перечисленных ранее.

1 голос
/ 06 октября 2019

(РЕДАКТИРОВАТЬ: добавлен второй альтернативный метод)

Использовать индексирование нескольких массивов:

import time

import numpy as np

n_mag, n_ang, n_x, n_y = 10, 12, 5, 6
shape = n_mag, n_ang, n_x, n_y
X = np.random.random_sample(shape) * 20

nb_indices = 100 # 61821
indices = np.c_[np.random.randint(0, n_x, nb_indices), np.random.randint(0, n_y, nb_indices)]

nb_mag_ang = 50 # 35182
mag_ang = np.c_[np.random.randint(0, n_mag, nb_mag_ang), np.random.randint(0, n_ang, nb_mag_ang)]

# original method
inner_points = []
start = time.time()
for x, y in indices:
    for mag, ang in mag_ang:
        if X[mag][ang][x][y] > 10:
            inner_points.append((y, x))
end = time.time()
print(end - start)

# faster method 1:
inner_points_faster1 = []
start = time.time()
for x, y in indices:
    if np.any(X[mag_ang[:, 0], mag_ang[:, 1], x, y] > 10):
        inner_points_faster1.append((y, x))
end = time.time()
print(end - start)

# faster method 2:
start = time.time()
# note: depending on the real size of mag_ang and indices, you may wish to do this the other way round ?
found = X[:, :, indices[:, 0], indices[:, 1]][mag_ang[:, 0], mag_ang[:, 1], :] > 10
# 'found' shape is (nb_mag_ang x nb_indices)
assert found.shape == (nb_mag_ang, nb_indices)
matching_indices_mask = found.any(axis=0)
inner_points_faster2 = indices[matching_indices_mask, :]
end = time.time()
print(end - start)

# finally assert equality of findings
inner_points = np.unique(np.array(inner_points))
inner_points_faster1 = np.unique(np.array(inner_points_faster1))
inner_points_faster2 = np.unique(inner_points_faster2)
assert np.array_equal(inner_points, inner_points_faster1)
assert np.array_equal(inner_points, inner_points_faster2)

дает

0.04685807228088379
0.0
0.0

(конечно, если вы увеличитеформа время не будет равно нулю для второго и третьего)

Последнее замечание: здесь я использую «уникальный» в конце, но было бы разумно сделать это заранее для indices и mag_ang массивы (кроме случаев, когда вы уверены, что они уже уникальны)

1 голос
/ 06 октября 2019

Используйте numpy напрямую. Если indices и mag_ang - это массивы из двух столбцов каждый для соответствующей координаты:

(x, y), (mag, ang) = indices.T, mag_ang.T
index_matrix = np.meshgrid(mag, ang, x, y).T.reshape(-1,4)
inner_mag, inner_ang, inner_x, inner_y = np.where(X[index_matrix] > 10)

Теперь переменные inner... содержат массивы для каждой координаты. Чтобы получить единый список парсов, вы можете сжать inner_y и inner_x.

0 голосов
/ 06 октября 2019

Использование np.where

inner = np.where(X > 10)
a, b, x, y = zip(*inner)
inner_points = np.vstack([y, x]).T
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...