【Numpy】數值計算工具Numpy的基本操作
- 2020 年 2 月 21 日
- 筆記
1、Numpy簡介
(1)Scipy庫簡介 Scipy庫是基於Python生態的一款開源數值計算、科學與工程應用的開源軟體,包括常用的Numpy、pandas、matplotlib等庫。 Scipy庫官網:http://www.scipy.org/。 (2)Numpy 官網參考文檔:https://docs.scipy.org/doc/numpy-dev/user/index.html。 Numpy:Numerical Python,即數值Python包,是Python進行科學計算的一個基礎包,因此要更好理解和掌握Python科學計算包,尤其是pandas,需要先行掌握Numpy庫的用法。 (3)Numpy包括的內容 Numpy系統是Python的一種開源的數值計算擴展,是一個用python實現的科學計算包。包括: 1)一個具有矢量算術運算和複雜廣播能力的快速且節省空間的多維數組,稱為ndarray(N-dimensional array object)。 2)用於對整組數據進行 快速運算的標準數學函數, ufunc (universal function object)。 3)用於整合C/C++和Fortran程式碼的工具包。 4)實用的線性代數、傅里葉變換和隨機數生成函數。numpy和稀疏矩陣運算包scipy配合使用更加方便。 (4)Numpy發展歷史 1)1995年 Jim HugUNin開發了Numeric。 2)隨後,Numarray包誕生。 3)Travis Oliphants整合Numeric和Numarray,開發Numpy,於2006年發布第一個版本。 4)Numpy(Numeric Python)提供了許多高級的數值編程工具,如:矩陣數據類型、矢量處理,以及精密的運算庫。專為進行嚴格的數字處理而產生。多為很多大型金融公司使用,以及核心的科學計算組織如:Lawrence Livermore,NASA用其處理一些本來使用C++,Fortran或Matlab等所做的任務。 5)使用Anaconda發行版的Python,已經幫我們事先安裝好了Numpy模組,因此無需另外安裝。 6)依照標準的Numpy約定,習慣使用 import numpy as np方式導入該模組。
2、Numpy的數據結構:ndarray,一種多維數組對象
(1)ndarray介紹 ndarray:N-dimensional array, N維數組。一種由相同類型的元素組成的多維數組,元素數量是事先指定好的。元素的數據類型由dtype(data-type)對象來指定,每個ndarray只有一種dtype類型。ndarray大小固定,創建好數組時一旦指定好大小,就不會再發生改變。 (2)ndarray屬性 ndim:維度數量。 shape:一個表示各維度大小的元組,即數組的形狀。 dtype:一個用於說明數組元素數據類型的對象。 size:元素總個數,即shape中各數組相乘。
>> import numpy as np >> a = np.array([ [ [3.4, 5, 6, 8], [3, 2.4, 5, 7] ], [ [2.3, 4, 5, 6], [0.9, 5, 6, 1] ], [ [9, 6.7, 3, 2], [1, 3, 4, 5] ] ]) >> a.ndim # ndarray數組維數 3 >> a.dtype # ndarray數組元素數據類型 dtype('float64') >> a.shape # ndarray數組形狀 (3L, 2L, 4L) >> a.size # ndarray數組元素總個數 24ndarray的shape屬性計算:先規範顯示出數組,然後層層去中括弧對,直到無中括弧對,每去一層,一個維度,去掉的一層 [ ]中的元素個數(逗號隔開)即該維度的元素個數。 (3)ndarray的常見創建方式 array函數:接收一個普通的Python序列,轉成ndarray。
>> a = np.array([ [ [3.4, 5, 6, 8], [3, 2.4, 5, 7] ], [ [2.3, 4, 5, 6], [0.9, 5, 6, 1] ], [ [9, 6.7, 3, 2], [1, 3, 4, 5] ] ]) >> b = np.array(( ( (1, 2, 3, 4), (4, 5, 6, 7), (9, 3, 4, 5) ), ( (0, 2, 3, 4), (4, 5, 6, 7), (4, 5, 2, 3) ) )) >> c = np.array([ ( (3.4, 4.5, 6, 1), (2.0, 3, 4, 1), (3, 5.6, 3, 1), (0.9, 2, 3, 1) ), ( (1.2, 3, 4, 5), (4.5, 6, 7, 2), (9.8, 2, 3, 1), (2.3, 4, 5, 7) ) ])
zeros函數:創建指定長度或形狀的全零數組。
>> np.zeros((3, 4)) array([[0., 0., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.]])
ones函數:創建指定長度或形狀的全1數組。
>> np.ones((4, 6)) array([[1., 1., 1., 1., 1., 1.], [1., 1., 1., 1., 1., 1.], [1., 1., 1., 1., 1., 1.], [1., 1., 1., 1., 1., 1.]])
empty函數:創建一個沒有任何具體值的數組(準確地說是一些未初始化的垃圾值)。
>> np.empty((2, 3, 4)) array([[[0., 0., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.]], [[0., 0., 0., 0.], [0., 0., 0., 0.], [0., 0., 0., 0.]]])
arange函數:類似於python的range函數,通過指定開始值、終值和步長來創建一維數組,注意數組不包括終值。
>> np.arange(20) array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]) >> np.arange(0, 20, 1) array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]) >> np.arange(0, 20, 2) array([0, 2, 4, 6, 8, 10, 12, 14, 16, 18]) >> np.arange(0, 12).reshape(3, 4) array([[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11]])
linspace函數:通過指定開始值、終值和元素個數來創建一維數組,可以通過endpoint關鍵字指定是否包括終值,預設設置是包括終值。
>> np.linspace(0, 10, 5) array([0., 2.5, 5., 7.5, 10.])
logspace函數:和linspace類似,不過它創建等比數列。
>> np.logspace(0, 2, 5) array([1., 3.16227766, 10., 31.6227766, 100.])
使用隨機數填充數組,即使用numpy.random模組的random()函數,數組所包含的元素數量由參數決定。
>> np.random.random((2, 2, 4)) array([[[0.37103481, 0.20852992, 0.06803338, 0.01894039], [0.39276509, 0.72441318, 0.94296218, 0.37620134]], [[0.01425183, 0.64108487, 0.38092613, 0.969736], [0.09067193, 0.26279249, 0.20117111, 0.79920612]]])
(4)Numpy中的數據類型 創建Numpy數組時可以通過dtype屬性顯式指定數據類型,如果不指定,Numpy會自己推斷出合適的數據類型,所以一般無需顯式指定。astype方法可以轉換數組的元素數據類型,得到一個新數組。
>> ndarray01 = np.array([1, 2, 3, 4]) >> ndarray01.dtype dtype('int32') >> ndarray02 = ndarray01.astype(float) >> ndarray02.dtype dtype('float64')數值型dtype的命名方式:一個類型名(比如int、float),後面接著一個用於表示各元素位長的數字,比如表中的雙精度浮點值,即Python中的float對象,需要佔用8個位元組(即64位),因此該類型在Numpy中就記為float64。 每種類型有一個相對應的類型程式碼,即簡寫方式,參照下面的表格。
數據類型 | 類型程式碼 | 說明 |
|---|---|---|
int_ | 默認整型(與C中的long相同,通常為int64或者int32) | |
intc | 完全等同於C中的long(通常為int64或者int32) | |
intp | 表示索引的整型,與C中的size_t相同,通常為int64或者int32 | |
int8 | i1 | 位元組(-128 ~ 127),1個位元組 |
int16 | i2 | 整型(-32768 ~ 32767),2個位元組 |
int32 | i4 | 整型(-2147483648 ~ 2147483647),4個位元組 |
int64 | i8 | 整型(- 9223372036854775808~9223372036854775807),8位元組 |
uint8 | u1 | 無符號整型(0~255) |
uint16 | u2 | 無符號整型(0~65535) |
uint32 | u4 | 無符號整型(0~4294967295) |
uint64 | u8 | 無符號整型(0 ~ 18446744073709551615) |
float_ | float64的簡寫形式 | |
float16 | f2 | 半精度浮點型:符號位,5位指數,10位小數部分 |
float32 | f4或者f | 單精度浮點型:符號位,8位指數,23位小數部分 |
float64 | f8或者d | 雙精度浮點型:符號位,11位指數,52位小數部分 |
float128 | f16或者g | 擴展精度浮點數 |
complex_ | c16 | complex128的簡寫形式 |
complex64 | c8 | 複數,由兩個32位的浮點數來表示(實數部分和虛數部分) |
complex128 | c16 | 複數,由兩個64位的浮點數來表示(實數部分和虛數部分) |
bool_ | 以一個位元組形式存儲的布爾值(True或者False) | |
bool | ? | 存儲True和False值的布爾類型 |
object | O | Python對象類型 |
String_ | S | 固定長度的字元串類型(每個字元1個位元組)。例如要創建一個字元長度為8的字元串,應使用S8 |
Unicode_ | U | 固定長度的unicode類型(位元組數由平台決定),跟字元串的定義方式一樣,比如U20 |
>> d = np.array(["Python", "Scala", "Java", "C#"]) >> d array(['Python', 'Scala', 'Java', 'C#'], dtype='|S6') >> d.dtype dtype('S6') >> e = np.array(["Python", "Scala", "Java", "C++"], dtype=np.string_) >> e array(['Python', 'Scala', 'Java', 'C++'], dtype='|S6') >> e = np.array(["Python", "Scala", "Java", "C++"], dtype='S8') >> e array(['Python', 'Scala', 'Java', 'C++'], dtype='|S8')(5)改變ndarray的形狀 直接修改ndarray的shape值可以改變ndarray的形狀。 使用reshape函數也改變ndarray的形狀,可以創建一個改變了尺寸的新數組,原數組的shape保持不變,但注意他們共享記憶體空間,因此修改任何一個也對另一個產生影響,因此注意新數組的元素個數必須與原數組一樣。 當指定新數組某個軸的元素為-1時,將根據數組元素的個數自動計算此軸的長度。
>> a = np.arange(0, 20, 2) >> print a >> print a.size [0 2 4 6 8 10 12 14 16 18] 10 >> a.reshape(2, 5) array([[0, 2, 4, 6, 8], [10, 12, 14, 16, 18]]) >> a.reshape(-1, 5) array([[0, 2, 4, 6, 8], [10, 12, 14, 16, 18]]) >> b = a.reshape(2, -1) >> b array([[0, 2, 4, 6, 8], [10, 12, 14, 16, 18]]) >> b.shape() (2L, 5L) >> b.shape = 5, -1 >> b array([[0, 2], [4, 6], [8, 10], [12, 14], [16, 18]])
3、Numpy基本操作
(1)數組與標量的運算、數組之間的運算 數組不用循環即可對每個元素執行批量運算,這通常就叫做矢量化,即用數組表達式代替循環的做法。 矢量化數組運算性能要比純Python方式快上一兩個數量級。 大小相等的數組之間的任何算術運算都會將運算應用到元素級。 數組與標量之間的運算:
>> import numpy as np >> arr1 = np.array([1, 2, 3, 4, 5]) >> arr1 + 2 array([3, 4, 5, 6, 7]) >> arr1 - 2 array([-1, 0, 1, 2, 3]) >> arr1 * 2 array([2, 4, 6, 8, 10]) >> 1/arr1 array([1, 0, 0, 0, 0]) >> from __future__ import division >> 1/arr1 array([1., 0.5, 0.33333333, 0.25, 0.2]) >> arr1 ** 2 array([1, 4, 9, 16, 25])
大小相等的數組之間的運算:
>> arr1 = np.array([[1, 2.0], [1.9, 3.4]]) >> arr2 = np.array([[3.6, 1.2], [2.0, 1.2]]) >> arr1 + arr2 array([[4.6, 3.2], [3.9, 4.6]]) >> arr1 - arr2 array([[-2.6, 0.8], [-0.1, 2.2]]) >> arr1 * arr2 array([[3.6, 2.4], [3.8, 4.08]]) >> arr1 / arr2 array([[0.27777778, 1.66666667], [0.95, 2.83333333]])
像上面例子展現出來的,加、減、乘、除、冪運算等,可以用於數組與標量、大小相等數組之間。在Numpy中,大小相等的數組之間運算為元素級運算,即只用於位置相同的元素之間,所得到的運算結果組成一個新的數組,運算結果的位置跟操作數位置相同。 (2)數組的矩陣積(matrix product) 兩個二維矩陣(多維數組即矩陣)滿足第一個矩陣的列數與第二個矩陣的行數相同,那麼可以進行矩陣乘法,即矩陣積,矩陣積不是元素級的運算。 兩個矩陣相乘結果所得到的的數組中每個元素為第一個矩陣中與該元素行號相同的元素與第二個矩陣中與該元素列號相同的元素,兩兩相乘後求和。
>> arr1 = np.array([ [120, 60, 220], [115, 23, 201], [132, 48, 230] ]) >> arr2 = np.array([ [12.34, 0.04], [204.56, 2.34], [9.89, 0.45] ]) >> arr1.dot(arr2) array([[15930.2, 244.2], [8111.87, 148.87], [13722.46, 221.1]]) >> np.dot(arr1, arr2) array([[15930.2, 244.2], [8111.87, 148.87], [13722.46, 221.1]])
(3)多維數組的索引
>> arr = np.array([ [ [2, 3, 4, 5], [1, 3, 4, 9] ], [ [0, 3, 4, 8], [2, 4, 9, 4] ], [ [1, 4, 5, 8], [2, 5, 6, 8] ], [ [2, 3, 6, 8], [3, 4, 8, 9] ] ]) >> arr.shape (4L, 2L, 4L) >> arr[3] array([[2, 3, 6, 8], [3, 4, 8, 9]]) >> arr[3][1] array([3, 4, 8, 9]) >> arr[3][1][2] 8 >> arr[3, 1, 2] 8
(4)Numpy中數組的切片 在各維度上單獨切片,如果某維度都保留,則直接使用 : 冒號,不指定起始值和終止值。
>> arr array([[[2, 3, 4, 5], [1, 3, 4, 9]], [[0, 3, 4, 8], [2, 4, 9, 4]], [[1, 4, 5, 8], [2, 5, 6, 8]], [[2, 3, 6, 8], [3, 4, 8, 9]]]) >> arr[1] array([[0, 3, 4, 8], [2, 4, 9, 4]]) >> arr[1][0] array([0, 3, 4, 8]) >> arr[1][0][1:3] array([3, 4]) >> arr[1][1][1:3] array([4, 9]) >> arr[1, :, 1:3] array([[3, 4], [4, 9]])
注意Numpy中通過切片得到的新數組,只是原來數組的一個視圖,因此對新數組進行操作也會影響原數組。 (5)布爾型索引
>> A = np.random.random((2, 2)) >> A array([[0.42349554, 0.52963875], [0.86297436, 0.12893687]]) >> A < 0.5 array([[True, False], [False, True]]) >> A[A < 0.5] # True的位置上的元素取出組成一個新的數組(一維) array([0.42349554, 0.12893687]) >> names = np.array(['Merry', 'Tom', 'John']) >> names array(['Merry', 'Tom', 'John'], dtype='|S5') >> names = np.array('Tom', 'Merry') >> names == 'Tom' array([True, False], dtype=bool) >> scores = np.array([ [98, 87, 45, 34], [88, 45, 23, 98] ]) >> scores[names == 'Tom'] array([[98, 87, 45, 34]]) >> scores[names == 'Tom', 2] array([45]) >> names array(['Tom', 'John', 'Merry', 'Jill'], dtype='|S5') >> scores array([[98, 97, 67, 99], [96, 87, 78, 65], [88, 87, 67, 66], [86, 84, 82, 78]]) >> scores[~((names == 'Tom')|(names == 'Jill'))] array([[96, 87, 78, 65], [88, 87, 67, 66]]) >> scores[(names != 'Tom')&(names != 'Jill')] array([[96, 87, 78, 65], [88, 87, 67, 66]])(6)花式索引 花式索引(Fancy indexing)指的是利用整數數組進行索引。
>> 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[[0, 3, 5]] array([[0, 1, 2, 3], [12, 13, 14, 15], [20, 21, 22, 23]]) >> arr[[0, 3, 5], [0, 3, 2]] array([0, 15, 22]) >> arr[np.ix_([0, 3, 5], [0, 2, 3])] # ix_函數產生一個索引器 array([[0, 2, 3], [12, 14, 15], [20, 22, 23]])
(7)數組轉置與軸對換 transpose函數用於數組轉置,對於二維數組來說就是行列互換。 數組的T屬性,也是轉置。
>> 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.transpose() array([[0, 4, 8, 12, 16, 20, 24, 28], [1, 5, 9, 13, 17, 21, 25, 29], [2, 6, 10, 14, 18, 22, 26, 30], [3, 7, 11, 15, 19, 23, 27, 31]]) >> arr.T array([[0, 4, 8, 12, 16, 20, 24, 28], [1, 5, 9, 13, 17, 21, 25, 29], [2, 6, 10, 14, 18, 22, 26, 30], [3, 7, 11, 15, 19, 23, 27, 31]])
(8)通用函數:快速的元素級數組函數 ufunc:一種對ndarray中的數據執行元素級運算的函數,也可以看做是簡單函數(接受一個或多個標量值,併產生一個或多個標量值)的矢量化包裝器。
>> arr = np.arange(10).reshape(2, -1) >> arr array([[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]]) >> np.sqrt(arr) array([[0., 1., 1.41421356, 1.73205081, 2.], [2.23606798, 2.44948974, 2.64575131, 2.82842712, 3.]])
常見的一元通用函數如下:
一元ufunc | 說明 |
|---|---|
abs,fabs | 計算整數、浮點數或複數的絕對值。對於非複數值,可以使用更快的fabs |
sqrt | 計算各元素的平方根,相當於 arr ** 0.5 |
square | 計算各元素的平方,相當於arr ** 2 |
exp | 計算各元素的指數e的x次方 |
log, log10, log2, log1p | 分別為自然對數、底數是10的log,底數為2的log,log(1+x) |
sign | 計算各元素的正負號:1 正數,0 零 , -1 負數 |
cell | 計算各元素的ceiling值,即大於等於該值的最小整數 |
floor | 計算各元素的floor值,即小於等於該值的最大整數 |
rint | 將各元素值四捨五入到最接近的整數,保留dtype |
modf | 將數組的小數位和整數部分以兩個獨立數組的形式返回 |
isnan | 返回一個表示「哪些值是NaN(不是一個數字)」的布爾類型數組 |
isfinite,isinf | 分別返回一個表示「哪些元素是有窮的(非inf,非NaN)」或「哪些元素是無窮的」的布爾型數組 |
cos、cosh、sin、sinh、tan、tanh | 普通型和雙曲型三角函數 |
arccos,arccosh,arcsin、arctan、arctanh | 反三角函數 |
logical_not | 計算各元素not x的真值,相當於 ~ 和 -arr |
常見的二元通用函數如下:
二元ufunc | 說明 |
|---|---|
add | 將數組中相同位置對應的元素相加 |
substract | 從第一個數組中減去第二個數組中的元素 |
multiply | 數組元素相乘 |
divide、floor_divive | 除法或者向下圓整除法(丟棄餘數) |
pow | 對第一個數組中的元素A,根據第二個數組中的相應元素B,計算A的B次方 |
maximum、fmax | 元素級別的最大值,fmax會忽略NaN |
minimum、fmin | 元素級別的最小值,fmin會忽略NaN |
mod | 元素級的求模(除法的餘數) |
copysign | 將第二個數組中的值的符號複製給第一個數組中對應位置的值 |
greater、greater_equal、less、less_equal、equal、not_equal | 執行元素級別的比較運算,最終產生布爾型數組 |
logical_and、logical_or、logical_xor | 執行元素級別的布爾邏輯運算,相當於中綴運算符&、|、^ |
(9)聚合函數 聚合函數是對一組值(比如一個數組)進行操作,返回一個單一值作為結果的函數。因此求數組所有元素之和、求所有元素的最大最小值以及標準差的函數就是聚合函數。
>> arr = np.array([1.0, 2.0, 3.0, 4.0]) >> arr.max() 4.0 >> arr.min() 1.0 >> arr.mean() 2.5 >> arr.std() 1.1180339887498949 >> np.sqrt(np.power(arr - arr.mean(), 2).sum() / arr.size) # 計算標準差 1.1180339887498949
聚合函數可以指定對數值的某個軸元素進行操作。
>> arr array([[1, 2, 3, 4], [3, 4, 5, 6]]) >> arr.mean(axis=0) # 對同一列上的元素進行聚合 array([2., 3., 4., 5.]) >> arr.mean(axis=1) # 對同一行上的元素進行聚合 array([2.5, 4.5]) >> arr.sum(axis=0) array([4, 6, 8, 10]) >> arr.max(axis=1) array([4, 6]) >> arr.std(axis=0) array([1., 1., 1., 1.])
(10)np.where函數 np.where函數是三元表達式 x if condition else y的矢量化版本。
>> xarr = np.array([1.1, 1.2, 1.3, 1.4, 1.5]) >> yarr = np.array([2.1, 2.2, 2.3, 2.4, 2.5]) >> condition = np.array([True, False, True, True, False]) >> result = [(x if c else y) for x, y, c in zip(xarr, yarr, condition)] >> result [1.1000000000000001, 2.2000000000000002, 1.3, 1.3999999999999999, 2.5] >> result = np.where(condition, xarr, yarr) >> result [1.1000000000000001, 2.2000000000000002, 1.3, 1.3999999999999999, 2.5]
將數組中所有NaN缺失值替換為0。
>> arr = np.array([[1, 2, np.NaN, 4], [3, 4, 5, np.NaN]]) >> arr array([[1., 2., nan, 4.], [3., 4., 5., nan]]) >> np.isnan(arr) array([[False, False, True, False], [False, False, False, True]], dtype=bool) >> np.where(np.isnan(arr), 0, arr) array([[1., 2., 0., 4.], [3., 4., 5., 0.]])
(11)np.unique函數 求數組中不重複的元素。
>> productTypes = np.array(['圖書', '數碼', '小吃', '美食', '男裝', '美食', '女裝', '小吃']) >> productTypes array(['xe5x9bxbexe4xb9xa6', 'xe6x95xb0xe7xa0x81', 'xe5xb0x8fxe5x90x83', 'xe7xbex8exe9xa3x9f', 'xe7x94xb7xe8xa3x85', 'xe7xbex8exe9xa3x9f', 'xe5xa5xb3xe8xa3x85', 'xe5xb0x8fxe5x90x83'], dtype='|S6') >> np.unique(productTypes) array(['xe5x9bxbexe4xb9xa6', 'xe6x95xb0xe7xa0x81', 'xe5xb0x8fxe5x90x83', 'xe7xbex8exe9xa3x9f', 'xe7x94xb7xe8xa3x85', 'xe5xa5xb3xe8xa3x85'], dtype='|S6') >> for pt in np.unique(productTypes): print pt 圖書 數碼 小吃 美食 男裝 女裝
4、數組數據文件讀寫
(1)將數組以二進位格式保存到磁碟
>> data array([[1, 2, 3, 4], [2, 3, 4, 5], [6, 7, 8, 9], [2, 4, 5, 6]]) >> np.save('data', data) >> np.load('data.npy') array([[1, 2, 3, 4], [2, 3, 4, 5], [6, 7, 8, 9], [2, 4, 5, 6]])(2)存取文本文件
>> x = np.arange(0.0, 5.0, 1.0) >> np.savetxt('data.csv', x, delimiter=',') >> np.loadtxt('data.csv', delimiter=',') array([0., 1., 2., 3., 4.]) >> np.genfromtxt('data.csv', delimiter=',') array([0., 1., 2., 3., 4.])
