Разница между формой numpy.array (R, 1) и (R,)
В numpy
некоторые операции возвращают форму (R, 1)
, но некоторые возвращаются (R,)
. Это сделает умножение матриц более утомительным, поскольку требуется явное reshape
. Например, учитывая матрицу M
, если мы хотим сделать, numpy.dot(M[:,0], numpy.ones((1, R)))
где R
- количество строк (конечно, та же проблема возникает и по столбцам). Мы получим matrices are not aligned
ошибку, так как M[:,0]
имеет форму, (R,)
но numpy.ones((1, R))
не имеет формы (1, R)
.
Итак, мои вопросы:
В чем разница между формой
(R, 1)
и(R,)
. Я знаю буквально, что это список чисел и список списков, где весь список содержит только число. Просто интересно, почему бы не спроектироватьnumpy
так, чтобы это благоприятствовало форме(R, 1)
вместо(R,)
более простого умножения матриц.Существуют ли лучшие способы для приведенного выше примера? Без явного изменения формы, подобного этому:
numpy.dot(M[:,0].reshape(R, 1), numpy.ones((1, R)))
Переведено автоматически
Ответ 1
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)
Ответ 2
Разница между (R,)
и (1,R)
буквально заключается в количестве индексов, которые вам нужно использовать. ones((1,R))
это двумерный массив, в котором всего одна строка. ones(R)
является вектором. Как правило, если для переменной не имеет смысла иметь более одной строки / столбца, вам следует использовать вектор, а не матрицу с одноэлементным измерением.
Для вашего конкретного случая есть несколько вариантов:
1) Просто сделайте второй аргумент вектором. Следующее работает нормально:
np.dot(M[:,0], np.ones(R))
2) Если вы хотите, чтобы matlab выполнял матричные операции, подобные matrix, используйте класс matrix
вместо ndarray
. Все матрицы принудительно преобразованы в двумерные массивы, и operator *
выполняет матричное умножение вместо поэлементного (поэтому вам не нужна точка). По моему опыту, это больше проблем, чем того стоит, но это может быть неплохо, если вы привыкли к matlab.
Ответ 3
Фигура представляет собой кортеж. Если есть только 1 измерение, фигура будет представлять собой одно число и просто пустой после запятой. Для 2+ измерений после всех запятых будет стоять число.
# 1 dimension with 2 elements, shape = (2,).
# Note there's nothing after the comma.
z=np.array([ # start dimension
10, # not a dimension
20 # not a dimension
]) # end dimension
print(z.shape)
(2,)
# 2 dimensions, each with 1 element, shape = (2,1)
w=np.array([ # start outer dimension
[10], # element is in an inner dimension
[20] # element is in an inner dimension
]) # end outer dimension
print(w.shape)
(2,1)
Ответ 4
Для базового класса массивов 2d массивы не более особенные, чем одномерные или 3d. Существуют некоторые операции, которые сохраняют размеры, некоторые уменьшают их, другие объединяют или даже расширяют их.
M=np.arange(9).reshape(3,3)
M[:,0].shape # (3,) selects one column, returns a 1d array
M[0,:].shape # same, one row, 1d array
M[:,[0]].shape # (3,1), index with a list (or array), returns 2d
M[:,[0,1]].shape # (3,2)
In [20]: np.dot(M[:,0].reshape(3,1),np.ones((1,3)))
Out[20]:
array([[ 0., 0., 0.],
[ 3., 3., 3.],
[ 6., 6., 6.]])
In [21]: np.dot(M[:,[0]],np.ones((1,3)))
Out[21]:
array([[ 0., 0., 0.],
[ 3., 3., 3.],
[ 6., 6., 6.]])
Другие выражения, которые дают тот же массив
np.dot(M[:,0][:,np.newaxis],np.ones((1,3)))
np.dot(np.atleast_2d(M[:,0]).T,np.ones((1,3)))
np.einsum('i,j',M[:,0],np.ones((3)))
M1=M[:,0]; R=np.ones((3)); np.dot(M1[:,None], R[None,:])
MATLAB начинался только с 2D-массивов. Более новые версии допускают большее количество измерений, но сохраняют нижнюю границу 2. Но вы все равно должны обратить внимание на разницу между матрицей строк и матрицей столбцов, имеющей форму (1,3)
v (3,1)
. Как часто вы писали [1,2,3].'
? Я собирался написать row vector
и column vector
, но с этим ограничением 2d в MATLAB нет никаких векторов - по крайней мере, не в математическом смысле вектора как одномерного.
Вы смотрели на np.atleast_2d
(также версии _1d и _3d)?
В более новом Python / numpy есть matmul
оператор
In [358]: M[:,0,np.newaxis]@np.ones((1,3))
Out[358]:
array([[0., 0., 0.],
[3., 3., 3.],
[6., 6., 6.]])
В numpy
поэлементное умножение в некотором смысле более простое, чем матричное. При сумме произведений в измерении размера 1 нет необходимости использовать dot/matmul
:
In [360]: M[:,0,np.newaxis]*np.ones((1,3))
Out[360]:
array([[0., 0., 0.],
[3., 3., 3.],
[6., 6., 6.]])
Здесь используется broadcasting
мощная функция, которая numpy
была всегда. MATLAB добавила ее совсем недавно.