Почему NumPy создает представление для x[[slice(None), 1, 2]]
В документации NumPy для расширенной индексации упоминается, что
Также признать, что
x[[1, 2, 3]]
вызовет расширенную индексацию, тогда какx[[1, 2, slice(None)]]
вызовет базовую нарезку.
Матрица хранится последовательно в памяти. Я понимаю, что имеет смысл взглянуть на x[[1, 2, slice(None)]]
так как элементы хранятся последовательно в памяти. Но почему Numpy возвращает взгляд на x[[1, slice(None), 2]]
или же x[[slice(None), 1, 2]]
, Например, предположим,
x = [[[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8]],
[[ 9, 10, 11],
[12, 13, 14],
[15, 16, 17]],
[[18, 19, 20],
[21, 22, 23],
[24, 25, 26]]]
x[[1, slice(None), 2]]
возвращает вид [11, 14, 17]
который не хранится последовательно в памяти, а также для x[[slice(None), 1, 2]]
который возвращается [5, 14, 23]
,
я бы хотел знать
Почему NumPy даже возвращает представление в этих двух случаях
Как NumPy обрабатывает адресацию памяти для создания этих представлений
2 ответа
Основное правило создания вида среза заключается в том, что к просматриваемым элементам можно обращаться со смещениями, шагами и счетчиками в исходном массиве.
Когда у вас есть индексирование, как x[[1, slice(None), 2]]
, вы получаете представление, потому что разрезание всей оси допускает определенное смещение, шаг и счет для представления среза с исходным массивом.
Например, с x = np.arange(27).reshape(3, 3, 3).copy()
, у нас есть:
In [79]: x_view = x[1, :, 2] # or equivalently x[[1, slice(None), 2]]
In [80]: x_view
Out[80]: array([11, 14, 17])
In [81]: x_view.base
Out[81]:
array([[[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8]],
[[ 9, 10, 11],
[12, 13, 14],
[15, 16, 17]],
[[18, 19, 20],
[21, 22, 23],
[24, 25, 26]]])
Тогда мы можем использовать numpy.byte_bounds
(не является частью общедоступного API, YMMV), чтобы проиллюстрировать смещение, чтобы получить наш фрагмент из нашего исходного массива.
In [82]: np.byte_bounds(x_view)[0] - np.byte_bounds(x_view.base)[0]
Out[82]: 88
Это имеет смысл, поскольку перед первым значением в срезе стоят 11 8-байтовых целых чисел, 11. NumPy вычисляет это смещение по формуле, которую вы можете увидеть здесь, используя шаги исходного массива.
In [93]: (x.strides * np.array([1, 0, 2])).sum()
Out[93]: 88
Шаги в нашей части просто становятся тем, к чему они стремились x
вдоль оси (или осей), на которой мы нарезаем. т.е. x.strides[1] == x_view.strides[0]
, Теперь вместе смещение, новые шаги и количество являются достаточной информацией для NumPy, чтобы просмотреть наш срез из нашего исходного массива.
In [94]: x_view.strides
Out[94]: (24,)
In [95]: x_view.size
Out[95]: 3
Наконец, причина, по которой вы запускаете модную индексацию с помощью x[[0, 1, 2]]
Например, потому что в отсутствие полного среза оси, как правило, невозможно сформулировать какое-либо новое смещение, порядок байтов, шаг и счет, чтобы мы могли просматривать срез с теми же базовыми данными.
Я люблю использовать __array_interface__
изучить атрибуты массива:
С вашим x
:
In [51]: x.__array_interface__
Out[51]:
{'data': (43241792, False),
'strides': None,
'descr': [('', '<i8')],
'typestr': '<i8',
'shape': (3, 3, 3),
'version': 3}
In [52]: x.strides
Out[52]: (72, 24, 8)
Это (3,3,3) массив. Последняя ось может быть отсканирована, шагая по 8 байт за раз, размер x.itemsize
, 3*8 шагов по рядам и 3*3*8 шагов по плоскостям (1-й тусклый).
In [53]: y = x[:,1,2]
In [54]: y.shape
Out[54]: (3,)
In [55]: y.strides
Out[55]: (72,)
In [56]: y.__array_interface__['data']
Out[56]: (43241832, False)
y
элементы можно адресовать пошагово по плоскостям, 3*3*8. 43241832 является отправной точкой, 40 байтов в буфер данных, 5*8
In [59]: y
Out[59]: array([ 5, 14, 23])
Таким образом, он начинается с 5-го элемента и переходит на одну плоскость за раз (9 элементов), всего 3 элемента.
Дело в том, что y.__array_interface__['data']
попадает в диапазон x
"данные" говорит мне, что y
это вид. Это представление, потому что комбинация этой начальной точки буфера, шагов и формы позволяет нам получить доступ ко всем значениям y
,
При расширенном индексировании невозможно (в общем) получить доступ к элементам с этими простыми параметрами, поэтому numpy
должен сделать копию данных.
Обратное представление возможно, просто изменив шаги и начальную точку "данных":
In [60]: z = y[::-1]
In [61]: z.__array_interface__
Out[61]:
{'data': (43241976, False),
'strides': (-72,),
'descr': [('', '<i8')],
'typestr': '<i8',
'shape': (3,),
'version': 3}
Транспонировать также меняет шаги:
In [62]: x.T.strides
Out[62]: (8, 24, 72)