Как увеличить выборку массива numpy с ближайшим соседом без сохранения повторяющихся значений?

#python #numpy

#питон #numpy #python

Вопрос:

Допустим, у меня есть массив 3×3 a и я хотел бы увеличить его до массива 30×30 b с интерполяцией ближайшего соседа.

Можно ли использовать метод, который на самом деле не хранит повторяющиеся значения? Что-то похожее на то, как broadcasting работает в numpy .

например, я хотел бы иметь такой объект, чтобы при вызове b[x, x] с 0 < x < 10 я получал a[0, 0] .

Комментарии:

1. Я не думаю, что это возможно. У вас может быть массив (3,10,3,10), который является постоянным вдоль осей 1 и 3 и хранит только 9 значений.

Ответ №1:

Я не верю, что есть какой-либо способ сделать это с помощью numpy. Способ работы широковещательной передачи в numpy заключается в том, что каждая ось имеет параметр «stride», который управляет вычислением следующего элемента вдоль оси. Так, например:

 In [1]: a = np.arange(10)

In [2]: a
Out[2]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

In [3]: b = a[::2]

In [4]: b
Out[4]: array([0, 2, 4, 6, 8])

In [5]: b.strides
Out[5]: (16,)

In [6]: a.strides
Out[6]: (8,)
  

В этом случае вы можете видеть, что b — это просто вид a, полученный путем удвоения шага по первому измерению. Итак, при доступе b[1] смещение вычисляется как b.__array_interface__['data'][0] b.strides[0] .

В вашем случае вам, по сути, нужен нелинейный шаг, который не поддерживается.

Вы, конечно, могли бы добиться этого самостоятельно, самостоятельно вычисляя индексы, т. Е.:

 a[x//10,x//10]
  

Комментарии:

1. Спасибо, я не знал о strides представлении. Считаете ли вы, что реализация C процедуры для выполнения разделения между двумя массивами разной формы, зацикливания на каждом элементе и последующего вызова a[x//10, x//10] может привести к аналогичной производительности по сравнению с простым разделением между двумя массивами одинаковой формы большего массива? например, сэкономит ли это память и время масштабирования?

Ответ №2:

Вы могли бы получить объект, подобный тому, который вы описываете, создав класс, который обертывает массив numpy и реализует пользовательский __getitem__ метод. Это может выглядеть примерно так, как показано ниже, где factor — коэффициент, с которым вы хотите увеличить выборку.

 class UpSampled:
    __slots__ = ('arr', 'factor')

    def __init__(self, arr, factor):
        self.arr = arr
        self.factor = factor

    def __getitem__(self, key):
        return self.arr[key // self.factor]
  

Затем вы бы использовали его, как показано ниже:

 o = UpSampled(np.array([
    UpSampled(np.array([0, 1, 2]), 10),
    UpSampled(np.array([3, 4, 5]), 10),
    UpSampled(np.array([6, 7, 8]), 10),
]), 10)

print(o[23][13])  # prints 7
  

Если вам нужно, чтобы объект был итерируемым, вы бы также реализовали __next__ и __iter__ :

 class UpSampled:
    __slots__ = ('arr', 'factor', '__index')

    def __init__(self, arr, factor):
        self.arr = arr
        self.factor = factor
        self.__index = 0

    def __getitem__(self, key):
        return self.arr[key // self.factor]

    def __iter__(self):
        self.__index = 0
        return self

    def __next__(self):
        try:
            result = self[self.__index]
        except IndexError:
            raise StopIteration
        self.__index  = 1
        return result
  

Хотя я не уверен, что это будет хорошо работать с библиотеками, которые специально ожидают numpy-массив.

Комментарии:

1. Большое спасибо за предоставление этой хорошей реализации. Проблема в том, что в принципе то, что я хочу сделать, это то, b/a где a shape фактически выполняется с увеличенной дискретизацией по требованию без сохранения, а затем с использованием векторизации numpy для математической операции. Что-то, что здесь не сработает. Мне нужно будет выполнить такую операцию с изображениями размером 10k x 10k, поэтому для повышения дискретизации и сохранения требуется время и память.