1. Ý nghĩa của hình dạng trong NumPy
Bạn viết, "Tôi biết theo nghĩa đen đó là danh sách các số và danh sách các danh sách trong đó tất cả danh sách chỉ chứa một số" nhưng đó là một cách không hữu ích để suy nghĩ về nó.
Cách tốt nhất để suy nghĩ về mảng NumPy là chúng bao gồm hai phần, bộ đệm dữ liệu chỉ là một khối các phần tử thô và một khung nhìn mô tả cách diễn giải bộ đệm dữ liệu.
Ví dụ: nếu chúng ta tạo một mảng gồm 12 số nguyên:
>>> a = numpy.arange(12)
>>> a
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
Sau đó a
bao gồm một bộ đệm dữ liệu, sắp xếp một cái gì đó như thế này:
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
và một khung nhìn mô tả cách diễn giải dữ liệu:
>>> a.flags
C_CONTIGUOUS : True
F_CONTIGUOUS : True
OWNDATA : True
WRITEABLE : True
ALIGNED : True
UPDATEIFCOPY : False
>>> a.dtype
dtype('int64')
>>> a.itemsize
8
>>> a.strides
(8,)
>>> a.shape
(12,)
Ở đây hình dạng (12,)
có nghĩa là mảng được lập chỉ mục bởi một chỉ mục duy nhất chạy từ 0 đến 11. Về mặt khái niệm, nếu chúng ta gắn nhãn cho chỉ mục này i
, mảng a
sẽ trông như thế này:
i= 0 1 2 3 4 5 6 7 8 9 10 11
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
Nếu chúng ta định hình lại một mảng, điều này sẽ không thay đổi bộ đệm dữ liệu. Thay vào đó, nó tạo ra một khung nhìn mới mô tả một cách khác để diễn giải dữ liệu. Vì vậy, sau:
>>> b = a.reshape((3, 4))
mảng b
có bộ đệm dữ liệu giống như a
, nhưng bây giờ nó được lập chỉ mục bởi hai chỉ số chạy tương ứng từ 0 đến 2 và 0 đến 3. Nếu chúng ta dán nhãn cho hai chỉ số i
và j
, mảng b
trông như thế này:
i= 0 0 0 0 1 1 1 1 2 2 2 2
j= 0 1 2 3 0 1 2 3 0 1 2 3
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
có nghĩa là:
>>> b[2,1]
9
Bạn có thể thấy rằng chỉ số thứ hai thay đổi nhanh chóng và chỉ mục đầu tiên thay đổi chậm. Nếu bạn thích vòng này theo cách khác, bạn có thể chỉ định order
tham số:
>>> c = a.reshape((3, 4), order='F')
dẫn đến một mảng được lập chỉ mục như thế này:
i= 0 1 2 0 1 2 0 1 2 0 1 2
j= 0 0 0 1 1 1 2 2 2 3 3 3
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
có nghĩa là:
>>> c[2,1]
5
Bây giờ cần phải rõ ràng ý nghĩa của một mảng có hình dạng với một hoặc nhiều kích thước kích thước 1. Sau:
>>> d = a.reshape((12, 1))
mảng d
được lập chỉ mục bởi hai chỉ số, đầu tiên chạy từ 0 đến 11 và chỉ số thứ hai luôn là 0:
i= 0 1 2 3 4 5 6 7 8 9 10 11
j= 0 0 0 0 0 0 0 0 0 0 0 0
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
và vì thế:
>>> d[10,0]
10
Thứ nguyên của chiều dài 1 là "miễn phí" (theo một nghĩa nào đó), vì vậy không có gì ngăn bạn đi đến thị trấn:
>>> e = a.reshape((1, 2, 1, 6, 1))
đưa ra một mảng được lập chỉ mục như thế này:
i= 0 0 0 0 0 0 0 0 0 0 0 0
j= 0 0 0 0 0 0 1 1 1 1 1 1
k= 0 0 0 0 0 0 0 0 0 0 0 0
l= 0 1 2 3 4 5 0 1 2 3 4 5
m= 0 0 0 0 0 0 0 0 0 0 0 0
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
và vì thế:
>>> e[0,1,0,0,0]
6
Xem tài liệu nội bộ NumPy để biết thêm chi tiết về cách các mảng được thực hiện.
2. Phải làm gì?
Vì numpy.reshape
chỉ cần tạo một chế độ xem mới, bạn không nên sợ sử dụng nó bất cứ khi nào cần thiết. Đây là công cụ phù hợp để sử dụng khi bạn muốn lập chỉ mục một mảng theo một cách khác.
Tuy nhiên, trong một tính toán dài, thường có thể sắp xếp để xây dựng các mảng với hình dạng "đúng" ở vị trí đầu tiên, và do đó giảm thiểu số lượng hình dạng lại và chuyển vị. Nhưng không nhìn thấy bối cảnh thực tế dẫn đến nhu cầu định hình lại, thật khó để nói những gì nên thay đổi.
Ví dụ trong câu hỏi của bạn là:
numpy.dot(M[:,0], numpy.ones((1, R)))
nhưng điều này không thực tế Đầu tiên, biểu thức này:
M[:,0].sum()
tính kết quả đơn giản hơn. Thứ hai, có thực sự có gì đặc biệt về cột 0 không? Có lẽ những gì bạn thực sự cần là:
M.sum(axis=0)