web-dev-qa-db-fra.com

Multiplier dans un tableau numpy

J'essaie de multiplier chacun des termes d'un tableau 2D par les termes correspondants d'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 de 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 un moyen élégant de le faire avec numpy? Merci beaucoup, Alex

61
Alex S

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 aussi transposer deux fois:

>>> (m.T * c).T
array([[ 0,  0,  0],
       [ 4,  5,  6],
       [14, 16, 18]])
86
jterrace

J'ai comparé les différentes options de vitesse et constaté qu'à ma grande surprise, toutes les options (à l'exception de diag) sont toutes aussi rapides. J'utilise personnellement

A * b[:, None]

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

enter image description here


Code pour reproduire l'intrigue:

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)",
)
23
Nico Schlömer

Encore une astuce (à partir de 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 me débrouille toujours dans ce nouvel outil einsum. J'ai 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 i en j, np.einsum('ij,j->ij',A,b), produit la matrice que Alex ne veut pas. Et np.einsum('ji,j->ji',A,b) effectue en effet la double transposition.

15
hpaulj

Vous pouvez également utiliser la multiplication de matrice (aussi appelé 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 plus élégant est probablement une question de goût.

15
James K

Pour les âmes perdues sur Google, utilisez numpy.expand_dims puis numpy.repeat fonctionnera 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]])
1