本章着重介紹NumPy基礎知識。
由於NumPy提供了一個非常易用的C語言API,可以將數據傳遞給用底層語言編寫的外部類庫,再由外部類庫將計算結果按照NumPy數組的方式返回。這個特徵使得Python可以對存量C/C++/Fortran代碼庫進行封裝,併爲這些代碼提供動態、易用的接口。
NumPy對含有大量數組的數據非常有效。
- NumPy在內部將數據存儲在連續的內存塊上,這與其他的Python內建數據結構時不同的。
- NumPy的算法庫是用C語言寫的,所以在操作數據內存時,不需要任何類型檢查或者其他管理操作。
- NumPy數組使用的內存量也小於其他Python內建序列。
- NumPy可以針對全量數組進行復雜計算而不需要寫Python循環。
計算效率對比如下:
import numpy as np
my_arr = np.arange(1000000)
my_list = list(range(1000000))
%time for i in range(10) : my_arr2 = my_arr * 2
Wall time: 26 ms
%time for i in range(10) : my_list2 = [x * 2 for x in my_list]
Wall time: 998 ms
NumPy的方法比Python方法要快10到100倍,並且使用的內存也少。
1. NumPy ndarray—多維數組對象
一個ndarray是一個通用的多維同類數據容器,包含的每一個元素均爲相同類型。
1.1 生成ndarray
- array函數接收任意的序列型對象生成一個ndarray數組。
data1 = [1,2,3,4]
arr1 = np.array(data1)
arr1
array([1, 2, 3, 4])
arr1.shape
(4,)
arr1.ndim
1
arr1.dtype
dtype('int32')
- 嵌套序列,例如同等長度的列表,將會自動轉化爲多維數組。
data2 = [[1,2,3,4], [5,6,7,8]]
arr2 = np.array(data2)
arr2
array([[1, 2, 3, 4],
[5, 6, 7, 8]])
- 其他函數zeros/ones/empty/ones_like/full_like/eye(特徵矩陣)
np.zeros(10)
np.zeros((2,3))
np.ones_like(data)
np.full((2,3), 2)
np.full_like(data, 3)
- arange是range的數組版
np.arange(10)
1.2 ndarry的數據類型
arr1.dtype
arr2.astype
1.3 數組算術
數組可以進行批量操作而無需任何for循環,這種特性成爲向量化。
廣播機制
1.4 基礎索引和切片
區別於Python的內建列表,數組的切片是原數組的視圖。這意味着數據並不是被複制了,任何對於視圖的修改都會反映到原數組上。
如果想要一份數組切片的拷貝而不是一份視圖,可以如下:
arr[5:8].copy()
多維數組引入軸的概念,0軸是行方向,1軸是列方向。
索引與切片的區別,對於多維數組來說,索引定位的某一個具體位置的元素,某一行或者某一列或某一個元素,切片就算也只取一行或者一列仍然可能是多維的。
arr2d = np.arange(9).reshape(3,3)
arr2d
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
arr_slice = arr2d[:2, 2]
arr_slice
array([2, 5])
arr_slice.shape
(2,)
arr_slice = arr2d[2:, 2:]
arr_slice
array([[8]])
arr_slice.shape
(1, 1)
arr_slice = arr2d[2, 2]
arr_slice
8
arr_slice.shape
()
arr_slice = arr2d[:, 2]
arr_slice
array([2, 5, 8])
arr_slice.shape
(3,)
arr_slice = arr2d[:, 2:]
arr_slice
array([[2],
[5],
[8]])
arr_slice.shape
(3, 1)
1.5 布爾索引
布爾數組的長度必須和數組軸索引長度一致。
布爾索引選擇數據時總是生成數據的拷貝。
data[names == 'Bob']
data[names != 'Bob']
data[~(names == 'Bob')] #對條件取反
mask = (names == 'Bob') | (names == 'Will') #python關鍵字and和or對布爾數組沒用,必須使用&和|來代替。
data[mask]
1.6 神奇索引
指的是使用整數數組來進行數據索引,將數組依據索引位置重新排列,或者選出一個符合特定順序的子集。
arr = np.empty((8,4))
for i in range(8):
arr[i] = i
arr
array([[0., 0., 0., 0.],
[1., 1., 1., 1.],
[2., 2., 2., 2.],
[3., 3., 3., 3.],
[4., 4., 4., 4.],
[5., 5., 5., 5.],
[6., 6., 6., 6.],
[7., 7., 7., 7.]])
- 傳遞一個包含指明所需順序的列表或數組
arr[[4, 3, 0, 6]]
array([[4., 4., 4., 4.],
[3., 3., 3., 3.],
[0., 0., 0., 0.],
[6., 6., 6., 6.]])
- 但多個索引數組時會根據每個索引元組對應的元素選出一個一維數組
arr = np.arange(32).reshape(8,4)
arr
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]])
arr[[1, 5, 7, 2], [0, 3, 1, 2]]
array([ 4, 23, 29, 10]) # 取出的分別時(1,0) (5,3)等位置的元素
- 想要達到通過選擇矩陣中行列的子集所形成的矩形區域,可以如下來實現
arr[[1, 5, 7, 2]][:, [0, 3, 1, 2]]
array([[ 4, 7, 5, 6],
[20, 23, 21, 22],
[28, 31, 29, 30],
[ 8, 11, 9, 10]])
1.7 數組轉置和換軸
轉置 arr.T
方法transpose可以接收包含軸編號的元組,用於置換軸
ndarray有一個swapaxes方法,該方法接收一對軸編號作爲參數,並對軸進行調整來重整數組
arr.swapaxes(1,2)
2.通用函數——快速的逐元素數組函數
通用函數就是對一些簡單函數的向量化封裝。
sqrt/exp/maximum/add等
multiply 將數組的對應元素相乘,不是矩陣的乘法!
3.面向數組編程
利用數組表達式來代替顯式循環的方法稱爲向量化。通常向量化的數組操作比純python的等價實現在速度上快一到兩個數量級(甚至更多)。
points = np.arange(-5,5,0.01)
xs, ys = np.meshgrid(points, points) #meshgrid函數接收兩個一維數組,並根據兩個數組的所有(x,y)對生成二維
xs
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]])
z = np.sqrt(xs ** 2 + ys ** 2)
z
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]])
3.1 條件邏輯操作數組
numpy.where函數是三元表達式 x if condtion else y 的向量化版本。
result = np.where(cond, xarr, yarr)
等價於以下循環代碼,但效率快很多
result = [(x if c else y) for x,y, c in zip(xarr, yarr, cond)]
where函數的第二和第三個參數也可以是標量,在數據分析中經常用於根據一個數組來生成一個新的數組。
np.where(arr > 0, 2, -2) #數組arr中正值替換爲2,負值替換爲 -2
where函數也支持標量和數組聯合使用。
np.where(arr>0, 2, arr) #僅將正值替換爲2
3.2 數學和統計方法
聚合函數的使用——sum mean std min max cumsum(累加)cumprod(累乘)
可以通過指定軸來確定聚合函數的方向,axis = 0表示按行的方向(橫着計算),axis = 1表示按列的方向(豎着計算)。
arr = np.random.rand(5,4)
arr
array([[0.36089492, 0.96022134, 0.22585348, 0.51348969],
[0.78062164, 0.37050394, 0.46274186, 0.33013851],
[0.21334633, 0.93623858, 0.22104246, 0.44392382],
[0.85695648, 0.14728384, 0.29550666, 0.49136345],
[0.0505092 , 0.53460095, 0.6288425 , 0.65028889]])
arr.mean(axis=1)
array([0.51511486, 0.48600149, 0.4536378 , 0.44777761, 0.46606039]) #得到 5個值
array.sum(axis=0)
array([2.26232858, 2.94884866, 1.83398696, 2.42920436]) #得到4個值
3.3 布爾值數組的方法
布爾值True和Fasle會被強制爲1和0,所以sum函數可以用於計算布爾值數組中的True個數。
any 檢查數組中是否至少有一個True
all 檢查數組中是否每個值都是True
3.4 排序
sort 方法支持按指定的軸進行排序。
3.5 唯一值及其他集合邏輯
np.unique 返回數組中唯一值排序後形成的數組。
np.unique(names)
sorted(set(names)) #純python實現
np.inld 可以檢查一個數組中的值是否存在於另一個數組中,並返回一個布爾值數組。
4. 使用數組進行文件輸入和輸出
np.save 和 np.load函數
arr = np,arange(10)
np.save('some_array', arr) # 默認爲未壓縮格式,後綴名爲.npy
np.load('some_array.npy')
np.savez('array_archive.npz', a=arr, b=arr) #未壓縮文件中保存多個數組
np.load('array_archive.npz') #載入.npz文件時得到一個字典型的對象,可通過鍵調用對應數組
5. 線性代數
注意numpy中*時矩陣的逐個元素乘積,而不是矩陣的點乘積,點乘操作需要dot函數,也可使用點乘操作符@
x.dot(y)
np.dot(x,y)
x @ np.ones(3)
numpy.linalg 是矩陣分解的標準函數集,可對矩陣求逆和行列式求解。常用的函數有:diag、dot、trace、det等。
6. 僞隨機數生成
僞隨機數是由具有確定性行爲的算法根據隨機數生成器中的隨機數種子生成的。
np.random.seed(1234)
隨機數支持根據各種分佈來抽取樣本,如均勻分佈、正態分佈、二項分佈、高斯分佈、卡方分佈等,均有對象的函數。
7. 隨機漫步
numpy使用實例。