Phép nhân khác nhau như thế nào đối với các lớp NumPy Matrix và Array?


130

Các tài liệu numpy khuyên bạn nên sử dụng mảng thay vì ma trận để làm việc với ma trận. Tuy nhiên, không giống như quãng tám (mà tôi đã sử dụng cho đến gần đây), * không thực hiện phép nhân ma trận, bạn cần sử dụng hàm matrixmultipy (). Tôi cảm thấy điều này làm cho mã rất khó đọc.

Có ai chia sẻ quan điểm của tôi, và đã tìm ra giải pháp?


8
Bạn đang hỏi ý kiến ​​và không phải là một câu hỏi. Có điều gì cụ thể hơn chúng tôi có thể giúp bạn hoặc có thể hướng dẫn bạn làm cho nó dễ đọc hơn không?
Wheaties

2
Trên thực tế, các tài liệu khuyên bạn nên sử dụng ma trận nếu bạn làm đại số tuyến tính và không muốn sử dụng phép nhân () vậy vấn đề là gì?
Matti Pastell

1
Tôi đã không đi qua các tài liệu chi tiết. Chỉ tò mò, những lợi thế nào mảng cung cấp trên lớp ma trận? Tôi thấy rằng các mảng không phân biệt giữa các hàng và cột. Có phải bởi vì các mảng được cho là được coi là tenxơ chứ không phải ma trận? Như Joe đã chỉ ra, thực tế là lớp ma trận là 2 chiều khá hạn chế. Suy nghĩ đằng sau kiểu thiết kế này là gì, tại sao không có một lớp ma trận duy nhất như matlab / octave?
elexhulk

Tôi đoán vấn đề chính là python không có .*cú pháp vs '*' cho phép nhân phần tử so với nhân ma trận. Nếu nó đã có thì tất cả sẽ đơn giản hơn mặc dù tôi ngạc nhiên khi họ chọn *nghĩa là nhân phần tử chứ không phải nhân ma trận.
Charlie Parker

Câu trả lời:


127

Lý do chính để tránh sử dụng matrixlớp là vì a) vốn dĩ là 2 chiều và b) có thêm chi phí so với mảng numpy "bình thường". Nếu tất cả những gì bạn đang làm là đại số tuyến tính, thì bằng mọi cách, hãy thoải mái sử dụng lớp ma trận ... Tuy nhiên, cá nhân tôi thấy nó rắc rối hơn giá trị của nó.

Đối với mảng (trước Python 3.5), sử dụng dotthay vì matrixmultiply.

Ví dụ

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

print np.dot(x,y)

Hoặc trong các phiên bản mới hơn của numpy, chỉ cần sử dụng x.dot(y)

Cá nhân, tôi thấy nó dễ đọc hơn nhiều so với *toán tử ngụ ý nhân ma trận ...

Đối với mảng trong Python 3.5, sử dụng x @ y.


10
Không thể đọc được khi bạn có một chồng các phép nhân, ví dụ x ' A' * A x.
elexhulk

14
@elexhulk - x.T.dot(A.T).dot(A).dot(x)Mặc dù vậy, không phải là không thể đọc được. Nếu bạn chủ yếu thực hiện phép nhân ma trận, thì bằng mọi cách, hãy sử dụng numpy.matrix!
Joe Kington

7
Nhân tiện, tại sao phép nhân ma trận được gọi là "dấu chấm"? Theo nghĩa nào thì nó là một sản phẩm chấm?
amcnabb

8
@amcnabb - Phép nhân ma trận đôi khi được gọi là "sản phẩm chấm" trong sách giáo khoa (trong những cuốn sách đó, sản phẩm chấm mà bạn nghĩ đến được gọi là "sản phẩm vô hướng" hoặc "sản phẩm chấm vô hướng"). Xét cho cùng, sản phẩm chấm vô hướng chỉ là phép nhân ma trận của hai vectơ, do đó, sử dụng "dấu chấm" để nói về phép nhân ma trận nói chung không phải là quá nhiều. Ký hiệu đặc biệt đó dường như (?) Phổ biến hơn trong các văn bản khoa học và kỹ thuật hơn là toán học, ít nhất là theo kinh nghiệm của tôi. Tỷ lệ phổ biến của nó trong numpy chủ yếu là vì numpy.matrixmultiplykhó loại.
Joe Kington

7
@amcnabb quan điểm là dấu chấm khái quát đến chiều kích tùy ý mà không mơ hồ. Chính điều này làm cho numpy.dottương đương với phép nhân ma trận. Nếu bạn thực sự không thích ký hiệu, hãy sử dụng matrixlớp.
Henry Gomersall

80

những điều quan trọng cần biết cho các hoạt động trên mảng NumPy so với các hoạt động trên ma trận NumPy là:

  • Ma trận NumPy là một lớp con của mảng NumPy

  • Các hoạt động của mảng NumPy là phần tử khôn ngoan (một khi phát sóng được tính đến)

  • Các phép toán ma trận NumPy tuân theo các quy tắc thông thường của đại số tuyến tính

một số đoạn mã để minh họa:

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

nhưng thao tác này không thành công nếu hai ma trận NumPy này được chuyển đổi thành mảng:

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

mặc dù sử dụng cú pháp NP.dot hoạt động với các mảng ; hoạt động này hoạt động như nhân ma trận:

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

vậy bạn có bao giờ cần một ma trận NumPy không? tức là, một mảng NumPy có đủ cho tính toán đại số tuyến tính không (với điều kiện bạn biết cú pháp đúng, tức là NP.dot)?

quy tắc dường như là nếu các đối số (mảng) có hình dạng (mxn) tương thích với phép toán đại số tuyến tính đã cho, thì bạn sẽ ổn, nếu không, NumPy sẽ ném.

ngoại lệ duy nhất tôi gặp phải (có khả năng là những người khác) là tính toán ma trận nghịch đảo .

bên dưới là các đoạn trong đó tôi đã gọi là một phép toán đại số tuyến tính thuần túy (trên thực tế, từ mô đun Đại số tuyến tính của Numpy) và được truyền vào một mảng NumPy

yếu tố quyết định của một mảng:

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

cặp eigenvector / 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   ]]))

định mức ma trận :

>>>> LA.norm(m)
22.0227

hệ số 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.]]))

thứ hạng ma trận :

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

điều kiện ma trận :

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

đảo ngược yêu cầu một ma trận NumPymặc dù:

>>> 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'

nhưng giả hành Moore-Penrose dường như chỉ hoạt động tốt

>>> 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) tính toán nghịch đảo của một mảng
db1234

Một điểm quan trọng cần lưu ý ở đây là * là phép nhân phần tử, dấu chấm là phép nhân ma trận thực sự. Vui lòng xem stackoverflow.com/a/18255635/1780570
Minh Triết

Lưu ý của IMP: các ma trận numpy cần tránh để thay vào đó là các mảng. Lưu ý từ tài liệu -> "Không còn nên sử dụng lớp này, ngay cả đối với đại số tuyến tính. Thay vào đó hãy sử dụng các mảng thông thường. Lớp này có thể bị xóa trong tương lai." Xem thêm stackoverflow.com/a/61156350/6043669
HopeKing


15

Có một tình huống mà toán tử dấu chấm sẽ đưa ra các câu trả lời khác nhau khi xử lý các mảng như khi xử lý các ma trận. Ví dụ: giả sử như sau:

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

Cho phép chuyển đổi chúng thành ma trận:

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

Bây giờ, chúng ta có thể thấy một đầu ra khác nhau cho hai trường hợp:

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

Để được cụ thể, * là phép nhân phần tử, chấm là phép nhân ma trận thực sự. Vui lòng xem stackoverflow.com/a/18255635/1780570
Minh Triết

Đó là bởi vì là một mảng numpy, aT == a, transpose không làm gì cả.
patapouf_ai

Nếu bạn viết tại = np.array ([[1], [2], [3]]), thì numpy.dot (at, b) sẽ cung cấp cho bạn tương tự. Sự khác biệt giữa matix và mảng không nằm ở dấu chấm mà là ở chuyển vị.
patapouf_ai

Hoặc thực tế, nếu bạn viết a = numpy.array ([[1,2,3]]) thì aT sẽ thực sự hoán vị và mọi thứ sẽ hoạt động giống như trong ma trận.
patapouf_ai

8

Tham khảo từ http://docs.scipy.org/doc/scipy/reference/tutorial/linalg.html

..., việc sử dụng lớp numpy.matrix không được khuyến khích , vì nó không thêm gì vào các đối tượng numpy.ndarray 2D và có thể dẫn đến sự nhầm lẫn về lớp nào đang được sử dụng. Ví dụ,

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

Các hoạt động scipy.linalg có thể được áp dụng như nhau cho các đối tượng numpy.matrix hoặc 2D numpy.ndarray .


7

Thủ thuật này có thể là những gì bạn đang tìm kiếm. Nó là một loại quá tải toán tử đơn giản.

Sau đó, bạn có thể sử dụng một cái gì đó giống như lớp Infix được đề xuất như thế này:

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

Một trích dẫn thích hợp từ PEP 465 - Một toán tử infix chuyên dụng cho phép nhân ma trận , như được đề cập bởi @ petr-viktorin, làm rõ vấn đề mà OP đang gặp phải:

[...] Numpy cung cấp hai loại khác nhau với các __mul__phương thức khác nhau . Đối với numpy.ndarraycác đối tượng, *thực hiện phép nhân theo phần tử và phép nhân ma trận phải sử dụng hàm gọi ( numpy.dot). Đối với numpy.matrixcác đối tượng, *thực hiện phép nhân ma trận và phép nhân theo phần tử yêu cầu cú pháp hàm. Viết mã sử dụng numpy.ndarrayhoạt động tốt. Viết mã bằng cách sử dụng numpy.matrixcũng hoạt động tốt. Nhưng rắc rối bắt đầu ngay khi chúng tôi cố gắng tích hợp hai đoạn mã này lại với nhau. Mã mong đợi ndarrayvà nhận được matrix, hoặc ngược lại, có thể bị sập hoặc trả về kết quả không chính xác

Việc giới thiệu @toán tử infix sẽ giúp thống nhất và đơn giản hóa mã ma trận python.


1

Hàm matmul (kể từ numpy 1.10.1) hoạt động tốt cho cả hai loại và trả về kết quả dưới dạng một lớp ma trận 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))

Đầu ra:

(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'>)

Vì python 3.5 như đã đề cập sớm, bạn cũng có thể sử dụng toán tử nhân ma trận mới @như

C = A @ B

và nhận được kết quả tương tự như trên.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.