การคูณแตกต่างกันอย่างไรสำหรับคลาส NumPy Matrix vs Array


130

เอกสาร numpy แนะนำให้ใช้อาร์เรย์แทนเมทริกซ์สำหรับการทำงานกับเมทริกซ์ อย่างไรก็ตามไม่เหมือนกับอ็อกเทฟ (ที่ฉันใช้จนถึงเมื่อไม่นานมานี้) * ไม่ทำการคูณเมทริกซ์คุณต้องใช้ฟังก์ชัน matrixmultipy () ฉันรู้สึกว่านี่ทำให้โค้ดอ่านไม่ออก

มีใครแบ่งปันมุมมองของฉันและพบวิธีแก้ไขหรือไม่?


8
คุณกำลังขอความคิดเห็นไม่ใช่คำถาม มีอะไรที่เฉพาะเจาะจงมากขึ้นที่เราสามารถช่วยคุณได้หรืออาจแนะนำคุณในการทำให้อ่านง่ายขึ้น
วีทตี้

2
จริงๆแล้วเอกสารแนะนำให้ใช้เมทริกซ์ถ้าคุณทำพีชคณิตเชิงเส้นและไม่ต้องการใช้การคูณ () แล้วปัญหาคืออะไร?
Matti Pastell

1
ฉันยังไม่ได้อ่านเอกสารโดยละเอียด แค่อยากรู้ว่าอาร์เรย์มีข้อดีอะไรบ้างในคลาสเมทริกซ์ ฉันพบว่าอาร์เรย์ไม่ได้แยกความแตกต่างระหว่างแถวและคอลัมน์ เป็นเพราะอาร์เรย์ควรถูกคิดว่าเป็นเทนเซอร์มากกว่าเมทริกซ์หรือไม่? ดังที่ Joe ชี้ให้เห็นความจริงที่ว่าคลาสเมทริกซ์เป็น 2-dim นั้นค่อนข้าง จำกัด อะไรคือความคิดที่อยู่เบื้องหลังการออกแบบประเภทนี้ทำไมไม่มีคลาสเมทริกซ์เดียวเช่น matlab / octave?
elexhobby

ฉันเดาว่าปัญหาหลักคือ python ไม่มี.*ไวยากรณ์ vs '*' สำหรับการคูณองค์ประกอบที่ชาญฉลาดเทียบกับเมทริกซ์ ถ้ามีแบบนั้นทุกอย่างจะง่ายขึ้นแม้ว่าฉันจะแปลกใจที่พวกเขาเลือกที่*จะหมายถึงการคูณด้วยองค์ประกอบที่ชาญฉลาดและไม่ใช่การคูณเมทริกซ์
Charlie Parker

คำตอบ:


127

เหตุผลหลักที่ควรหลีกเลี่ยงการใช้ไฟล์ matrixคลาสคือ a) มันเป็น 2 มิติโดยเนื้อแท้และ b) มีค่าใช้จ่ายเพิ่มเติมเมื่อเทียบกับอาร์เรย์ตัวเลข "ปกติ" หากสิ่งที่คุณทำคือพีชคณิตเชิงเส้นอย่างไรก็ตามอย่าลังเลที่จะใช้คลาสเมทริกซ์ ... โดยส่วนตัวแล้วฉันพบว่ามันมีปัญหามากกว่าที่จะคุ้มค่า

สำหรับอาร์เรย์ (ก่อน Python 3.5) ให้ใช้dotแทนmatrixmultiply.

เช่น

import numpy as np
x = np.arange(9).reshape((3,3))
y = np.arange(3)

print np.dot(x,y)

หรือในรุ่นที่ใหม่กว่าของ numpy เพียงแค่ใช้ x.dot(y)

โดยส่วนตัวแล้วฉันคิดว่ามันอ่านง่ายกว่าตัว*ดำเนินการที่หมายถึงการคูณเมทริกซ์ ...

สำหรับอาร์เรย์ใน Python 3.5 ให้ใช้x @ y.


10
ไม่สามารถอ่านได้เมื่อคุณมีสแต็กของการคูณเช่น x ' A' * A x
elexhobby

14
@elexhobby - x.T.dot(A.T).dot(A).dot(x)ไม่ว่าจะอ่านไม่ได้ imo สำหรับแต่ละคนของเขาเอง หากคุณกำลังทำการคูณเมทริกซ์เป็นหลักให้ใช้numpy.matrix!
Joe Kington

7
อย่างไรก็ตามเหตุใดการคูณเมทริกซ์จึงเรียกว่า "จุด" ผลิตภัณฑ์ดอทในแง่ใด
amcnabb

8
@amcnabb - การคูณเมทริกซ์บางครั้งเรียกว่า "ผลิตภัณฑ์ดอท" ในหนังสือเรียน (ในหนังสือเหล่านั้นผลิตภัณฑ์ดอทที่คุณคิดจะเรียกว่า "ผลิตภัณฑ์สเกลาร์" หรือ "ผลิตภัณฑ์จุดสเกลาร์") ผลคูณของสเกลาร์ดอทเป็นเพียงการคูณเมทริกซ์ของเวกเตอร์สองตัวดังนั้นการใช้ "จุด" เพื่อหมายถึงการคูณเมทริกซ์โดยทั่วไปจึงไม่เป็นการยืดออกมากนัก สัญกรณ์เฉพาะนั้นดูเหมือน (?) ทั่วไปในตำราวิศวกรรมและวิทยาศาสตร์มากกว่าคณิตศาสตร์อย่างน้อยก็ในประสบการณ์ของฉัน ความชุกของตัวเลขส่วนใหญ่เป็นเพราะnumpy.matrixmultiplyพิมพ์ยาก
Joe Kington

7
@amcnabb ประเด็นก็คือจุดเป็นภาพรวมของมิติโดยพลการโดยไม่มีความคลุมเครือ ซึ่งทำให้numpy.dotเทียบเท่ากับการคูณเมทริกซ์ ถ้าคุณไม่ชอบสัญกรณ์จริงๆให้ใช้matrixคลาส
Henry Gomersall

80

สิ่งสำคัญที่ควรทราบสำหรับการดำเนินการกับอาร์เรย์NumPy เทียบกับการดำเนินการบนเมทริกซ์ NumPy คือ:

  • NumPy matrix เป็นคลาสย่อยของอาร์เรย์ NumPy

  • การดำเนินการอาร์เรย์ NumPy เป็นองค์ประกอบที่ชาญฉลาด (เมื่อมีการกระจายสัญญาณ)

  • การดำเนินการของเมทริกซ์ NumPy เป็นไปตามกฎทั่วไปของพีชคณิตเชิงเส้น

ตัวอย่างโค้ดบางส่วนเพื่อแสดง:

>>> from numpy import linalg as LA
>>> import numpy as NP

>>> a1 = NP.matrix("4 3 5; 6 7 8; 1 3 13; 7 21 9")
>>> a1
matrix([[ 4,  3,  5],
        [ 6,  7,  8],
        [ 1,  3, 13],
        [ 7, 21,  9]])

>>> a2 = NP.matrix("7 8 15; 5 3 11; 7 4 9; 6 15 4")
>>> a2
matrix([[ 7,  8, 15],
        [ 5,  3, 11],
        [ 7,  4,  9],
        [ 6, 15,  4]])

>>> a1.shape
(4, 3)

>>> a2.shape
(4, 3)

>>> a2t = a2.T
>>> a2t.shape
(3, 4)

>>> a1 * a2t         # same as NP.dot(a1, a2t) 
matrix([[127,  84,  85,  89],
        [218, 139, 142, 173],
        [226, 157, 136, 103],
        [352, 197, 214, 393]])

แต่การดำเนินการนี้จะล้มเหลวหากเมทริกซ์ NumPy ทั้งสองนี้ถูกแปลงเป็นอาร์เรย์:

>>> a1 = NP.array(a1)
>>> a2t = NP.array(a2t)

>>> a1 * a2t
Traceback (most recent call last):
   File "<pyshell#277>", line 1, in <module>
   a1 * a2t
   ValueError: operands could not be broadcast together with shapes (4,3) (3,4) 

แม้ว่าการใช้NP.dotงานไวยากรณ์กับอาร์เรย์ ; การดำเนินการนี้ทำงานเหมือนการคูณเมทริกซ์:

>> NP.dot(a1, a2t)
array([[127,  84,  85,  89],
       [218, 139, 142, 173],
       [226, 157, 136, 103],
       [352, 197, 214, 393]])

คุณต้องการเมทริกซ์ NumPy หรือไม่? กล่าวคืออาร์เรย์ NumPy จะเพียงพอสำหรับการคำนวณพีชคณิตเชิงเส้น (หากคุณทราบไวยากรณ์ที่ถูกต้องเช่น NP.dot)

กฎดูเหมือนจะเป็นว่าถ้าอาร์กิวเมนต์ (อาร์เรย์) มีรูปร่าง (mxn) ที่เข้ากันได้กับการดำเนินการพีชคณิตเชิงเส้นที่กำหนดแสดงว่าคุณโอเคมิฉะนั้น NumPy จะพ่น

ข้อยกเว้นเดียวที่ฉันเจอ (มีแนวโน้มว่าคนอื่น ๆ ) กำลังคำนวณเมทริกซ์ผกผันคำนวณเมทริกซ์ผกผัน

ด้านล่างนี้เป็นตัวอย่างที่ฉันเรียกว่าการดำเนินการพีชคณิตเชิงเส้นบริสุทธิ์ (อันที่จริงจากโมดูล Linear Algebra ของ Numpy) และส่งผ่านในอาร์เรย์ NumPy

ดีเทอร์มิแนนต์ของอาร์เรย์:

>>> m = NP.random.randint(0, 10, 16).reshape(4, 4)
>>> m
array([[6, 2, 5, 2],
       [8, 5, 1, 6],
       [5, 9, 7, 5],
       [0, 5, 6, 7]])

>>> type(m)
<type 'numpy.ndarray'>

>>> md = LA.det(m)
>>> md
1772.9999999999995

eigenvectors / eigenvalueคู่:

>>> LA.eig(m)
(array([ 19.703+0.j   ,   0.097+4.198j,   0.097-4.198j,   5.103+0.j   ]), 
array([[-0.374+0.j   , -0.091+0.278j, -0.091-0.278j, -0.574+0.j   ],
       [-0.446+0.j   ,  0.671+0.j   ,  0.671+0.j   , -0.084+0.j   ],
       [-0.654+0.j   , -0.239-0.476j, -0.239+0.476j, -0.181+0.j   ],
       [-0.484+0.j   , -0.387+0.178j, -0.387-0.178j,  0.794+0.j   ]]))

บรรทัดฐานของเมทริกซ์:

>>>> LA.norm(m)
22.0227

การแยกตัวประกอบ qr :

>>> LA.qr(a1)
(array([[ 0.5,  0.5,  0.5],
        [ 0.5,  0.5, -0.5],
        [ 0.5, -0.5,  0.5],
        [ 0.5, -0.5, -0.5]]), 
 array([[ 6.,  6.,  6.],
        [ 0.,  0.,  0.],
        [ 0.,  0.,  0.]]))

อันดับเมทริกซ์:

>>> m = NP.random.rand(40).reshape(8, 5)
>>> m
array([[ 0.545,  0.459,  0.601,  0.34 ,  0.778],
       [ 0.799,  0.047,  0.699,  0.907,  0.381],
       [ 0.004,  0.136,  0.819,  0.647,  0.892],
       [ 0.062,  0.389,  0.183,  0.289,  0.809],
       [ 0.539,  0.213,  0.805,  0.61 ,  0.677],
       [ 0.269,  0.071,  0.377,  0.25 ,  0.692],
       [ 0.274,  0.206,  0.655,  0.062,  0.229],
       [ 0.397,  0.115,  0.083,  0.19 ,  0.701]])
>>> LA.matrix_rank(m)
5

เงื่อนไขเมทริกซ์:

>>> a1 = NP.random.randint(1, 10, 12).reshape(4, 3)
>>> LA.cond(a1)
5.7093446189400954

การผกผันต้องการเมทริกซ์ NumPyแม้ว่า:

>>> a1 = NP.matrix(a1)
>>> type(a1)
<class 'numpy.matrixlib.defmatrix.matrix'>

>>> a1.I
matrix([[ 0.028,  0.028,  0.028,  0.028],
        [ 0.028,  0.028,  0.028,  0.028],
        [ 0.028,  0.028,  0.028,  0.028]])
>>> a1 = NP.array(a1)
>>> a1.I

Traceback (most recent call last):
   File "<pyshell#230>", line 1, in <module>
   a1.I
   AttributeError: 'numpy.ndarray' object has no attribute 'I'

แต่pseudoinverse ของ Moore-Penroseดูเหมือนจะใช้งานได้ดี

>>> LA.pinv(m)
matrix([[ 0.314,  0.407, -1.008, -0.553,  0.131,  0.373,  0.217,  0.785],
        [ 1.393,  0.084, -0.605,  1.777, -0.054, -1.658,  0.069, -1.203],
        [-0.042, -0.355,  0.494, -0.729,  0.292,  0.252,  1.079, -0.432],
        [-0.18 ,  1.068,  0.396,  0.895, -0.003, -0.896, -1.115, -0.666],
        [-0.224, -0.479,  0.303, -0.079, -0.066,  0.872, -0.175,  0.901]])

>>> m = NP.array(m)

>>> LA.pinv(m)
array([[ 0.314,  0.407, -1.008, -0.553,  0.131,  0.373,  0.217,  0.785],
       [ 1.393,  0.084, -0.605,  1.777, -0.054, -1.658,  0.069, -1.203],
       [-0.042, -0.355,  0.494, -0.729,  0.292,  0.252,  1.079, -0.432],
       [-0.18 ,  1.068,  0.396,  0.895, -0.003, -0.896, -1.115, -0.666],
       [-0.224, -0.479,  0.303, -0.079, -0.066,  0.872, -0.175,  0.901]])

3
mInv = NP.linalg.inv (m) คำนวณผกผันของอาร์เรย์
db1234

ประเด็นสำคัญที่ควรทราบก็คือ * คือการคูณตามองค์ประกอบอย่างชาญฉลาดจุดคือการคูณเมทริกซ์ที่แท้จริง โปรดดูstackoverflow.com/a/18255635/1780570
Minh Triet

หมายเหตุ IMP: เมทริกซ์จำนวนมากควรหลีกเลี่ยงเพื่อสนับสนุนอาร์เรย์ หมายเหตุจากเอกสารประกอบ -> "ไม่แนะนำให้ใช้คลาสนี้อีกต่อไปแม้กระทั่งสำหรับพีชคณิตเชิงเส้นก็ตามให้ใช้อาร์เรย์ปกติแทนคลาสนี้อาจถูกลบออกในอนาคต" ดู stackoverflow.com/a/61156350/6043669
HopeKing

21

ใน 3.5, Python ในที่สุดก็มีผู้ประกอบการคูณเมทริกซ์ ไวยากรณ์คือa @ b.


2
ขอบคุณ! เย้ดีใจที่เห็นว่าไม่ใช่ฉันคนเดียวที่รู้สึกว่าสัญกรณ์ปัจจุบันไม่สามารถอ่านได้
elexhobby

15

มีสถานการณ์ที่ตัวดำเนินการจุดจะให้คำตอบที่แตกต่างกันเมื่อจัดการกับอาร์เรย์เช่นเดียวกับการจัดการกับเมทริกซ์ ตัวอย่างเช่นสมมติว่าต่อไปนี้:

>>> a=numpy.array([1, 2, 3])
>>> b=numpy.array([1, 2, 3])

ให้แปลงเป็นเมทริกซ์:

>>> am=numpy.mat(a)
>>> bm=numpy.mat(b)

ตอนนี้เราสามารถเห็นผลลัพธ์ที่แตกต่างกันสำหรับสองกรณี:

>>> print numpy.dot(a.T, b)
14
>>> print am.T*bm
[[1.  2.  3.]
 [2.  4.  6.]
 [3.  6.  9.]]

เพื่อให้เจาะจง * คือการคูณตามองค์ประกอบจุดคือการคูณเมทริกซ์ที่แท้จริง โปรดดูstackoverflow.com/a/18255635/1780570
Minh Triet

นั่นเป็นเพราะในฐานะอาร์เรย์ numpy aT == a ทรานสโพสไม่ได้ทำอะไรเลย
patapouf_ai

ถ้าคุณเขียนที่ = np.array ([[1], [2], [3]]) ดังนั้น numpy.dot (at, b) ควรให้ค่าเท่ากัน ความแตกต่างระหว่าง matix และ array ไม่ได้อยู่ในจุด แต่อยู่ในทรานสโพส
patapouf_ai

หรือที่จริงแล้วถ้าคุณเขียน a = numpy.array ([[1,2,3]]) aT ก็จะทรานสโพสจริงๆและทุกอย่างจะทำงานเหมือนในเมทริกซ์
patapouf_ai

8

อ้างอิงจากhttp://docs.scipy.org/doc/scipy/reference/tutorial/linalg.html

... การใช้คลาสnumpy.matrix ไม่ได้รับการสนับสนุนเนื่องจากไม่มีการเพิ่มสิ่งใดที่ไม่สามารถทำได้ด้วยวัตถุ2D numpy.ndarrayและอาจทำให้เกิดความสับสนว่าจะใช้คลาสใด ตัวอย่างเช่น,

>>> import numpy as np
>>> from scipy import linalg
>>> A = np.array([[1,2],[3,4]])
>>> A
    array([[1, 2],
           [3, 4]])
>>> linalg.inv(A)
array([[-2. ,  1. ],
      [ 1.5, -0.5]])
>>> b = np.array([[5,6]]) #2D array
>>> b
array([[5, 6]])
>>> b.T
array([[5],
      [6]])
>>> A*b #not matrix multiplication!
array([[ 5, 12],
      [15, 24]])
>>> A.dot(b.T) #matrix multiplication
array([[17],
      [39]])
>>> b = np.array([5,6]) #1D array
>>> b
array([5, 6])
>>> b.T  #not matrix transpose!
array([5, 6])
>>> A.dot(b)  #does not matter for multiplication
array([17, 39])

การดำเนินการscipy.linalgสามารถใช้ได้กับวัตถุ numpy.matrixหรือกับวัตถุ2D numpy.ndarrayอย่างเท่าเทียมกัน


7

เคล็ดลับนี้อาจเป็นสิ่งที่คุณกำลังมองหา มันเป็นตัวดำเนินการที่ง่ายเกินพิกัด

จากนั้นคุณสามารถใช้บางอย่างเช่นคลาส Infix ที่แนะนำดังนี้:

a = np.random.rand(3,4)
b = np.random.rand(4,3)
x = Infix(lambda x,y: np.dot(x,y))
c = a |x| b

5

คำพูดที่ตรงประเด็นจากPEP 465 - ตัวดำเนินการ infix เฉพาะสำหรับการคูณเมทริกซ์ตามที่ @ petr-viktorin กล่าวไว้ชี้แจงปัญหาที่ OP ได้รับที่:

[... ] numpy มีสองประเภทที่แตกต่างกันด้วย__mul__วิธีการที่แตกต่างกัน สำหรับอnumpy.ndarrayอบเจ็กต์ให้*ทำการคูณตามองค์ประกอบและการคูณเมทริกซ์ต้องใช้การเรียกฟังก์ชัน ( numpy.dot) สำหรับnumpy.matrixอ็อบเจ็กต์*ทำการคูณเมทริกซ์และการคูณตามองค์ประกอบต้องใช้ไวยากรณ์ของฟังก์ชัน การเขียนโค้ดโดยใช้numpy.ndarrayงานได้ดี การเขียนโค้ดโดยใช้numpy.matrixยังใช้งานได้ดี แต่ปัญหาเริ่มต้นทันทีที่เราพยายามรวมโค้ดสองชิ้นนี้เข้าด้วยกัน รหัสที่คาดว่าndarrayและได้รับmatrixหรือในทางกลับกันอาจขัดข้องหรือส่งคืนผลลัพธ์ที่ไม่ถูกต้อง

การแนะนำตัว@ดำเนินการ infix ควรช่วยในการรวมและลดความซับซ้อนของรหัสเมทริกซ์ไพ ธ อน


1

ฟังก์ชันmatmul (ตั้งแต่ numpy 1.10.1) ทำงานได้ดีสำหรับทั้งสองประเภทและส่งคืนผลลัพธ์เป็นคลาสเมทริกซ์ numpy:

import numpy as np

A = np.mat('1 2 3; 4 5 6; 7 8 9; 10 11 12')
B = np.array(np.mat('1 1 1 1; 1 1 1 1; 1 1 1 1'))
print (A, type(A))
print (B, type(B))

C = np.matmul(A, B)
print (C, type(C))

เอาท์พุท:

(matrix([[ 1,  2,  3],
        [ 4,  5,  6],
        [ 7,  8,  9],
        [10, 11, 12]]), <class 'numpy.matrixlib.defmatrix.matrix'>)
(array([[1, 1, 1, 1],
       [1, 1, 1, 1],
       [1, 1, 1, 1]]), <type 'numpy.ndarray'>)
(matrix([[ 6,  6,  6,  6],
        [15, 15, 15, 15],
        [24, 24, 24, 24],
        [33, 33, 33, 33]]), <class 'numpy.matrixlib.defmatrix.matrix'>)

เนื่องจาก python 3.5 ดังที่กล่าวไว้ในตอนต้นคุณยังสามารถใช้ตัวดำเนินการคูณเมทริกซ์ใหม่@เช่น

C = A @ B

และได้ผลลัพธ์เช่นเดียวกับด้านบน

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