numpy.array shape (R, 1) and (R,) 的區別

翻譯自:stackoverflow 回答 By Gareth Reeshtml

原問題

在 numpy 中,有些運算返回 shape 爲 (R, 1) 而有些返回 (R,)。因爲須要顯式調用 reshape,這會讓矩陣乘法變得更加繁瑣。舉例來講,假設有一個矩陣 M,若是咱們想執行 numpy.dot(M[:,0], numpy.ones((1, R))),其中 R 是行數(固然,換成列會有一樣的問題)。咱們會獲得 matrices are not aligned 異常,由於 M[:,0] 的 shape 是 (R,) 可是 numpy.ones((1, R)) 的 shape 是 (1, R)python

因此個人問題是:數組

  1. shape (R, 1) and (R,) 有什麼區別。我知道從字面意思上一個是數的 list,另外一個是隻包含一個數的 list 組成的 list。只是好奇爲何不把 numpy 設計成更傾向於 shape (R, 1) 而不是 (R,) 以讓矩陣乘法更簡單。bash

  2. 上面的例子有更好的方法嗎?而不用顯式地像這樣調用 reshapenumpy.dot(M[:,0].reshape(R, 1), numpy.ones((1, R)))ide

回答

1. NumPy 中 shape 的含義

你寫到,「我知道從字面意思上一個是數的 list,另外一個是隻包含一個數的 list 組成的 list」,可是用這種方式去想有點沒啥用。工具

考慮 NumPy 數組的最好方式是它們包含兩個部分,一個 數據緩衝區(data buffer),它只是一個原始數據塊(a block of raw elements), 以及一個描述如何解釋數據緩衝區的 視圖(view)。spa

好比,若是咱們建立一個包含 12 個整數的數組:翻譯

>>> a = numpy.arange(12)
>>> a
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])
複製代碼

這時 a 包含一個數據緩衝區,排列以下:設計

┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│  0 │  1 │  2 │  3 │  4 │  5 │  6 │  7 │  8 │  9 │ 10 │ 11 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
複製代碼

以及描述如何解釋數據的視圖:code

>>> 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,)
複製代碼

這裏的 shape (12,) 表示這個數組由單個索引索引,該索引從 0 到 11。從概念上講,若是咱們標記這個索引 i,數組 a 看起來像這樣:

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 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
複製代碼

若是咱們 reshape 一個數組,它不會改變數據緩衝區。相反,它建立了一個新視圖,描述瞭解釋數據的不一樣方式。因此當運行下面以後:

>>> b = a.reshape((3, 4))
複製代碼

數組 ba 有相同的數據緩衝區,可是如今被 兩個 索引索引,其中分別從 0 到 2 和 0 到 3。若是咱們標記兩個索引爲 ij,數組 b 像這樣:

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 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
複製代碼

這意味着:

>>> b[2,1]
9
複製代碼

你能夠看到第二個索引改變得快一點,第一個索引改變得慢。若是你但願反過來,能夠指定 order 參數:

>>> c = a.reshape((3, 4), order='F')
複製代碼

這會使得數組索引像這樣:

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[2,1]
5
複製代碼

如今應該清楚了數組具備一個或多個維度(dimensions)爲 1 的 shape 意味着什麼。通過執行:

>>> d = a.reshape((12, 1))
複製代碼

數組 d 被兩個索引索引,第一個從 0 到 11,第二個索引全是 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 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
複製代碼

因此:

>>> d[10,0]
10
複製代碼

維度爲 1 是 「自由的」(某種意義上來講),因此沒人你能阻止你這麼幹:

>>> e = a.reshape((1, 2, 1, 6, 1))
複製代碼

使得一個數組的全部變成這樣:

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 │
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
複製代碼

所以:

>>> e[0,1,0,0,0]
6
複製代碼

有關如何實現數組的更多詳細信息,請參見 NumPy 內部文檔

2. 該怎麼辦?

既然 numpy.reshape 只是建立了一個新視圖,那麼在有必要去使用它的時候不要感到懼怕。當你想要以不一樣的方式索引數組時,這是正確的工具。

然而,在長時間的計算中,一般能夠首先安排去構造具備「正確」形狀的數組,從而最大限度地減小 reshape 和 transpose 的次數。但在沒有看到致使有 reshape 需求的實際背景以前,很難說應該改變什麼。

你的問題中的例子是:

numpy.dot(M[:,0], numpy.ones((1, R)))
複製代碼

但這是不現實的。首先,這個表達式:

M[:,0].sum()
複製代碼

能夠更簡單地計算結果。其次,第 0 列真的有什麼特別的嗎?也許你真正須要的是:

M.sum(axis=0)
複製代碼
相關文章
相關標籤/搜索