Найдите Argmin слева / справа от каждого индекса в массиве

#python #numpy

#python #numpy

Вопрос:

Допустим, у меня есть массив:

 x = np.array([7., 5., 4., 1., 9., 2., 3., 6.,])
  

Для каждого индекса я хочу найти argmin справа от этого индекса, а также argmin слева от этого индекса. Конечные точки будут иметь значение argmin -1 , когда значение индекса недоступно, поэтому выходные данные будут:

 left_argmins = [-1, 0, 1, 2, 3, 3, 3, 3]
right_argmins = [3, 3, 3, 5, 5, 6, 7, -1]
  

Наивный подход (но медленный подход) был бы:

 right_argmins = np.full(len(x), -1)

for i in range(len(x)):
    min_val = np.inf
    idx = -1
    for j in range(i 1, len(x)):
        if x[j] < min_val:
            min_val = x[j]
            idx = j
    right_argmins[i] = idx

left_argmins = np.full(len(x), -1)

for i in range(len(x)):
    min_val = np.inf
    idx = -1
    for j in range(i):
        if x[j] < min_val:
            min_val = x[j]
            idx = j
    left_argmins[i] = idx
  

Каков наилучший способ добиться этого в NumPy?

Кроме того, что бы я сделал, если бы мне нужно было сделать это для 2D-массива?

Ответ №1:

Создайте матрицу с нижним треугольником бесконечности, затем используйте широковещательную передачу, чтобы она занимала по одному элементу вашего массива за раз. После этого простой argmin выполнит работу:

 lower_tri = np.ones((len(x), len(x)))
lower_tri[np.tril(lower_tri, -1)==1] = float('inf')
lower_tri = lower_tri * x
np.argmin(lower_tri, axis=1)
  

приводит к

 array([3, 3, 3, 3, 5, 5, 6, 7])
  

Для другого направления просто используйте вместо этого верхний треугольник:

 upper_tri = np.ones((len(x), len(x)))
upper_tri[np.triu(upper_tri, 1)==1] = float('inf')
upper_tri = upper_tri * x
np.argmin(upper_tri, axis=1)
# array([0, 1, 2, 3, 3, 3, 3, 3])
  

затем вы можете добавлять или дополнять -1 по желанию.

но имейте в виду, что при этом используется O (n ^ 2) памяти, которая может иметь значение, x является большой.

Ответ №2:

Вы можете накапливать индексы, которые приводят к уникальному массиву argsort индексов, применяя оператор minimum:

 argidx = np.argsort(x)
u, idx = np.unique(argidx, return_index=True)
left_argmins = argidx[np.minimum.accumulate(idx)]
right_argmins = argidx[np.minimum.accumulate(idx[::-1])][::-1]
  

Вывод:

 left_argmins: [0, 1, 2, 3, 3, 3, 3, 3]
right_argmins: [3, 3, 3, 3, 5, 5, 6, 7]
  

Ответ №3:

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

 x = np.array([7., 5., 4., 1., 9., 2., 3., 6.,])

left_argmins = []
right_argmins = []
for i in range(len(x)):
    to_left = x[:i]
    if to_left.any():
        left_argmins.append(to_left.argmin())
    else:
        left_argmins.append(-1)
    to_right = x[i   1:]
    if to_right.any():
        right_argmins.append(to_right.argmin()   (i   1))
    else:
        right_argmins.append(-1)