Разница между 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).

так что мои вопросы являются:

  1. в чем разница между формой (R, 1) и (R,). Я знаю, что буквально это список чисел и список списков, где весь список содержит только число. Просто интересно, почему не дизайн numpy Так что это благоприятствует форме (R, 1) вместо (R,) для более простого перемножения матриц.

  2. есть ли лучшие способы для приведенного выше примера? Без явного изменения формы, как это:numpy.dot(M[:,0].reshape(R, 1), numpy.ones((1, R)))

5 197

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)))