Разница между numpy.форма массива (R, 1) и (R,)
на numpy, некоторые операции возвращаются в форме (R, 1) но какой-то доход (R,). Это сделает умножение матрицы более утомительным, так как явное  - это. Например, задана матрица 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)))
5 ответов:
1. Значение фигур в NumPy
вы пишете: "Я знаю буквально, что это список чисел и список списков, где весь список содержит только число", но это немного бесполезный способ подумать об этом.
лучший способ думать о массивах NumPy заключается в том, что они состоят из двух частей, a буфер данных который является просто блоком необработанных элементов, и view, который описывает, как интерпретировать данные буфера.
для например, если мы создаем массив из 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вы можете видеть, что второй индекс изменяется быстро, а первый индекс изменяется медленно. Если вы предпочитаете, чтобы это было наоборот, вы можете указать :
>>> 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)
разницу между
(R,)и(1,R)буквально количество индексов, которые вам нужно использовать.ones((1,R))является 2-D массив, который имеет только одну строку.ones(R)- это вектор. Как правило, если для переменной не имеет смысла иметь более одной строки/столбца, вы должны использовать вектор, а не матрицу с одноэлементным измерением.для вашего конкретного случая, есть несколько вариантов:
1) Просто сделайте второй аргумент вектором. Этот следующие прекрасно работает:
np.dot(M[:,0], np.ones(R))2) Если вы хотите matlab как матричные операции, используйте класс
matrixвместоndarray. Все матрицы вынуждены быть 2-D массивы, и оператор*умножение матрицы вместо поэлементного (поэтому вам не нужна точка). По моему опыту, это больше проблем, что стоит, но это может быть хорошо, если вы привыкли к matlab.
для своего базового класса массивов 2d-массивы не более особенные, чем 1d или 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 нет никаких векторов - по крайней мере, не в математическом смысле вектора как 1d.вы смотрели
np.atleast_2d(также _1d и _3d версии)?
фигура представляет собой кортеж. Если есть только 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)
1) причина не предпочесть форму
(R, 1)over(R,)что это излишне все усложняет. Кроме того, почему было бы предпочтительнее иметь форму(R, 1)по умолчанию для вектора длины-R вместо(1, R)? Лучше держать его простым и быть явным, когда вам требуются дополнительные измерения.2) для вашего примера, вы вычисляете внешний продукт, так что вы можете сделать это без
reshapeзвонок с помощьюnp.outer:np.outer(M[:,0], numpy.ones((1, R)))