การคูณในอาร์เรย์จำนวนนับ


89

ฉันพยายามคูณคำศัพท์แต่ละคำในอาร์เรย์ 2 มิติตามเงื่อนไขที่เกี่ยวข้องในอาร์เรย์ 1D นี่เป็นเรื่องง่ายมากถ้าฉันต้องการคูณทุกคอลัมน์ด้วยอาร์เรย์ 1D ดังที่แสดงในฟังก์ชันnumpy.multiply แต่ผมอยากทำตรงข้ามคูณแต่ละเทอมในแถว กล่าวอีกนัยหนึ่งฉันต้องการคูณ:

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

และรับ

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

แต่แทนที่จะได้รับ

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

มีใครรู้บ้างว่ามีวิธีที่ยอดเยี่ยมในการทำเช่นนั้นด้วย numpy หรือไม่? ขอบคุณมากอเล็กซ์


3
อ่าฉันคิดออกเหมือนที่ส่งคำถามไป ขั้นแรกให้ทรานสโพสเมทริกซ์กำลังสองคูณแล้วเปลี่ยนคำตอบ
Alex S

ดีกว่าที่จะเปลี่ยนแถวเป็นเมทริกซ์คอลัมน์จากนั้นคุณไม่จำเป็นต้องเปลี่ยนคำตอบใหม่ หากA * Bคุณต้องทำการA * B[...,None]เปลี่ยนBโดยการเพิ่มแกนใหม่ ( None)
askewchan

ขอบคุณจริงๆ ปัญหาคือเมื่อคุณมีอาร์เรย์ 1D ที่เรียก. transpose () หรือ. T บนมันไม่ได้เปลี่ยนเป็นอาร์เรย์คอลัมน์มันจะปล่อยให้เป็นแถวเท่าที่ฉันรู้ว่าคุณต้องกำหนดเป็นคอลัมน์ ออกจากค้างคาว ชอบx = [[1],[2],[3]]หรือบางสิ่งบางอย่าง
Alex S

คำตอบ:


119

การคูณปกติเหมือนที่คุณแสดง:

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

หากคุณเพิ่มแกนมันจะทวีคูณตามที่คุณต้องการ:

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

คุณยังสามารถเปลี่ยนสองครั้ง:

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

ด้วยวิธีแกนใหม่ทำให้สามารถคูณอาร์เรย์ 1D สองอาร์เรย์และสร้างอาร์เรย์ 2D ได้ เช่น[a,b] op [c,d] -> [[a*c, b*c], [a*d, b*d]].
kon psych

50

ฉันได้เปรียบเทียบตัวเลือกต่างๆสำหรับความเร็วและพบว่า - ทำให้ฉันประหลาดใจมาก - ตัวเลือกทั้งหมด (ยกเว้นdiag) นั้นเร็วพอ ๆ กัน ผมเองใช้

A * b[:, None]

(หรือ(A.T * b).T) เพราะมันสั้น

ป้อนคำอธิบายภาพที่นี่


รหัสเพื่อสร้างพล็อตซ้ำ:

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
สัมผัสที่ดีในการให้รหัสสำหรับพล็อต ขอบคุณ.
RockNwaves

17

คุณยังสามารถใช้การคูณเมทริกซ์ (aka dot product):

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

numpy.dot(c,a)

ที่หรูหรากว่านั้นน่าจะเป็นเรื่องของรสนิยม


2
ดี +1 ไม่คิดอย่างนั้น
jterrace

10
dotที่นี่มากเกินไปจริงๆ คุณแค่ทำการคูณโดยไม่จำเป็นด้วย 0 และเพิ่มเป็น 0
Bi Rico

2
นอกจากนี้ยังอาจทำให้เกิดปัญหาหน่วยความจำในกรณีที่คุณต้องการคูณเวกเตอร์ nx1 เป็นเมทริกซ์ nxd โดยที่ d มีขนาดใหญ่กว่า n
Jonasson

การลงคะแนนเนื่องจากช้าและใช้หน่วยความจำจำนวนมากเมื่อสร้างdiagเมทริกซ์หนาแน่น
Nico Schlömer

16

ยังมีเคล็ดลับอีกอย่าง (ณ v1.6)

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

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

ฉันมีความเชี่ยวชาญในการกระจายเสียง ( newaxis) แต่ฉันยังคงหาวิธีeinsumใช้เครื่องมือใหม่นี้อยู่ ผมก็เลยลองหาวิธีแก้ปัญหานี้สักหน่อย

การกำหนดเวลา (โดยใช้ Ipython timeit):

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

อนึ่งการเปลี่ยนแปลงiไปj, np.einsum('ij,j->ij',A,b)ผลิตเมทริกซ์ว่าอเล็กซ์ไม่ได้ต้องการ และที่จริงแล้วทnp.einsum('ji,j->ji',A,b)รานสโพสคู่


1
หากคุณจะใช้เวลานี้บนคอมพิวเตอร์ที่มีอาร์เรย์ขนาดใหญ่พอที่จะใช้เวลาอย่างน้อยสองสามมิลลิวินาทีและโพสต์ผลลัพธ์ที่นี่พร้อมกับข้อมูลระบบที่เกี่ยวข้องของคุณจะได้รับการชื่นชมมาก
Daniel

1
ด้วยอาร์เรย์ที่ใหญ่กว่า (100x100) จำนวนสัมพัทธ์จะเท่ากัน einsumm(25 ไมโคร) เร็วเป็นสองเท่า (dot-diag ทำให้ช้าลงมากขึ้น) นี่คือ np 1.7 ที่คอมไพล์ใหม่ด้วย 'libatlas3gf-sse2' และ 'libatlas-base-dev' (Ubuntu 10.4, โปรเซสเซอร์เดี่ยว) timeitให้ดีที่สุด 10,000 ลูป
hpaulj

1
นี่เป็นคำตอบที่ยอดเยี่ยมและฉันคิดว่ามันเป็นคำตอบที่ควรได้รับการยอมรับ อย่างไรก็ตามโค้ดที่เขียนไว้ข้างต้นนั้นให้เมทริกซ์ที่ Alex พยายามหลีกเลี่ยง (บนเครื่องของฉัน) คนที่พูดว่าไม่ถูกต้องเป็นเรื่องที่ถูกต้อง
Yair Daon

การกำหนดเวลาทำให้เข้าใจผิดที่นี่ dot-diag แย่กว่าอีกสามตัวเลือกมากและ einsum ก็ไม่ได้เร็วกว่าตัวเลือกอื่น ๆ
Nico Schlömer

@ NicoSchlömerคำตอบของฉันคือเกือบ 5 ปีและมีหลายnumpyเวอร์ชั่น
hpaulj

1

สำหรับวิญญาณที่หายไปใน google การใช้numpy.expand_dimsแล้วnumpy.repeatจะได้ผลและจะทำงานในกรณีมิติที่สูงขึ้นด้วย (เช่นการคูณรูปร่าง (10, 12, 3) ด้วย (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]])

-4

ทำไมคุณไม่ทำ

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

??


6
แนวทางที่แน่นอนนั้นแสดงอยู่แล้วในคำตอบที่ยอมรับฉันไม่เห็นว่าสิ่งนี้เพิ่มอะไร
Baum mit Augen
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.