Se multiplier dans un tableau numpy

87

J'essaie de multiplier chacun des termes dans un tableau 2D par les termes correspondants dans un tableau 1D. C'est très facile si je veux multiplier chaque colonne par le tableau 1D, comme indiqué dans la fonction numpy.multiply . Mais je veux faire le contraire, multiplier chaque terme dans la rangée. En d'autres termes, je veux multiplier:

[1,2,3]   [0]
[4,5,6] * [1]
[7,8,9]   [2]

et obtenir

[0,0,0]
[4,5,6]
[14,16,18]

mais à la place je reçois

[0,2,6]
[0,5,12]
[0,8,18]

Est-ce que quelqu'un sait s'il existe une façon élégante de le faire avec numpy? Merci beaucoup, Alex

Alex S
la source
3
Ah j'ai compris tout comme j'ai soumis la question. Transposez d'abord la matrice carrée, multipliez, puis transposez la réponse.
Alex S
Mieux vaut transposer la ligne dans une matrice de colonnes alors vous n'avez pas à transposer à nouveau la réponse. Si A * Bvous deviez faire A * B[...,None]ce qui transpose Ben ajoutant un nouvel axe ( None).
askewchan
Merci, c'est vrai. Le problème est que lorsque vous avez un tableau 1D appelant .transpose () ou .T dessus, il ne le transforme pas en tableau de colonnes, il le laisse sous forme de ligne, donc pour autant que je sache, vous devez le définir comme une colonne dès le départ. Comme x = [[1],[2],[3]]ou quelque chose.
Alex S

Réponses:

114

Multiplication normale comme vous l'avez montré:

>>> import numpy as np
>>> m = np.array([[1,2,3],[4,5,6],[7,8,9]])
>>> c = np.array([0,1,2])
>>> m * c
array([[ 0,  2,  6],
       [ 0,  5, 12],
       [ 0,  8, 18]])

Si vous ajoutez un axe, il se multipliera comme vous le souhaitez:

>>> m * c[:, np.newaxis]
array([[ 0,  0,  0],
       [ 4,  5,  6],
       [14, 16, 18]])

Vous pouvez également transposer deux fois:

>>> (m.T * c).T
array([[ 0,  0,  0],
       [ 4,  5,  6],
       [14, 16, 18]])
jterrace
la source
Avec la nouvelle méthode d'axes, il est possible de multiplier deux tableaux 1D et de générer un tableau 2D. Par ex [a,b] op [c,d] -> [[a*c, b*c], [a*d, b*d]].
kon psych
47

J'ai comparé les différentes options pour la vitesse et j'ai constaté que - à ma grande surprise - toutes les options (sauf diag) sont également rapides. J'utilise personnellement

A * b[:, None]

(ou (A.T * b).T) parce que c'est court.

entrez la description de l'image ici


Code pour reproduire le tracé:

import numpy
import perfplot


def newaxis(data):
    A, b = data
    return A * b[:, numpy.newaxis]


def none(data):
    A, b = data
    return A * b[:, None]


def double_transpose(data):
    A, b = data
    return (A.T * b).T


def double_transpose_contiguous(data):
    A, b = data
    return numpy.ascontiguousarray((A.T * b).T)


def diag_dot(data):
    A, b = data
    return numpy.dot(numpy.diag(b), A)


def einsum(data):
    A, b = data
    return numpy.einsum("ij,i->ij", A, b)


perfplot.save(
    "p.png",
    setup=lambda n: (numpy.random.rand(n, n), numpy.random.rand(n)),
    kernels=[
        newaxis,
        none,
        double_transpose,
        double_transpose_contiguous,
        diag_dot,
        einsum,
    ],
    n_range=[2 ** k for k in range(14)],
    logx=True,
    logy=True,
    xlabel="len(A), len(b)",
)
Nico Schlömer
la source
2
Belle touche fournissant le code de l'intrigue. Merci.
rocksNwaves
17

Vous pouvez également utiliser la multiplication matricielle (aka produit scalaire):

a = [[1,2,3],[4,5,6],[7,8,9]]
b = [0,1,2]
c = numpy.diag(b)

numpy.dot(c,a)

Ce qui est le plus élégant est probablement une question de goût.

James K
la source
2
gentil, +1, je n'y ai pas pensé
jterrace
10
dotest vraiment exagéré ici. Vous faites juste des multiplications inutiles par 0 et des ajouts à 0.
Bi Rico
2
cela peut également déclencher des problèmes de mémoire au cas où vous voudriez multipier un vecteur nx1 dans une matrice nxd où d est supérieur à n.
Jonasson
Le vote négatif car c'est lent et utilise beaucoup de mémoire lors de la création de la diagmatrice dense .
Nico Schlömer
16

Encore une autre astuce (à partir de la v1.6)

A=np.arange(1,10).reshape(3,3)
b=np.arange(3)

np.einsum('ij,i->ij',A,b)

Je maîtrise la diffusion numpy ( newaxis), mais je trouve toujours mon chemin dans ce nouvel einsumoutil. J'avais donc joué un peu pour trouver cette solution.

Timings (en utilisant Ipython timeit):

einsum: 4.9 micro
transpose: 8.1 micro
newaxis: 8.35 micro
dot-diag: 10.5 micro

Incidemment, changer un ien j, np.einsum('ij,j->ij',A,b)produit la matrice dont Alex ne veut pas. Et np.einsum('ji,j->ji',A,b)fait, en effet, la double transposition.

hpaulj
la source
1
Si vous voulez chronométrer cela sur un ordinateur avec des baies suffisamment grandes pour que cela prenne au moins quelques millisecondes et que vous publiez les résultats ici avec les informations système pertinentes, ce serait très apprécié.
Daniel
1
avec un tableau plus grand (100x100), les nombres relatifs sont à peu près les mêmes. einsumm(25 micro) est deux fois plus rapide que les autres (dot-diag ralentit davantage). Il s'agit de np 1.7, fraîchement compilé avec 'libatlas3gf-sse2' et 'libatlas-base-dev' (Ubuntu 10.4, processeur unique). timeitdonne le meilleur de 10000 boucles.
hpaulj
1
C'est une excellente réponse et je pense que c'est celle qui aurait dû être acceptée. Cependant, le code écrit ci-dessus donne en fait la matrice qu'Alex essayait d'éviter (sur ma machine). Celui que Hpaulj a dit est faux est en fait le bon.
Yair Daon
Les horaires sont trompeurs ici. dot-diag est vraiment bien pire que les trois autres options, et einsum n'est pas plus rapide que les autres non plus.
Nico Schlömer
@ NicoSchlömer, ma réponse a presque 5 ans, et de nombreuses numpyversions sont en arrière.
hpaulj
1

Pour ces âmes perdues sur google, utiliser numpy.expand_dimsthen numpy.repeatfonctionnera, et fonctionnera également dans les cas de dimension supérieure (c'est-à-dire en multipliant une forme (10, 12, 3) par un (10, 12)).

>>> import numpy
>>> a = numpy.array([[1,2,3],[4,5,6],[7,8,9]])
>>> b = numpy.array([0,1,2])
>>> b0 = numpy.expand_dims(b, axis = 0)
>>> b0 = numpy.repeat(b0, a.shape[0], axis = 0)
>>> b1 = numpy.expand_dims(b, axis = 1)
>>> b1 = numpy.repeat(b1, a.shape[1], axis = 1)
>>> a*b0
array([[ 0,  2,  6],
   [ 0,  5, 12],
   [ 0,  8, 18]])
>>> a*b1
array([[ 0,  0,  0],
   [ 4,  5,  6],
   [14, 16, 18]])
Christopher Pratt
la source
-4

Pourquoi tu ne fais pas juste

>>> m = np.array([[1,2,3],[4,5,6],[7,8,9]])
>>> c = np.array([0,1,2])
>>> (m.T * c).T

??

Panos
la source
6
Cette approche exacte est déjà montrée dans la réponse acceptée, je ne vois pas en quoi cela ajoute quoi que ce soit.
Baum mit Augen