Эквивалентность между изменением формы, шагами и транспонированием? (нулевой)

прошу прощения за очень наивный вопрос. У меня есть эти строки цифрового кода с транспонированием в начале, а затем используются шаги и изменение формы. Интересно, смогу ли я, переупорядочив индексы в изменении формы и шаге, избавиться от первой функции транспонирования? Пожалуйста, может кто-нибудь помочь мне с этим?

import numpy as np

# Input data
array = np.array([[1, 60],
                  [2, 70],
                  [3, 80],
                  [4, 90]])
depth = 3

# Function
n_rows, n_cols = array.shape
array = array.T    # transpose function here
shape = array.shape[:-1] + (array.shape[-1] - depth + 1, depth - 1)
strides = array.strides + (array.strides[-1],)
array = np.lib.stride_tricks.as_strided(array, shape=shape, strides=strides)
reshaped = np.reshape(
    np.swapaxes(array, 0, 1),
    (n_rows-depth+1, (depth - 1) * n_cols),
)

Тогда ввод и результат:

array
Out[5]: 
array([[ 1, 60],
       [ 2, 70],
       [ 3, 80],
       [ 4, 90]])


reshaped
Out[6]: 
array([[ 1,  2, 60, 70],
       [ 2,  3, 70, 80]])
Структурированный массив Numpy
Структурированный массив Numpy
Однако в реальных проектах я чаще всего имею дело со списками, состоящими из нескольких типов данных. Как мы можем использовать массивы numpy, чтобы...
1
0
60
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

делай все с помощью as_strided

Вы не можете сделать все это всего лишь шагами.

Массив (скажем, 2d-массив здесь) — это базовый адрес addr, dtype, форма (H,W)s и шаги (st1, st2), например Arr[i,j] — это данные типа dtype, найденные по адресу addr + st1*i + st2*j

Конечно, вы можете иметь разные представления одного и того же массива, не меняя базовый адрес и изменяя остальные. Именно это и делают transpose, reshape (иногда), index (иногда) и as_strided.

Например, Arr.T — это массив, такой как Arr.T[i,j], который находится по адресу addr + st2*i + st1*j. Итак, Arr.T — это то же самое, что as_strided(Arr, shape=(W,H), strides=(st2, st1)).

Аналогично, Arr[:,::2] — это массив, такой как Arr[:,::2][i,j], который находится по адресу addr+st1*i+st2*j*2, поэтому он совпадает с as_strided(Arr, shape=(H,W//2), strides=(st1, st2*2)).

Я мог бы добавить еще много примеров. Но есть одно общее для всех них: в конце адреса новых элементов представления newView[i,j] можно вычислить по формуле addr + α.i + β.j. И нет таких α, β, которые могли бы привести к вашему результирующему массиву. Вы можете увидеть это очень легко. reshaped[0,0] равен 1, то есть тот, который находится по адресу addr + st1*0 + st2*0 в исходном массиве. А в новом должен быть на newAddr + α.0 + β.0.
Пока все хорошо, `newAddr = addr=

reshaped[0,1] равен 2, поэтому элемент по адресу addr + st1*1 + st2*0 в исходном массиве. И это должно быть по адресу addr + α*0 + β*1 в новом. Итак, β=st1. Все идет нормально.

reshaped[0,2] равно 60. Итак, элемент по адресу addr + st1*0 + st2*1 в исходном массиве. И это должно быть по адресу attr + α*0 + β*2 в новом. Итак, β=st2//2. Здесь у нас есть проблема. Потому что β=st1. Так должно быть st2=2st1. Но вы не можете это контролировать. Конечно, вы можете выбрать новый шаг просмотра. Но не старый. А в вашем примере мы знаем, что это не так. Если бы st2 был 2st1, то array[0,1] должно быть таким же, как array[2,0], что, очевидно, не так.

(Для α это легко: мы видим, что α=st1 работает во всех случаях).

Короче говоря, не существует возможных α и β, которые приводят к тому, что as_strided(Arr, shape=(W,H), strides=(α,β) становится тем, чем вы хотите.

просто транспонируйте+as_strided с помощью одного as_strided

Там, конечно, это возможно. array.T — это то же самое, что и массив, только с инвертированной формой и шагом.

Итак, если вы удалите array = array.T, просто

shape = array.shape[-1:0:-1] + (array.shape[0] - depth + 1, depth-1)
strides = array.strides[::-1] + (array.strides[0],)

То есть сделайте то же самое, но при условии, что то, что вы назвали array.shape[0] или array.strides[0], теперь array.shape[-1] или array.strides[-1] сейчас, от которого вы избавились array=array.T.

Итак, все вместе

import numpy as np

# Input data
array = np.array([[1, 60],
                  [2, 70],
                  [3, 80],
                  [4, 90]])
depth = 3

# Function
n_rows, n_cols = array.shape
shape = array.shape[-1:0:-1] + (array.shape[0] - depth + 1, depth-1)
strides = array.strides[::-1] + (array.strides[0],)
array = np.lib.stride_tricks.as_strided(array, shape=shape, strides=strides)
reshaped = np.reshape(
    np.swapaxes(array, 0, 1),
    (n_rows-depth+1, (depth - 1) * n_cols),
)

Большое спасибо за вашу помощь и это ясное объяснение @chrslg!

pierre_j 03.07.2024 18:21

Мне нравится обертка sliding_window_view для as_strided. Это проще и безопаснее в использовании.

In [353]: arr = np.array([[1, 60],
     ...:                   [2, 70],
     ...:                   [3, 80],
     ...:                   [4, 90]])

In [354]: W = np.lib.stride_tricks.sliding_window_view(arr,(2,2))

In [355]: W
Out[355]: 
array([[[[ 1, 60],
         [ 2, 70]]],


       [[[ 2, 70],
         [ 3, 80]]],


       [[[ 3, 80],
         [ 4, 90]]]])

In [356]: W.shape
Out[356]: (3, 1, 2, 2)

sliding_window_view производит больше, чем вам нужно, но легко индексируется:

In [357]: W[:2,0]
Out[357]: 
array([[[ 1, 60],
        [ 2, 70]],

       [[ 2, 70],
        [ 3, 80]]])

Теперь осталось только транспонировать (мне пришлось с этим поэкспериментировать) и окончательное изменение формы:

In [358]: W[:2,0].transpose(0,2,1)
Out[358]: 
array([[[ 1,  2],
        [60, 70]],

       [[ 2,  3],
        [70, 80]]])

In [359]: W[:2,0].transpose(0,2,1).reshape(2,4)
Out[359]: 
array([[ 1,  2, 60, 70],
       [ 2,  3, 70, 80]])

Или с помощью первоначального транспонирования просто подмножество и изменение формы:

In [366]: W = np.lib.stride_tricks.sliding_window_view(arr.T,(2,2))

In [367]: W[0,:2].reshape(2,4)
Out[367]: 
array([[ 1,  2, 60, 70],
       [ 2,  3, 70, 80]])

Так что мне все еще нужно одно транспонирование/замена.

Спасибо за предложение этого решения @hpaulj! Упрощение кода с помощью этой обертки кажется привлекательным. Я проведу расследование!

pierre_j 03.07.2024 18:22
import numpy as np

# Input data
array = np.array([[1, 60],
                  [2, 70],
                  [3, 80],
                  [4, 90]])
depth = 3

n_rows,n_cols =array.shape 
print(array.shape)#(4, 2)

shape = (n_rows - depth +1, depth, n_cols) 
print(shape)#(2, 3, 2)

strides = (array.strides[0],array.strides[0],array.strides[1])
print(strides)#(8, 8, 4)
array_strided = np.lib.stride_tricks.as_strided(x =array, shape=shape, strides=strides)
print(array_strided)
'''
[[[ 1 60]
  [ 2 70]
  [ 3 80]]

 [[ 2 70]
  [ 3 80]
  [ 4 90]]]
'''
print(array_strided.shape)#(2, 3, 2)

windowed_subarray = array_strided[:,:-1,:]
print(windowed_subarray)
'''
[[[ 1 60]
  [ 2 70]]

 [[ 2 70]
  [ 3 80]]]

'''
flattened  =array_strided[:,:-1,:].reshape(-1,(depth -1)* n_cols)
print(flattened)
'''
[[ 1 60  2 70]
 [ 2 70  3 80]]
'''

Другие вопросы по теме

Похожие вопросы