歡迎光臨
每天分享高質量文章

Python必備基礎:這些NumPy的神操作你都掌握了嗎?

導讀:NumPy是Python的基礎,更是資料科學的通用語言。

本文簡單介紹NumPy模組的兩個基本物件ndarray、ufunc,介紹ndarray物件的幾種生成方法及如何存取其元素、如何操作矩陣或多維陣列、如何進行資料合併與展平等。最後說明通用函式及廣播機制。

 

 

作者:吳茂貴,王冬,李濤,楊本法

如需轉載請聯絡大資料(ID:hzdashuju)

 

 

NumPy為何如此重要?實際上Python本身含有串列(list)和陣列(array),但對於大資料來說,這些結構有很多不足。因串列的元素可以是任何物件,因此串列中所儲存的是物件的指標。這樣為了儲存一個簡單的[1,2,3],都需要有3個指標和3個整數物件。

對於數值運算來說,這種結構顯然比較浪費記憶體和CPU計算時間。至於array物件,它直接儲存數值,和C語言的一維陣列比較類似。但是由於它不支援多維,也沒有各種運算函式,因此也不適合做數值運算。

 

NumPy(Numerical Python 的簡稱)的誕生彌補了這些不足,它提供了兩種基本的物件:ndarray(N-dimensional array object)和 ufunc(universal function object)。ndarray是儲存單一資料型別的多維陣列,而ufunc則是能夠對陣列進行處理的函式。

 

NumPy的主要特點:

 

  • ndarray,快速,節省空間的多維陣列,提供陣列化的算術運算和高階的廣播功能。

  • 使用標準數學函式對整個陣列的資料進行快速運算,而不需要編寫迴圈。

  • 讀取/寫入磁碟上的陣列資料和操作儲存器映像檔案的工具。

  • 線性代數,隨機數生成,以及傅裡葉變換的能力。

  • 整合C、C++、Fortran程式碼的工具。

 

在使用 NumPy 之前,需要先匯入該模組:

 

import numpy as np

 

 

01 生成ndarray的幾種方式

 

NumPy封裝了一個新的資料型別ndarray,一個多維陣列物件,該物件封裝了許多常用的數學運算函式,方便我們進行資料處理以及資料分析,那麼如何生成ndarray呢?這裡我們介紹生成ndarray的幾種方式,如從已有資料中建立;利用random建立;建立特殊多維陣列;使用arange函式等。

 

1. 從已有資料中建立

 

直接對python的基礎資料型別(如串列、元組等)進行轉換來生成ndarray。

 

(1)將串列轉換成ndarray

 

import numpy as np
list1 = [3.14,2.17,0,1,2]
nd1 = np.array(list1)
print(nd1)
print(type(nd1))

列印結果:

[ 3.14  2.17  0.    1.    2.  ]

 

(2)巢狀串列可以轉換成多維ndarray

 

import numpy as np
list2 = [[3.14,2.17,0,1,2],[1,2,3,4,5]]
nd2 = np.array(list2)
print(nd2)
print(type(nd2))

列印結果:

[[ 3.14  2.17  0.    1.    2.  ]
 [ 1.    2.    3.    4.    5.  ]]

 

如果把(1)和(2)中的串列換成元組也同樣適合。

 

2. 利用random模組生成ndarray

 

在深度學習中,我們經常需要對一些變數進行初始化,適當的初始化能提高模型的效能。通常我們用隨機數生成模組random來生成,當然random模組又分為多種函式:

  • random生成0到1之間的隨機數;

  • uniform生成均勻分佈隨機數;

  • randn生成標準正態的隨機數;

  • normal生成正態分佈;

  • shuffle隨機打亂順序;

  • seed設定隨機數種子等。

下麵我們列舉幾個簡單示例。

 

import numpy as np

nd5 = np.random.random([3,3])
print(nd5)
print(type(nd5))

 

列印結果:

 

[[ 0.88900951  0.47818541  0.91813526]
 [ 0.48329167  0.63730656  0.14301479]
 [ 0.9843789   0.99257093  0.24003961]]

 

生成一個隨機種子,對生成的隨機數打亂。

 

import numpy as np

np.random.seed(123)
nd5_1 = np.random.randn(2,3)
print(nd5_1)
np.random.shuffle(nd5_1)
print("隨機打亂後資料")
print(nd5_1)
print(type(nd5_1))

 

列印結果:

 

[[-1.0856306   0.99734545  0.2829785 ]
 [-1.50629471 -0.57860025  1.65143654]]

 

隨機打亂後資料為:

 

[[-1.50629471 -0.57860025  1.65143654]
 [-1.0856306   0.99734545  0.2829785 ]]

 

3. 建立特定形狀的多維陣列

 

資料初始化時,有時需要生成一些特殊矩陣,如0或1的陣列或矩陣,這時我們可以利用np.zeros、np.ones、np.diag來實現,下麵我們透過幾個示例來說明。

 

import numpy as np

#生成全是0的3x3矩陣
nd6 = np.zeros([3,3])
#生成全是1的3x3矩陣
nd7 = np.ones([3,3])
#生成3階的單位矩陣
nd8= np.eye(3)
#生成3階對角矩陣
print (np.diag([123]))

 

我們還可以把生成的資料儲存到磁碟,然後從磁碟讀取。

 

import numpy as np
nd9 = np.random.random([5,5])
np.savetxt(X=nd9,fname='./test2.txt')
nd10 = np.loadtxt('./test2.txt')

 

4. 利用arange函式

 

arange是numpy模組中的函式,其格式為:arange([start,] stop[, step,], dtype=None)。根據start與stop指定的範圍,以及step設定的步長,生成一個 ndarray,其中start預設為0,步長step可為小數。

 

import numpy as np

print(np.arange(10))
print(np.arange(0,10))
print(np.arange(14,0.5))
print(np.arange(9-1-1))

 

 

02 存取元素

 

上節我們介紹了生成ndarray的幾種方法,資料生成後,如何讀取我們需要的資料?這節我們介紹幾種讀取資料的方法。

 

import numpy as np
np.random.seed(2018)
nd11 = np.random.random([10])
#獲取指定位置的資料,獲取第4個元素
nd11[3]
#擷取一段資料
nd11[3:6]
#擷取固定間隔資料
nd11[1:6:2]
#倒序取數
nd11[::-2]
#擷取一個多維陣列的一個區域內資料
nd12=np.arange(25).reshape([5,5])
nd12[1:3,1:3]
#擷取一個多維陣列中,數值在一個值域之內的資料
nd12[(nd12>3)&(nd12<10)]
#擷取多維陣列中,指定的行,如讀取第2,3行
nd12[[1,2]]  #或nd12[1:3,:]
##擷取多維陣列中,指定的列,如讀取第2,3列
nd12[:,1:3]

 

如果你對上面這些獲取方式還不是很清楚,沒關係,下麵我們透過圖形的方式說明如何獲取多維陣列中的元素,如圖1-1所示,左邊為運算式,右邊為對應獲取元素。

 

▲圖1-1 獲取多維陣列中的元素

 

獲取陣列中的部分元素除透過指定索引標簽外,還可以使用一些函式來實現,如透過random.choice函式從指定的樣本中進行隨機抽取資料。

 

import numpy as np
from numpy import random as nr

a=np.arange(1,25,dtype=float)
c1=nr.choice(a,size=(3,4))  #size指定輸出陣列形狀
c2=nr.choice(a,size=(3,4),replace=False)  #replace預設為True,即可重覆抽取
#下式中引數p指定每個元素對應的抽取機率,預設為每個元素被抽取的機率相同
c3=nr.choice(a,size=(3,4),p=a / np.sum(a))
 print("隨機可重覆抽取")
print(c1)
print("隨機但不重覆抽取")
print(c2)
print("隨機但按制度機率抽取")
print(c3)

列印結果:

隨機可重覆抽取
[[  7.  22.  19.  21.]
 [  7.   5.   5.   5.]
 [  7.   9.  22.  12.]]
隨機但不重覆抽取
[[ 21.   9.  15.   4.]
 [ 23.   2.   3.   7.]
 [ 13.   5.   6.   1.]]
隨機但按制度機率抽取
[[ 15.  19.  24.   8.]
 [  5.  22.   5.  14.]
 [  3.  22.  13.  17.]]

 

 

03 矩陣操作

 

深度學習中經常涉及多維陣列或矩陣的運算,正好NumPy模組提供了許多相關的計算方法,下麵介紹一些常用的方法。

 

import numpy as np

nd14=np.arange(9).reshape([3,3])

#矩陣轉置
np.transpose(nd14)

#矩陣乘法運算
a=np.arange(12).reshape([3,4])
b=np.arange(8).reshape([4,2])
a.dot(b)

#求矩陣的跡
a.trace()
#計算矩陣行列式
np.linalg.det(nd14)

#計算逆矩陣
c=np.random.random([3,3])
np.linalg.solve(c,np.eye(3))

 

上面介紹的幾種方法是numpy.linalg模組中的函式,numpy.linalg模組中的函式是滿足行業標準級的Fortran庫。

 

numpy.linalg中常用函式:

  • diag:以一維陣列方式傳回方陣的對角線元素

  • dot矩陣乘法

  • trace求跡,即計算對角線元素的和

  • det計算矩陣列式

  • eig計算方陣的本徵值和本徵向量

  • inv計算方陣的逆

  • qr計算qr分解

  • svd計算奇異值分解svd

  • solve解線性方程組Ax = b,其中A為方陣

  • lstsq計算Ax=b的最小二乘解

 

 

04 資料合併與展平

 

在機器學習或深度學習中,會經常遇到需要把多個向量或矩陣按某軸方向進行合併的情況,也會遇到展平的情況,如在摺積或迴圈神經網路中,在全連線層之前,需要把矩陣展平。這節介紹幾種資料合併和展平的方法。

 

1. 合併一維陣列

 

import numpy as np
a=np.array([1,2,3])
b=np.array([4,5,6])
c=np.append(a,b)
print(c)
#或利用concatenate
d=np.concatenate([a,b])
print(d)

 

列印結果:

 

[1 2 3 4 5 6]
[1 2 3 4 5 6]

2. 多維陣列的合併

 

import numpy as np
a=np.arange(4).reshape(2,2)
b=np.arange(4).reshape(2,2)
#按行合併
c=np.append(a,b,axis=0)
print(c)
print("合併後資料維度",c.shape)
#按列合併
d=np.append(a,b,axis=1)
print("按列合併結果:")
print(d)
print("合併後資料維度",d.shape)

 

列印結果:

 

[[0 1]
 [2 3]
 [0 1]
 [2 3]]
合併後資料維度 (4, 2)
按列合併結果:
[[0 1 0 1]
 [2 3 2 3]]
合併後資料維度 (2, 4) 

 

3. 矩陣展平

 

import numpy as np
nd15=np.arange(6).reshape(2,-1)
print(nd15)
#按照列優先,展平。
print("按列優先,展平")
print(nd15.ravel('F'))
#按照行優先,展平。
print("按行優先,展平")
print(nd15.ravel())

 

列印結果:

 

[[0 1 2]
 [3 4 5]]
按列優先,展平
[0 3 1 4 2 5]
按行優先,展平
[0 1 2 3 4 5]

 

 

05 通用函式

 

NumPy提供了兩種基本的物件,即ndarray和ufunc物件。前面我們對ndarray做了簡單介紹,本節將介紹它的另一個物件ufunc。

ufunc(通用函式)是universal function的縮寫,它是一種能對陣列的每個元素進行操作的函式。許多ufunc函式都是在C語言級別實現的,因此它們的計算速度非常快。

此外,功能比math模組中的函式更靈活。math模組的輸入一般是標量,但NumPy中的函式可以是向量或矩陣,而利用向量或矩陣可以避免迴圈陳述句,這點在機器學習、深度學習中經常使用。以下為NumPy中的常用幾個通用函式:

  • sqrt:計算序列化資料的平方根

  • sin,cos三角函式

  • abs計算序列化資料的絕對值

  • dot矩陣運算

  • log,log10,log2對數函式

  • exp指數函式

  • cumsum,cumproduct累計求和,求積

  • sum對一個序列化資料進行求和

  • mean計算均值

  • median計算中位數

  • std計算標準差

  • var計算方差

  • corrcoef計算相關係數

 

1. 使用math與numpy函式效能比較

 

import time
import math
import numpy as np
x = [i * 0.001 for i in np.arange(1000000)]
start = time.clock()
for i, t in enumerate(x):
    x[i] = math.sin(t)
print ("math.sin:", time.clock() - start )

x = [i * 0.001 for i in np.arange(1000000)]
x = np.array(x)
start = time.clock()
np.sin(x)
print ("numpy.sin:", time.clock() - start )

 

列印結果:

 

math.sin: 0.5169950000000005
numpy.sin: 0.05381199999999886

 

由此可見,numpy.sin比math.sin快近10倍。

 

2. 使用迴圈與向量運算比較

 

充分使用Python的NumPy庫中的內建函式(built-in function),實現計算的向量化,可大大提高執行速度。NumPy庫中的內建函式使用了SIMD指令。例如下麵所示在Python中使用向量化要比使用迴圈計算速度快得多。

 

import time
import numpy as np

x1 = np.random.rand(1000000)
x2 = np.random.rand(1000000)
##使用迴圈計算向量點積
tic = time.process_time()
dot = 0
for i in range(len(x1)):
    dot+= x1[i]*x2[i]
toc = time.process_time()
print ("dot = " + str(dot) + "\n for loop----- Computation time = " + str(1000*(toc - tic)) + "ms")
##使用numpy函式求點積
tic = time.process_time()
dot = 0
dot = np.dot(x1,x2)
toc = time.process_time()
print ("dot = " + str(dot) + "\n verctor version---- Computation time = " + str(1000*(toc - tic)) + "ms")

 

列印結果:

 

dot = 250215.601995
 for loop----- Computation time = 798.3389819999998ms
dot = 250215.601995
 verctor version---- Computation time = 1.885051999999554ms

 

從程式執行結果上來看,該例子使用for迴圈的執行時間是使用向量運算的執行時間的約400倍。因此,深度學習演演算法中,一般都使用向量化矩陣運算。

 

 

06 廣播機制

 

廣播機制(Broadcasting)的功能是為了方便不同shape的陣列(NumPy庫的核心資料結構)進行數學運算。廣播提供了一種向量化陣列操作的方法,以便在C中而不是在Python中進行迴圈,這通常會帶來更高效的演演算法實現。廣播的相容原則為:

 

  • 對齊尾部維度。

  • shape相等or其中shape元素中有一個為1。

 

以下透過實體來具體說明。

 

import numpy as np
a=np.arange(10)
b=np.arange(10)
#兩個shape相同的陣列相加
print(a+b)
#一個陣列與標量相加
print(a+3)
#兩個向量相乘
print(a*b)

#多維陣列之間的運算
c=np.arange(10).reshape([5,2])
d=np.arange(2).reshape([1,2])
#首先將d陣列進行複製擴充為[5,2],如何複製請參考圖1-2,然後相加。
print(c+d)

▲圖1-2 NumPy多維陣列相加

 

列印結果:

 

[ 0  2  4  6  8 10 12 14 16 18]
[ 3  4  5  6  7  8  9 10 11 12]
[ 0  1  4  9 16 25 36 49 64 81]
[[ 0  2]
 [ 2  4]
 [ 4  6]
 [ 6  8]
 [ 8 10]]

 

有時為了保證矩陣運算正確,我們可以使用reshape()函式來變更矩陣的維度。

 

 

07 小結

 

閱讀完本文,你已get到如下技能:

 

√ 如何生成NumPy的ndarray的幾種方式。

√ 如何存取元素。

√ 如何操作矩陣。

√ 如何合併或拆分資料。

√ NumPy的通用函式。

√ NumPy的廣播機制。

 

如果想進一步瞭解NumPy,大家可參考:

http://www.numpy.org/

 

關於作者:吳茂貴,BI和大資料專家,就職於中國外匯交易中心,在BI、資料挖掘與分析、資料倉庫、機器學習等領域有超過20年的工作經驗,在Spark機器學習、TensorFlow深度學習領域大量的實踐經驗。

王冬,任職於博世(中國)投資有限公司,負責Bosch企業BI及工業4.0相關大資料和資料挖掘專案。對機器學習、人工智慧有多年實踐經驗。

李濤,參與過多個人工智慧專案,如研究開發服務機器人、無人售後店等專案。熟悉python、caffe、TensorFlow等,對深度學習、尤其對計算機視覺方面有較深理解。

楊本法,高階演演算法工程師,在機器學習、文字挖掘、視覺化等領域有多年實踐經驗。熟悉Hadoop、Spark生態圈的相關技術,對Python有豐富的實戰經驗。

本文摘編自《Python深度學習:基於TensorFlow》,經出版方授權釋出。

延伸閱讀《Python深度學習:基於TensorFlow

點選上圖瞭解及購買

轉載請聯絡微信:DoctorData

推薦語:從Python和數學,到機器學習和TensorFlow,再到深度學習的應用和擴充套件,為深度學習提供全棧解決方案。 

贊(0)

分享創造快樂