Как создать представление массива `numpy`, которое повторяется

#python #arrays #numpy #numpy-slicing

#python #массивы #numpy #numpy-нарезка

Вопрос:

Как я могу вырезать numpy массив за пределы его формы, чтобы значения в массиве повторялись без необходимости сохранять весь массив в памяти? Вот что я хотел бы сделать:

 x = numpy.array([[1, 2], [3, 4]])
x[0:3, 0:3]
->
[[1, 2, 1, 2],
 [3, 4, 3, 4],
 [1, 2, 1, 2],
 [3, 4, 3, 4]]
  

Я знаю о numpy.repeat и numpy.tile , но оба они создают копию массива, и я хотел бы нарезать мой массив, как x[1238123:1238143,5328932:5328941] без необходимости создавать миллионы копий меньшего массива.

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

1. Не могли бы вы объяснить, почему? Я имею в виду, если вы знаете, что массив повторяется и период, который он повторяет, зачем вам обращаться к нему с 1238123:1238143,5328932:5328941 конкретно?

2. В любом случае, может x[(x.shape[0] - 1) % 1238123: (x.shape[0] - 1) % 1238143, (x.shape[1] - 1) % 5328932: (x.shape[1] - 1) % 5328941] быть тем, что вы ищете? Если это так, это может быть обернуто в функцию или с помощью подкласса np.ndarray

Ответ №1:

С strides помощью хитростей мы можем создать представление 4d:

 In [18]: x = numpy.array([[1, 2], [3, 4]])
In [19]: as_strided = np.lib.stride_tricks.as_strided
In [20]: X = as_strided(x, shape=(2,2,2,2), strides=(0,16,0,8))
In [21]: X
Out[21]: 
array([[[[1, 2],
         [1, 2]],

        [[3, 4],
         [3, 4]]],


       [[[1, 2],
         [1, 2]],

        [[3, 4],
         [3, 4]]]])
  

Которое может быть преобразовано в желаемый массив:

 In [22]: X.reshape(4,4)
Out[22]: 
array([[1, 2, 1, 2],
       [3, 4, 3, 4],
       [1, 2, 1, 2],
       [3, 4, 3, 4]])
  

Но это изменение формы создаст копию X .

Этот (2,2) массив может использоваться в вычислениях как (1,1,2,2) массив, который при необходимости расширяется до (2,2,2,2):

 In [25]: x[None,None,:,:]
Out[25]: 
array([[[[1, 2],
         [3, 4]]]])
In [26]: np.broadcast_to(x,(2,2,2,2))
Out[26]: 
array([[[[1, 2],
         [3, 4]],

        [[1, 2],
         [3, 4]]],


       [[[1, 2],
         [3, 4]],

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

Таким образом, трансляция позволяет нам использовать представление массива в более крупных вычислениях.

Ответ №2:

Массивы NumPy этого не поддерживают. Массив должен иметь последовательный шаг в каждом измерении, а в массиве, который вы хотите, этого не будет.

Вы могли бы реализовать свой собственный пользовательский тип для результата, но он не будет работать со скоростью NumPy и не будет напрямую совместим с NumPy — в лучшем случае, любая функция NumPy, которую вы пытались вызвать, должна была бы сначала создать реальный массив из вашего объекта.

Если вашему варианту использования нужны только небольшие фрагменты, как в вашем x[1238123:1238143,5328932:5328941] примере, вам, вероятно, лучше всего настроить конечные точки фрагмента до эквивалентных меньших значений, затем разбить на плитки и срезать.

Ответ №3:

Используйте numpy.ndarray.take дважды для 2D-массива (тройной для 3D-массива и т.д.). Каждый раз вы должны указывать другую ось. Для случая, который вам требовался:

 x.take(range(0, 4), mode='wrap', axis = 0).take(range(0, 4), mode='wrap', axis = 1)
  

что привело бы к

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