Numpy 學習筆記


Numpy 基礎

  • Numpy 是 Python 科學計算的基礎,學會如何創建、讀取、更改向量數據。

創建向量有許多方法,舉例說明:

import numpy as np
print(np.array([2,3,4])) # 可以從列表轉換而來,np.array 會嘗試為數組推斷出一個較為合適的數據類型
[2 3 4]
print(np.zeros( (3,4) , dtype=np.int32))  # zeros 可以創建指定長度或形狀的全 0 數組
[[0 0 0 0]
 [0 0 0 0]
 [0 0 0 0]]
print(np.ones( (2,3,4), dtype=np.float ))  # ones 可以創建指定長度或形狀的全 1 數組,可以指定 dtype
[[[ 1.  1.  1.  1.]
  [ 1.  1.  1.  1.]
  [ 1.  1.  1.  1.]]

 [[ 1.  1.  1.  1.]
  [ 1.  1.  1.  1.]
  [ 1.  1.  1.  1.]]]
print(np.empty( (2,3) )) # empty 可以創建一個沒有任何具體值的數組,
[[ 0.  0.  0.]
 [ 0.  0.  0.]]
print(np.arange( 10, 30, 5 )) # arange 是 Python 內置函數 range 的數組版,第三個參數是步長
[10 15 20 25]
print(np.linspace( 0, 2, 9 )) # 同 arrange 類似,只是第三個參數表示要生成的數組元素個數
[ 0.    0.25  0.5   0.75  1.    1.25  1.5   1.75  2.  ]
np.zeros((2,3)) # 創建一個二維數組,注意要加括號
array([[ 0.,  0.,  0.],
       [ 0.,  0.,  0.]])
np.random.normal(10,3,(2,4)) # 二維數組元素來自於期望為 10,標准差為 3 的正態分布
array([[  9.05422825,  15.64840644,   8.10973337,  14.19151934],
       [  7.25610522,  13.53550744,   8.62793042,  16.15319148]])

np.logspace 這種比較少用的就不提了,感興趣的可自行查詢。隨機數生成的方法是比較常用的:

np.random.randn(5) # 從標准正態分布中生成 5 個元素
array([ 0.12789087,  0.82489732, -1.86115425,  0.50760795, -0.3306407 ])
norm10 = np.random.normal(10,3,5) # 從期望為 10,標准差為 3 的正態分布中生成 5 個元素
norm10
array([  9.60796679,  11.27292882,  10.80855564,  12.13837214,  10.25883022])
np.arange(8).reshape(2,4) # 一維數組可以通過 reshape 轉成二維數組
array([[0, 1, 2, 3],
       [4, 5, 6, 7]])

讀取向量:

arr = np.arange(10) # 一維數組
print(arr)
[0 1 2 3 4 5 6 7 8 9]
print(arr[2])
2
print(arr[2:4])
[2 3]
arr2d = np.array([[1,2,3],[4,5,6],[7,8,9]]) # 二維數組
print(arr2d[2])
[7 8 9]
print(arr2d[0][2])
3
print(arr2d[0, 2]) # 該訪問方式同上面是等價的
3
print(arr2d[arr2d<5]) # 還可以指定截取符合條件的數據,也叫邏輯索引
[1 2 3 4]

更改向量:

arr = np.arange(10)
arr[5:8] = 12  # 可以直接修改
print(arr)
[ 0  1  2  3  4 12 12 12  8  9]
arr_slice = arr[5:8]
arr_slice[1] = 12345 # 也可以通過切片來修改
print(arr)
[    0     1     2     3     4    12 12345    12     8     9]
arr[arr>10] = 0 # 通過邏輯索引修改
print arr
[0 1 2 3 4 0 0 0 8 9]
  • 思考 np.array 和 list 有什么不同。

第一個不同,在高維數組中,numpy.array 支持比 list 更多的索引方式。舉例說明:

a = [[1, 2 , 3],[4, 5, 6]]
import numpy as np
b = np.array(a)
print type(a)
print a
print type(b)
print b
<type 'list'>
[[1, 2, 3], [4, 5, 6]]
<type 'numpy.ndarray'>
[[1 2 3]
 [4 5 6]]
print a[1][2]
print a[1][:]
6
[4, 5, 6]
print a[1,2] # Wrong
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-60-9da16148905d> in <module>()
----> 1 print a[1,2]


TypeError: list indices must be integers, not tuple
print a[1, :] # Wrong
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-61-f921c5f2e22f> in <module>()
----> 1 print a[1, :]


TypeError: list indices must be integers, not tuple
print b[1][2]
print b[1][:]
print b[1, 2]
print b[1, :]
6
[4 5 6]
6
[4 5 6]

第二個不同,list 的元素可以是任何對象,而 np.array 的所有元素必須是相同類型的。比如當 list 和 np.array 存儲的都是數值元素時,list 可以修改其中元素為字符串,但 np.array 就不行,要報錯。

lst = [1,2,3,4,5,6]
arr = np.array(lst)
lst[-1] = 'openmind'
lst
[1, 2, 3, 4, 5, 'openmind']
arr[-1] = 'openmind'
arr
---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

<ipython-input-121-da05f2764aba> in <module>()
----> 1 arr[-1] = 'openmind'
      2 arr


ValueError: invalid literal for long() with base 10: 'openmind'

list 所保存的是對象的指針。這樣為了保存一個簡單的[1,2,3],需要有3個指針和三個整數對象。對於數值運算來說這種結構顯然比較浪費內存和CPU計算時間。np.array 是存儲單一數據類型的多維數組,運算效率要高。

第三個不同,np.array 的大小是創建時就指定的,不能改變大小,而 list 可以隨時添加元素進去,list 有 append 函數。在實際使用中會經常遇到運行前不知道數組大小的情況,這時候就可以初始化 list 為空,然后在運行中根據需要添加元素進去,最后計算時可把 list 轉為 np.array 以提高效率。可見,list 和 np.array 有合作的空間。

  • 如何使用 mask 方法快速截取數據。

mask 是一種按條件提取數組中數據的方法。舉例即可說明,如下例要提取向量中的偶數。

import numpy.ma as ma
import numpy as np
a = np.arange(10)
a
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
mask = a%2==0
mask
array([ True, False,  True, False,  True, False,  True, False,  True, False], dtype=bool)
a[mask]
array([0, 2, 4, 6, 8])

Array 高維數組操作

  • 思考 view 和 copy 的區別。

view 是淺復制,copy 是深復制。什么意思呢?

淺復制就是不復制,view 創建的新的數組對象指向同一數據,對 view 上的任何修改都會直接反映到源數組上。數組切片就是原始數組的視圖。舉個例子就明白了。

arr = np.arange(10)
arr_slice = arr[5:8]
arr_slice[1] = 12345
arr
array([    0,     1,     2,     3,     4,     5, 12345,     7,     8,     9])

看到了吧,對 arr_slice[1] 的修改就是對 arr 的修改,因為 arr_slice 是 arr 的視圖。

reshape 也是一種 view,修改 reshape 后的數組內容會影響到原數組。

arr1 = np.arange(8)
arr2 = arr1.reshape(2,4)
arr2[0,0]=1234
arr1
array([1234,    1,    2,    3,    4,    5,    6,    7])

深復制才是真正的復制,copy 就是生成了數組切片的一個份副本。對該副本的操作就不會影響到原數組的值。

arr_copy = arr.copy()
arr_copy[1] = 56789
arr
array([    0,     1,     2,     3,     4,     5, 12345,     7,     8,     9])

可見,對 copy 值的修改並沒有影響原數組。

  • np.array 還有哪些屬性和方法?

屬性:dtype, size, ndim, shape, nbytes

創建 np.array 時,如果沒有顯式指定,np.array 會為新建的數組推斷出一個較為合適的數據類型,中途如果要修改 dtype 就只有通過 astype 方法顯式轉換 dtype。

arr = np.array([1, 2, 3, 4, 5])
print arr.dtype
arr[0] = 3.1415
print arr.dtype
float_arr = arr.astype(np.float64)
print float_arr.dtype
int64
int64
float64

一元通用函數:abs, fabs, sqrt, square, exp, log, log10, sign, ceil, floor, cos, cosh, sin, sinh, tan, tanh……

二元通用函數:add, subtract, multiply, divide, power, maximum, minimum, mod, copysign, greater, less, equal……

arr1 = np.arange(4)
arr2 = np.arange(10,14)
print(arr1,'+',arr2,'=',arr1+arr2) # 對應元素相加
(array([0, 1, 2, 3]), '+', array([10, 11, 12, 13]), '=', array([10, 12, 14, 16]))
print(arr1,'*',arr2,'=',arr1*arr2) # 注意這里的乘是兩數組中對位元素相加,跟線性代數中的矩陣乘法不同
(array([0, 1, 2, 3]), '*', array([10, 11, 12, 13]), '=', array([ 0, 11, 24, 39]))

基本數組統計方法:sum, mean, std, var, min, max, argmin, argmax, cumsum, cumprod

arr = np.arange(12).reshape(3,4) # 看下二維數組,可以按行求和,可以按列求和
print arr
print arr.sum(axis = 0) # 求每一列的和,即按列求和,相當於抹掉了行維度
print arr.sum(axis = 1) # 求每一行的和,即按行求和,相當於抹掉了列維度
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]
[12 15 18 21]
[ 6 22 38]

數組集合運算:unique(x), intersect1d(x), union1d(x,y), in1d(x,y), setdiff1d(x,y), setxor1d(x,y)

數組文件輸入輸出:save, load, loadtxt, genfromtxt

線性代數:diag, dot, trace, det, eig, inv, pinv, qr, svd, solve, lstsq

隨機數生成:seed, permutation, shuffle, rand, randint, randn, binomial, normal, beta, chisquare, gamma, uniform

  • 理解什么是 broadcasting?如何使用?

廣播(broadcastring)是指不同形狀的數組之間算術運算的執行方式。例如,將標量值跟數組合並時就會發生最簡單的廣播。

arr = np.arange(5)
4 * arr
array([ 0,  4,  8, 12, 16])

低維變量遇到高維變量會自動補全,如上面就是把 4 復制成一個同 arr 一樣的含 5 個元素的一維向量,然后對位元素相乘,也可以說標量值 4 被廣播到了 arr 的所有元素上。

再比如數據分析中經常用到的距平化處理,對數組的每一列減去平均值。

arr = np.random.randn(4,3)
arr - arr.mean(0)
array([[-0.00186826,  0.36242026,  1.20839815],
       [ 1.47094699,  1.02999549, -0.79207756],
       [-1.55548229,  0.27482805, -1.05206272],
       [ 0.08640356, -1.6672438 ,  0.63574213]])

可見,所謂廣播,也就是把對矩陣的操作廣播到每個元素的操作。

如果列向量遇到行向量會如何呢?

np.arange(3).reshape((3, 1)) + np.arange(3)
array([[0, 1, 2],
       [1, 2, 3],
       [2, 3, 4]])

一圖勝千言,看了下圖,廣播的規則就一目了然了。

只要遵循一定的規則,低維度的值是可以被廣播到數組的任意維度的。

只要記住,遇到不同維度的運算,先對低維度補全,補全到相同的維度再計算,就不會迷惑。

Numpy 中的線性代數

  • 如何計算向量、矩陣相乘?

Numpy 提供了一個用於矩陣乘法的 dot 函數。先來看下向量相乘:

v1 = np.array([1,2,3])  # 先來看下向量相乘
v2 = np.array([2,3,4])
print np.dot(v1,v2)
print np.dot(v1.T,v2)
print np.dot(v1,v2.T)
print np.dot(v1.T,v2.T)
print v1
print v1.T
20
20
20
20
[1 2 3]
[1 2 3]

由上可見,在 Numpy 中,向量是不分行向量和列向量的,轉置對向量不起作用,這點跟預想的不一樣。要注意的是,reshape 不能轉置向量,reshape 只是改變數組的形狀。

print v1.reshape(3,1) # 試圖通過 reshape 轉置向量
print v1
np.dot(v1.reshape(3,1),v1) # 失敗的線性相乘
[[1]
 [2]
 [3]]
[1 2 3]



---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

<ipython-input-174-ed17a869fdc6> in <module>()
      1 print v1.reshape(3,1)
      2 print v1
----> 3 np.dot(v1.reshape(3,1),v1)


ValueError: shapes (3,1) and (3,) not aligned: 1 (dim 1) != 3 (dim 0)

再來看向量和矩陣相乘。

v1 = np.array([2, 3, 4]) 
A = np.arange(6).reshape(2,3)
print np.dot(A, v1) # 2 行 3 列的矩陣乘 3 行 1 列的向量應等於 2 行 1 列的矩陣
print np.dot(A, v1.T) # 看結果再次證明,Numpy 是不分行向量和列向量的
B = np.arange(6).reshape(3,2)
print np.dot(v1, B) # 1 行 3 列的向量乘 3 行 2 列的向量等於 1 行 2 列的向量
[11 38]
[11 38]
[22 31]

可以這么看,當矩陣乘向量,即向量在后時,那么向量就是列向量;當向量乘矩陣,即向量在前時,那么向量就是行向量。

矩陣和矩陣相乘,只要符合維度要求即可。

x = np.array([[1,2,3],[4,5,6]]) # 2 行 3 列的矩陣
y = np.array([[6.,23],[-1,7],[8,9]]) # 3 行 2 列的矩陣
x.dot(y) # 得 2 行 2 列的矩陣
[[1 2 3]
 [4 5 6]]





array([[  28.,   64.],
       [  67.,  181.]])
  • 如何從文件中讀取數據?

前面都是在內存空間中計算 np.array,那么它怎么和磁盤空間進行輸入輸出交互呢?有兩種讀寫方式,一種是 Text 模式,一種是 Binary 模式。

Text 模式是可以把數組以字符串的方式存到文本文件中,人們用編輯器打開文件讀得懂,也可以手動修改。只能用於一維和二維數組。

Binary 模式是以二進制存儲,跟內存中格式一模一樣,包含數組的大小和維度,沒有信息損失,原模原樣存儲自然效率高。但人們不能讀懂,無法編輯。

arr = np.arange(10).reshape(2,5)
np.savetxt('test.out',arr,fmt='%2e',header='My dataset') # 以 Text 模式存儲
!cat test.out
# My dataset
0.000000e+00 1.000000e+00 2.000000e+00 3.000000e+00 4.000000e+00
5.000000e+00 6.000000e+00 7.000000e+00 8.000000e+00 9.000000e+00
arr2 = np.loadtxt('test.out') # 以 Text 模式讀取
print(arr2)
[[ 0.  1.  2.  3.  4.]
 [ 5.  6.  7.  8.  9.]]
np.save('test.npy', arr2) # 以 Binary 模式存儲
!cat test.npy   # 可以看到 Binary 模式存儲的內容好像一團亂碼
�NUMPY F {'descr': '<f8', 'fortran_order': False, 'shape': (2, 5), }          
              �?       @      @      @      @      @      @       @      "@
arr2n = np.load('test.npy') # 以 Binary 模式讀取
print arr2n
[[ 0.  1.  2.  3.  4.]
 [ 5.  6.  7.  8.  9.]]

通過 np.savez 可以將多個數組保存到一個壓縮文件中,將數組以關鍵字參數的形式傳入即可。

np.savez('test.npz', arr, arr2) # 以 Binary 模式存儲多個數組
arrays = np.load('test.npz')
arrays.files
!cat test.npz # 一團亂碼
�   �   	   arr_1.npy�NUMPY F {'descr': '<f8', 'fortran_order': False, 'shape': (2, 5), }          
              �?       @      @      @      @      @      @       @      "@PK     l��Hx��   �   	   arr_0.npy�NUMPY F {'descr': '<i8', 'fortran_order': False, 'shape': (2, 5), }          
�   �   	           ��    arr_1.npyPK     l��Hx��   �   	           ���   arr_0.npyPK      n   �    

讀取文件還有 pandas 的 read_csv 和 read_table 函數,后面的課程會講到。

小結

Numpy 提供了 Array 這種數據結構,提供了所有 Python 環境中數值計算的底層支持,Array 使向量化計算更為容易,Array 有大量方便的內置函數。

補充閱讀

參考資料


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM