Python科學計算-----NumPy(一)
Numpy
NumPy 是 Numerical Python的簡稱,是Python的高效能運算和資料分析的基礎核心包。與Python的基本資料型別相比,其具有以下突出優勢:
NumPy提供了兩種基本的物件:ndarray(N-dimensional array object)和ufunc(universal function object)。ndarray用來儲存單一資料型別的多維陣列,ufunc是對陣列進行處理的函式。
ndarray
筆者在初次接觸NumPy的陣列物件時,一直搞不清其與Python的基本資料物件List,array的區別,在此對三者用於數值操作時的差異進行對比:
基本屬性
ndarray.ndim
陣列的最大維度,也叫做陣列的軸的個數(從0開始)。
In [12]: a = np.arange(20 ).reshape(1,4,5)
In [13]: a
Out[13]:
array([[[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19]]])
In [14]: a.ndim
Out[14]: 3
ndarray.shape
陣列的形狀,是一個整數元組,顯示了每個軸的大小。對於一個n行m列的陣列,shape就是元組(n,m) ,元祖的長度也是軸的總數。
In [15]: a.shape
Out[15]: (1L, 4L, 5L)
In [18]: len(a.shape)
Out[18]: 3
使用shape改變陣列形狀
In [19]: a.shape = 2,2,5 #也可以是a.shape = ((2,2,5))
In [20]: a
Out[20]:
array([[[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9]],
[[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19]]])
如果陣列的某個軸的長度被設定為-1,則該軸的長度將被自動計算出來
In [38]: a.shape = 4,-1
In [39]: a
Out[39]:
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19]])
使用reshape方法建立一個改變了尺寸的新的陣列,原陣列的大小不變
In [21]: a.reshape(5,2,2)
Out[21]:
array([[[ 0, 1],
[ 2, 3]],
[[ 4, 5],
[ 6, 7]],
[[ 8, 9],
[10, 11]],
[[12, 13],
[14, 15]],
[[16, 17],
[18, 19]]])
In [22]: a
Out[22]:
array([[[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9]],
[[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19]]])
注意:1、使用reshape建立的陣列是原陣列的試圖,和原陣列共享記憶體,修改任何一個數組的元素值都會同時改變兩個陣列。
2、使用reshape並不改變陣列中元素在記憶體中的位置,這和矩陣的轉置不同,後者將改變元素在記憶體中的位置。
In [45]: b = a.reshape(5,2,2)
In [46]: b[2]=10
In [47]: b
Out[47]:
array([[[ 0, 1],
[ 2, 3]],
[[ 4, 5],
[ 6, 7]],
[[10, 10],
[10, 10]],
[[12, 13],
[14, 15]],
[[16, 17],
[18, 19]]])
In [48]: a
Out[48]:
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 10, 10],
[10, 10, 12, 13, 14],
[15, 16, 17, 18, 19]])
ndarray.size
陣列中總的元素個數,等於元組shape的內積
In [49]: a.shape
Out[49]: (4L, 5L)
In [50]: a.size
20
ndarray.dtype
一個描述陣列中元素型別的物件。NumPy提供了 numpy.int32, numpy.int16, 和 numpy.float64 等型別。建立陣列的時候可以指定陣列內元素的型別,對dtype屬性指定元素型別
In [54]: a = arange(0,19,2,dtype=float)
In [55]: a
Out[55]: array([ 0., 2., 4., 6., 8., 10., 12., 14., 16., 18.])
In [56]: a = arange(0,19,2,dtype=complex)
In [57]: a
Out[57]:
array([ 0.+0.j, 2.+0.j, 4.+0.j, 6.+0.j, 8.+0.j, 10.+0.j,
12.+0.j, 14.+0.j, 16.+0.j, 18.+0.j])
ndarray.itemsize
陣列中每個元素的大小。
例如 陣列元素型別是 float64 的itemsize為 8 (=64/8), complex32 型別的 itemsize是4 (=32/8). 等價於ndarray.dtype.itemsize.
>>> a = np.array([[1.3,3.5],[3.6,6.]])
>>> a.dtype
dtype('float64')
>>> a.itemsize
8
建立陣列
NumPy建立陣列的方式很多,主要有以下幾種:
array建立陣列
使用Python的array模組建立陣列,傳入的引數類似於List建立的多維陣列,引數也可以是dtype,用於指定資料的型別
>>> np.array([[1,2,3],[4,5,6]])
array([[1, 2, 3],
[4, 5, 6]])
等價於
In [10]: a = [[1,2,3],[4,5,6]]
In [11]: np.array(a)
Out[11]:
array([[1, 2, 3],
[4, 5, 6]])
注意:不能缺少最外圍的[ ]
>>> np.array([1,2,3],[4,5,6])
Traceback (most recent call last):
File "<pyshell#47>", line 1, in <module>
np.array([1,2,3],[4,5,6])
TypeError: data type not understood
佔位符建立陣列
以上幾種方式只是對資料建立方式的簡單解釋,實際應用中,陣列中的元素常常是未知的,但是其大小是確定的,因此可以使用佔位符來初始化陣列的內容,NumPy提供了幾種佔位符方法。每種方法都可以指定元素的型別
np.zeros
建立一個內容全是0的陣列
>>> np.zeros((3,4))
array([[ 0., 0., 0., 0.],
[ 0., 0., 0., 0.],
[ 0., 0., 0., 0.]])
np.ones
建立內容全是1的陣列, 可以通過dtype制定引數資料型別
>>> np.ones((2,3,4),dtype=np.float64)
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.]]])
np.empty
不對內容進行初始化,只分配記憶體空間。內容常常是未初始化的垃圾值
指定範圍生成陣列
np.arange
前兩個引數是元素值的範圍,最後一個引數是相鄰元素之間的跨度(也稱為步長)
>>> np.arange(0,1,0.1)
array([ 0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9])
如果步長為負數,表示起始值大於終止值
In [103]: x = arange(7,2,-2)
In [104]: x
Out[104]: array([7, 5, 3])
np.linspace
對於浮點型別的陣列,給出範圍和步長,通常很難預測元素的數量(與浮點數的精度有關),因此對於浮點型別陣列,最好指定其元素個數,而不是其步長。
前兩個引數依然是元素值範圍,最後一個引數是產生的資料個數。
>>> np.linspace(1,6,4)
array([ 1. , 2.66666667, 4.33333333, 6. ])
np.logspace
logspace函式和linspace函式類似,但是前者建立以10為底的等比數列,如下,建立一個含有10個元素的等比數列,範圍在1(10^0)到(10^20)
In [58]: a = logspace(0,20,10)
In [59]: a
Out[59]:
array([ 1.00000000e+00, 1.66810054e+02, 2.78255940e+04,
4.64158883e+06, 7.74263683e+08, 1.29154967e+11,
2.15443469e+13, 3.59381366e+15, 5.99484250e+17,
1.00000000e+20])
也可以使用frombuffer, fromstring, fromfile等函式可以從位元組序列建立陣列
也可以自定義函式,生成陣列,使用fromfunction。
隨機數生成陣列
np.random.rand()
[0,1)上的均勻分佈產生產生的隨機樣本,引數是生成的陣列的大小
In [5]: np.random.rand(2,3)
Out[5]:
array([[ 0.26865184, 0.62972262, 0.58904232],
[ 0.47760496, 0.18785592, 0.85511524]])
np.random.randn()
不指定引數,預設服從標準正態分佈(sigma為1,mu為0)
指定引數,服從 N(mu, sigma^2)的正態分佈隨機產生的樣本,可以使用:
sigma * np.random.randn(…) + mu
In [6]: np.random.randn(2,3)
Out[6]:
array([[ 0.88717589, -0.22562155, 1.44601412],
[ 1.15406383, 0.75708636, 0.69189156]])
#其中mu是3, sigma是2.5
In [7]: 2.5*np.random.randn(2,3)+3
Out[7]:
array([[ 1.22514961, 3.72308164, 2.62028917],
[ 3.25345077, 4.35030785, 3.76266858]])
更多關於產生隨機數的方法參見NumPy的random模組
索引和切片
一維陣列切片
一維陣列的切片與python的list切片差不多,主要的不同是,陣列切片返回的是原陣列的檢視,即資料不會被複制,任何對切片後陣列的修改都會反映到原陣列上,而List的切片返回的是切片後的副本。
多維陣列索引
陣列的索引靈活而豐富,大部分索引的陣列操作都是通過索引進行的。一維陣列的索引較為簡單,本文不再詳述,接下來引入幾種常見的多維陣列的索引。
整數索引
不同軸的索引對應單個整數,不同軸的索引以逗號隔開
In [5]: a
Out[5]:
array([[ 0.03664104, 0.63714562, 0.1265633 ],
[ 0.12740582, 0.03731622, 0.17917975],
[ 0.04590225, 0.74800245, 0.74495221],
[ 0.99799526, 0.90753861, 0.93806741]])
#只選擇第1個軸的索引
In [6]: a[1]
Out[6]: array([ 0.12740582, 0.03731622, 0.17917975])
#選擇第1個軸和第二個軸的索引
In [8]: a[1,2]
Out[8]: 0.17917974571757078
注意:通過整數索引獲取的資料是原陣列的檢視,兩者共享記憶體,改變對應索引的資料會相應地改變原陣列的資料。
In [29]: a[1][1]=10
In [30]: a
Out[30]:
array([[ 0.03664104, 0.63714562, 0.1265633 ],
[ 0.12740582, 10. , 0.17917975],
[ 0.04590225, 0.74800245, 0.74495221],
[ 0.99799526, 0.90753861, 0.93806741]])
切片索引
陣列的每一個軸(axis)都有一個索引,可以對一個或者多個軸進行切片,也可以與整數索引混合使用,相鄰軸之間的索引引數使用逗號隔開。
In [32]: a[1:3,1:3]
Out[32]:
array([[ 10. , 0.17917975],
[ 0.74800245, 0.74495221]])
In [34]: a[1:3,1]
Out[34]: array([ 10. , 0.74800245])
使用:(冒號)選擇陣列某個軸的所有元素
In [36]: a[:,1]
Out[36]: array([ 0.63714562, 10. , 0.74800245, 0.90753861])
負數索引,從尾部開始選取資料
In [40]: a[:,-1]
Out[40]: array([ 0.1265633 , 0.17917975, 0.74495221, 0.93806741])
負數切片,逆序選擇指定間隔的資料,切片的起始值要大小終止值
In [44]: a[:,-3:-1]
Out[44]:
array([[ 0.03664104, 0.63714562],
[ 0.12740582, 10. ],
[ 0.04590225, 0.74800245],
[ 0.99799526, 0.90753861]])
引數的個數少於軸的個數,缺失引數的軸預設為完整的切片
In [45]: a[1] #等價於a[1,:]
Out[45]: array([ 0.12740582, 10. , 0.17917975])
注意:通過切片索引獲取的資料也是原陣列的檢視。
布林型索引
對陣列進行算數運算,將會產生一個布林型陣列,運算子可以是>,<,==,!=,|(或),&(與),-(負)等
In [51]: a = np.array(['one','two','three'])
In [55]: a == 'two'
Out[55]: array([False, True, False], dtype=bool)
In [53]: b = np.random.rand(3,4)
In [54]: b
Out[54]:
array([[ 0.23529625, 0.87521492, 0.1038766 , 0.10058617],
[ 0.25178891, 0.1172799 , 0.51411217, 0.86013535],
[ 0.75510171, 0.81136768, 0.12842083, 0.22549127]])
In [56]: b[a=='two']
Out[56]: array([[ 0.25178891, 0.1172799 , 0.51411217, 0.86013535]])
布林型索引也可以和整數型索引和切片索引混合使用
In [57]: b[a=='two',:3]
Out[57]: array([[ 0.25178891, 0.1172799 , 0.51411217]])
In [58]: b[a=='two',3]
Out[58]: array([ 0.86013535])
注意:1、通過布林索引從原陣列中選取的資料,將複製到新建立的陣列中。2、布林陣列的長度要與原陣列被索引的軸的長度相等。
#布林陣列的長度大於陣列第1軸的長度
In [59]: a = np.array(['one','two','three','four'])
#選取元素索引在陣列對應軸範圍內,發出警告
In [60]: a!='four'
Out[60]: array([ True, True, True, False], dtype=bool)
In [61]: b[a!='four',:3]
D:\Python27\Scripts\ipython:1: VisibleDeprecationWarning: boolean index did not match indexed array along dimension 0; dimension is 3 but corresponding boolean dimension is 4
Out[62]:
array([[ 0.23529625, 0.87521492, 0.1038766 ],
[ 0.25178891, 0.1172799 , 0.51411217],
[ 0.75510171, 0.81136768, 0.12842083]])
In [63]: a!='one'
Out[63]: array([False, True, True, True], dtype=bool)
#選取元素索引不在陣列對應軸範圍內,發出警告並報錯
In [64]: b[a!='one',:3]
D:\Python27\Scripts\ipython:1: VisibleDeprecationWarning: boolean index did not match indexed array along dimension 0; dimension is 3 but corresponding boolean dimension is 4
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-61-0bbfcbfce657> in <module>()
----> 1 b[a!='one',:3]
IndexError: index 3 is out of bounds for axis 0 with size 33
花式索引(fancy indexing)
使用整數陣列進行索引。
單個整數陣列索引。
In [66]: a = np.random.rand(4,3,2)
In [67]: a
Out[67]:
array([[[ 0.16476724, 0.76267306],
[ 0.73596533, 0.79242585],
[ 0.56103429, 0.7122396 ]],
[[ 0.35827947, 0.10465505],
[ 0.5550632 , 0.0872461 ],
[ 0.57150649, 0.07027542]],
[[ 0.61577706, 0.81542217],
[ 0.24874289, 0.15173563],
[ 0.96633552, 0.85607585]],
[[ 0.96521609, 0.41418405],
[ 0.67424316, 0.04368679],
[ 0.37650282, 0.96858812]]])
In [69]: a[[1,3]]
Out[69]:
array([[[ 0.35827947, 0.10465505],
[ 0.5550632 , 0.0872461 ],
[ 0.57150649, 0.07027542]],
[[ 0.96521609, 0.41418405],
[ 0.67424316, 0.04368679],
[ 0.37650282, 0.96858812]]])
多個整數陣列索引
In [70]: a[[1,3],[0,1]] #等價於選取元素a[1,0]和a[3,1]
Out[70]:
array([[ 0.35827947, 0.10465505],
[ 0.67424316, 0.04368679]])
In [71]: a[[1,3],[0,1],[0,1]] #等價於選取元素a[1,0,0]和a[3,1,1]
Out[71]: array([ 0.35827947, 0.04368679])
也可以使用元祖選取陣列元素,與多個整數陣列選取元素的方法沒有差別
In [77]: a[(1,3),(0,1),(0,1)]
Out[77]: array([ 0.35827947, 0.04368679])
注意:1、通過整數陣列索引從原陣列中選取的資料,將複製到新建立的陣列中。2、每個軸對應的整數陣列的長度必須相同。
In [72]: a[[1,2],[1,2,3]]
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-72-7e69e200063a> in <module>()
----> 1 a[[1,2],[1,2,3]]
IndexError: shape mismatch: indexing arrays could not be broadcast together with shapes (2,) (3,)
關於陣列切片,NumPy也允許使用多個點代替完整切片
比如:x是有可以5個軸的陣列
x[1,2,…]等價於x[1,2,:,:,:],
x[…,3]等價於 x[:,:,:,:,3]
x[4,…,5,:] 等價於x[4,:,:,5,:].
想了解更多陣列索引方法,參見NumPy的indexing模組
輸出和展開
一維陣列的輸出和展開類似於List,多維陣列的輸出,都是相對於第一個軸進行輸出。
In [78]: a
Out[78]:
array([[[ 0.16476724, 0.76267306],
[ 0.73596533, 0.79242585],
[ 0.56103429, 0.7122396 ]],
[[ 0.35827947, 0.10465505],
[ 0.5550632 , 0.0872461 ],
[ 0.57150649, 0.07027542]],
[[ 0.61577706, 0.81542217],
[ 0.24874289, 0.15173563],
[ 0.96633552, 0.85607585]],
[[ 0.96521609, 0.41418405],
[ 0.67424316, 0.04368679],
[ 0.37650282, 0.96858812]]])
In [80]: for row in a:
....: print row
....: print '====='
....:
[[ 0.16476724 0.76267306]
[ 0.73596533 0.79242585]
[ 0.56103429 0.7122396 ]]
=====
[[ 0.35827947 0.10465505]
[ 0.5550632 0.0872461 ]
[ 0.57150649 0.07027542]]
=====
[[ 0.61577706 0.81542217]
[ 0.24874289 0.15173563]
[ 0.96633552 0.85607585]]
=====
[[ 0.96521609 0.41418405]
[ 0.67424316 0.04368679]
[ 0.37650282 0.96858812]]
=====
也可以使用flat方法將陣列元素單個輸出,,flat生成矩陣或者資料的迭代器 ,輸出當個元素
In [81]: for element in a.flat:
....: print element
....:
0.164767240673
0.762673061164
0.735965327746
0.792425846826
0.561034293545
0.712239602363
0.358279466566
0.104655050665
0.555063196968
0.0872460992886
0.571506485379
0.0702754155223
0.615777061656
0.815422174299
0.24874289315
0.151735634542
0.966335515789
0.856075852512
0.965216090091
0.414184052308
0.674243163403
0.043686792205
0.376502823114
0.968588116022
陣列的展開一般使用方法flatten,ravel,主要用於將矩陣或者陣列降為1維,兩者的主要區別是:flatten返回時是原來陣列或者矩陣的副本,而ravel返回的是原陣列或者矩陣的檢視,兩者共享記憶體,任何一個被改變,另外一個也會被改變。
#使用flatten()方法展開陣列
In [88]: b = a.flatten()
In [89]: b
Out[89]:
array([ 0.16476724, 0.76267306, 0.73596533, 0.79242585, 0.56103429,
0.7122396 , 0.35827947, 0.10465505, 0.5550632 , 0.0872461 ,
0.57150649, 0.07027542, 0.61577706, 0.81542217, 0.24874289,
0.15173563, 0.96633552, 0.85607585, 0.96521609, 0.41418405,
0.67424316, 0.04368679, 0.37650282, 0.96858812])
In [90]: b[2]=10
In [91]: b
Out[91]:
array([ 0.16476724, 0.76267306, 10. , 0.79242585,
0.56103429, 0.7122396 , 0.35827947, 0.10465505,
0.5550632 , 0.0872461 , 0.57150649, 0.07027542,
0.61577706, 0.81542217, 0.24874289, 0.15173563,
0.96633552, 0.85607585, 0.96521609, 0.41418405,
0.67424316, 0.04368679, 0.37650282, 0.96858812])
In [92]: a
Out[92]:
array([[[ 0.16476724, 0.76267306],
[ 0.73596533, 0.79242585],
[ 0.56103429, 0.7122396 ]],
[[ 0.35827947, 0.10465505],
[ 0.5550632 , 0.0872461 ],
[ 0.57150649, 0.07027542]],
[[ 0.61577706, 0.81542217],
[ 0.24874289, 0.15173563],
[ 0.96633552, 0.85607585]],
[[ 0.96521609, 0.41418405],
[ 0.67424316, 0.04368679],
[ 0.37650282, 0.96858812]]])
#使用ravel()方法展開陣列
In [93]: b = a.ravel()
In [94]: b
Out[94]:
array([ 0.16476724, 0.76267306, 0.73596533, 0.79242585, 0.56103429,
0.7122396 , 0.35827947, 0.10465505, 0.5550632 , 0.0872461 ,
0.57150649, 0.07027542, 0.61577706, 0.81542217, 0.24874289,
0.15173563, 0.96633552, 0.85607585, 0.96521609, 0.41418405,
0.67424316, 0.04368679, 0.37650282, 0.96858812])
In [95]: b[2]=20
In [96]: b
Out[96]:
array([ 0.16476724, 0.76267306, 20. , 0.79242585,
0.56103429, 0.7122396 , 0.35827947, 0.10465505,
0.5550632 , 0.0872461 , 0.57150649, 0.07027542,
0.61577706, 0.81542217, 0.24874289, 0.15173563,
0.96633552, 0.85607585, 0.96521609, 0.41418405,
0.67424316, 0.04368679, 0.37650282, 0.96858812])
In [98]: a
Out[98]:
array([[[ 0.16476724, 0.76267306],
[ 20. , 0.79242585],
[ 0.56103429, 0.7122396 ]],
[[ 0.35827947, 0.10465505],
[ 0.5550632 , 0.0872461 ],
[ 0.57150649, 0.07027542]],
[[ 0.61577706, 0.81542217],
[ 0.24874289, 0.15173563],
[ 0.96633552, 0.85607585]],
[[ 0.96521609, 0.41418405],
[ 0.67424316, 0.04368679],
[ 0.37650282, 0.96858812]]])
注:文中所有的模組可以使用help(np.模組名字)的方式檢視該模組的詳細說明。使用dir(**)檢視模組的內建函式,使用ipython的童鞋可以使用?(內省)的方式檢視不同模組的詳細使用方法