6
votes

Np.dot transpose-t-il automatiquement les vecteurs?

J'essaie de calculer les moments du premier et du deuxième ordre pour un portefeuille d'actions (c'est-à-dire le rendement attendu et l'écart type).

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

Le rendement attendu est donc normalement calculé par

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

avec x1, ..., xn sont des poids avec une contrainte que tous les poids doivent totaliser 1 et 'signifie que le vecteur est transposé.

Maintenant, je m'interroge un peu sur le numpy dot, car

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

et

returns = np.dot(expected_returns_annual, weights)

donnent les mêmes résultats.

J'ai testé aussi la forme des poids.T et des poids.

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)

La forme des poids.T doit être (, 5) et non (5,), mais numpy les affiche comme égales ( J'ai aussi essayé np.transpose, mais il y a le même résultat)

Est-ce que quelqu'un sait pourquoi numpy se comporte de cette façon? À mon avis, le produit np.dot façonne automatiquement le vecteur correctement pourquoi pour que le vecteur produit fonctionne bien. Est-ce correct?

Meilleures salutations Tom


1 commentaires

(, 5) n'est pas une expression python valide. (5,) est un tuple à un seul élément. En tant que forme, cela signifie un tableau à 5 éléments 1d, pas un vecteur de ligne ou de colonne au sens matlab. Transpose change l'ordre des axes. Avec un seul axe, la transposée d'un 1d est elle-même.


4 Réponses :


1
votes

J'ai eu la même question il y a quelque temps. Il semble que lorsqu'une de vos matrices est unidimensionnelle, numpy comprendra automatiquement ce que vous essayez de faire.

La documentation pour la fonction point a une explication plus spécifique de la logique appliquée:

Si a et b sont des tableaux 1-D, c'est le produit interne de vecteurs (sans conjugaison complexe).

Si a et b sont des tableaux 2-D, il s'agit d'une multiplication matricielle, mais en utilisant matmul ou a @ b est préférable.

Si a ou b vaut 0-D (scalaire), cela équivaut à multiplier et il est préférable d'utiliser numpy.multiply (a, b) ou a * b.

Si a est un tableau N-D et b est un tableau 1-D, c'est un produit somme sur le dernier axe de a et b.

Si a est un tableau N-D et b est un tableau M-D (où M> = 2), c'est une somme produit sur le dernier axe de a et l'avant-dernier axe de b:


0 commentaires

1
votes

Dans NumPy, une transposition .T inverse l'ordre des dimensions, ce qui signifie qu'elle ne fait rien à votre tableau unidimensionnel poids .

C'est une source courante de confusion pour les personnes venant de Matlab, dans lequel les tableaux unidimensionnels n'existent pas. Voir Transposer un tableau NumPy pour une discussion antérieure à ce sujet.

np.dot (x, y) a un comportement compliqué sur les tableaux de plus grande dimension, mais son comportement lorsqu'il est alimenté en deux tableaux unidimensionnels est très simple: il prend le produit interne. Si nous voulions obtenir le résultat équivalent en tant que produit matriciel d'une ligne et d'une colonne, nous devrons écrire quelque chose comme

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

en ajoutant une dimension de fin à y pour le transformer en "colonne", multiplier, puis reconvertir notre tableau à un élément en un scalaire. Mais np.dot (x, y) est beaucoup plus rapide et efficace, donc nous utilisons juste ça.


Edit: en fait, c'était stupide de ma part. Vous pouvez, bien sûr, simplement écrire la multiplication matricielle x @ y pour obtenir un comportement équivalent à np.dot pour les tableaux à une dimension, comme le souligne l'excellente réponse de tel. p>


2 commentaires

Il y a de bonnes informations dans cette réponse, mais vous faites en sorte que l'utilisation de @ semble trop compliquée. Si vous faites simplement x @ y , vous obtiendrez votre produit interne attendu (étant donné que x et y sont tous deux 1D ). matmul fait la promotion de la forme automatiquement.


oui, je viens de m'en rendre compte et j'ai fait un montage. j'ai oublié ce comportement de x @ y - merci!



9
votes

La sémantique de np.dot n'est pas géniale

Comme le souligne Dominique Paul, np.dot a un comportement très hétérogène en fonction des formes des entrées. Ajoutant à la confusion, comme le souligne l'OP dans sa question, étant donné que weights est un tableau 1D, np.array_equal (weights, weights.T) est Vrai ( array_equal teste l'égalité de la valeur et de la forme).

Recommandation: utilisez np.matmul ou l'équivalent @ à la place

Si vous êtes quelqu'un qui débute avec Numpy, je vous conseille d'abandonner complètement np.dot . Ne l'utilisez pas du tout dans votre code. À la place, utilisez np.matmul ou l'opérateur équivalent @ . Le comportement de @ est plus prévisible que celui de np.dot , tout en restant pratique à utiliser. Par exemple, vous obtiendrez le même produit scalaire pour les deux tableaux 1D que vous avez dans votre code comme ceci:

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

Vous pouvez vous prouver que cela donne la même réponse que np.dot avec cet assert:

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

Conceptuellement, @ gère ce cas en promouvant les deux tableaux 1D vers les tableaux 2D appropriés (bien que l'implémentation ne le fasse pas nécessairement). Par exemple, si vous avez x avec la forme (N,) et y avec la forme (M,) , si vous faites x @ y , les formes seront promues de telle sorte que:

returns = expected_returns_annual @ weights

Comportement complet de matmul / @

Voici ce que le les documents ont à dire sur matmul / @ et les formes des entrées / sorties :

  • Si les deux arguments sont 2D, ils sont multipliés comme des matrices conventionnelles.
  • Si l'un des arguments est N-D, N> 2, il est traité comme une pile de matrices résidant dans les deux derniers index et diffusé en conséquence.
  • Si le premier argument est 1-D, il est promu en matrice en ajoutant un 1 à ses dimensions. Après la multiplication de la matrice, le 1 préfixé est supprimé.
  • Si le deuxième argument est 1-D, il est promu en matrice en ajoutant un 1 à ses dimensions. Après la multiplication de la matrice, le 1 ajouté est supprimé.

Notes: les arguments pour utiliser @ sur dot

Comme le souligne hpaulj dans les commentaires, np. array_equal (x.dot (y), x @ y) pour tous les x et y qui sont 1D ou Tableaux 2D . Alors pourquoi est-ce que je préfère (et pourquoi devriez-vous) @ ? Je pense que le meilleur argument pour utiliser @ est que cela aide à améliorer votre code de manière petite mais significative:

  • @ est explicitement un opérateur de multiplication matricielle. x @ y générera une erreur si y est un scalaire, alors que dot fera l'hypothèse que vous vouliez en fait juste une multiplication élémentaire. Cela peut potentiellement entraîner un bogue difficile à localiser dans lequel dot renvoie silencieusement un résultat de garbage (j'ai personnellement rencontré celui-là). Ainsi, @ vous permet d'être explicite sur votre propre intention pour le comportement d'une ligne de code.

  • Étant donné que @ est un opérateur, il a une syntaxe courte et intéressante pour contraindre divers types de séquence en tableaux, sans avoir à les convertir explicitement. Par exemple, [0,1,2] @ np.arange (3) est une syntaxe valide.

    • Pour être honnête, alors que [0,1,2] .dot (arr) n'est évidemment pas valide, np.dot ([0,1,2], arr) est valide (bien que plus détaillé que d'utiliser @ ).
  • Lorsque vous avez besoin d'étendre votre code pour traiter de nombreuses multiplications matricielles au lieu d'une seule, les cas ND pour @ sont une généralisation conceptuellement simple / vectorisation des cas D inférieurs.


5 commentaires

Je ne vois aucune réelle différence (ou avantage) entre dot et matmul lorsqu'il s'agit de 1 et 2 tableaux. 1d @ 2d produit la même chose un point (1d, 2d) , etc. C'est peut-être parce que j'ai utilisé point assez longtemps, mais le discours matmul sur les dimensions en préfixe ou en report semble inutilement détaillé.


Je le pensais aussi au début, mais après l'avoir mâché un peu, je pense que le concept de promotion de la forme aide à rendre le comportement de tous les 1D x ND et ND x 1D cas plus concrets / faciles à dériver dans votre tête.


Pourtant, vous avez raison (je pense?) Qu'il n'y a aucun cas dans lequel dot vs matmul fait une différence pour toute combinaison de 1D et Tableaux 2D . Ainsi, en suivant le Zen de Python, dans n'importe quelle base de code donnée, vous devez choisir l'un ou l'autre et vous en tenir à lui. Ma préférence va à matmul , car @ est tout simplement chouette, et quand vous avez besoin d'étendre aux tableaux ND , je n'ai personnellement jamais trouvé un cas d'utilisation du comportement ND de dot .


Un grand merci à toutes les réponses sur ce post. Ils font la lumière sur l'utilisation de np.dot pour moi. J'ai juste un background économique et j'ai donc supposé que l'attribut shape de Python devrait afficher les dimensions des tableaux 1d différents (équivalent aux mathématiques).


Oui, le cas ND pour dot est déroutant et rarement utile. C'est l'une des principales raisons pour lesquelles matmul a été ajouté. Mais j'aime aussi le meilleur contrôle (et la clarté) de la notation einsum .



0
votes

La forme des poids: T doit être (, 5) et non (5,),

suggère une certaine confusion sur l'attribut shape . shape est un tuple Python ordinaire, c'est-à-dire juste un ensemble de nombres, un pour chaque dimension du tableau. C'est analogue à la taille d'une matrice MATLAB.

(5,) est juste la façon d'afficher un tuple à 1 élément. Le , est requis en raison de l'ancien historique Python d'utilisation de () comme simple regroupement.

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

Ainsi, le , dans (5,) n'a pas de signification spéciale numpy , et

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

Une différence clé entre numpy et MATLAB est que les tableaux peuvent avoir n'importe quel nombre de dimensions (jusqu'à 32). MATLAB a une limite inférieure de 2.

Le résultat est qu'un tableau numpy à 5 éléments peut avoir des formes (5,) , ( 1,5) , (5,1) , (1,5,1) `, etc.

La gestion d'un poids 1d dans votre exemple est mieux expliqué dans la documentation np.dot . Le décrire comme produit interne me semble assez clair. Mais je suis également satisfait du

additionner le produit sur le dernier axe de a et l'avant-dernier axe de b

description, ajustée pour le cas où b n'a qu'un seul axe.

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

In:

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

il vous manque un )?

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

Et le * signifie produit matriciel ? Pas un produit élémentaire (. * dans MATLAB)? (R1, ..., Rn) aurait la taille (n, 1). (x1, ..., xn) ' taille (1, n). Le produit (1,1) .

Au fait, cela soulève une autre différence. MATLAB étend les dimensions vers la droite (n, 1,1 ...). numpy les étend vers la gauche (1,1, n) (si nécessaire par diffusion). Les dimensions initiales sont les plus extérieures. Ce n'est pas une différence aussi critique que la limite inférieure de taille 2, mais ne doit pas être ignorée.


1 commentaires

Bien sûr, je veux dire (x1, ..., xn) '* (R1, ..., Rn) et non (x1, ..., xn' * (R1, ..., Rn). Et vous avez raison avec vos hypothèses selon lesquelles il s'agit d'un produit matriciel, et donc le résultat est juste un scalaire (dim (1,1)).