Предположим, у меня есть массив arr
и индексы idx
.
Для каждого i
, входящего в idx
, я хочу увеличить arr[i]
на единицу.
Невекторизованный подход будет таким:
import numpy as np
arr = np.zeros(5)
idx = [0, 1, 1, 2, 0]
for i in idx:
arr[i] += 1
Есть ли способ векторизовать это?
Обратите внимание, что arr[idx] += 1
недействителен из-за повторяющихся индексов.
arr = np.zeros(1)
idx = [0, 0]
arr[idx] += 1 # arr becomes array([1]), not array([2])
Конечно, использование np.unique()
может достичь той же цели в этом примере с одномерным массивом. Но на самом деле я пытаюсь иметь дело с 2D-массивом и сомневаюсь, что подсчет элементов будет лучшим решением.
np.unique
действительно работает, но кажется есть лишнее замедление. Я хотел бы более быстрый подход (если существует).
Вот пример 2D-индексов для 10 000 точек без дубликатов.
arr = np.zeros((10000, 10000))
idx = np.stack([np.arange(10000), np.arange(10000)])
%timeit np.unique(idx, axis=1, return_counts=True) # takes 1.93 ms
%timeit arr[idx[0], idx[1]] += 1 # takes 235 μs
Судя по всему, итерация по индексации примерно в 10 раз быстрее.
Ответ @PaulS был быстрее, чем np.unique
.
%timeit np.add.at(arr, (idx[0], idx[1]), 1) # takes 925 μs
Вот пример со случайным индексом для проверки повторяющихся индексов.
arr = np.zeros((10000, 10000))
ran = (np.random.rand(10000)*10).astype(int)
idx = np.stack([ran, ran])
%timeit np.unique(idx, axis=1, return_counts=True) # takes 3.24 ms
%timeit np.add.at(arr, (idx[0], idx[1]), 1) # takes 859 μs
(изменить: опечатка)
Я пытаюсь реализовать алгоритм преобразования линии Хафа, используя NumPy. (Причина, по которой я не использую cv2.HoughLines()
, заключается в том, что я хочу получить результат непосредственно из координат точек, а не из двоичного массива).
Получить кривые в плоскости (r, θ)
было легко, но у меня возникли проблемы с реализацией аккумулятора векторизованным способом. В настоящее время я полагаюсь на преобразование 2D-данных в 1D. Есть ли более приятный и быстрый способ выполнить накопление?
Заранее спасибо!
Используйте numpy.unique, чтобы получить уникальные индексы и их количество:
idx2, cnt = np.unique(idx, return_counts=True)
arr[idx2] += cnt
Обновления arr
:
array([2, 2, 1, 0, 0])
arr = np.zeros([3, 4], dtype=int)
idx = [[0, 0, 2, 0],
[1, 1, 3, 1]]
idx2, cnt = np.unique(idx, axis=1, return_counts=True)
arr[*idx2] = cnt
Выход:
array([[0, 3, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 1]])
Если индексы транспонированы:
arr = np.zeros([3, 4], dtype=int)
idx = idx = [[0, 1], [0, 1], [2, 3], [0, 1]]
idx2, cnt = np.unique(idx, axis=0, return_counts=True)
arr[*idx2.T] = cnt
@JSS, почему? это очень хорошо работает с массивом 2D (ND) (см. обновленный пример)
Кроме того, прямое использование arr[idx]
было бы неоднозначным, если бы вы использовали умножение и имели дублирующуюся координату, хотели бы вы умножить 2 раза на 1? или на 2?
Потому что np.unique
кажется немного медленным. Пожалуйста, смотрите мой обновленный вопрос.
1D массивы
Другое возможное решение:
np.add.at(arr, idx, 1)
Выход:
[2. 2. 1. 0. 0.]
2D массивы
(Спасибо, @mozway, за ваш пример, который я сейчас использую здесь.)
arr = np.zeros([3, 4], dtype=int)
idx = [[0, 0, 2, 0],
[1, 1, 3, 1]]
np.add.at(arr, (idx[0], idx[1]), 1)
Выход:
array([[0, 3, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 1]])
Спасибо! Это работает как шарм без потери скорости.
Спасибо за ответ! Но, как я уже говорил в своем вопросе, есть ли другой способ?