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)