Sự khác biệt giữa hình dạng numpy.array (R, 1) và (R,)


320

Trong numpy, một số hoạt động trở lại trong hình dạng (R, 1)nhưng một số trở lại (R,). Điều này sẽ làm cho phép nhân ma trận trở nên tẻ nhạt hơn vì reshapeđược yêu cầu rõ ràng . Ví dụ, được đưa ra một ma trận M, nếu chúng ta muốn làm numpy.dot(M[:,0], numpy.ones((1, R)))ở đâu Rlà số lượng hàng (tất nhiên, vấn đề tương tự cũng xảy ra theo cột). Chúng tôi sẽ nhận được matrices are not alignedlỗi vì M[:,0]có hình dạng (R,)nhưng numpy.ones((1, R))có hình dạng (1, R).

Vì vậy, câu hỏi của tôi là:

  1. Sự khác biệt giữa hình dạng (R, 1)(R,). 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ả các danh sách chỉ chứa một số. Chỉ cần tự hỏi tại sao không thiết kế numpyđể nó ủng hộ hình dạng (R, 1)thay vì (R,)để nhân ma trận dễ dàng hơn.

  2. Có cách nào tốt hơn cho ví dụ trên không? Nếu không định hình lại một cách rõ ràng như thế này:numpy.dot(M[:,0].reshape(R, 1), numpy.ones((1, R)))


3
Điều này có thể giúp. Không phải với việc tìm kiếm một giải pháp thực tế mặc dù.
keyer

1
Giải pháp phù hợp: numpy.ravel (M [:, 0]) - chuyển đổi hình dạng từ (R, 1) thành (R,)
Andi R

Câu trả lời:


545

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 đó abao 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 asẽ 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 bcó 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ố ij, mảng btrô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 ordertham 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ì?

numpy.reshapechỉ 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)

34
Điều này cực kỳ hữu ích trong việc suy nghĩ về cách các mảng được lưu trữ. Cảm ơn bạn! Truy cập một cột (hoặc hàng) của ma trận (2-d) để tính toán ma trận tiếp theo là bất tiện vì tôi luôn phải định hình lại cột một cách thích hợp. Mỗi lần tôi phải thay đổi hình dạng từ (n,) thành (n, 1).
OfLettersAndNumbers

3
@SammyLee: Sử dụng newaxisnếu bạn cần trục khác, ví dụ a[:, j, np.newaxis]jcột thứ của a, và a[np.newaxis, i]ihàng thứ.
Gareth Rees

Tôi đang cố gắng vẽ các chỉ số để hiểu rõ hơn trên giấy bằng mô hình này và tôi dường như không hiểu được nó, nếu tôi có hình 2 x 2 x 4 tôi hiểu 2 đầu tiên có thể hiểu là 0000000011111111 và 4 cuối cùng có thể được hiểu được hiểu là 0123012301230123 điều gì xảy ra với cái giữa?
PirateApp

3
Một cách dễ dàng để nghĩ về điều này là numpy đang hoạt động chính xác như mong đợi ở đây, nhưng việc in các bộ dữ liệu của Python có thể gây hiểu nhầm. Trong (R, )trường hợp, hình dạng của ndarraymột tuple với một phần tử duy nhất, do Python được in bằng dấu phẩy. Nếu không có dấu phẩy thừa, nó sẽ mơ hồ với một biểu thức trong ngoặc đơn . Một ndarrayvới một chiều duy nhất có thể mặc dù là một vectơ chiều dài cột R. Trong (R, 1)trường hợp, bộ dữ liệu có hai phần tử, vì vậy có thể được coi là một vectơ hàng (hoặc ma trận có 1 hàng dài R.
Michael Yang

1
@ Alex-droidAD: Xem câu hỏi này và câu trả lời của nó.
Gareth Rees

16

Sự khác biệt giữa (R,)và theo (1,R)nghĩa đen là số lượng chỉ số mà bạn cần sử dụng. ones((1,R))là mảng 2 chiều chỉ có một hàng. ones(R)là một vectơ. Nói chung, nếu nó không có ý nghĩa cho biến có nhiều hơn một hàng / cột, thì bạn nên sử dụng một vectơ, không phải là ma trận có kích thước đơn.

Đối với trường hợp cụ thể của bạn, có một vài lựa chọn:

1) Chỉ cần làm cho đối số thứ hai là một vectơ. Sau đây hoạt động tốt:

    np.dot(M[:,0], np.ones(R))

2) Nếu bạn muốn MATLAB như các hoạt động ma trận, hãy sử dụng lớp matrixthay vì ndarray. Tất cả các ma trận buộc phải trở thành mảng 2 chiều và toán tử *thực hiện phép nhân ma trận thay vì phần tử thông minh (vì vậy bạn không cần dấu chấm). Theo kinh nghiệm của tôi, điều này có nhiều rắc rối hơn đáng giá, nhưng nó có thể tốt nếu bạn đã quen với matlab.


Đúng. Tôi đã mong đợi một hành vi giống như matlab hơn. Tôi sẽ xem xét matrixlớp học. Có vấn đề gì với matrixlớp BTW?
clwen

2
Vấn đề với matrixnó chỉ là 2D và cũng vì nó làm quá tải toán tử '*', các hàm được viết cho ndarraycó thể không thành công nếu được sử dụng trên a matrix.
Evan

11

Hình dạng là một tuple. Nếu chỉ có 1 chiều, hình sẽ là một số và chỉ để trống sau dấu phẩy. Đối với 2+ kích thước, sẽ có một số sau tất cả các dấu phẩy.

# 1 dimension with 2 elements, shape = (2,). 
# Note there's nothing after the comma.
z=np.array([  # start dimension
    10,       # not a dimension
    20        # not a dimension
])            # end dimension
print(z.shape)

(2,)

# 2 dimensions, each with 1 element, shape = (2,1)
w=np.array([  # start outer dimension 
    [10],     # element is in an inner dimension
    [20]      # element is in an inner dimension
])            # end outer dimension
print(w.shape)

(2.1)


5

Đối với lớp mảng cơ sở của nó, mảng 2d không đặc biệt hơn mảng 1d hoặc 3d. Có một số thao tác bảo toàn kích thước, một số làm giảm chúng, kết hợp khác hoặc thậm chí mở rộng chúng.

M=np.arange(9).reshape(3,3)
M[:,0].shape # (3,) selects one column, returns a 1d array
M[0,:].shape # same, one row, 1d array
M[:,[0]].shape # (3,1), index with a list (or array), returns 2d
M[:,[0,1]].shape # (3,2)

In [20]: np.dot(M[:,0].reshape(3,1),np.ones((1,3)))

Out[20]: 
array([[ 0.,  0.,  0.],
       [ 3.,  3.,  3.],
       [ 6.,  6.,  6.]])

In [21]: np.dot(M[:,[0]],np.ones((1,3)))
Out[21]: 
array([[ 0.,  0.,  0.],
       [ 3.,  3.,  3.],
       [ 6.,  6.,  6.]])

Các biểu thức khác cho cùng một mảng

np.dot(M[:,0][:,np.newaxis],np.ones((1,3)))
np.dot(np.atleast_2d(M[:,0]).T,np.ones((1,3)))
np.einsum('i,j',M[:,0],np.ones((3)))
M1=M[:,0]; R=np.ones((3)); np.dot(M1[:,None], R[None,:])

MATLAB bắt đầu chỉ với mảng 2D. Các phiên bản mới hơn cho phép nhiều kích thước hơn, nhưng vẫn giữ giới hạn dưới là 2. Nhưng bạn vẫn phải chú ý đến sự khác biệt giữa ma trận hàng và cột một, một có hình (1,3)v (3,1). Bạn có thường xuyên viết [1,2,3].'không? Tôi sẽ viết row vectorcolumn vector, nhưng với ràng buộc 2d đó, không có bất kỳ vectơ nào trong MATLAB - ít nhất là không theo nghĩa toán học của vectơ là 1d.

Bạn đã xem np.atleast_2d(cả phiên bản _1d và _3d) chưa?


1

1) Lý do không thích hình dạng (R, 1)hơn (R,)là nó làm phức tạp mọi thứ một cách không cần thiết. Bên cạnh đó, tại sao nên có hình dạng (R, 1)theo mặc định cho một vectơ độ dài R thay vì (1, R)? Tốt hơn là giữ cho nó đơn giản và rõ ràng khi bạn yêu cầu các kích thước bổ sung.

2) Ví dụ của bạn, bạn đang tính toán một sản phẩm bên ngoài để bạn có thể thực hiện việc này mà không cần reshapegọi bằng cách sử dụng np.outer:

np.outer(M[:,0], numpy.ones((1, R)))

Cảm ơn câu trả lời. 1) M[:,0]về cơ bản là nhận được tất cả các hàng với phần tử đầu tiên, vì vậy nó có ý nghĩa (R, 1)hơn là có (1, R). 2) Không phải lúc nào cũng có thể thay thế bằng np.outer, ví dụ, chấm cho ma trận theo hình (1, R) sau đó (R, 1).
clwen

1) Vâng, đó có thể là quy ước nhưng điều đó làm cho nó ít thuận tiện hơn trong các trường hợp khác. Quy ước cũng có thể là cho M [1, 1] trả về một mảng hình (1, 1) nhưng điều đó cũng thường không thuận tiện hơn một vô hướng. Nếu bạn thực sự muốn hành vi giống như ma trận, thì bạn nên sử dụng một matrixđối tượng. 2) Trên thực tế, np.outerlàm việc bất kể hình dạng là (1, R), (R, 1)hoặc kết hợp cả hai.
bogatron

0

Có rất nhiều câu trả lời tốt ở đây rồi. Nhưng đối với tôi thật khó để tìm thấy một số ví dụ, trong đó hình dạng hoặc mảng có thể phá vỡ tất cả chương trình.

Vì vậy, đây là một:

import numpy as np
a = np.array([1,2,3,4])
b = np.array([10,20,30,40])


from sklearn.linear_model import LinearRegression
regr = LinearRegression()
regr.fit(a,b)

Điều này sẽ thất bại với lỗi:

ValueError: Dự kiến ​​mảng 2D, thay vào đó có mảng 1D

nhưng nếu chúng ta thêm reshapevào a:

a = np.array([1,2,3,4]).reshape(-1,1)

Điều này hoạt động chính xác!

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.