Причина, почему numpy rollaxis настолько запутанна?
Поведение функции numpy rollaxis меня смущает.
В документации говорится:
Переверните указанную ось назад, пока она не окажется в заданном положении.
И для параметра start
:
Ось катится, пока она не окажется перед этим положением.
Для меня это уже как-то непоследовательно.
Хорошо, прямой пример (из документации):
>>> a = np.ones((3,4,5,6))
>>> np.rollaxis(a, 1, 4).shape
(3, 5, 6, 4)
Ось с индексом 1 (4) откатывается назад, пока она не окажется перед индексом 4.
Теперь, когда индекс start
меньше, чем индекс axis
, мы имеем такое поведение:
>>> np.rollaxis(a, 3, 1).shape
(3, 6, 4, 5)
Вместо переноса оси с индексом 3 перед индексом 1 она заканчивается на 1.
Почему? Почему ось не всегда скатывается к указанному индексу start
?
Ответы
Ответ 1
Большая часть путаницы вытекает из нашей человеческой интуиции - как мы думаем о перемещении оси. Мы могли бы указать несколько шагов качки (назад или вперед 2 шага) или местоположение в конечном кортеже формы или местоположение относительно исходной формы.
Я думаю, что ключом к пониманию rollaxis
является фокусировка на слотах в исходной форме. Самое общее утверждение, которое я могу придумать, это:
Roll a.shape[axis]
в позицию до a.shape[start]
before
в этом контексте означает то же, что и в списке insert()
. Таким образом, можно вставить до конца.
Основное действие rollaxis
:
axes = list(range(0, n))
axes.remove(axis)
axes.insert(start, axis)
return a.transpose(axes)
Если axis<start
, тогда start-=1
для учета действия remove
.
Отрицательные значения получают +=n
, поэтому rollaxis(a,-2,-3)
совпадает с np.rollaxis(a,2,1)
. например a.shape[-3]==a.shape[1]
. Список insert
также допускает отрицательную позицию вставки, но rollaxis
не использует эту функцию.
Итак, ключи понимают, что remove/insert
пара действий и понимание transpose(x)
.
Я подозреваю, что rollaxis
предназначен для более интуитивной версии transpose
. Достигает ли это того или нет, это другой вопрос.
Вы предлагаете либо отказаться от start-=1
, либо применить к нему плату
Опущение это не изменит ваши 2 примера. Это влияет только на случай rollaxis(a,1,4)
, а axes.insert(4,1)
совпадает с axes.insert(3,1)
, когда axes
равно [0,2,3]
. 1
по-прежнему помещается в конец. Изменение этого теста немного:
np.rollaxis(a,1,3).shape
# (3, 5, 4, 6) # a.shape[1](4) placed before a.shape[3](6)
без -=1
# transpose axes == [0, 2, 3, 1]
# (3, 5, 6, 4) # the 4 is placed at the end, after 6
Если вместо этого -=1
применяется всегда
np.rollaxis(a,3,1).shape
# (3, 6, 4, 5)
становится
(6, 3, 4, 5)
теперь 6
находится перед 3
, который был оригиналом a.shape[0]
. После рулона 3
находится a.shape[1]
. Но это другая спецификация roll
.
Это сводится к определению start
. Является ли позиция в исходном порядке или позицией в возвращенном порядке?
Если вы предпочитаете думать о start
как о позиции индекса в конечной форме, было бы проще удалить часть before
и просто сказать "переместить axis
в dest
slot '?
myroll(a, axis=3, dest=0) => (np.transpose(a,[3,0,1,2])
myroll(a, axis=1, dest=3) => (np.transpose(a,[0,2,3,1])
Простое отключение теста -=1
может сделать трюк (опустив обработку отрицательных чисел и границ)
def myroll(a,axis,dest):
x=list(range(a.ndim))
x.remove(axis)
x.insert(dest,axis)
return a.transpose(x)
Ответ 2
NumPy v1.11 и new включают новую функцию moveaxis
, которую я рекомендую использовать вместо rollaxis
(отказ от ответственности: Я написал это!). Ось источника всегда заканчивается в пункте назначения без каких-либо смежных проблем, связанных с вопросом, в зависимости от того, больше или меньше start
end
:
import numpy as np
x = np.zeros((1, 2, 3, 4, 5))
for i in range(5):
print(np.moveaxis(x, 3, i).shape)
Результаты в:
(4, 1, 2, 3, 5)
(1, 4, 2, 3, 5)
(1, 2, 4, 3, 5)
(1, 2, 3, 4, 5)
(1, 2, 3, 5, 4)
Ответ 3
a = np.arange(1*2*3*4*5).reshape(1,2,3,4,5)
np.rollaxis(a,axis,start)
'axis' - это индекс оси, которая должна быть перемещена, начиная с 0. В моем примере ось в положении 0 равна 1.
'start' - это индекс (снова начинающийся с 0) оси, который мы хотели бы перенести нашу выбранную ось раньше.
Итак, если start = 2, ось в положении 2 равна 3, поэтому выбранная ось будет перед 3.
Примеры:
>>> np.rollaxis(a,0,2).shape # the 1 will be before the 3.
(2, 1, 3, 4, 5)
>>> np.rollaxis(a,0,3).shape # the 1 will be before the 4.
(2, 3, 1, 4, 5)
>>> np.rollaxis(a,1,2).shape # the 2 will be before the 3.
(1, 2, 3, 4, 5)
>>> np.rollaxis(a,1,3).shape # the 2 will be before the 4.
(1, 3, 2, 4, 5)
Итак, после рулона число на оси перед рулоном будет размещено непосредственно перед номером, начинающимся до рулона.
Если вы думаете о том, что такое rollaxis, это очень просто и имеет прекрасный смысл, хотя странно, что они решили его спроектировать таким образом.
Итак, что происходит, когда ось и начало одинаковы? Ну, вы, очевидно, не можете поставить число перед собой, поэтому число не перемещается, и инструкция становится no-op.
Примеры:
>>> np.rollaxis(a,1,1).shape # the 2 can't be moved to before the 2.
(1, 2, 3, 4, 5)
>>> np.rollaxis(a,2, 2).shape # the 3 can't be moved to before the 3.
(1, 2, 3, 4, 5)
Как насчет перемещения оси до конца? Ну, после номера нет номера, но вы можете указать начало как после окончания.
Пример:
>>> np.rollaxis(a,1,5).shape # the 2 will be moved to the end.
(1, 3, 4, 5, 2)
>>> np.rollaxis(a,2,5).shape # the 3 will be moved to the end.
(1, 2, 4, 5, 3)
>>> np.rollaxis(a,4,5).shape # the 5 is already at the end.
(1, 2, 3, 4, 5)