Uyuşmuş bir dizide çarpma


89

Bir 2B dizideki her bir terimi 1B dizisindeki karşılık gelen terimlerle çarpmaya çalışıyorum. Numpy.multiply işlevinde gösterildiği gibi, her sütunu 1B dizisi ile çarpmak istersem bu çok kolaydır . Ama tam tersini yapmak istiyorum, her terimi satırda çarpın. Başka bir deyişle, çarpmak istiyorum:

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

ve Al

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

ama onun yerine alırım

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

Bunu numpy ile yapmanın zarif bir yolu olup olmadığını bilen var mı? Çok teşekkürler Alex


3
Ah, soruyu sorduğum gibi anladım. Önce kare matrisi ters çevirin, çarpın, sonra cevabı değiştirin.
Alex S

Satırı bir sütun matrisine dönüştürmek daha iyidir, o zaman cevabı yeniden dönüştürmeniz gerekmez. Eğer A * Byapmanız gereken ediyorum A * B[...,None]hangi transposes Byeni bir eksen ekleyerek ( None).
askewchan

Teşekkürler, bu doğru. Sorun, .transpose () veya .T'yi çağıran bir 1D diziniz olduğunda, onu bir sütun dizisine dönüştürmüyor, bir satır olarak bırakıyor, bildiğim kadarıyla bir sütun olarak tanımlamanız gerekiyor hemen yarasa. Gibi x = [[1],[2],[3]]veya başka bir şey.
Alex S

Yanıtlar:


119

Gösterdiğiniz gibi normal çarpma:

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

Bir eksen eklerseniz, istediğiniz şekilde çarpacaktır:

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

Ayrıca iki kez transpoze edebilirsiniz:

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

Yeni eksen yöntemi ile iki 1D dizisini çarpmak ve bir 2D dizi oluşturmak mümkündür. Örn [a,b] op [c,d] -> [[a*c, b*c], [a*d, b*d]].
kon psych

50

Farklı hız seçeneklerini karşılaştırdım ve - şaşırtıcı bir şekilde - tüm seçeneklerin (hariç diag) eşit derecede hızlı olduğunu gördüm . Şahsen kullanıyorum

A * b[:, None]

(veya (A.T * b).T) kısa olduğu için.

görüntü açıklamasını buraya girin


Grafiği yeniden oluşturmak için kod:

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)",
)

2
Arsa kodunu sağlamak güzel bir dokunuş. Teşekkürler.
rockNwaves

17

Matris çarpımını da kullanabilirsiniz (diğer adıyla nokta çarpımı):

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

numpy.dot(c,a)

Hangisi daha zarif, muhtemelen bir zevk meselesidir.


2
güzel, +1, bunu düşünmedim
jterrace

10
dotburada gerçekten çok fazla. Sadece 0 ile gereksiz çarpma ve 0'a eklemeler yapıyorsunuz.
Bi Rico

2
bu aynı zamanda bir nx1 vektörünü d'nin n'den büyük olduğu bir nxd matrisiyle çarpmak istediğinizde bellek sorunlarını tetikleyebilir.
Jonasson

Bu şekilde Downvoting yavaş ve yoğun oluştururken çok fazla bellek kullanır diagmatris.
Nico Schlömer

16

Yine başka bir numara (v1.6'dan itibaren)

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

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

Uyuşmuş yayıncılık konusunda uzmanım ( newaxis), ancak yine de bu yeni einsumaraçta yolumu buluyorum . Bu yüzden bu çözümü bulmak için biraz uğraştım.

Zamanlamalar (Ipython timeit kullanarak):

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

Bu arada, değişen iiçin j, np.einsum('ij,j->ij',A,b)Alex istemediğini matris oluşturur. Ve np.einsum('ji,j->ji',A,b)aslında çift devrik yapar.


1
Bunu, en az birkaç milisaniye sürecek kadar büyük dizilerle bilgisayarda zamanlarsanız ve sonuçları ilgili sistem bilgilerinizle birlikte buraya gönderirseniz çok memnun oluruz.
Daniel

1
daha büyük bir dizi (100x100) ile göreli sayılar yaklaşık olarak aynıdır. einsumm(25 mikro), diğerlerinden iki kat daha hızlıdır (nokta-diag daha fazla yavaşlar). Bu np 1.7'dir, 'libatlas3gf-sse2' ve 'libatlas-base-dev' (Ubuntu 10.4, tek işlemci) ile yeni derlenmiştir. timeit10000 döngünün en iyisini verir.
hpaulj

1
Bu harika bir cevap ve bence kabul edilmesi gereken cevap bu. Ancak, yukarıda yazılan kod aslında Alex'in kaçınmaya çalıştığı matrisi (benim makinemde) veriyor. Hpaulj'un yanlış olduğunu söylediği kişi aslında doğru olanıdır.
Yair Daon

Zamanlamalar burada yanıltıcıdır. dot-diag gerçekten diğer üç seçenekten çok daha kötüdür ve einsum da diğerlerinden daha hızlı değildir.
Nico Schlömer

@ NicoSchlömer, cevabım yaklaşık 5 yaşında ve birçok numpyversiyon geri döndü.
hpaulj

1

Google'da kaybolan ruhlar için o numpy.expand_dimszaman kullanmak numpy.repeatişe yarayacak ve aynı zamanda daha yüksek boyutlu durumlarda da işe yarayacaktır (yani bir şekli (10, 12, 3) a (10, 12) ile çarparak).

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

-4

Neden sadece yapmıyorsun

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

??


6
Bu kesin yaklaşım zaten kabul edilen cevapta gösteriliyor, bunun nasıl bir şey kattığını anlamıyorum.
Baum mit Augen
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.