Python:一篇文章掌握Numpy的基本用法
分享一篇有趣兒的文章:Python:一篇文章掌握Numpy的基本用法
前言
Numpy是一個開源的Python科學計算庫,它是python科學計算庫的基礎庫,許多其他著名的科學計算庫如Pandas,Scikit-learn等都要用到Numpy庫的一些功能。
本文主要內容如下:
- Numpy數組對象
- 創建ndarray數組
- Numpy的數值類型
- ndarray數組的屬性
- ndarray數組的切片和索引
- 處理數組形狀
- 數組的類型轉換
- numpy常用統計函數
- 數組的廣播
1 Numpy數組對象
Numpy中的多維數組稱為ndarray,這是Numpy中最常見的數組對象。ndarray對象通常包含兩個部分:
- ndarray數據本身
- 描述數據的元數據
Numpy數組的優勢
- Numpy數組通常是由相同種類的元素組成的,即數組中的數據項的類型一致。這樣有一個好處,由於知道數組元素的類型相同,所以能快速確定存儲數據所需空間的大小。
- Numpy數組能夠運用向量化運算來處理整個數組,速度較快;而Python的列表則通常需要藉助循環語句遍歷列表,運行效率相對來說要差。
- Numpy使用了優化過的C API,運算速度較快
關於向量化和標量化運算,對比下面的參考例子就可以看出差異
- 使用python的list進行循環遍歷運算
def pySum(): a = list(range(10000)) b = list(range(10000)) c = [] for i in range(len(a)): c.append(a[i]**2 + b[i]**2) return c
%timeit pySum()
10 loops, best of 3: 49.4 ms per loop
- 使用numpy進行向量化運算
import numpy as npdef npSum(): a = np.arange(10000) b = np.arange(10000) c = a**2 + b**2 return c
%timeit npSum()
The slowest run took 262.56 times longer than the fastest. This could mean that an intermediate result is being cached.1000 loops, best of 3: 128 μs per loop
從上面的運行結果可以看出,numpy的向量化運算的效率要遠遠高於python的循環遍歷運算(效率相差好幾百倍)。
(1ms=1000μs)2 創建ndarray數組
首先需要導入numpy庫,在導入numpy庫時通常使用「np」作為簡寫,這也是Numpy官方倡導的寫法。
當然,你也可以選擇其他簡寫的方式或者直接寫numpy,但還是建議用「np」,這樣你的程序能和大都數人的程序保持一致。
import numpy as np
創建ndarray數組的方式有很多種,這裡介紹我使用的較多的幾種:
Method 1: 基於list或tuple
# 一維數組 # 基於listarr1 = np.array([1,2,3,4])print(arr1)# 基於tuplearr_tuple = np.array((1,2,3,4))print(arr_tuple)# 二維數組 (2*3)arr2 = np.array([[1,2,4], [3,4,5]])arr2
[1 2 3 4][1 2 3 4]array([[1, 2, 4], [3, 4, 5]])
請注意:
- 一維數組用print輸出的時候為 [1 2 3 4],跟python的列表是有些差異的,沒有「,」
- 在創建二維數組時,在每個子list外面還有一個"[]",形式為「[[list1], [list2]]」
Method 2: 基於np.arange
# 一維數組arr1 = np.arange(5)print(arr1)# 二維數組arr2 = np.array([np.arange(3), np.arange(3)])arr2
[0 1 2 3 4]array([[0, 1, 2], [0, 1, 2]])
Method 3: 基於arange以及reshape創建多維數組
# 創建三維數組arr = np.arange(24).reshape(2,3,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]]])
- 請注意:arange的長度與ndarray的維度的乘積要相等,即 24 = 2X3X4
- 用numpy.random創建數組的方法,可以參考下面的文章
為什麼你用不好Numpy的random函數?
- 其他創建ndarray的方法,各位小夥伴們自己可以研究下。
3 Numpy的數值類型
Numpy的數值類型如下:
每一種數據類型都有相應的數據轉換函數,參考示例如下:
np.int8(12.334)
12
np.float64(12)
12.0
np.float(True)
1.0
bool(1)
True
在創建ndarray數組時,可以指定數值類型:
a = np.arange(5, dtype=float)a
array([ 0., 1., 2., 3., 4.])
- 請注意,複數不能轉換成為整數類型或者浮點數,比如下面的代碼會運行出錯
# float(42 + 1j)
4 ndarray數組的屬性
- dtype屬性,ndarray數組的數據類型,數據類型的種類,前面已描述。
np.arange(4, dtype=float)
array([ 0., 1., 2., 3.])
# D表示複數類型
np.arange(4, dtype=D)
array([ 0.+0.j, 1.+0.j, 2.+0.j, 3.+0.j])
np.array([1.22,3.45,6.779], dtype=int8)
array([1, 3, 6], dtype=int8)
- ndim屬性,數組維度的數量
a = np.array([[1,2,3], [7,8,9]])a.ndim
2
- shape屬性,數組對象的尺度,對於矩陣,即n行m列,shape是一個元組(tuple)
a.shape
(2, 3)
- size屬性用來保存元素的數量,相當於shape中nXm的值
a.size
6
- itemsize屬性返回數組中各個元素所佔用的位元組數大小。
a.itemsize
4
- nbytes屬性,如果想知道整個數組所需的位元組數量,可以使用nbytes屬性。其值等於數組的size屬性值乘以itemsize屬性值。
a.nbytes
24
a.size*a.itemsize
24
- T屬性,數組轉置
b = np.arange(24).reshape(4,6)b
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]])
b.T
array([[ 0, 6, 12, 18], [ 1, 7, 13, 19], [ 2, 8, 14, 20], [ 3, 9, 15, 21], [ 4, 10, 16, 22], [ 5, 11, 17, 23]])
- 複數的實部和虛部屬性,real和imag屬性
d = np.array([1.2+2j, 2+3j])d
array([ 1.2+2.j, 2.0+3.j])real屬性返回數組的實部
d.real
array([ 1.2, 2. ])
imag屬性返回數組的虛部
d.imag
array([ 2., 3.])
- flat屬性,返回一個numpy.flatiter對象,即可迭代的對象。
e = np.arange(6).reshape(2,3)earray([[0, 1, 2], [3, 4, 5]])f = e.flatf<numpy.flatiter at 0x65eaca0>for item in f: print(item)012345
可通過位置進行索引,如下:
f[2]2f[[1,4]]array([1, 4])
也可以進行賦值
e.flat=7earray([[7, 7, 7], [7, 7, 7]])e.flat[[1,4]]=1earray([[7, 1, 7], [7, 1, 7]])
下圖是對ndarray各種屬性的一個小結
5 ndarray數組的切片和索引
- 一維數組
一維數組的切片和索引與python的list索引類似。
a = np.arange(7)aarray([0, 1, 2, 3, 4, 5, 6])a[1:4]array([1, 2, 3])# 每間隔2個取一個數a[ : 6: 2]array([0, 2, 4])
- 二維數組的切片和索引,如下所示:
插播一條硬廣:技術文章轉發太多。本文涉及的代碼量比較多,如需要查看源代碼,請在微信公眾號「Python數據之道」(ID:PyDataRoad)後台回復關鍵字「2017026」。
6 處理數組形狀
6.1 形狀轉換
- reshape()和resize()
b.reshape(4,3)array([[ 0, 1, 2], [ 3, 4, 5], [ 6, 7, 8], [ 9, 10, 11]])barray([[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9, 10, 11]])b.resize(4,3)barray([[ 0, 1, 2], [ 3, 4, 5], [ 6, 7, 8], [ 9, 10, 11]])
函數resize()的作用跟reshape()類似,但是會改變所作用的數組,相當於有inplace=True的效果
- ravel()和flatten(),將多維數組轉換成一維數組,如下:
b.ravel()array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])b.flatten()array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])barray([[ 0, 1, 2], [ 3, 4, 5], [ 6, 7, 8], [ 9, 10, 11]])
兩者的區別在於返回拷貝(copy)還是返回視圖(view),flatten()返回一份拷貝,需要分配新的內存空間,對拷貝所做的修改不會影響原始矩陣,而ravel()返回的是視圖(view),會影響原始矩陣。
參考如下代碼:
- 用tuple指定數組的形狀,如下:
b.shape=(2,6)barray([[ 0, 1, 20, 3, 4, 5], [ 6, 7, 8, 9, 10, 11]])
- 轉置
前面描述了數組轉置的屬性(T),也可以通過transpose()函數來實現
b.transpose()array([[ 0, 6], [ 1, 7], [20, 8], [ 3, 9], [ 4, 10], [ 5, 11]])
6.2 堆疊數組
barray([[ 0, 1, 20, 3, 4, 5], [ 6, 7, 8, 9, 10, 11]])c = b*2carray([[ 0, 2, 40, 6, 8, 10], [12, 14, 16, 18, 20, 22]])
- 水平疊加
hstack()np.hstack((b,c))array([[ 0, 1, 20, 3, 4, 5, 0, 2, 40, 6, 8, 10], [ 6, 7, 8, 9, 10, 11, 12, 14, 16, 18, 20, 22]])
column_stack()函數以列方式對數組進行疊加,功能類似hstack()
np.column_stack((b,c))array([[ 0, 1, 20, 3, 4, 5, 0, 2, 40, 6, 8, 10], [ 6, 7, 8, 9, 10, 11, 12, 14, 16, 18, 20, 22]])
- 垂直疊加
vstack()np.vstack((b,c))array([[ 0, 1, 20, 3, 4, 5], [ 6, 7, 8, 9, 10, 11], [ 0, 2, 40, 6, 8, 10], [12, 14, 16, 18, 20, 22]])
row_stack()函數以行方式對數組進行疊加,功能類似vstack()
np.row_stack((b,c))array([[ 0, 1, 20, 3, 4, 5], [ 6, 7, 8, 9, 10, 11], [ 0, 2, 40, 6, 8, 10], [12, 14, 16, 18, 20, 22]])
- concatenate()方法,通過設置axis的值來設置疊加方向
axis=1時,沿水平方向疊加
axis=0時,沿垂直方向疊加
np.concatenate((b,c),axis=1)array([[ 0, 1, 20, 3, 4, 5, 0, 2, 40, 6, 8, 10], [ 6, 7, 8, 9, 10, 11, 12, 14, 16, 18, 20, 22]])np.concatenate((b,c),axis=0)array([[ 0, 1, 20, 3, 4, 5], [ 6, 7, 8, 9, 10, 11], [ 0, 2, 40, 6, 8, 10], [12, 14, 16, 18, 20, 22]])
由於針對數組的軸為0或1的方向經常會混淆,通過示意圖,或許可以更好的理解。
關於數組的軸方向示意圖,以及疊加的示意圖,如下:
深度疊加
這個有點燒腦,舉個例子如下,自己可以體會下:
arr_dstack = np.dstack((b,c))print(arr_dstack.shape)arr_dstack(2, 6, 2)array([[[ 0, 0], [ 1, 2], [20, 40], [ 3, 6], [ 4, 8], [ 5, 10]], [[ 6, 12], [ 7, 14], [ 8, 16], [ 9, 18], [10, 20], [11, 22]]])
疊加前,b和c均是shape為(2,6)的二維數組,疊加後,arr_dstack是shape為(2,6,2)的三維數組。
深度疊加的示意圖如下:
6.3 數組的拆分
跟數組的疊加類似,數組的拆分可以分為橫向拆分、縱向拆分以及深度拆分。
涉及的函數為 hsplit()、vsplit()、dsplit() 以及split()
barray([[ 0, 1, 20, 3, 4, 5], [ 6, 7, 8, 9, 10, 11]])
- 沿橫向軸拆分(axis=1)
np.hsplit(b, 2)[array([[ 0, 1, 20], [ 6, 7, 8]]), array([[ 3, 4, 5], [ 9, 10, 11]])]np.split(b,2, axis=1)[array([[ 0, 1, 20], [ 6, 7, 8]]), array([[ 3, 4, 5], [ 9, 10, 11]])]
- 沿縱向軸拆分(axis=0)
np.vsplit(b, 2)[array([[ 0, 1, 20, 3, 4, 5]]), array([[ 6, 7, 8, 9, 10, 11]])]np.split(b,2,axis=0)[array([[ 0, 1, 20, 3, 4, 5]]), array([[ 6, 7, 8, 9, 10, 11]])]
- 深度拆分
arr_dstackarray([[[ 0, 0], [ 1, 2], [20, 40], [ 3, 6], [ 4, 8], [ 5, 10]], [[ 6, 12], [ 7, 14], [ 8, 16], [ 9, 18], [10, 20], [11, 22]]])np.dsplit(arr_dstack,2)[array([[[ 0], [ 1], [20], [ 3], [ 4], [ 5]], [[ 6], [ 7], [ 8], [ 9], [10], [11]]]), array([[[ 0], [ 2], [40], [ 6], [ 8], [10]], [[12], [14], [16], [18], [20], [22]]])]
拆分的結果是原來的三維數組拆分成為兩個二維數組。
這個燒腦的拆分過程可以自行分析下~~
7 數組的類型轉換
- 數組轉換成list,使用tolist()
barray([[ 0, 1, 20, 3, 4, 5], [ 6, 7, 8, 9, 10, 11]])b.tolist()[[0, 1, 20, 3, 4, 5], [6, 7, 8, 9, 10, 11]]
- 轉換成指定類型,astype()函數
b.astype(float)array([[ 0., 1., 20., 3., 4., 5.], [ 6., 7., 8., 9., 10., 11.]])
8 numpy常用統計函數
常用的函數如下:
請注意函數在使用時需要指定axis軸的方向,若不指定,默認統計整個數組。
- np.sum(),返回求和
- np.mean(),返回均值
- np.max(),返回最大值
- np.min(),返回最小值
- np.ptp(),數組沿指定軸返回最大值減去最小值,即(max-min)
- np.std(),返回標準偏差(standard deviation)
- np.var(),返回方差(variance)
- np.cumsum(),返回累加值
- np.cumprod(),返回累乘積值
barray([[ 0, 1, 20, 3, 4, 5], [ 6, 7, 8, 9, 10, 11]])np.max(b)20# 沿axis=1軸方向統計np.max(b,axis=1)array([20, 11])# 沿axis=0軸方向統計np.max(b,axis=0)array([ 6, 7, 20, 9, 10, 11])np.min(b)0
- np.ptp(),返回整個數組的最大值減去最小值,如下:
np.ptp(b)20# 沿axis=0軸方向np.ptp(b, axis=0)array([ 6, 6, 12, 6, 6, 6])# 沿axis=1軸方向np.ptp(b, axis=1)array([20, 5])
- np.cumsum(),沿指定軸方向進行累加
b.resize(4,3)barray([[ 0, 1, 20], [ 3, 4, 5], [ 6, 7, 8], [ 9, 10, 11]])np.cumsum(b, axis=1)array([[ 0, 1, 21], [ 3, 7, 12], [ 6, 13, 21], [ 9, 19, 30]], dtype=int32)np.cumsum(b, axis=0)array([[ 0, 1, 20], [ 3, 5, 25], [ 9, 12, 33], [18, 22, 44]], dtype=int32)
- np.cumprod(),沿指定軸方向進行累乘積 (Return the cumulative product of the elements along the given axis)
np.cumprod(b,axis=1)array([[ 0, 0, 0], [ 3, 12, 60], [ 6, 42, 336], [ 9, 90, 990]], dtype=int32)np.cumprod(b,axis=0)array([[ 0, 1, 20], [ 0, 4, 100], [ 0, 28, 800], [ 0, 280, 8800]], dtype=int32)
9 數組的廣播
當數組跟一個標量進行數學運算時,標量需要根據數組的形狀進行擴展,然後執行運算。
這個擴展的過程稱為「廣播(broadcasting)」
barray([[ 0, 1, 20], [ 3, 4, 5], [ 6, 7, 8], [ 9, 10, 11]])d = b + 2darray([[ 2, 3, 22], [ 5, 6, 7], [ 8, 9, 10], [11, 12, 13]])
寫在最後
numpy涵蓋的內容其實是非常豐富的,本文僅僅介紹了numpy一些常用的基本功能,算是對numpy的一個入門級的簡單的較為全面的描述。
numpy官方的《Numpy Reference》文檔,光頁面數量就有1500+頁,如想要系統的學習numpy,建議仔細閱讀官方的參考文檔,可在其官方網站進行查閱。當然,資料都是英文版的,可能看起來難度稍微大點,看習慣了就好。
你想更深入了解學習Python知識體系,你可以看一下我們花費了一個多月整理了上百小時的幾百個知識點體系內容:
【超全整理】《Python自動化全能開發從入門到精通》筆記全放送
推薦閱讀:
※Numpy中Meshgrid函數介紹及2種應用場景
※如何用python numpy產生一個正態分布隨機數的向量或者矩陣?
※python numpy的樣本標準差怎麼寫?