NumPy在數據分析就像Django在web開發中那樣出名,就是老大哥,也是在工作中最常用的庫,沒有之一,今天給大家詳細的講解一下這個庫的妙用! 103456743
進階 廣播法則(rule) 廣播法則能使通用函數有意義地處理不具有相同形狀的輸入。 廣播第一法則是,如果所有的輸入數組維度不都相同,一個“1”將被重復地添加在維度較小的數組上直至所有的數組擁有一樣的維度。 廣播第二法則確定長度為1的數組沿著特殊的方向表現地好像它有沿著那個方向最大形狀的大小。對數組來說,沿著那個維度的數組元素的值理應相同。 應用廣播法則之后,所有數組的大小必須匹配。更多細節可以從這個文檔找到。 第二種通過布爾來索引的方法更近似于整數索引;對數組的每個維度我們給一個一維布爾數組來選擇我們想要的切片。 >>> a = arange(12).reshape(3,4)
>>> b1 = array([False,True,True]) # first dim selection
>>> b2 = array([True,False,True,False]) # second dim selection
>>>
>>> a[b1,:] # selecting rows
array([[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
>>>
>>> a[b1] # same thing
array([[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
>>>
>>> a[:,b2] # selecting columns
array([[ 0, 2],
[ 4, 6],
[ 8, 10]])
>>>
>>> a[b1,b2] # a weird thing to do
array([ 4, 10]) 注意一維數組的長度必須和你想要切片的維度或軸的長度一致,在之前的例子中,b1是一個秩為1長度為三的數組(a的行數),b2(長度為4)與a的第二秩(列)相一致。7 ix_()函數
>>> a = array([2,3,4,5])
>>> b = array([8,5,4])
>>> c = array([5,4,6,8,3])
>>> ax,bx,cx = ix_(a,b,c)
>>> ax
array([[[2]],
[[3]],
[[4]],
[[5]]])
>>> bx
array([[[8],
[5],
[4]]])
>>> cx
array([[[5, 4, 6, 8, 3]]])
>>> ax.shape, bx.shape, cx.shape
((4, 1, 1), (1, 3, 1), (1, 1, 5))
>>> 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
>>> a[3] b[2]*c[4]
17 你也可以實行如下簡化: def ufunc_reduce(ufct, *vectors):
vs = ix_(*vectors)
r = ufct.identity
for v in vs:
r = ufct(r,v)
return r 然后這樣使用它: >>> ufunc_reduce(add,a,b,c)
array([[[15, 14, 16, 18, 13],
[12, 11, 13, 15, 10],
[11, 10, 12, 14, 9]],
[[16, 15, 17, 19, 14],
[13, 12, 14, 16, 11],
[12, 11, 13, 15, 10]],
[[17, 16, 18, 20, 15],
[14, 13, 15, 17, 12],
[13, 12, 14, 16, 11]],
[[18, 17, 19, 21, 16],
[15, 14, 16, 18, 13],
[14, 13, 15, 17, 12]]]) 這個reduce與ufunc.reduce(比如說add.reduce)相比的優勢在于它利用了廣播法則,避免了創建一個輸出大小乘以向量個數的參數數組。8 用字符串索引 參見RecordArray。 線性代數 繼續前進,基本線性代數包含在這里。 簡單數組運算 參考numpy文件夾中的linalg.py獲得更多信息 >>> from numpy import *
>>> from numpy.linalg import *
>>> a = array([[1.0, 2.0], [3.0, 4.0]])
>>> print a
[[ 1. 2.]
[ 3. 4.]]
>>> a.transpose()
array([[ 1., 3.],
[ 2., 4.]])
>>> inv(a)
array([[-2. , 1. ],
[ 1.5, -0.5]])
>>> u = eye(2) # unit 2x2 matrix; 'eye' represents 'I'
>>> u
array([[ 1., 0.],
[ 0., 1.]])
>>> j = array([[0.0, -1.0], [1.0, 0.0]])
>>> dot (j, j) # matrix product
array([[-1., 0.],
[ 0., -1.]])
>>> trace(u) # trace
2.0
>>> y = array([[5.], [7.]])
>>> solve(a, y)
array([[-3.],
[ 4.]])
>>> eig(j)
(array([ 0. 1.j, 0.-1.j]),
array([[ 0.70710678 0.j, 0.70710678 0.j],
[ 0.00000000-0.70710678j, 0.00000000 0.70710678j]]))
Parameters:
square matrix
Returns
The eigenvalues, each repeated according to its multiplicity.
The normalized (unit 'length') eigenvectors, such that the
column ``v[:,i]`` is the eigenvector corresponding to the
eigenvalue ``w[i]`` . 矩陣類 這是一個關于矩陣類的簡短介紹。 >>> A = matrix('1.0 2.0; 3.0 4.0')
>>> A
[[ 1. 2.]
[ 3. 4.]]
>>> type(A) # file where class is defined
<class 'numpy.matrixlib.defmatrix.matrix'>
>>> A.T # transpose
[[ 1. 3.]
[ 2. 4.]]
>>> X = matrix('5.0 7.0')
>>> Y = X.T
>>> Y
[[5.]
[7.]]
>>> print A*Y # matrix multiplication
[[19.]
[43.]]
>>> print A.I # inverse
[[-2. 1. ]
[ 1.5 -0.5]]
>>> solve(A, Y) # solving linear equation
matrix([[-3.],
[ 4.]]) 索引:比較矩陣和二維數組 注意NumPy中數組和矩陣有些重要的區別。NumPy提供了兩個基本的對象:一個N維數組對象和一個通用函數對象。其它對象都是建構在它們之上 的。特別的,矩陣是繼承自NumPy數組對象的二維數組對象。對數組和矩陣,索引都必須包含合適的一個或多個這些組合:整數標量、省略號 (ellipses)、整數列表;布爾值,整數或布爾值構成的元組,和一個一維整數或布爾值數組。矩陣可以被用作矩陣的索引,但是通常需要數組、列表或者 其它形式來完成這個任務。 現在有些和Python索引不同的了:你可以同時使用逗號分割索引來沿著多個軸索引。 >>> print A[:,1]; print A[:,1].shape
[1 5 9]
(3,)
>>> print M[:,1]; print M[:,1].shape
[[1]
[5]
[9]]
(3, 1) >>> A[:,[1,3]]
array([[ 1, 3],
[ 5, 7],
[ 9, 11]]) 稍微復雜點的方法是使用 >>> A[:,].take([1,3],axis=1)
array([[ 1, 3],
[ 5, 7],
[ 9, 11]]) 如果我們想跳過第一行,我們可以這樣: >>> A[1:,].take([1,3],axis=1)
array([[ 5, 7],
[ 9, 11]]) 或者我們僅僅使用 >>> A[ix_((1,2),(1,3))]
array([[ 5, 7],
[ 9, 11]]) 為了讀者的方便,在次寫下之前的矩陣: >>> A[ix_((1,2),(1,3))]
array([[ 5, 7],
[ 9, 11]]) 現在讓我們做些更復雜的。比如說我們想要保留第一行大于1的列。一種方法是創建布爾索引: >>> A[0,:]>1
array([False, False, True, True], dtype=bool)
>>> A[:,A[0,:]>1]
array([[ 2, 3],
[ 6, 7],
[10, 11]]) 就是我們想要的!但是索引矩陣沒這么方便。 >>> M[0,:]>1
matrix([[False, False, True, True]], dtype=bool)
>>> M[:,M[0,:]>1]
matrix([[2, 3]]) 技巧和提示 下面我們給出簡短和有用的提示。 “自動”改變形狀 更改數組的維度,你可以省略一個尺寸,它將被自動推導出來。 >>> a = arange(30)
>>> a.shape = 2,-1,3 # -1 means 'whatever is needed'
>>> 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]]]) 2 NumPy-快速處理數據標準安裝的Python中用列表(list)保存一組值,可以用來當作數組使用,不過由于列表的元素可以是任何對象,因此列表中所保存的是對象的指針。這樣為了保存一個簡單的[1,2,3],需要有3個指針和三個整數對象。對于數值運算來說這種結構顯然比較浪費內存和CPU計算時間。 此外Python還提供了一個array模塊,array對象和列表不同,它直接保存數值,和C語言的一維數組比較類似。但是由于它不支持多維,也沒有各種運算函數,因此也不適合做數值運算。 NumPy的誕生彌補了這些不足,NumPy提供了兩種基本的對象:ndarray(N-dimensional array object)和 ufunc(universal function object)。ndarray(下文統一稱之為數組)是存儲單一數據類型的多維數組,而ufunc則是能夠對數組進行處理的函數。 數組的大小可以通過其shape屬性獲得: >>> a.shape(4,)>>> c.shape(3, 4) 數組a的shape只有一個元素,因此它是一維數組。而數組c的shape有兩個元素,因此它是二維數組,其中第0軸的長度為3,第1軸的長度為4。還可以通過修改數組的shape屬性,在保持數組元素個數不變的情況下,改變數組每個軸的長度。下面的例子將數組c的shape改為(4,3),注意從(3,4)改為(4,3)并不是對數組進行轉置,而只是改變每個軸的大小,數組元素在內存中的位置并沒有改變: 數組的元素類型可以通過dtype屬性獲得。上面例子中的參數序列的元素都是整數,因此所創建的數組的元素類型也是整數,并且是32bit的長整型。可以通過dtype參數在創建時指定元素類型: >>> np.array([[1, 2, 3, 4],[4, 5, 6, 7], [7, 8, 9, 10]], dtype=np.float)array([[ 1., 2., 3., 4.], [ 4., 5., 6., 7.], [ 7., 8., 9., 10.]])>>> np.array([[1, 2, 3, 4],[4, 5, 6, 7], [7, 8, 9, 10]], dtype=np.complex)array([[ 1. 0.j, 2. 0.j, 3. 0.j, 4. 0.j], [ 4. 0.j, 5. 0.j, 6. 0.j, 7. 0.j], [ 7. 0.j, 8. 0.j, 9. 0.j, 10. 0.j]]) 上面的例子都是先創建一個Python序列,然后通過array函數將其轉換為數組,這樣做顯然效率不高。因此NumPy提供了很多專門用來創建數組的函數。下面的每個函數都有一些關鍵字參數,具體用法請查看函數說明。
使用布爾數組 當使用布爾數組b作為下標存取數組x中的元素時,將收集數組x中所有在數組b中對應下標為True的元素。使用布爾數組作為下標獲得的數組不和原始數組共享數據空間,注意這種方式只對應于布爾數組,不能使用布爾列表。 .1.3 多維數組 多維數組的存取和一維數組類似,因為多維數組有多個軸,因此它的下標需要用多個值來表示,NumPy采用組元(tuple)作為數組的下標。如圖2.1所示,a為一個6x6的數組,圖中用顏色區分了各個下標以及其對應的選擇區域。 組元不需要圓括號 雖然我們經常在Python中用圓括號將組元括起來,但是其實組元的語法定義只需要用逗號隔開即可,例如 x,y=y,x 就是用組元交換變量值的一個例子。
2.1.4 結構數組 在C語言中我們可以通過struct關鍵字定義結構類型,結構中的字段占據連續的內存空間,每個結構體占用的內存大小都相同,因此可以很容易地定義結構數組。和C語言一樣,在NumPy中也很容易對這種結構數組進行操作。只要NumPy中的結構定義和C語言中的定義相同,NumPy就可以很方便地讀取C語言的結構數組的二進制數據,轉換為NumPy的結構數組。 假設我們需要定義一個結構數組,它的每個元素都有name, age和weight字段。在NumPy中可以如下定義 內存對齊 C語言的結構體為了內存尋址方便,會自動的添加一些填充用的字節,這叫做內存對齊。例如如果把下面的name[32]改為name[30]的話,由于內存對齊問題,在name和age中間會填補兩個字節,最終的結構體大小不會改變。因此如果numpy中的所配置的內存大小不符合C語言的對齊規范的話,將會出現數據錯位。為了解決這個問題,在創建dtype對象時,可以傳遞參數align=True,這樣numpy的結構數組的內存對齊和C語言的結構體就一致了。 用下面的字典參數也可以定義結構類型,字典的關鍵字為結構中字段名,值為字段的類型描述,但是由于字典的關鍵字是沒有順序的,因此字段的順序需要在類型描述中給出,類型描述是一個組元,它的第二個值給出字段的字節為單位的偏移量,例如age字段的偏移量為25個字節: >>> np.dtype({'surname':('S25',0),'age':(np.uint8,25)})dtype([('surname', '|S25'), ('age', '|u1')]) 2.1.5 內存結構 下面讓我們來看看ndarray數組對象是如何在內存中儲存的。如圖2.3所示,關于數組的描述信息保存在一個數據結構中,這個結構引用兩個對象:一塊用于保存數據的存儲區域和一個用于描述元素類型的dtype對象。 sin函數的第二個參數也是x,那么它所做的事情就是對x中的每給值求正弦值,并且把結果保存到x中的對應的位置中。此時函數的返回值仍然是整個計算的結果,只不過它就是x,因此兩個變量的id是相同的(變量t和變量x指向同一塊內存區域)。 我用下面這個小程序,比較了一下numpy.math和Python標準庫的math.sin的計算速度:: 請注意numpy.sin的計算速度只有math.sin的1/5。這是因為numpy.sin為了同時支持數組和單個值的計算,其C語言的內部實現要比math.sin復雜很多,如果我們同樣在Python級別進行循環的話,就會看出其中的差別了。此外,numpy.sin返回的數的類型和math.sin返回的類型有所不同,math.sin返回的是Python的標準float類型,而numpy.sin則返回一個numpy.float64類型: 由于Python的操作符重載功能,計算兩個數組相加可以簡單地寫為a b,而np.add(a,b,a)則可以用a =b來表示。下面是數組的運算符和其對應的ufunc函數的一個列表,注意除號'/'的意義根據是否激活__future__.division有所不同。
值得注意的是用frompyfunc得到的函數計算出的數組元素的類型為object,因為frompyfunc函數無法保證Python函數返回的數據類型都完全一致。因此還需要再次 y2.astype(np.float64)將其轉換為雙精度浮點數組。 2.2.1 廣播 當我們使用ufunc函數對兩個數組進行計算時,ufunc函數會對這兩個數組的對應元素進行計算,因此它要求這兩個數組有相同的大小(shape相同)。如果兩個數組的shape不同的話,會進行如下的廣播(broadcasting)處理:
上述4條規則理解起來可能比較費勁,讓我們來看一個實際的例子。 先創建一個二維數組a,其shape為(6,1): 這樣加法運算的兩個輸入數組的shape分別為(6,1)和(1,5),根據規則2,輸出數組的各個軸的長度為輸入數組各個軸上的長度的最大值,可知輸出數組的shape為(6,5)。 由于b的第0軸上的長度為1,而a的第0軸上的長度為6,因此為了讓它們在第0軸上能夠相加,需要將b在第0軸上的長度擴展為6,這相當于: ogrid是一個很有趣的對象,它像一個多維數組一樣,用切片組元作為下標進行存取,返回的是一組可以用來廣播計算的數組。其切片下標有兩種形式:
.2.2 ufunc的方法 ufunc函數本身還有些方法,這些方法只對兩個輸入一個輸出的ufunc函數有效,其它的ufunc對象調用這些方法時會拋出ValueError異常。 reduce 方法和Python的reduce函數類似,它沿著axis軸對array進行操作,相當于將<op>運算符插入到沿axis軸的所有子數組或者元素當中。 <op>.reduce (array=, axis=0, dtype=None) 例如: if indices[i] < indices[i 1]:
result[i] = np.reduce(a[indices[i]:indices[i 1]])
else:
result[i] = a[indices[i] 而最后一個元素如下計算: np.reduce(a[indices[-1]:]) 因此上面例子中,結果的每個元素如下計算而得: 2.3 矩陣運算 NumPy和Matlab不一樣,對于多維數組的運算,缺省情況下并不使用矩陣運算,如果你希望對數組進行矩陣運算的話,可以調用相應的函數。 matrix對象 numpy庫提供了matrix類,使用matrix類創建的是矩陣對象,它們的加減乘除運算缺省采用矩陣方式計算,因此用法和matlab十分類似。但是由于NumPy中同時存在ndarray和matrix對象,因此用戶很容易將兩者弄混。這有違Python的“顯式優于隱式”的原則,因此并不推薦在較復雜的程序中使用matrix。下面是使用matrix的一個例子: 使用numpy.savetxt和numpy.loadtxt可以讀寫1維和2維的數組: |
|