基礎(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)行切分。切分時可以指定:
- 目標(biāo)數(shù)組個數(shù) ——將自動計算切分后的數(shù)組的形狀
- 切分列下表 —— 將在指定的列下標(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ī)則如下:
- 讓所有輸入數(shù)組都向其中shape最長的數(shù)組看齊,shape中不足的部分都通過在前面加1補齊
- 輸出數(shù)組的shape是輸入數(shù)組shape的各個軸上的最大值
- 如果輸入數(shù)組的某個軸和輸出數(shù)組的對應(yīng)軸的長度相同或者其長度為1時,這個數(shù)組能夠用來計算,否則出錯
-
當(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()

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

進(jìn)一步學(xué)習(xí)
如果完全消化了本課的內(nèi)容,進(jìn)一步學(xué)習(xí)建議:
- 閱讀《利用Python進(jìn)行數(shù)據(jù)分析》
- 閱讀NumPy手冊
