Давайте попробуем 2d (4d после объединения):
In [374]: ones = np.ones((3,4),int)
In [375]: arr = np.array([[ones*0, ones],[ones*2, ones*3]])
In [376]: arr
Out[376]:
array([[[[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]],
[[1, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 1]]],
[[[2, 2, 2, 2],
[2, 2, 2, 2],
[2, 2, 2, 2]],
[[3, 3, 3, 3],
[3, 3, 3, 3],
[3, 3, 3, 3]]]])
In [377]: arr.shape
Out[377]: (2, 2, 3, 4)
Обратите внимание, что исходные элементы массива «вместе».arr
имеет свой собственный буфер данных с копиями исходных массивов, но он был сделан с относительно эффективными блочными копиями.
Мы можем легко транспонировать оси:
In [378]: arr.transpose(2,3,0,1)
Out[378]:
array([[[[0, 1],
[2, 3]],
[[0, 1],
[2, 3]],
...
[[0, 1],
[2, 3]]]])
Теперь это 12 (2,2) массивы.Это view
, использующий буфер данных arr's
.Он просто отличается по форме и шагам.Выполнение этой транспонирования довольно эффективно и не медленнее, когда arr
очень большой.И большая часть математики в транспонированном массиве будет почти такой же эффективной, как и в оригинальном arr
(из-за многопоточности).Если есть различия в скорости, это будет связано с кэшированием на глубоком уровне.
Но для некоторых действий потребуется копия.Например, транспонированный массив нельзя копировать без копии.Исходные 0, 1 и т. Д. Больше не вместе.
In [379]: arr.transpose(2,3,0,1).ravel()
Out[379]:
array([0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1,
2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3,
0, 1, 2, 3])
Я мог бы создать тот же массив 1d с помощью
In [380]: tarr = np.empty((3,4,2,2), int)
In [381]: tarr[...,0,0] = ones*0
In [382]: tarr[...,0,1] = ones*1
In [383]: tarr[...,1,0] = ones*2
In [384]: tarr[...,1,1] = ones*3
In [385]: tarr.ravel()
Out[385]:
array([0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1,
2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3,
0, 1, 2, 3])
. Это tarr
- это то, что вы пытаетесь создать. 'напрямую '.
Еще один способ взглянуть на эту конструкцию - присвоить значения массиву .flat
с шагами - вставьте 0 в каждый 4-й слот, 1 в соседние и т. д.:
In [386]: tarr.flat[0::4] = ones*0
In [387]: tarr.flat[1::4] = ones*1
In [388]: tarr.flat[2::4] = ones*2
In [389]: tarr.flat[3::4] = ones*3
Вот еще один «прямой» способ - используйте np.stack
(версия concatenate
) для создания (3,4,4) массива, который затем можно изменить:
np.stack((ones*0,ones*1,ones*2,ones*3),2).reshape(3,4,2,2)
То, что stack
, по сути:
In [397]: ones1 = ones[...,None]
In [398]: np.concatenate((ones1*0, ones1*1, ones1*2, ones1*3),axis=2)
Обратите внимание, что эта цель (3,4,2,2) может быть преобразована в (12,4) (и vv) бесплатно.Таким образом, возникает первоначальная проблема: легче ли построить (4,12) и транспонировать, или сначала построить (12,4)?Это действительно двумерная проблема, а не (m + n) d.