Причина, почему 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)