Np.dot автоматически транспонирует векторы? - PullRequest
0 голосов
/ 12 января 2019

Я пытаюсь вычислить моменты первого и второго порядка для портфеля акций (т.е. ожидаемый доход и стандартное отклонение).

expected_returns_annual
Out[54]: 
           ticker
adj_close  CNP       0.091859
           F        -0.007358
           GE        0.095399
           TSLA      0.204873
           WMT      -0.000943
dtype: float64

type(expected_returns_annual)
Out[55]: pandas.core.series.Series



weights = np.random.random(num_assets)
weights /= np.sum(weights)
returns = np.dot(expected_returns_annual, weights)

Таким образом, ожидаемый доход обычно рассчитывается как

(x1, ..., xn '* (R1, ..., Rn)

с x1, ..., xn являются весами с ограничением, что все веса должны суммироваться до 1, и 'означает, что вектор транспонирован.

Теперь мне немного интересно узнать о функции numpy dot, потому что

returns = np.dot(expected_returns_annual, weights)

и

returns = np.dot(expected_returns_annual, weights.T)

дают те же результаты.

Я также проверил форму весов. Т и весов.

weights.shape
Out[58]: (5,)
weights.T.shape
Out[59]: (5,)

Форма weights.T должна быть (, 5), а не (5,), но numpy отображает их как равные (я также пробовал np.transpose, но результат тот же)

Кто-нибудь знает, почему numpy ведет себя так? На мой взгляд, продукт np.dot автоматически формирует вектор правильно, почему векторный продукт работает хорошо. Это правильно?

С наилучшими пожеланиями Том

Ответы [ 4 ]

0 голосов
/ 12 января 2019

Форма весов. Т должно быть (, 5), а не (5,),

предполагает некоторую путаницу в атрибуте shape. shape - это обычный кортеж Python, то есть просто набор чисел, по одному для каждого измерения массива. Это аналог * size матрицы MATLAB.

(5,) - это просто способ отображения кортежа из 1 элемента. , требуется из-за более старой истории Python использования () в качестве простой группировки.

In [22]: tuple([5])
Out[22]: (5,)

Таким образом, , в (5,) не имеет специального значения numpy, а

In [23]: (,5)
  File "<ipython-input-23-08574acbf5a7>", line 1
    (,5)
     ^
SyntaxError: invalid syntax

Ключевое различие между numpy и MATLAB заключается в том, что массивы могут иметь любое количество измерений (до 32). MATLAB имеет нижнюю границу 2 *. 1023 *

В результате массив из 5 элементов numpy может иметь формы (5,), (1,5), (5,1), (1,5,1) `и т. Д.

Обработка массива 1d weight в вашем примере лучше всего объясняется в документации np.dot. Описать его как inner product мне кажется достаточно ясным. Но я также доволен

сумма произведений по последней оси a и второй по последней оси b

описание, с учетом случая, когда b имеет только одну ось.

(5,) with (5,n) => (n,)     # 5 is the common dimension
(n,5) with (5,) => (n,)
(n,5) with (5,1) => (n,1)

В

(x1,...,xn' * (R1,...,Rn)

вам не хватает )?

(x1,...,xn)' * (R1,...,Rn)

А * означает матричный продукт? Не поэлементный продукт (.* в MATLAB)? (R1,...,Rn) будет иметь размер (n, 1). (x1,...,xn)' размер (1, n). Продукт (1,1).

Кстати, это вызывает еще одну разницу. MATLAB расширяет размеры вправо (n, 1,1 ...). numpy расширяет их влево (1,1, n) (если необходимо при трансляции). Начальные размеры самые внешние. Это не такая критическая разница, как нижняя граница размера 2, но ее не следует игнорировать.

0 голосов
/ 12 января 2019

В NumPy транспонирование .T меняет порядок измерений, что означает, что оно ничего не делает с вашим одномерным массивом weights.

Это распространенный источник путаницы для людей, прибывающих из Matlab, в которых одномерные массивы не существуют. См. Транспонирование массива NumPy для более раннего обсуждения этого.

np.dot(x,y) имеет сложное поведение на многомерных массивах, но его поведение при подаче двух одномерных массивов очень просто: оно принимает внутренний продукт. Если бы мы хотели получить эквивалентный результат в виде матричного произведения строки и столбца, мы должны были бы написать что-то вроде

np.asscalar(x @ y[:, np.newaxis])

добавление конечного измерения к y, чтобы превратить его в «столбец», умножить, а затем преобразовать наш одноэлементный массив обратно в скаляр. Но np.dot(x,y) намного быстрее и эффективнее, поэтому мы просто используем это.


Редактировать: на самом деле, это было глупо с моей стороны. Конечно, вы можете просто написать умножение матриц x @ y, чтобы получить эквивалентное поведение np.dot для одномерных массивов, как указывает превосходный ответ tel.

0 голосов
/ 12 января 2019

Семантика np.dot не велика

Как указывает Доминик Поль, np.dot имеет очень разнородное поведение в зависимости от формы входов. Добавление к путанице, как указывает ОП в своем вопросе, учитывая, что weights - это одномерный массив, np.array_equal(weights, weights.T) - это True (array_equal проверяет равенство как значения, так и формы).

Рекомендация: используйте np.matmul или эквивалент @ вместо

Если вы только начинаете с Numpy, я советую вам полностью отказаться от np.dot. Не используйте его в своем коде вообще. Вместо этого используйте np.matmul или эквивалентный оператор @. Поведение @ более предсказуемо, чем поведение np.dot, и в то же время удобно использовать. Например, вы получите тот же точечный продукт для двух массивов 1D, которые есть в вашем коде, например:

returns = expected_returns_annual @ weights

Вы можете доказать себе, что это дает тот же ответ, что и np.dot с этим assert:

assert expected_returns_annual @ weights == expected_returns_annual.dot(weights)

Концептуально, @ обрабатывает этот случай путем преобразования двух 1D массивов в соответствующие 2D массивы (хотя реализация не обязательно делает это). Например, если у вас есть x с формой (N,) и y с формой (M,), если вы сделаете x @ y, фигуры будут преобразованы так, что:

x.shape == (1, N)
y.shape == (M, 1)

Полное поведение matmul / @

Вот что говорят документы о matmul / @ и формах входов / выходов :

  • Если оба аргумента двумерные, они умножаются как обычные матрицы.
  • Если любой из аргументов равен N-D, N> 2, он обрабатывается как стек матриц, находящихся в последних двух индексах, и транслируется соответствующим образом.
  • Если первый аргумент - 1-D, он преобразуется в матрицу, добавляя 1 к ее измерениям. После умножения матрицы префиксированный 1 удаляется.
  • Если вторым аргументом является 1-D, он повышается до матрицы, добавляя 1 к ее измерениям. После умножения матрицы добавленная 1 удаляется.

Примечания: аргументы для использования @ over dot

Как указывает hpaulj в комментариях, np.array_equal(x.dot(y), x @ y) для всех x и y, которые являются 1D или 2D массивами. Так почему я (и почему вы) предпочитаете @? Я думаю, что лучший аргумент в пользу использования @ заключается в том, что он помогает улучшить ваш код небольшими, но значительными способами:

  • @ явно является оператором умножения матриц. x @ y выдаст ошибку, если y - скаляр, тогда как dot сделает предположение, что вы на самом деле просто хотели поэлементное умножение Это может потенциально привести к трудно локализируемой ошибке, в которой dot молча возвращает результат мусора (я лично столкнулся с этим). Таким образом, @ позволяет вам явно указывать свои намерения в отношении поведения строки кода.

  • Поскольку @ является оператором, у него есть приятный короткий синтаксис для приведения различных типов последовательностей в массивы, без необходимости явного приведения их. Например, [0,1,2] @ np.arange(3) является допустимым синтаксисом.

    • Чтобы быть справедливым, хотя [0,1,2].dot(arr) явно недопустимо, np.dot([0,1,2], arr) допустимо (хотя более многословно, чем использование @).
  • Когда вам нужно расширить свой код для обработки множества умножений матриц вместо одного, ND падежа для @ являются концептуально простым обобщением / векторизацией нижних * D падежей.

0 голосов
/ 12 января 2019

У меня был тот же вопрос некоторое время назад. Кажется, что когда одна из ваших матриц является одномерной, numpy автоматически выяснит, что вы пытаетесь сделать.

В документации для функции точки есть более конкретное объяснение применяемой логики:

Если a и b являются одномерными массивами, это внутреннее произведение векторов (без комплексного сопряжения).

Если a и b являются двумерными массивами, это матричное умножение, но с использованием matmul или a @ b предпочтительнее.

Если a или b равен 0-D (скаляр), это эквивалентно умножению и использование numpy.multiply (a, b) или a * b является предпочтительным.

Если a является массивом N-D, а b является массивом 1-D, это произведение суммы на последняя ось а и б.

Если a является массивом N-D, а b является массивом M-D (где M> = 2), это сумма произведение на последнюю ось а и от второй к последней оси б:

...