Mengalikan dalam larik numpy

89

Saya mencoba mengalikan masing-masing istilah dalam array 2D dengan istilah yang sesuai dalam array 1D. Ini sangat mudah jika saya ingin mengalikan setiap kolom dengan array 1D, seperti yang ditunjukkan pada numpy.multiply fungsi . Tapi saya ingin melakukan yang sebaliknya, mengalikan setiap suku di baris. Dengan kata lain saya ingin memperbanyak:

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

dan dapatkan

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

tapi malah saya dapatkan

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

Adakah yang tahu jika ada cara elegan untuk melakukannya dengan numpy? Terima kasih banyak, Alex

Alex S.
sumber
3
Ah, saya menemukan jawabannya tepat saat saya mengajukan pertanyaan. Pertama-tama, ubah urutan matriks kuadrat, kalikan, lalu ubah jawabannya.
Alex S
Lebih baik mengubah urutan baris menjadi matriks kolom, maka Anda tidak perlu mengubah urutan jawabannya. Jika A * BAnda harus melakukan A * B[...,None]transposisi yang mana Bdengan menambahkan sumbu baru ( None).
askewchan
Terima kasih, itu benar. Masalahnya adalah ketika Anda memiliki array 1D yang memanggil .transpose () atau .T di atasnya tidak mengubahnya menjadi array kolom, ia membiarkannya sebagai baris, sejauh yang saya tahu Anda harus mendefinisikannya sebagai kolom langsung dari kelelawar. Suka x = [[1],[2],[3]]atau sesuatu.
Alex S

Jawaban:

119

Perkalian normal seperti yang Anda tunjukkan:

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

Jika Anda menambahkan sumbu, itu akan mengalikan seperti yang Anda inginkan:

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

Anda juga dapat mengubah urutan dua kali:

>>> (m.T * c).T
array([[ 0,  0,  0],
       [ 4,  5,  6],
       [14, 16, 18]])
jterrace
sumber
Dengan metode sumbu baru dimungkinkan untuk mengalikan dua larik 1D dan menghasilkan larik 2D. Mis [a,b] op [c,d] -> [[a*c, b*c], [a*d, b*d]].
kon psych
50

Saya telah membandingkan opsi kecepatan yang berbeda dan menemukan bahwa - sangat mengejutkan saya - semua opsi (kecuali diag) sama cepatnya. Saya pribadi menggunakan

A * b[:, None]

(atau (A.T * b).T) karena pendek.

masukkan deskripsi gambar di sini


Kode untuk mereproduksi plot:

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(13)],
    xlabel="len(A), len(b)",
)
Nico Schlömer
sumber
2
Sentuhan yang bagus memberikan kode untuk plot. Terima kasih.
rocksNwaves
17

Anda juga bisa menggunakan perkalian matriks (alias perkalian titik):

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

numpy.dot(c,a)

Mana yang lebih elegan mungkin soal selera.

James K
sumber
2
bagus, +1, tidak memikirkan itu
jterrace
10
dotbenar-benar berlebihan di sini. Anda hanya melakukan perkalian yang tidak perlu dengan 0 dan penambahan menjadi 0.
Bi Rico
2
ini juga dapat memicu masalah memori jika Anda ingin menggandakan vektor nx1 ke matriks nxd di mana d lebih besar dari n.
Jonasson
Downvoting karena ini lambat dan menggunakan banyak memori saat membuat diagmatriks padat .
Nico Schlömer
16

Namun trik lain (pada v1.6)

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

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

Saya mahir dengan penyiaran numpy ( newaxis), tetapi saya masih menemukan cara untuk menggunakan einsumalat baru ini . Jadi saya bermain-main sedikit untuk menemukan solusi ini.

Pengaturan waktu (menggunakan waktu Ipython):

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

Kebetulan, mengubah ike j, np.einsum('ij,j->ij',A,b), menghasilkan matriks yang Alex tidak mau. Dan np.einsum('ji,j->ji',A,b), pada dasarnya, transpose ganda.

hpaulj
sumber
1
Jika Anda akan mengatur waktu ini di komputer dengan larik yang cukup besar sehingga membutuhkan setidaknya beberapa milidetik dan memposting hasilnya di sini bersama dengan informasi sistem yang relevan, itu akan sangat dihargai.
Daniel
1
dengan larik yang lebih besar (100x100) angka relatifnya hampir sama. einsumm(25 mikro) dua kali lebih cepat dari yang lain (dot-diag lebih melambat). Ini adalah np 1.7, baru saja dikompilasi dengan 'libatlas3gf-sse2' dan 'libatlas-base-dev' (Ubuntu 10.4, prosesor tunggal). timeitmemberikan yang terbaik dari 10.000 loop.
hpaulj
1
Ini adalah jawaban yang bagus dan saya pikir itu adalah jawaban yang seharusnya diterima. Namun, kode yang ditulis di atas sebenarnya memberikan matriks yang Alex coba hindari (di komputer saya). Yang hpaulj katakan salah sebenarnya yang benar.
Yair Daon
Pengaturan waktunya menyesatkan di sini. dot-diag sebenarnya jauh lebih buruk daripada tiga opsi lainnya, dan einsum juga tidak lebih cepat dari yang lain.
Nico Schlömer
@ NicoSchlömer, jawaban saya hampir berumur 5 tahun, dan banyak numpyversi sebelumnya.
hpaulj
1

Bagi mereka yang kehilangan jiwa di google, menggunakan numpy.expand_dimsthen numpy.repeatakan berhasil, dan juga akan berfungsi dalam kasus dimensi yang lebih tinggi (yaitu mengalikan bentuk (10, 12, 3) dengan (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
sumber
-4

Mengapa Anda tidak melakukannya saja

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

??

Panos
sumber
6
Pendekatan yang tepat itu sudah ditunjukkan dalam jawaban yang diterima, saya tidak melihat bagaimana ini menambahkan apa pun.
Baum mit Augen