NumPy基礎(chǔ)

基礎(chǔ)

NumPy提供的最主要的對象就是同數(shù)據(jù)類型的多維數(shù)組。他實際上可以被理解成一張表(元素通常是數(shù)組),所有元素具有相同的類型,這些元素可以使用由正整數(shù)構(gòu)建的元組(tuple)來索引。在NumPy中,各個維度被稱為軸(axes)。軸的總數(shù)被稱為秩(rank)。
舉例來說,三維空間的一個點的坐標(biāo)是[1,2,3],它就是一個rank是1的一個數(shù)組。因為它只要?axis,而這個axis的長度是3.
再來看一個例子:
[[1.,0.,0.],
[0.,1.,2.]]
該數(shù)組的rank是2(兩個維度),第一個維度的長度是2,第二個維度的長度是3。
NumPy的數(shù)組類叫做ndarray。同時也有個別名array。這里需要注意一下,numpy.array和Python標(biāo)準(zhǔn)庫的array.array完全不是一個東西,后者僅僅支持一維數(shù)組且提供的功能較少。ndarray對象比較重要的屬性有:

ndarray.ndim
數(shù)組的axes的數(shù)目。

ndarray.shape
數(shù)組的形狀。如一個n * m的矩陣,它的shape就是(n,m)。而且len(ndarray.shape)==ndarray.ndim

ndarray.size
數(shù)組中所有元素的個數(shù)。它實際上是ndarray.shape中所有值的乘積。

ndarray.dtype
用于描述數(shù)組中元素類型的對象。我們可以使用Python標(biāo)準(zhǔn)類型來創(chuàng)建或指定dtype。不過NumPy也提供了一些它自己的類型,比如:numpy.int32,numpy.int16,numpy.float64等。

ndarray.itemsize
數(shù)組中單個元素以字節(jié)計的大小。比如,若類型是float64,則itemsize就是8(64/8)。它限ndarray.dtype.itemsize是等價的。

ndarray.data
存儲數(shù)組中實際的數(shù)據(jù)。通常我們不會直接用這個屬性,因為當(dāng)我們需要訪問數(shù)據(jù)時幾乎總是使用數(shù)組提供的索引功能。

一個例子

import numpy as np 
a = np.arange(15).reshape(3,5)
a
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14]])
a.shape
(3, 5)
a.ndim
2
a.dtype.name,a.itemsize
('int32', 4)
a.size
15
type(a)
numpy.ndarray
b = np.array([6,7,8])
b
array([6, 7, 8])
type(b)
numpy.ndarray

創(chuàng)建數(shù)組

有好幾種方法可以創(chuàng)建數(shù)組。
比如:通過array,我們可以從普通的Python列表或者元組直接創(chuàng)建。元素的類型根據(jù)源類型推斷。

a = np.array([2,3,4])
a
array([2, 3, 4])
a.dtype
dtype('int32')
b = np.array([1.2,3.5,5.1])
b.dtype
dtype('float64')

一個常見的錯誤是,把數(shù)組內(nèi)容當(dāng)做參數(shù)直接傳給array,而不是作為一個列表或元組:

a = np.array(1,2,3,4) #錯誤
a = np.array([1,2,3,4]) #正確
---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

<ipython-input-16-f55a2f3e0f54> in <module>()
----> 1 a = np.array(1,2,3,4) #錯誤
      2 a = np.array([1,2,3,4]) #正確


ValueError: only 2 non-keyword arguments accepted

array將兩層嵌套序列轉(zhuǎn)成二維數(shù)組,三層嵌套序列轉(zhuǎn)成三維數(shù)組,以此類推:

b = np.array([(1.5,2,3),(4,5,6)])
b
array([[1.5, 2. , 3. ],
       [4. , 5. , 6. ]])

數(shù)據(jù)類型在創(chuàng)建時可以指定:

c = np.array([[1,2],[3,4]],dtype=complex)
c
array([[1.+0.j, 2.+0.j],
       [3.+0.j, 4.+0.j]])

在實際的工作中,我們常常需要固定形狀大小的數(shù)組,但在定義數(shù)組時還沒有加載數(shù)據(jù)。
NumPy為我們提供了一些包含占位符的數(shù)組創(chuàng)建函數(shù)。這樣我們就可以避免使用動態(tài)數(shù)組,因為動態(tài)數(shù)組在增長的時候很耗時的。

  • zeros函數(shù)可以創(chuàng)建所有元素均為0的數(shù)組;
  • ones函數(shù)可以創(chuàng)建所有元素均為1的數(shù)組;
  • empty函數(shù)創(chuàng)建的數(shù)組,其元素都是隨機的?。?!

默認(rèn)情況下,創(chuàng)建時的dtype都是float64;

np.zeros((3,4)) #他的參數(shù)并不是數(shù)據(jù),而是數(shù)組的形狀(shape)
array([[0., 0., 0., 0.],
       [0., 0., 0., 0.],
       [0., 0., 0., 0.]])
np.ones((2,3,4),dtype=np.int16) # 指定數(shù)據(jù)類型
array([[[1, 1, 1, 1],
        [1, 1, 1, 1],
        [1, 1, 1, 1]],

       [[1, 1, 1, 1],
        [1, 1, 1, 1],
        [1, 1, 1, 1]]], dtype=int16)
np.empty((2,3)) # 一定要小心,元素都是隨機的
array([[1.5, 2. , 3. ],
       [4. , 5. , 6. ]])

NumPy提供了一個類似于Python標(biāo)準(zhǔn)庫中range類似的函數(shù)np.arange來生成數(shù)字序列

np.arange(10,30,5) # start,stop,step
array([10, 15, 20, 25])
np.arange(0,2,0.3) #接受浮點數(shù)參數(shù)
array([0. , 0.3, 0.6, 0.9, 1.2, 1.5, 1.8])

不過因為浮點數(shù)的有限精度問題,arange返回的數(shù)組長度可能無法預(yù)知。因此,最好使用linspace,它可以指定所需要的數(shù)組長度,而不需要設(shè)定步長:

np.linspace(0,2,9)
array([0.  , 0.25, 0.5 , 0.75, 1.  , 1.25, 1.5 , 1.75, 2.  ])
from numpy import pi
x = np.linspace(0,2*pi,100)
f = np.sin(x)
f
array([ 0.00000000e+00,  6.34239197e-02,  1.26592454e-01,  1.89251244e-01,
        2.51147987e-01,  3.12033446e-01,  3.71662456e-01,  4.29794912e-01,
        4.86196736e-01,  5.40640817e-01,  5.92907929e-01,  6.42787610e-01,
        6.90079011e-01,  7.34591709e-01,  7.76146464e-01,  8.14575952e-01,
        8.49725430e-01,  8.81453363e-01,  9.09631995e-01,  9.34147860e-01,
        9.54902241e-01,  9.71811568e-01,  9.84807753e-01,  9.93838464e-01,
        9.98867339e-01,  9.99874128e-01,  9.96854776e-01,  9.89821442e-01,
        9.78802446e-01,  9.63842159e-01,  9.45000819e-01,  9.22354294e-01,
        8.95993774e-01,  8.66025404e-01,  8.32569855e-01,  7.95761841e-01,
        7.55749574e-01,  7.12694171e-01,  6.66769001e-01,  6.18158986e-01,
        5.67059864e-01,  5.13677392e-01,  4.58226522e-01,  4.00930535e-01,
        3.42020143e-01,  2.81732557e-01,  2.20310533e-01,  1.58001396e-01,
        9.50560433e-02,  3.17279335e-02, -3.17279335e-02, -9.50560433e-02,
       -1.58001396e-01, -2.20310533e-01, -2.81732557e-01, -3.42020143e-01,
       -4.00930535e-01, -4.58226522e-01, -5.13677392e-01, -5.67059864e-01,
       -6.18158986e-01, -6.66769001e-01, -7.12694171e-01, -7.55749574e-01,
       -7.95761841e-01, -8.32569855e-01, -8.66025404e-01, -8.95993774e-01,
       -9.22354294e-01, -9.45000819e-01, -9.63842159e-01, -9.78802446e-01,
       -9.89821442e-01, -9.96854776e-01, -9.99874128e-01, -9.98867339e-01,
       -9.93838464e-01, -9.84807753e-01, -9.71811568e-01, -9.54902241e-01,
       -9.34147860e-01, -9.09631995e-01, -8.81453363e-01, -8.49725430e-01,
       -8.14575952e-01, -7.76146464e-01, -7.34591709e-01, -6.90079011e-01,
       -6.42787610e-01, -5.92907929e-01, -5.40640817e-01, -4.86196736e-01,
       -4.29794912e-01, -3.71662456e-01, -3.12033446e-01, -2.51147987e-01,
       -1.89251244e-01, -1.26592454e-01, -6.34239197e-02, -2.44929360e-16])

作為補充,可以看看他們的文檔:array,zeros,zero_like(生成一個和原矩陣相同形狀的全是0的矩陣),numpy.random.rand,numpy.random.randn,fromfunction,fromfile
可以在notebook里面直接np.array?閱讀,也可以去Numpy的官方文檔。

打印數(shù)組

打印出來非常類似于嵌套函數(shù)

a = np.arange(6) # 一維
print(a)
[0 1 2 3 4 5]
b = np.arange(12).reshape(3,4) # 二維
print(b)
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]
c = np.arange(24).reshape(2,3,4) # 二維
print(c)
[[[ 0  1  2  3]
  [ 4  5  6  7]
  [ 8  9 10 11]]

 [[12 13 14 15]
  [16 17 18 19]
  [20 21 22 23]]]

如果數(shù)組太長,NumPy會自動忽略中間部分的數(shù)據(jù),只打印首尾:

print(np.arange(10000))
[   0    1    2 ... 9997 9998 9999]
print(np.arange(10000).reshape(100,100))
[[   0    1    2 ...   97   98   99]
 [ 100  101  102 ...  197  198  199]
 [ 200  201  202 ...  297  298  299]
 ...
 [9700 9701 9702 ... 9797 9798 9799]
 [9800 9801 9802 ... 9897 9898 9899]
 [9900 9901 9902 ... 9997 9998 9999]]

當(dāng)然,我們可以通過設(shè)置set_printoptions選項來關(guān)閉該功能:
np.set_printoptions(threshold='nan)

基礎(chǔ)操作

在數(shù)組上進(jìn)行算術(shù)操作都是元素級別的操作

a = np.array([20,30,40,50])
b = np.arange(4) 
c = a - b
c
array([20, 29, 38, 47])
b ** 2
array([0, 1, 4, 9], dtype=int32)
10 * np.sin(a)
array([ 9.12945251, -9.88031624,  7.4511316 , -2.62374854])
a < 45
array([ True,  True,  True, False])

在線性代數(shù)中,乘號 * 一般表達(dá)的是矩陣乘法,但在NumPy中它表示元素級別的乘法,矩陣乘法在NumPy中使用dot:

A = np.array([[1,1],
            [0,1]])
B = np.array([[2,0],
            [3,4]])
A * B #元素級別乘法
array([[2, 0],
       [0, 4]])
A.dot(B)
array([[5, 4],
       [3, 4]])
np.dot(A,B)
array([[5, 4],
       [3, 4]])

上面的操作符都會新建一個數(shù)組,而有一些操作符則會直接修改現(xiàn)有數(shù)組,比如+=,*=

a = np.ones((2,3),dtype=int)
b = np.random.random((2,3))
a *= 3
a
array([[3, 3, 3],
       [3, 3, 3]])
b += a
b
array([[3.96590659, 3.57016481, 3.16824186],
       [3.7746882 , 3.05928896, 3.94018039]])
a += b # 類型轉(zhuǎn)換失敗,因為b是float,更加精確,而a是int
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-50-294cacd62d6f> in <module>()
----> 1 a += b


TypeError: Cannot cast ufunc add output from dtype('float64') to dtype('int32') with casting rule 'same_kind'

ndarray類里內(nèi)置了很多方法來完成一些一元操作符,比如計算所有元素之和:

a = np.random.random((2,3))
a
array([[0.80608285, 0.07392699, 0.49798093],
       [0.69115847, 0.82903752, 0.63852812]])
print(a.sum(),a.min(),a.max())
18 3 3

默認(rèn)情況下,這些方法不關(guān)心數(shù)組的形狀,會將所有元素一起計算,不過可以通過制定axis(軸)來計算沿著該軸的數(shù)據(jù):

b = np.arange(12).reshape(3,4)
b
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
b.sum(axis=0)
array([12, 15, 18, 21])
b.min(axis=1)
array([0, 4, 8])
b.cumsum(axis=1)
array([[ 0,  1,  3,  6],
       [ 4,  9, 15, 22],
       [ 8, 17, 27, 38]], dtype=int32)

通用函數(shù)

numpy提供了一些常用的數(shù)學(xué)函數(shù),如:sin,cos,exp等,操作是作用于每個單獨的元素上,并產(chǎn)生一個新的數(shù)組。

B = np.arange(3)
B
array([0, 1, 2])
np.exp(B)
array([1.        , 2.71828183, 7.3890561 ])
np.sqrt(B)
array([0.        , 1.        , 1.41421356])
C = np.array([2.,-1,4.])
np.add(B,C)
array([2., 0., 6.])

索引,切片以及遍歷

一維數(shù)組非常類似于python的序列,他們可以被索引,被切片,被遍歷

a = np.arange(10) ** 3
a
array([  0,   1,   8,  27,  64, 125, 216, 343, 512, 729], dtype=int32)
a[2]
8
a[2:5]
array([ 8, 27, 64], dtype=int32)
a[:6:2] = -1000 # 等價于 a[0:6:2] = -1000 從開始到第6個(不含),以2為步長將元素設(shè)置為-1000
a
array([-1000,     1, -1000,    27, -1000,   125,   216,   343,   512,
         729], dtype=int32)
a[::-1] #數(shù)組倒序
array([  729,   512,   343,   216,   125, -1000,    27, -1000,     1,
       -1000], dtype=int32)
for i in a:
    print(i ** (1/3.0))
nan
1.0
nan
3.0
nan
5.0
5.999999999999999
6.999999999999999
7.999999999999999
8.999999999999998


C:\Users\ttc\Anaconda3\lib\site-packages\ipykernel_launcher.py:2: RuntimeWarning: invalid value encountered in power

多維數(shù)組每一個軸均有一個索引,這些索引使用“,”分隔,以元組的形式表現(xiàn):

def f(x,y):
    return 10 * x + y
b = np.fromfunction(f,(5,4),dtype=int)
b
array([[ 0,  1,  2,  3],
       [10, 11, 12, 13],
       [20, 21, 22, 23],
       [30, 31, 32, 33],
       [40, 41, 42, 43]])
b[2,3]
23
b[0:5,1]
array([ 1, 11, 21, 31, 41])
b[:,1]
array([ 1, 11, 21, 31, 41])
b[1:3,:]
array([[10, 11, 12, 13],
       [20, 21, 22, 23]])

當(dāng)提供的索引不夠,那么NumPy會自動補全其他軸的索引:

b[-1] # 等價于b[-1,:]
array([40, 41, 42, 43])

另外,NumPy還有...這種寫法,它會自動判斷所需要填補的索引:
比如,我們現(xiàn)在有一個擁有五個軸(axis)的數(shù)組:

  • x[1,2,...]等價于 x[1,2,:,:,:]
  • x[...,3]等價于x[:,:,:,:,3]
  • x[4,...,5,:] 等價于x[4,:,:,5,:]
c = np.array([[[0,1,2],
               [10,12,13]],
               [[100,101,102],
               [110,112,113]]])
c.shape
(2, 2, 3)
c[1,...]
array([[100, 101, 102],
       [110, 112, 113]])
c[...,2]
array([[  2,  13],
       [102, 113]])

遍歷從第一個軸開始的:

for row in b:
    print(row)
[0 1 2 3]
[10 11 12 13]
[20 21 22 23]
[30 31 32 33]
[40 41 42 43]

不過,要是想要將數(shù)組"打平"訪問,可以使用flat屬性:

for element in b.flat:
    print(element,end=' ')
0 1 2 3 10 11 12 13 20 21 22 23 30 31 32 33 40 41 42 43 

形狀操作

修改一個數(shù)組的形狀

a = np.floor(10 * np.random.random((3,4)))
a
array([[4., 9., 4., 0.],
       [7., 1., 6., 6.],
       [2., 7., 4., 0.]])
a.shape
(3, 4)

在NumPy中有多種方法可以改變一個數(shù)組的形狀。
請注意,下面三種方法均返回一個修改后的數(shù)組,但又不改變原來的數(shù)組:

a.ravel() # 返回打平的數(shù)組
array([4., 9., 4., 0., 7., 1., 6., 6., 2., 7., 4., 0.])
a.reshape(6,2) # 返回目標(biāo)形狀的數(shù)組
array([[4., 9.],
       [4., 0.],
       [7., 1.],
       [6., 6.],
       [2., 7.],
       [4., 0.]])
a.T # 返回它的轉(zhuǎn)置
array([[4., 7., 2.],
       [9., 1., 7.],
       [4., 6., 4.],
       [0., 6., 0.]])

由ravel()返回的數(shù)組,其元素的順序默認(rèn)是C語言風(fēng)格的,即最右側(cè)的索引“變化最快”,因此a[0,0]的下一個元素是a[0,1],如果傳給ravel()的數(shù)組時從其他數(shù)組切片得到的,或者使用不尋常的選項構(gòu)建的,那就不得不拷貝一次數(shù)據(jù)了。
ravel()和reshape()函數(shù)本身也接受一個可選的參數(shù),可以使用Fortran風(fēng)格(Fortran-style)的數(shù)組u,它最左側(cè)的索引變化的最快,即a[0,0]的下一個元素是a[1,0]。
reshape函數(shù)不會改變原數(shù)組,而ndarray.resize方法則會改變原數(shù)組:

a
array([[4., 9., 4., 0.],
       [7., 1., 6., 6.],
       [2., 7., 4., 0.]])
a.resize((2,6))
a
array([[4., 9., 4., 0., 7., 1.],
       [6., 6., 2., 7., 4., 0.]])

如果在reshape操作時將某一維度設(shè)置為-1,那么該維度就會被自動計算,比如:

a.reshape(3,-1) # 只在乎數(shù)組第一維是3,第二維讓系統(tǒng)自動計算
array([[4., 9., 4., 0.],
       [7., 1., 6., 6.],
       [2., 7., 4., 0.]])

將不同的數(shù)組堆疊(stacking)起來

多個數(shù)組可以沿著不同的軸堆疊起來:

a = np.floor(10 * np.random.random((2,2)))
a
array([[4., 3.],
       [5., 3.]])
b = np.floor(10 * np.random.random((2,2)))
b
array([[9., 7.],
       [9., 3.]])
np.vstack((a,b)) # 垂直堆疊
array([[4., 3.],
       [5., 3.],
       [9., 7.],
       [9., 3.]])
np.hstack((a,b)) # 水平堆疊
array([[4., 3., 9., 7.],
       [5., 3., 9., 3.]])

column_stack可以將一維數(shù)組作為一列插入一個二維數(shù)組:

c = np.array([3,3])
np.column_stack((a,c))
array([[4., 3., 3.],
       [5., 3., 3.]])

對于超過二維的情況,不討論

將一個數(shù)組切分成多個

使用hsplit,我們可以將一個數(shù)組沿水平方向進(jìn)行切分。切分時可以指定:

  1. 目標(biāo)數(shù)組個數(shù) ——將自動計算切分后的數(shù)組的形狀
  2. 切分列下表 —— 將在指定的列下標(biāo)處進(jìn)行切分
a = np.floor(10 * np.random.random((2,12)))
a
array([[5., 4., 7., 6., 0., 6., 2., 8., 7., 1., 2., 5.],
       [3., 9., 3., 0., 4., 1., 3., 1., 0., 9., 2., 1.]])
np.hsplit(a,3) # 將a橫向切分成三個數(shù)組
[array([[5., 4., 7., 6.],
        [3., 9., 3., 0.]]), array([[0., 6., 2., 8.],
        [4., 1., 3., 1.]]), array([[7., 1., 2., 5.],
        [0., 9., 2., 1.]])]
np.hsplit(a,(3,4)) # 在第三列和第四列進(jìn)行切分,從1開始計數(shù)
[array([[5., 4., 7.],
        [3., 9., 3.]]), array([[6.],
        [0.]]), array([[0., 6., 2., 8., 7., 1., 2., 5.],
        [4., 1., 3., 1., 0., 9., 2., 1.]])]

類似的,還有vsplit,而array_split則允許指定切分的軸

拷貝(copy)與視圖(view)

當(dāng)我們操作數(shù)組的時候,其數(shù)據(jù)有時候會被拷貝到一個新的數(shù)組,而有時又不會。這對于初學(xué)者而言常常是一個覺得很不清晰的地方。
實際上,一共有三種情況:

根本不拷貝

簡單的賦值不會發(fā)生任何拷貝行為:

a = np.arange(12)
b = a # 不會新建對象
b is a # a和b是對同一個ndarray對象的兩個名字
True
b.shape = 3,4 # 同時會修改a的形狀
a.shape
(3, 4)

Python以引用的方式傳遞可變對象,所以函數(shù)調(diào)用不會產(chǎn)生拷貝:

def f(x):
    print(id(x))
    
print(id(a)) # id是一個對象(實例)獨一無二的標(biāo)識符
f(a)
87464496
87464496

視圖和淺拷貝

不同的數(shù)據(jù)對象可以共享相同的數(shù)據(jù)。view方法新建一個數(shù)組對象,但仍然使用相同的數(shù)據(jù)。

c = a.view()
c is a
False
c.base is a # c僅僅是a中數(shù)據(jù)的一個視圖
True
c.flags.owndata # c不擁有自己的數(shù)據(jù)
False
c.shape = 2,6
a.shape
(3, 4)
c[0,4] = 1234 # a的數(shù)據(jù)會改變?。?!
a
array([[   0,    1,    2,    3],
       [1234,    5,    6,    7],
       [   8,    9,   10,   11]])
c
array([[   0,    1,    2,    3, 1234,    5],
       [   6,    7,    8,    9,   10,   11]])

對一個數(shù)組進(jìn)行切片會返回它的視圖:

s = a[:,1:3]
s[:] = 10
a
array([[   0,   10,   10,    3],
       [1234,   10,   10,    7],
       [   8,   10,   10,   11]])

深拷貝

copy方法會對數(shù)組及其數(shù)據(jù)做一次完整的拷貝。

d = a.copy() # 新建一個數(shù)組對象,而且把數(shù)據(jù)也拷貝了一份
d is a
False
d.base is a # d不從a共享任何東西
False
d[0,0] = 9999
a
array([[   0,   10,   10,    3],
       [1234,   10,   10,    7],
       [   8,   10,   10,   11]])

基礎(chǔ)函數(shù)及方法總覽

我們這里按照類目列出了一些最常用的NumPy的函數(shù)及方法。請參考routines以了解更全的列表

Array Creation

arange,array,copy ...

Conversions

ndarray.astype,atleast_1d ....

Mainpulations

array_split,column_stack ...

Questions

all,any,nonzero,where

Odering

argmax,argmin,argsort,max,min,ptp,searchsorted,sort

Operations

choose,compress,cumprod,cumsum,inner....

Basic Statistics

cov,mean,std,var

Basic Linear Algebra

cross,dot,outer,linalg.svd,vdot

廣播機制

Numpy的Universal functions 中要求輸入的數(shù)組shape是一致的,當(dāng)數(shù)組的shape不想等的時候,則會使用廣播機制,調(diào)整數(shù)組使得shape一樣,滿足規(guī)則,則可以運算,否則就出錯
四條規(guī)則如下:

  1. 讓所有輸入數(shù)組都向其中shape最長的數(shù)組看齊,shape中不足的部分都通過在前面加1補齊
  2. 輸出數(shù)組的shape是輸入數(shù)組shape的各個軸上的最大值
  3. 如果輸入數(shù)組的某個軸和輸出數(shù)組的對應(yīng)軸的長度相同或者其長度為1時,這個數(shù)組能夠用來計算,否則出錯
  4. 當(dāng)輸入數(shù)組的某個軸的長度為1時,沿著此軸運算時都用此軸上的第一組值


    image.png

時髦索引(fancy indexing)及索引技巧

NumPy比一般的Python序列支持更多的索引功能。
在支持使用整型數(shù)字及切片來進(jìn)行索引的基礎(chǔ)上,數(shù)組還可以使用整形數(shù)據(jù)和布爾型數(shù)組來進(jìn)行索引。

使用索引數(shù)組(array if indices)來進(jìn)行索引

a = np.arange(12) ** 2 # 12個平方數(shù)
i = np.array([1,1,3,8,5]) # 一些索引
a[i]
array([ 1,  1,  9, 64, 25], dtype=int32)
j = np.array([[3,4],[9,7]]) # 一個二維的索引數(shù)組
a[j]
array([[ 9, 16],
       [81, 49]], dtype=int32)

當(dāng)數(shù)組a是多維數(shù)組的時候,一個單獨的索引數(shù)組表達(dá)的是數(shù)組a的第一個維度。
下面這個例子定義了一個調(diào)色盤,然后圖片以調(diào)色盤的索引來表示,它可以詮釋上面那句話的含義:

palette = np.array([[0,0,0],[255,0,0],[0,255,0],[0,0,255],[255,255,255]])
image = np.array([[0,1,2,0],
                 [0,3,4,0]])
palette[image]
array([[[  0,   0,   0],
        [255,   0,   0],
        [  0, 255,   0],
        [  0,   0,   0]],

       [[  0,   0,   0],
        [  0,   0, 255],
        [255, 255, 255],
        [  0,   0,   0]]])

我們也可以讓索引有多個維度。不過,所有的索引數(shù)組在每一個維度上就必須有同樣的形狀:

a = np.arange(12).reshape(3,4)
a
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
i = np.array([[0,1],
              [1,2]])  # 數(shù)組a的第一個維度
j = np.array([[2,1],
              [3,3]])  # 數(shù)組a的第二個維度
a[i,j] # i 和 j必須要有相同的形狀   (0,2),(1,1),(1,3),(2,3)
array([[ 2,  5],
       [ 7, 11]])
a[i,2]
array([[ 2,  6],
       [ 6, 10]])
a[:,j]
array([[[ 2,  1],
        [ 3,  3]],

       [[ 6,  5],
        [ 7,  7]],

       [[10,  9],
        [11, 11]]])

我們也可以把i和j打包到一個序列中,然后使用這個序列來進(jìn)行索引:

l = [i,j]
a[l]
array([[ 2,  5],
       [ 7, 11]])

但是得千萬注意,不要把i和j放在一個數(shù)組(ndarray)里,因為這個數(shù)組表達(dá)的是以第一個維度來對數(shù)組a進(jìn)行索引。

s = np.array([i,j])
a[s]
---------------------------------------------------------------------------

IndexError                                Traceback (most recent call last)

<ipython-input-135-537bf3252a3e> in <module>()
      1 s = np.array([i,j])
----> 2 a[s]


IndexError: index 3 is out of bounds for axis 0 with size 3
a[tuple(s)] # 等價于a[i,j]
array([[ 2,  5],
       [ 7, 11]])

另一個常用的使用數(shù)組進(jìn)行索引的方式是:在一個時間依賴的序列中找到最大值:

time=np.linspace(20,145,5)
data = np.sin(np.arange(20)).reshape(5,4)
time
array([ 20.  ,  51.25,  82.5 , 113.75, 145.  ])
data
array([[ 0.        ,  0.84147098,  0.90929743,  0.14112001],
       [-0.7568025 , -0.95892427, -0.2794155 ,  0.6569866 ],
       [ 0.98935825,  0.41211849, -0.54402111, -0.99999021],
       [-0.53657292,  0.42016704,  0.99060736,  0.65028784],
       [-0.28790332, -0.96139749, -0.75098725,  0.14987721]])
ind = data.argmax(axis=0)
ind
array([2, 0, 3, 1], dtype=int64)
time[ind]
array([ 82.5 ,  20.  , 113.75,  51.25])
data_max = data[ind,range(data.shape[1])]
data_max
array([0.98935825, 0.84147098, 0.99060736, 0.6569866 ])
np.all(data.max(axis=0) == data_max)  # 比較所有元素
True

使用索引數(shù)組不僅可以訪問數(shù)組元素,還可以更改數(shù)組元素:

a = np.arange(5)
a
array([0, 1, 2, 3, 4])
a[[1,3,4]] = 0
a
array([0, 0, 2, 0, 0])

不過如果同一個索引在索引數(shù)組中出現(xiàn)多次的話,那么賦值操作實際上就會被多次執(zhí)行:

a = np.arange(5)
a[[0,0,2]] = [1,2,3] # 0位置被賦值了兩次
a
array([2, 1, 3, 3, 4])

不過要注意的是,python的+=語法結(jié)構(gòu)的行為會出乎你的預(yù)料,建議不要使用+=操作:

a[[0,0,2]] +=1 # 0位置出現(xiàn)兩次 但是并沒有多次執(zhí)行+1
a
array([3, 1, 4, 3, 4])

使用布爾數(shù)組來索引

前面我們使用整型的索引數(shù)組來訪問數(shù)據(jù)的時候,我們實際上是提供了想要訪問的數(shù)據(jù)的坐標(biāo)。
而布爾數(shù)組則有所不同,它明確指示了哪些數(shù)組元素是想要的,哪些數(shù)組元素是不想要的。
一種最自然的布爾數(shù)組的想法是,用于索引的布爾數(shù)組形狀和數(shù)據(jù)數(shù)組的形狀完全一致:

a = np.arange(12).reshape(3,4)
b = a > 4
b
array([[False, False, False, False],
       [False,  True,  True,  True],
       [ True,  True,  True,  True]])
a[b]
array([ 5,  6,  7,  8,  9, 10, 11])

這種性質(zhì)非常適合賦值:

a[b] = 0
a
array([[0, 1, 2, 3],
       [4, 0, 0, 0],
       [0, 0, 0, 0]])

第二種使用布爾索引的方式跟整形索引更加相似。
對于數(shù)組的每一個維度,使用一個一維數(shù)組來指定所需要的數(shù)據(jù)切片:

a = np.arange(12).reshape(3,4)
a
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
b1 = np.array([False,True,True]) # 第一維
b2 = np.array([True,False,True,False]) # 第二維
a[b1,:] # 選擇行
array([[ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
a[:,b2] # 選擇列
array([[ 0,  2],
       [ 4,  6],
       [ 8, 10]])
a[b1,b2] # 選定坐標(biāo),(1,0),(2,2)這個實際上很奇怪,一般不會用
array([ 4, 10])

ix_()函數(shù)

ix_函數(shù)可以用于使用不同的向量來生成它們之間的笛卡爾積。
比如我們現(xiàn)在有a,b,c三個長度不一致的向量,我們想計算所有可能的a+b* c的值:

a = np.array([2,3,4,5])
b = np.array([8,5,4])
c = np.array([5,4,6,8,3])

ax,bx,cx = np.ix_(a,b,c)
result = ax+bx*cx
result
array([[[42, 34, 50, 66, 26],
        [27, 22, 32, 42, 17],
        [22, 18, 26, 34, 14]],

       [[43, 35, 51, 67, 27],
        [28, 23, 33, 43, 18],
        [23, 19, 27, 35, 15]],

       [[44, 36, 52, 68, 28],
        [29, 24, 34, 44, 19],
        [24, 20, 28, 36, 16]],

       [[45, 37, 53, 69, 29],
        [30, 25, 35, 45, 20],
        [25, 21, 29, 37, 17]]])
result[3,2,4]
17

上面用了廣播機制,技巧比較高

基本的線性代數(shù)

簡單的數(shù)組操作

如有必要,請閱讀linalg的文檔以了解更多

a = np.array([[1.0,2.0],[3.0,4.0]])
print(a)
[[1. 2.]
 [3. 4.]]
a.transpose() #轉(zhuǎn)置
array([[1., 3.],
       [2., 4.]])
np.linalg.inv(a) #求逆
array([[-2. ,  1. ],
       [ 1.5, -0.5]])
u = np.eye(2) # 單位陣
u
array([[1., 0.],
       [0., 1.]])
j = np.array([[0.0,-1.0],[1.0,0.0]])
np.dot(j,j) # 矩陣乘法
array([[-1.,  0.],
       [ 0., -1.]])
np.trace(u) # 矩陣的跡
2.0

解線性方程:

x_1 + 2x_2 = 5

3x_1 + 4x_2 = 7

y = np.array([[5.],[7.]])
np.linalg.solve(a,y)
array([[-3.],
       [ 4.]])
np.linalg.eig(j) # 求特征值和特征向量
(array([0.+1.j, 0.-1.j]),
 array([[0.70710678+0.j        , 0.70710678-0.j        ],
        [0.        -0.70710678j, 0.        +0.70710678j]]))

技巧與小貼士

這里給一些短小但有用的貼士。

“自動”變形

在改變一個數(shù)組的形狀的時候,我們可以省略一個維度的值,它會被自動計算出來:

a = np.arange(30)
a.shape = 2,-1,3 # 這里的-1指的是在滿足其他維度的情況下,這個維度自動幫我算出來
a.shape
(2, 5, 3)
a
array([[[ 0,  1,  2],
        [ 3,  4,  5],
        [ 6,  7,  8],
        [ 9, 10, 11],
        [12, 13, 14]],

       [[15, 16, 17],
        [18, 19, 20],
        [21, 22, 23],
        [24, 25, 26],
        [27, 28, 29]]])

向量堆疊

我們?nèi)绾螌⒌乳L的行向量組建成一個二維的數(shù)組呢?
我們可以使用column_stack,dstack,hstack,vstack。實際上只要記住stack這個詞就夠了。

x = np.arange(0,10,2)
y = np.arange(5)

m = np.vstack([x,y])
m
array([[0, 2, 4, 6, 8],
       [0, 1, 2, 3, 4]])
xy = np.hstack([x,y])
xy
array([0, 2, 4, 6, 8, 0, 1, 2, 3, 4])

超過二維時,函數(shù)的邏輯有些怪,不需要關(guān)心,不怎么用

直方圖

NumPy的histogram函數(shù)作用在一個數(shù)組上的時候,會返回一堆向量:

  • 每個桶的技術(shù)
  • 同的劃分

請注意,matplotlib也有一個函數(shù)(hist)可以創(chuàng)建直方圖,它和NumPy的不一樣。
二者主要的區(qū)別在于,matplotlib的hist會自動繪制出直方圖,而numpy.histogram僅僅生成數(shù)據(jù)。

import matplotlib.pyplot as plt
mu,sigma = 2,0.5
v = np.random.normal(mu,sigma,10000) # 使用正態(tài)分布隨機生成一萬個點

# matplotlib版本
plt.hist(v,bins=50,density=1) # 數(shù)據(jù)都規(guī)范化了
plt.show()
output_199_0.png
# NumPy版本
# 先生成histogram數(shù)據(jù),然后再繪制
(n,bins)= np.histogram(v,bins=50,density=True) # bins是區(qū)間的上下界
plt.plot(.5*(bins[1:]+bins[:-1]),n)
plt.show()
output_200_0.png

進(jìn)一步學(xué)習(xí)

如果完全消化了本課的內(nèi)容,進(jìn)一步學(xué)習(xí)建議:

  1. 閱讀《利用Python進(jìn)行數(shù)據(jù)分析》
  2. 閱讀NumPy手冊
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容