прошу прощения за очень наивный вопрос. У меня есть эти строки цифрового кода с транспонированием в начале, а затем используются шаги и изменение формы. Интересно, смогу ли я, переупорядочив индексы в изменении формы и шаге, избавиться от первой функции транспонирования? Пожалуйста, может кто-нибудь помочь мне с этим?
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]])
Вы не можете сделать все это всего лишь шагами.
Массив (скажем, 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=(α,β)
становится тем, чем вы хотите.
Там, конечно, это возможно. 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),
)
Мне нравится обертка 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! Упрощение кода с помощью этой обертки кажется привлекательным. Я проведу расследование!
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]]
'''
Большое спасибо за вашу помощь и это ясное объяснение @chrslg!