1. Значення форм у NumPy
Ви пишете: "Я буквально знаю, що це список номерів і список списків, де весь список містить лише число", але це трохи не корисний спосіб подумати про це.
Найкращий спосіб подумати про масиви NumPy - це те, що вони складаються з двох частин, буфера даних, який є лише блоком необроблених елементів, і подання, яке описує, як інтерпретувати буфер даних.
Наприклад, якщо ми створимо масив з 12 цілих чисел:
>>> a = numpy.arange(12)
>>> a
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
Потім a
складається з буфера даних, розташованого приблизно так:
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
і представлення, яке описує, як інтерпретувати дані:
>>> a.flags
C_CONTIGUOUS : True
F_CONTIGUOUS : True
OWNDATA : True
WRITEABLE : True
ALIGNED : True
UPDATEIFCOPY : False
>>> a.dtype
dtype('int64')
>>> a.itemsize
8
>>> a.strides
(8,)
>>> a.shape
(12,)
Тут форма (12,)
означає, що масив індексується одним індексом, який працює від 0 до 11. Концептуально, якщо ми позначимо цей єдиний індекс i
, масив a
виглядає так:
i= 0 1 2 3 4 5 6 7 8 9 10 11
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
Якщо ми переглянемо масив, це не змінить буфер даних. Натомість він створює новий погляд, який описує інший спосіб інтерпретації даних. Отже після:
>>> b = a.reshape((3, 4))
масив b
має той самий буфер даних, що і a
, але тепер він індексується двома індексами, які працюють від 0 до 2 і 0 до 3 відповідно. Якщо ми позначимо два індекси i
та j
, масив b
виглядає так:
i= 0 0 0 0 1 1 1 1 2 2 2 2
j= 0 1 2 3 0 1 2 3 0 1 2 3
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
це означає, що:
>>> b[2,1]
9
Ви можете бачити, що другий індекс змінюється швидко, а перший індекс повільно змінюється. Якщо ви хочете, щоб це було навпаки, ви можете вказати order
параметр:
>>> c = a.reshape((3, 4), order='F')
що призводить до масиву, індексованого так:
i= 0 1 2 0 1 2 0 1 2 0 1 2
j= 0 0 0 1 1 1 2 2 2 3 3 3
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
це означає, що:
>>> c[2,1]
5
Тепер має бути зрозуміло, що означає для масиву форму з одним або кількома розмірами розміром 1. Після:
>>> d = a.reshape((12, 1))
масив d
індексується двома індексами, перший з яких працює від 0 до 11, а другий індекс завжди дорівнює 0:
i= 0 1 2 3 4 5 6 7 8 9 10 11
j= 0 0 0 0 0 0 0 0 0 0 0 0
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
і так:
>>> d[10,0]
10
Розмір довжини 1 "вільний" (в деякому сенсі), тому нічого не заважає вам їхати в місто:
>>> e = a.reshape((1, 2, 1, 6, 1))
надання масиву, індексованого так:
i= 0 0 0 0 0 0 0 0 0 0 0 0
j= 0 0 0 0 0 0 1 1 1 1 1 1
k= 0 0 0 0 0 0 0 0 0 0 0 0
l= 0 1 2 3 4 5 0 1 2 3 4 5
m= 0 0 0 0 0 0 0 0 0 0 0 0
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
і так:
>>> e[0,1,0,0,0]
6
Докладніше про те, як реалізуються масиви, перегляньте документацію щодо внутрішніх ресурсів NumPy .
2. Що робити?
Оскільки numpy.reshape
тільки створюється новий погляд, вам не слід боятися його використовувати, коли це необхідно. Це правильний інструмент, який потрібно використовувати, коли ви хочете індексувати масив по-іншому.
Однак у довгих обчисленнях, як правило, можливо домогтися спорудження масивів з "правильною" формою в першу чергу і таким чином мінімізувати кількість змін і транспозицій. Але не бачачи фактичного контексту, який призвів до необхідності переформування, важко сказати, що слід змінити.
Приклад вашого запитання:
numpy.dot(M[:,0], numpy.ones((1, R)))
але це не реально. По-перше, це вираз:
M[:,0].sum()
обчислює результат простіше. По-друге, чи дійсно є щось особливе у стовпці 0? Можливо, що вам насправді потрібно:
M.sum(axis=0)