元組是一個固定長度,不可改變的Python序列對象。建立元組的最簡單方式,是用逗號分隔一列值:python
In [1]: tup = 4, 5, 6 In [2]: tup Out[2]: (4, 5, 6)
當用複雜的表達式定義元組,最好將值放到圓括號內,以下所示:算法
In [3]: nested_tup = (4, 5, 6), (7, 8) In [4]: nested_tup Out[4]: ((4, 5, 6), (7, 8))
將元組賦值給相似元組的變量,Python會試圖拆分等號右邊的值,即便含有元組的元組也會被拆分:數組
In [23]: tup = 3,5,(8,9) In [24]: a,b,(c,d) = tup In [25]: c Out[25]: 8
替換變量的名字:dom
In [26]: a,b = 1,2 In [27]: a Out[27]: 1 In [28]: a,b = b,a In [29]: a Out[29]: 2
變量拆分經常使用來迭代元組或列表序列:函數
In [21]: seq = [(1,2,3),(4,5,6),(7,8,9)] In [22]: for a,b,c in seq: ...: print('a={0},b={1},c={2}'.format(a,b,c)) ...: a=1,b=2,c=3 a=4,b=5,c=6 a=7,b=8,c=9
從元組的開頭「摘取」幾個元素。它使用了特殊的語法*rest
,這也用在函數簽名中以抓取任意長度列表的位置參數:性能
In [35]: values = 1,2,3,4,5 In [36]: a,b,*rest = values In [37]: a,b Out[37]: (1, 2) In [38]: rest Out[38]: [3, 4, 5]
rest的部分是想要捨棄的部分,rest的名字不重要,能夠取任意名字。學習
由於元組的大小和內容不能修改,它的實例方法都很輕量。其中一個頗有用的就是count
(也適用於列表),它能夠統計某個值得出現次數:測試
In [43]: tup = (2,3,6,1,4,7,9,3,2,4,2) In [44]: tup.count(2) Out[44]: 3
基於NumPy的算法要比純Python快10到100倍(甚至更快),而且使用的內存更少。大數據
In [45]: import numpy as np In [46]: my_arr = np.arange(1000000) In [47]: my_list = list(range(1000000)) In [48]: %time for _ in range(10): my_arr2 = my_arr * 2 Wall time: 23.9 ms In [49]: %time for _ in range(10): my_list2 = [x * 2 for x in my_list] Wall time: 815 ms
NumPy最重要的一個特色就是其N維數組對象(即ndarray),該對象是一個快速而靈活的大數據集容器。ndarray是一個通用的同構數據多維容器,也就是說,其中的全部元素必須是相同類型的。每一個數組都有一個shape(一個表示各維度大小的元組)和一個dtype(一個用於說明數組數據類型的對象)。spa
注:當你在本書中看到「數組」、「NumPy數組」、"ndarray"時,基本上都指的是同同樣東西,即ndarray對象。
(1)使用array函數
In [57]: data1 = [4,5,6,7,9,8] In [58]: data1 = [4,5.3,6,7,9,8] In [59]: arr1 = np.array(data1) In [60]: arr1 Out[60]: array([4. , 5.3, 6. , 7. , 9. , 8. ])
(2)嵌套序列
In [61]: data2 = [[1,3,4,5],[6,7,8,9]] In [62]: arr2 = np.array(data2) In [63]: arr2 Out[63]: array([[1, 3, 4, 5], [6, 7, 8, 9]])
np.array會嘗試爲新建的這個數組推斷出一個較爲合適的數據類型。數據類型保存在一個特殊的dtype對象中。
好比說,在上面的兩個例子中,咱們有:
In [65]: arr1.dtype Out[65]: dtype('float64') In [66]: arr2.dtype Out[66]: dtype('int32')
(3)zeros和ones分別能夠建立指定長度或形狀的全0或全1數組。empty能夠建立一個沒有任何具體值的數組。要用這些方法建立多維數組,只需傳入一個表示形狀的元組便可。
(4)arange是Python內置函數range的數組版。
下表爲一些數組建立函數,若是沒有特別指定,數據類型基本都是float64(浮點數)。
表2-1-1 數組建立函數
(1)下表NumPy所支持的所有數據類型:
表2-1-1 Numpy支持的數據類型
(2)數據類型的相互轉換
In [5]: arr = np.array([2,3,5,6,1]) In [6]: arr.dtype Out[6]: dtype('int32')
In [7]: float_arr = arr.astype(np.float64) In [8]: float_arr.dtype Out[8]: dtype('float64')
In [13]: arr = np.array([2.3 ,5.6 ,5.1 ,7.8]) In [14]: arr Out[14]: array([2.3, 5.6, 5.1, 7.8]) In [15]: arr.astype(np.int32) Out[15]: array([2, 5, 5, 7])
不用編寫循環便可對數據執行批量運算,稱其爲矢量化(vectorization)。大小相等的數組之間的任何算術運算都會將運算應用到元素級:
(1)數組與標量的算術運算會將標量值傳播到各個元素;
(2)大小相同的數組之間的比較會生成布爾值數組:
In [21]: arr1 = np.array([[1,2,3],[8,4,9]]) In [22]: arr2 = np.array([[6,2,1],[5,8,7]]) In [23]: arr2 > arr1 Out[23]: array([[ True, False, False], [False, True, False]])
NumPy數組的索引是一個內容豐富的主題,由於選取數據子集或單個元素的方式有不少。跟列表最重要的區別在於,數組切片是原始數組的視圖。這意味着數據不會被複制,視圖上的任何修改都會直接反映到源數組上。
切片[ : ]會給數組中的全部值賦值,「只有冒號」表示選取整個軸,所以你能夠像下面這樣只對高維軸進行切片。
圖2-4-1 二維數組切片
假設咱們有一個用於存儲數據的數組以及一個存儲姓名的數組(含有重複項)。在這裏,我將使用numpy.random中的randn函數生成一些正態分佈的隨機數據:
In [29]: names = np.array(['lf','hg','yc','lf']) #存儲姓名的數組 In [30]: data = np.random.randn(4,4) #存儲數據的數組 In [31]: names Out[31]: array(['lf', 'hg', 'yc', 'lf'], dtype='<U2') In [32]: data #標紅的數據爲對應‘lf’的行 Out[32]: array([[ 0.01043648, 0.44103911, 0.47307017, -0.24815588], [ 0.6013753 , 0.57293908, 0.94169844, -1.4131715 ], [ 0.1938855 , -0.78327988, -0.41211459, -1.17359429], [ 1.33697091, -0.60614391, 0.86125283, -0.66521007]]) In [33]: names == 'lf' #判斷名字爲‘lf’ Out[33]: array([ True, False, False, True]) In [34]: data[names == 'lf'] #從data中,獲得名字對應‘lf’的數據 Out[34]: array([[ 0.01043648, 0.44103911, 0.47307017, -0.24815588], [ 1.33697091, -0.60614391, 0.86125283, -0.66521007]]) In [35]: data[~(names == 'lf')] #除"lf"之外的其餘值,既可使用不等於符號(!=),也能夠經過~對條件進行否認,獲得除‘lf’的其他數據。 Out[35]: array([[ 0.6013753 , 0.57293908, 0.94169844, -1.4131715 ], [ 0.1938855 , -0.78327988, -0.41211459, -1.17359429]]) In [36]: data[names == 'lf', 2:] #也能夠進行切片操做 Out[36]: array([[ 0.47307017, -0.24815588], [ 0.86125283, -0.66521007]])
data[(names == 'lf') & (names == 'yc')] #選取這三個名字中的兩個須要組合應用多個布爾條件,使用&(和)、|(或)之類的布爾算術運算符便可。 Out[37]: array([], shape=(0, 4), dtype=float64) #注意:Python中,關鍵字 and 和 or 在布爾型數組中無效,必須使用 & 與 | 。 data[(names == 'lf') | (names == 'yc')] Out[38]: array([[ 0.01043648, 0.44103911, 0.47307017, -0.24815588], [ 0.1938855 , -0.78327988, -0.41211459, -1.17359429], [ 1.33697091, -0.60614391, 0.86125283, -0.66521007]])
經過布爾型數組設置值是一種常常用到的手段。咱們只須要將知足咱們所需條件的行,設置爲所需的值便可。
花式索引(Fancy indexing)是一個NumPy術語,它指的是利用整數數組進行索引。
In [47]: arr = np.arange(63).reshape(9,7) #reshape():數組重塑 In [48]: arr Out[48]: 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, 30, 31, 32, 33, 34], [35, 36, 37, 38, 39, 40, 41], [42, 43, 44, 45, 46, 47, 48], [49, 50, 51, 52, 53, 54, 55], [56, 57, 58, 59, 60, 61, 62]]) In [49]: arr[[1, 5, 7, 2], [0, 3, 1, 2]] #最終選出的是元素(1,0)、(5,3)、(7,1)和(2,2)。不管數組是多少維的,花式索引老是一維的。 Out[49]: array([ 7, 38, 50, 16])
注:向數組的實例方法reshape傳入一個表示新形狀的元組便可實現將數組從一個形狀轉換爲另外一個形狀,稱爲「數組重塑」。
將一維數組轉換爲多維數組的運算過程相反的運算一般稱爲扁平化(flattening)或散開(raveling)。
獲得矩形區域的結果:
In [50]: arr[[1, 5, 7, 2]][:, [0, 3, 1, 2]] Out[50]: array([[ 7, 10, 8, 9], [35, 38, 36, 37], [49, 52, 50, 51], [14, 17, 15, 16]])
花式索引跟切片不同,它老是將數據複製到新數組中。
轉置是重塑的一種特殊形式,它返回的是源數據的視圖(不會進行任何複製操做)。數組不只有transpose方法,還有一個特殊的T屬性:
(1)二維數組
In [52]: arr = np.arange(15).reshape(5,3) In [53]: arr Out[53]: array([[ 0, 1, 2], [ 3, 4, 5], [ 6, 7, 8], [ 9, 10, 11], [12, 13, 14]]) In [54]: arr.T #使用T,實現數組轉置 ,簡單的轉置可使用.T,它其實就是進行軸對換而已。 Out[54]: array([[ 0, 3, 6, 9, 12], [ 1, 4, 7, 10, 13], [ 2, 5, 8, 11, 14]])
In [55]: np.dot(arr.T,arr) #使用np.dot()計算矩陣內積 Out[55]: array([[270, 300, 330], [300, 335, 370], [330, 370, 410]])
(2)高維數組
transpose須要獲得一個由軸編號組成的元組才能對這些軸進行轉置:
In [56]: arr = np.arange(16).reshape(2,2,4) In [57]: arr Out[57]: array([[[ 0, 1, 2, 3], [ 4, 5, 6, 7]], [[ 8, 9, 10, 11], [12, 13, 14, 15]]]) In [58]: arr.transpose((1,0,2)) #第一個軸被換成了第二個,第二個軸被換成了第一個,最後一個軸不變 Out[58]: array([[[ 0, 1, 2, 3], [ 8, 9, 10, 11]], [[ 4, 5, 6, 7], [12, 13, 14, 15]]]) In [60]: arr.transpose((2,0,1)) Out[60]: array([[[ 0, 4], [ 8, 12]], [[ 1, 5], [ 9, 13]], [[ 2, 6], [10, 14]], [[ 3, 7], [11, 15]]])
另:swapaxes也是返回源數據的視圖(不會進行任何複製操做)。
通用函數(即ufunc)是一種對ndarray中的數據執行元素級運算的函數。你能夠將其看作簡單函數(接受一個或多個標量值,併產生一個或多個標量值)的矢量化包裝器。
如:一元函數(sqrt 和 exp),二元函數(add 和 maximum)等。
有的函數能夠返回多個數組,如:modf ,它是Python內置函數divmod的矢量化版本,它會返回浮點數數組的小數和整數部分:
In [17]: arr = np.random.randn(5)*10 In [18]: arr Out[18]: array([ -3.21933715, -10.36535038, -10.67542382, -8.46654835, -9.11858314]) In [19]: xiaoshu ,zhengshu = np.modf(arr) In [20]: xiaoshu Out[20]: array([-0.21933715, -0.36535038, -0.67542382, -0.46654835, -0.11858314]) #使用函數 modf 返回的小數部分 In [21]: zhengshu Out[21]: array([ -3., -10., -10., -8., -9.]) #使用函數 modf 返回的整數部分
下表爲一些經常使用一元函數:
表3-1 一元函數
表3-2 二元函數
In [24]: arr1 = np.arange(-5,5,0.01) In [25]: x , y = np.meshgrid(arr1 ,arr1) #np.meshgrid函數接受兩個一維數組,併產生兩個二維矩陣 In [26]: y Out[26]: array([[-5. , -5. , -5. , ..., -5. , -5. , -5. ], [-4.99, -4.99, -4.99, ..., -4.99, -4.99, -4.99], [-4.98, -4.98, -4.98, ..., -4.98, -4.98, -4.98], ..., [ 4.97, 4.97, 4.97, ..., 4.97, 4.97, 4.97], [ 4.98, 4.98, 4.98, ..., 4.98, 4.98, 4.98], [ 4.99, 4.99, 4.99, ..., 4.99, 4.99, 4.99]]) In [27]: x Out[27]: array([[-5. , -4.99, -4.98, ..., 4.97, 4.98, 4.99], [-5. , -4.99, -4.98, ..., 4.97, 4.98, 4.99], [-5. , -4.99, -4.98, ..., 4.97, 4.98, 4.99], ..., [-5. , -4.99, -4.98, ..., 4.97, 4.98, 4.99], [-5. , -4.99, -4.98, ..., 4.97, 4.98, 4.99], [-5. , -4.99, -4.98, ..., 4.97, 4.98, 4.99]]) In [28]: z = np.sqrt(x **2 + y**2) In [29]: z Out[29]: array([[7.07106781, 7.06400028, 7.05693985, ..., 7.04988652, 7.05693985, 7.06400028], [7.06400028, 7.05692568, 7.04985815, ..., 7.04279774, 7.04985815, 7.05692568], [7.05693985, 7.04985815, 7.04278354, ..., 7.03571603, 7.04278354, 7.04985815], ..., [7.04988652, 7.04279774, 7.03571603, ..., 7.0286414 , 7.03571603, 7.04279774], [7.05693985, 7.04985815, 7.04278354, ..., 7.03571603, 7.04278354, 7.04985815], [7.06400028, 7.05692568, 7.04985815, ..., 7.04279774, 7.04985815, 7.05692568]]) In [30]: import matplotlib.pyplot as plt #用 matplotlib 建立了這個二維數組的可視化 In [31]: plt.imshow(z, cmap=plt.cm.gray);plt.colorbar() #下圖用 matplotlib 的 imshow 函數建立的 Out[31]: <matplotlib.colorbar.Colorbar at 0x1e3ca4c6e10>
numpy.where函數是三元表達式x if condition else y的矢量化版本。
In [36]: x = np.random.randn(4) In [37]: y = np.random.randn(4) In [38]: x Out[38]: array([ 1.48118742, -1.96970611, 2.25096645, -0.54246986]) In [39]: y Out[39]: array([-1.02410078, -0.11525904, -3.04483051, 0.39291614]) In [40]: c = ([True,False,False,True]) In [41]: result = np.where(c , x, y) #np.where()當c中條件知足,取x;不然,取y。 In [42]: result Out[42]: array([ 1.48118742, -0.11525904, -3.04483051, -0.54246986])
np.where的第二個和第三個參數沒必要是數組,它們均可以是標量值。在數據分析工做中,where一般用於根據另外一個數組而產生一個新的數組。
In [45]: arr = np.random.randn(3,3) In [46]: arr Out[46]: array([[ 0.2914581 , -1.36593999, -0.31432138], [-0.93846987, -0.20832555, 0.89131944], [-0.61511922, 0.92639632, 0.70555075]]) In [47]: np.where(arr<0 , '負數' , '非負數') #使用np.where(),將<0的數替換爲「負數」,不然替換爲「非負數」 Out[47]: array([['非負數', '負數', '負數'], ['負數', '負數', '非負數'], ['負數', '非負數', '非負數']], dtype='<U3')
使用np.where,能夠將標量和數組結合起來。
In [48]: arr = np.random.randn(3,3) In [49]: arr Out[49]: array([[-1.64514297, -0.75800804, 2.75419085], [-2.10894613, 0.88960921, 0.61008557], [-0.35183569, -0.76112232, -0.88589693]]) In [50]: np.where(arr<0 , 0 ,arr) #利用np.where()將數組中小於0的,替換成0 Out[50]: array([[0. , 0. , 2.75419085], [0. , 0.88960921, 0.61008557], [0. , 0. , 0. ]])
sum、mean以及標準差std等聚合計算(aggregation,一般叫作約簡(reduction))既能夠當作數組的實例方法調用,也能夠當作頂級NumPy函數使用。
arr.mean() 和 np.mean(arr)是相同的,mean和sum這類的函數能夠接受一個axis選項參數,用於計算該軸向上的統計值,最終結果是一個少一維的數組。
其餘,在多維數組中,累加函數(如cumsum)返回的是一樣大小的數組,可是會根據每一個低維的切片沿着標記軸計算部分聚類:
In [57]: arr = np.arange(9).reshape(3,3) #將0-8,依次放入3x3的數組 In [58]: arr Out[58]: array([[0, 1, 2], [3, 4, 5], [6, 7, 8]]) In [59]: arr.cumsum(axis=0) #當axis=0時,cumsum表示,x軸累加 Out[59]: array([[ 0, 1, 2], [ 3, 5, 7], [ 9, 12, 15]], dtype=int32) In [60]: arr.cumprod(axis=1) #當axis=1時,cumprod表示,y軸累積 Out[60]: array([[ 0, 0, 0], [ 3, 12, 60], [ 6, 42, 336]], dtype=int32) In [61]: arr.cumsum(axis=1) #當axis=1時,cumsum表示,y軸累加 Out[61]: array([[ 0, 1, 3], [ 3, 7, 12], [ 6, 13, 21]], dtype=int32)
any用於測試數組中是否存在一個或多個True,而all則檢查數組中全部值是否都是True:
In [62]: bools = np.array([True,False,False,False]) In [63]: bools.any() Out[63]: True In [64]: bools.all() Out[64]: False
NumPy數組也能夠經過sort方法就地排序,多維數組能夠在任何一個軸向上進行排序,只需將軸編號傳給sort便可。計算數組分位數最簡單的辦法是對其進行排序,而後選取特定位置的值。
(1)np.unique():用於找出數組中的惟一值並返回已排序的結果。
In [65]: arr = np.array([3,4,7,3,5,2,2,3]) In [66]: np.unique(arr) Out[66]: array([2, 3, 4, 5, 7])
(2)np.in1d():測試一個數組中的值在另外一個數組中的成員資格,返回一個布爾型數組。
arr = np.array([3,4,7,3,5,2,2,3]) np.in1d(arr , [1,2,5]) Out[68]: array([False, False, False, False, True, True, True, False])
表4.6 數組的集合運算
NumPy可以讀寫磁盤上的文本數據或二進制數據。
np.save和np.load是讀寫磁盤數組數據的兩個主要函數。默認狀況下,數組是以未壓縮的原始二進制格式保存在擴展名爲.npy的文件中的。若是文件路徑末尾沒有擴展名.npy,則該擴展名會被自動加上。而後就能夠經過np.load讀取磁盤上的數組。
In [69]: arr = np.arange(10) In [70]: np.save('test_arry',arr) #np.save(),寫入文件 In [71]: np.load('test_arry.npy') #np.load(),讀取文件 Out[71]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) In [72]: np.savez('tttest_arry', a=arr , b=arr) #np.savez()能夠將多個數組保存到一個未壓縮文件中,將數組以關鍵字參數的形式傳入。 In [73]: arch = np.load('tttest_arry.npz') #加載.npz文件時,你會獲得一個相似字典的對象. In [74]: arch['a'] #就像讀取字典 Out[74]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) In [75]: np.savez_compressed('array_savez_compressed', a=arr, b=arr) #使用numpy.savez_compressed,能夠將數據壓縮。
線性代數(如矩陣乘法、矩陣分解、行列式以及其餘方陣數學等)是任何數組庫的重要組成部分。NumPy提供了一個用於矩陣乘法的dot函數(既是一個數組方法也是numpy命名空間中的一個函數),x.dot(y)等價於np.dot(x, y)。
一個二維數組跟一個大小合適的一維數組的矩陣點積運算以後將會獲得一個一維數組。
@符也能夠用做中綴運算符,進行矩陣乘法,如:x @ np.ones(3) 。
numpy.linalg中有一組標準的矩陣分解運算以及諸如求逆和行列式之類的東西。
In [1]: import numpy as np In [2]: from numpy.linalg import inv, qr In [3]: x = np.random.randn(5,5) In [4]: mat = x.T.dot(x) In [5]: inv(mat) Out[5]: array([[10.74988502, -5.23823973, 3.4098932 , -4.01219508, 11.97285628], [-5.23823973, 2.79246653, -1.69907634, 2.17129017, -5.93229317], [ 3.4098932 , -1.69907634, 1.1877896 , -1.29521734, 3.83419893], [-4.01219508, 2.17129017, -1.29521734, 2.4106194 , -4.61201453], [11.97285628, -5.93229317, 3.83419893, -4.61201453, 13.47415598]]) In [6]: mat.dot(inv(mat)) Out[6]: array([[ 1.00000000e+00, 9.29519278e-15, 1.95410401e-15, -4.41590319e-15, 4.61147788e-15], [ 7.07066551e-16, 1.00000000e+00, -5.47843147e-16, 2.15026418e-15, -7.95052824e-15], [ 1.27735688e-15, -2.93361528e-15, 1.00000000e+00, -2.73367886e-15, 3.56280748e-15], [ 6.28103643e-16, -3.18294121e-16, 9.84088913e-17, 1.00000000e+00, 3.07491619e-16], [-1.08920071e-14, -2.31928826e-15, -3.79022227e-16, -1.56267753e-15, 1.00000000e+00]]) In [7]: q ,r = qr(mat) In [8]: r Out[8]: array([[-13.96664998, 3.71421781, -1.18244965, 1.19323932, 14.84179656], [ 0. , -6.67361393, -2.02000028, 1.34310676, -1.91257227], [ 0. , 0. , -10.59251334, 0.28032169, 3.12315558], [ 0. , 0. , 0. , -1.20613986, -0.42373857], [ 0. , 0. , 0. , 0. , 0.0502477 ]])
表6.1 經常使用的numpy.linalg函數
numpy.random模塊對Python內置的random進行了補充,增長了一些用於高效生成多種機率分佈的樣本值的函數。
In [1]: import numpy as np In [2]: samples = np.random.normal(size=(4,4)) #np.random.normal()獲得一個標準正態分佈的數組 In [3]: samples Out[3]: array([[ 1.6148936 , -1.08305526, 0.23173604, 0.95963586], [ 2.47264752, 0.95717801, 0.58122765, 0.10627349], [-0.66822577, -1.54174138, 2.06643621, -0.76198218], [-0.92518965, -1.57405853, -0.21520583, 0.2834875 ]])
Python內置的random模塊則只能一次生成一個樣本值。若是須要產生大量樣本值,numpy.random快了不止一個數量級。
僞隨機數,是由於它們都是經過算法基於隨機數生成器種子,在肯定性的條件下生成的。
可以使用np.random.seed更改隨機數生成種子,numpy.random的數據生成函數使用了全局的隨機種子。要避免全局狀態,你可使用np.random.RandomState,建立一個與其它隔離的隨機數生成器。
表7.1 部分numpy.random函數