前言
NUMPY(以下簡稱NP)是Python資料分析必不可少的第三方庫,np的出現一定程度上解決了Python運算效能不佳的問題,同時提供了更加精確的資料型別。如今,np被Python其它科學計算包作為基礎包,已成為Python 資料分析的基礎,可以說,NP是SciPy、Pandas等資料處理或科學計算庫最基本的函式功能庫。因此,理解np的資料型別對python資料分析十分有幫助。
下面,本文將介紹Np的常用操作和基本資料型別。
NP提供了以下重點功能。
- 一個強大的N維陣列物件ndarray
- 廣功能函式
- 整合C/C++/Fortran程式碼的工具
- 提供了線性代數、傅立葉變換、隨機數生成的相關功能
為了更加直觀的瞭解Np的強大與作用,我們先看作用再看方法:
使用NUMPY運算元據集
在運算元據之前,我們先來理解什麼是維度:
什麼是維度
維度是一組資料的組織形式,不同資料維度可能表示不同的含義。一維資料由對等關係的有序或無序資料構成,採用線性方式組織,可以用陣列表示。
通俗來講,
1,2,3,4
這麼一行資料就可以稱之為一維資料,但如果我們再對其摺疊:
1,2,
3,4
那麼他就成為了二維資料,又可以稱之為矩陣。
什麼是資料集
資料集,顧名思義就是資料的集合,是用以訓練程式的資料集合,一般是二維或者多維數表。
如果我們想自己手工新建一個資料集,可以直接新建一個文字檔案,只要有恰當的資料,都可以稱之為資料集:
1 2 3 4 |
城市,環比,同比,定基 北京,100.1,100.2,100.3 上海,111.1,111.2,111.3 南京,133.0,133.3,133.4 |
比如這樣,我們就可以稱上面的檔案稱之為資料集。我們還注意到,上面資料是使用逗號作為分隔符分隔資料的,它簡單描述了資料的內容和含義,並使用半形逗號作為分隔符。像這樣,用逗號分隔的資料集就稱之為CSV(Comma-Separated Value,逗號分隔值)資料集,它是一種常見的檔案格式,用來儲存批量的資料。它就像一張excel表,用來儲存簡單結構的資料。
怎麼樣,資料集的概念是否特別簡單呢?
生成資料集
資料集是一個簡單的概念,但每次使用手工的方式去寫畢竟不方便,所以,我們可以使用np的內建函式來生成資料集:
1 |
np.savetxt(frame,array,fmt='%.18e",delimiter=None) |
- frame:檔案、字串、或產生器的名字,可以是.gz,.bz2的壓縮檔案
- arrray:存入檔案的NP的陣列
- fmt(format):寫入檔案的格式,如%d,%.2f,%.18e(預設,科學計數法保留18位)
- delemiter:分割字串,預設是任何空格。
我們可以這樣寫下程式碼:
1 2 |
a= np.arange(20).reshape(4,5) np.savetxt('demo.csv',a,fmt='%d',delimiter=',') |
這樣,我們就會在當前的工作目錄下發現一個新的demo.csv,用記事本開啟,裡面是一個4 * 5的矩陣,元素0~19。
讀取資料集
既然生成,那就可以讀取,同樣使用np:
1 |
np.loadtxt(frame,dtype=np.float,delimiter=None,inpack=False) |
- frame:指定讀入的檔案來源
- dtype:資料型別,預設為np.float。
- delimiter:分割字串
- unpack:預設為False讀入檔案寫入一個陣列,如果為True,讀入屬性將分別寫入不同變數
同樣的我們只需要寫下程式碼:
1 |
np.loadtxt("demo.csv",delimiter=",") |
就可以檢視到我們先前寫入的陣列a。
CSV檔案的侷限
可以發現,CSV檔案只能有效儲存和讀取一維和二維陣列,因為更高的維度無法更直觀的文字下顯現出來,這時,更加靈活的存取方式就呼之欲出了,但講之前先賣個關子,再介紹一個不太常用的方法:
tofile:對於NP中的ndarray陣列,我們可以使用NP中的tofile方法。
1 |
a.tofile(frame,sep='',format='%d') |
- frame:檔案,字串
- 資料分割字串,如果不寫,將使用二進位制檔案儲存
- format:寫入資料的格式
同樣,我們只需要命令:
1 2 3 |
import numpy as np a = np.arange(100).reshape(5,10,2) a.tofile("a.dat",sep=',',format='%d') |
就可以生成新的CSV資料集。
此時,我們如果開啟a.dat檔案,我們可以看到陣列1,2,3……99。但是與CSV不同,這個檔案並沒有包含數字的維度資訊,他只是將陣列所有元素逐一的列出。而且如果我們不指定sep,將儲存為二進位制檔案,雖然對人不可讀,但將佔用更小的空間。
既然tofile可以儲存文字檔案,那麼也很容易猜到對應的fromfile可以還原這些資訊。
1 |
np.fromfile(frame,dtype=float,count=-1,sep='') |
- frame:檔案
- dtype:讀取元素使用的資料型別,預設為float
- count:讀檔案的個數,預設-1,讀取全部
- sep:資料分割字串,如果是空串,寫入檔案為二進位制。
如果我們想要重新恢復資料的維度資訊,我們需要重新使用reshape來恢復維度資訊:
1 |
c = np.fromfile("b.dat",sep=',',dtype=np.int).reshape(5,10,2) |
不得不說,當我看到這個方法時感覺這兩個真是蠢爆了,使用savetxt / loadtxt 至少還能儲存個二維資訊,而使用了tofile / fromfile 方法居然把數被伸展為一維的,然後自己記住維度資訊(╯‵□′)╯︵┻━┻。
因此,為了儲存更復雜的資料型別,二維以上的資料資訊,save / load 函式成功解決了這個問題:(為了方便,兩個函式就放到一起了)
儲存 / 讀取高維度資料
1 2 3 4 |
np.save(frame,array)或np.savez(fname,array)(壓縮) + frame:檔名,以.npy為副檔名,壓縮副檔名為.npz + array:陣列變數 np.load(fname) |
Demo:
1 2 3 |
a = np.arange(100).reshape(5,10,2) np.save("a.npy",a) b=np.load("a.npy") |
附錄
附錄中提供NP的常用方法及註釋,做查詢用。
np陣列定義
1 2 3 4 5 6 7 8 9 10 11 12 |
>>>lst = [[1,3,5],[2,4,6]] >>>np_lst = np.array(lst,dtype=np.float) >>>print(np_lst.shape)#返回陣列的行列 >>>print(np_lst.ndim)#返回陣列的維數 >>>print(np_lst.dtype)#返回資料型別,float預設為64 >>>print(np_lst.itemsize)#np.array每個元素的大小,float64佔8個位元組 >>>print(np_lst.size)#大小,6個元素 (2, 3) 2 float64 8 6 |
初始化陣列
1 2 3 4 5 6 |
>>>print(np.zeros([2,4])#初始化一個2行4列的陣列 >>>print(np.ones([2,4]) [[ 0. 0. 0. 0.] [ 0. 0. 0. 0.]] [[ 1. 1. 1. 1.] [ 1. 1. 1. 1.]] |
隨機序列
1 2 3 |
>>>print(np.random.rand(2,4))#將生成一個處於0~1之間2行4列的隨機數序列(不加引數將只返回一個) [[ 0.39531286 0.4845 0.1463168 0.82327991] [ 0.89042255 0.65049931 0.43890163 0.89577744]] |
如果想要多個隨機整數:
1 2 3 4 5 6 7 8 9 |
print(np.random.randint(22,55,3))#必須有(前兩個引數)指定範圍,第三個引數用於指定生成的個數 [27 40 29] print(np.random.randn(2,4))#生成標準正態隨機數 [[-1.15561548 0.3689953 0.38253231 -1.16346441] [-1.32625322 -0.41707673 -0.11822205 -0.95807535]] print(np.random.choice([10,20,40,33]))#從指定可迭代的陣列中生成隨機數 20 print(np.random.beta(1,10,4))#生成4個beta分佈 [ 0.02258548 0.25848896 0.00696899 0.0609543 ] |
多維陣列運算
1 2 |
print(np.arange(1,11,2))#得到step為2的range序列 [1 3 5 7 9] |
還可以使用reshape函式,對陣列結構重定義:
1 2 3 |
print(np.arange(1,11).reshape(2,5))#(5可以預設為-1) [[ 1 2 3 4 5] [ 6 7 8 9 10]] |
下面介紹一些常用的運算操作:
1 2 3 4 |
lst=np.arange(1,11).reshape(2,5) print(np.exp(lst))#自然指數操作 [[ 2.71828183e+00 7.38905610e+00 2.00855369e+01 5.45981500e+01 1.48413159e+02] [ 4.03428793e+02 1.09663316e+03 2.98095799e+03 8.10308393e+03 2.20264658e+04]] |
此外,還可以sqrt、log、sin、sum、max等操作:
我們下定義一個三維陣列:
1 2 3 4 5 6 7 |
lst = np.array([ [[1,2,3,4],[4,5,6,7]], [[7,8,9,10],[10,11,12,13]], [[14,15,16,17],[18,19,20,21]] ]) print(lst.sum()) 252 |
我們可以看到sum方法對lst的所有元素都進行了求和,此外我們還可以通過對sum方法增加引數axis的方式來設定求和的深入維度:
1 2 3 4 5 6 7 8 9 10 11 |
print(lst.sum(axis=0)) [[22 25 28 31]#22=1+7+14;25=2+8+15 [32 35 38 41]] print(lst.sum(axis=1)) [[ 5 7 9 11]#5=1+4;7=2+5 [17 19 21 23] [32 34 36 38]] print(lst.sum(axis=2)) [[10 22]#10=1+2+3+4;22=4+5+6+7 [34 46] [62 78]] |
這裡的axis取值為陣列維數-1,axis可以理解為進行運算操作時的深入程度,axis越大,深入程度越大。同理,不僅sum函式,max等函式也可以一樣理解。
相加運算
numpy.array是np最簡單的資料結構。np.array相比與Python原生列表其強大之處在於可以實現對陣列資料的運算。我們知道,list只能對元素的追加。而numpy是真正意義上的資料運算。
例如
1 2 3 4 5 6 7 |
In [1]: import numpy as np In [2]: list1 = np.array([10,20,30,40]) In [3]: list2 = np.array([4,3,2,1]) In [4]: print(list1) [10 20 30 40] In [5]: print(list1+list2) [14 23 32 41] |
但np最強大的地方不在於簡單的一維運算,Np對矩陣也能進行基本的運算操作:
1 2 3 4 5 6 7 8 |
lst1 =np.array([10,20,30,40]) lst2 = np.array([4,3,2,1]) print(np.dot(lst1.reshape([2,2]),lst2.reshape([2,2]))) [[10 22] [34 46] [62 78]] [[ 80 50] [200 130]] |
此外,由於原生list沒有確定的資料型別,所以維護起來成本較高,而使用C編寫的numpy,則可以宣告各種常見的資料型別:
1 2 |
lst = [[1,3,5],[2,4,6]] np_lst = np.array(lst,dtype=np.float) |
np所支援的資料型別都有bool、int8/16/32/64/128/、uint8/16/32/64/128、float16/32/43、complex64/128、string。
總結
Python作為一門弱型別語言,有其不可避免的缺點。但NP的出現,彌補了這些缺點,使其具備了構造複雜資料型別的能力,為Python資料分析提供了基礎。