Я пытаюсь перебрать пустые строки и поместить индекс каждого кластера из 3 элементов, который содержит наименьшее значение, в другую строку. Это должно быть в контексте слева, посередине, справа; левый и правый края смотрят только на два значения («левый и средний» или «средний и правый»), но все в середине должно выглядеть на все 3.

Для циклов сделать это тривиально, но это очень медленно. Некоторая разновидность векторизации, вероятно, ускорит это.

Например:

 [1 18 3 6 2]
 # should give the indices...
 [0 0 2 4 4] # matching values 1 1 3 2 2

Медленно для цикла реализации:

for y in range(height):
    for x in range(width):
        i = 0 if x == 0 else x - 1
        other_array[y,x] = np.argmin(array[y,i:x+2]) + i
1
John Laughlin 21 Фев 2020 в 05:02

2 ответа

Лучший ответ

ПРИМЕЧАНИЕ. См. обновление ниже, чтобы найти решение без циклов for.

Это работает для массива любого количества измерений:

def window_argmin(arr):
    padded = np.pad(
        arr,
        [(0,)] * (arr.ndim-1) + [(1,)],
        'constant',
        constant_values=np.max(arr)+1,
    )
    slices = np.concatenate(
        [
            padded[..., np.newaxis, i:i+3]
            for i in range(arr.shape[-1])
        ],
        axis=-2,
    )
    return (
        np.argmin(slices, axis=-1) + 
        np.arange(-1, arr.shape[-1]-1)
    )

Код использует np.pad для дополнения последнего измерения массива дополнительным числом слева и одним справа, поэтому мы всегда можем использовать окна из 3 элементов для аргумента argmin. Он устанавливает дополнительные элементы как max + 1, поэтому они никогда не будут выбраны argmin.

Затем он использует np.concatenate списка срезов, чтобы добавить новое измерение с каждым из трехэлементных окон. Это единственное место, где мы используем цикл for, и мы только зацикливаемся на последнем измерении, один раз, чтобы создать отдельные 3-элементные окна. (См. Обновление ниже для решения, которое удаляет этот цикл for.)

Наконец, мы вызываем np.argmin в каждом из окон.

Нам нужно настроить их, что мы можем сделать, добавив смещение первого элемента окна (которое на самом деле равно -1 для первого окна, поскольку это мягкий элемент). Мы можем выполнить настройку с помощью простой суммы arange массив, который работает с трансляцией.

Вот тест с вашим образцом массива:

>>> x = np.array([1, 18,  3,  6,  2])

>>> window_argmin(x)

array([0, 0, 2, 4, 4])

И пример 3d:

>>> z

array([[[ 1, 18,  3,  6,  2],
        [ 1,  2,  3,  4,  5],
        [ 3,  6, 19, 19,  7]],

       [[ 1, 18,  3,  6,  2],
        [99,  4,  4, 67,  2],
        [ 9,  8,  7,  6,  3]]])

>>> window_argmin(z)

array([[[0, 0, 2, 4, 4],
        [0, 0, 1, 2, 3],
        [0, 0, 1, 4, 4]],

       [[0, 0, 2, 4, 4],
        [1, 1, 1, 4, 4],
        [1, 2, 3, 4, 4]]])

ОБНОВЛЕНИЕ . Вот версия, в которой используется stride_tricks, который не использует циклы for:

def window_argmin(arr):
    padded = np.pad(
        arr,
        [(0,)] * (arr.ndim-1) + [(1,)],
        'constant',
        constant_values=np.max(arr)+1,
    )
    slices = np.lib.stride_tricks.as_strided(
        padded,
        shape=arr.shape + (3,),
        strides=padded.strides + (padded.strides[-1],),
    )
    return (
        np.argmin(slices, axis=-1) + 
        np.arange(-1, arr.shape[-1]-1)
    )

Что помогло мне придумать решение для хитростей, так это эта недоработанная проблема с просьбой добавить функция скользящего окна, ссылающаяся на пример ее реализации, поэтому я просто адаптировал ее для это конкретный случай. Это все еще в значительной степени волшебство для меня, но это работает. 😁

Проверено и работает, как ожидается, для массивов разного числа измерений.

1
filbranden 21 Фев 2020 в 15:22
import numpy as np
array = [1, 18, 3, 6, 2]
array.insert(0, np.max(array) + 1)     # right shift of array
# [19, 1, 18, 3, 6, 2]

other_array = [ np.argmin(array[i-1:i+2]) + i - 2 for i in range(1, len(array)) ]

array.remove(np.max(array))            # original array
# [1, 18, 3, 6, 2]
0
Top Developer 21 Фев 2020 в 02:44