Python numpy 學習筆記
阿新 • • 發佈:2018-12-22
《NumPy快速入門手冊》寫得很好,也推薦閱讀
一. numpy
1. 介紹
2. numpy引用
"""numpy的主要物件是一個規範格式的多維陣列"""
import numpy as np
二. 建立numpy陣列
1. 通過list、tuple或其他序列,建立陣列
### 通過list、tuple或其他序列,建立陣列
data = np.array([[1, 2, 3], [4, 5, 6]]) # 生成np的陣列,注意入參不能是多個元素:如np.array(1,2,3,4),否則會報錯
data2 = np.array( [[1, 3], [5, 7], [9, 11]], dtype=complex) # 可以直接指定資料型別dtype
print(data2)
''' 可以看到資料格式轉成了複數
[[ 1.+0.j 3.+0.j]
[ 5.+0.j 7.+0.j]
[ 9.+0.j 11.+0.j]]
'''
2. 建立特定格式和內容的陣列
a. 全一陣列
# 全一陣列
one_np = np.ones([3,2], dtype=int) # 建立全為1的二維陣列,第一個引數shape決定了陣列的格式
print(one_np)
'''
[[1 1]
[1 1]
[1 1]]
'''
b. 全零陣列
# 全零陣列
zero_np = np.zeros([2, 3, 2], dtype=float) # 建立了一個三維陣列,每個陣列元素都是0
print(zero_np)
'''
[[[ 0. 0.]
[ 0. 0.]
[ 0. 0.]]
[[ 0. 0.]
[ 0. 0.]
[ 0. 0.]]]
'''
c. 空隨機陣列
# 空隨機陣列
random_np = np.empty([2, 2]) # 根據系統的狀態隨機生成一些佔位元素,陣列完全是隨機的
print(random_np)
'''
[[ 1.33511562e-306 6.23040373e-307]
[ 1.20161730e-306 1.02360527e-306]]
'''
3. 數字序列陣列
### 數字序列陣列
# arange() 起點start + 終點stop(不包含) + 總共節點數step
data3 = np.arange(1, 9, 2) # 類似於range()函式, 一維陣列
print(data3) # [1 3 5 7]
# linspace() 起點start + 終點stop(包含) + 總共節點數num
data4 = np.linspace(0, 5) # linspace(start, stop, num=50)生成從起點到終點(包括),均勻間隔的一維陣列。
# 其中num預設為50,表示陣列長度(因此元素間隔寬度為(stop - start)/num )
print(data4)
'''
array([ 0. , 0.10204082, 0.20408163, 0.30612245, 0.40816327,
0.51020408, 0.6122449 , 0.71428571, 0.81632653, 0.91836735,
1.02040816, 1.12244898, 1.2244898 , 1.32653061, 1.42857143,
1.53061224, 1.63265306, 1.73469388, 1.83673469, 1.93877551,
2.04081633, 2.14285714, 2.24489796, 2.34693878, 2.44897959,
2.55102041, 2.65306122, 2.75510204, 2.85714286, 2.95918367,
3.06122449, 3.16326531, 3.26530612, 3.36734694, 3.46938776,
3.57142857, 3.67346939, 3.7755102 , 3.87755102, 3.97959184,
4.08163265, 4.18367347, 4.28571429, 4.3877551 , 4.48979592,
4.59183673, 4.69387755, 4.79591837, 4.89795918, 5. ])
'''
4. 隨機數
a. np.random.rand 生成[0,1)區間內平均分佈的陣列,引數為維度
### 隨機數
# 生成[0,1)區間內平均分佈的陣列,引數為維度
data5 = np.random.rand(2, 3, 5)
print(data5)
'''
[[[ 0.11460522 0.78475528 0.41318688 0.9830661 0.23276821]
[ 0.31333353 0.99497377 0.31270973 0.55575352 0.30095844]
[ 0.83803498 0.07725709 0.60617707 0.17494517 0.7324532 ]]
[[ 0.03576562 0.84033749 0.81820702 0.46633063 0.06972207]
[ 0.00538072 0.44754953 0.23233634 0.78536293 0.43381988]
[ 0.85224057 0.40323818 0.07592376 0.23596046 0.49922436]]]
'''
b. np.random.randn 生成標準正態分佈規律的陣列(數出現的概率滿足標準正太分佈),引數為維度
# 生成標準正態分佈規律的陣列(數出現的概率滿足標準正太分佈),引數為維度
data6 = np.random.randn(2, 3, 5)
print(data6)
'''
[[[-0.51493028 1.78516665 -0.38657793 0.70038985 -1.24254944]
[-0.80613041 0.63745159 0.74201648 0.39451194 -0.94476024]
[-0.47833675 -0.92092758 1.01658297 0.85712149 -0.21944187]]
[[ 1.3295873 -0.10871527 0.31088519 -0.60094403 1.80437504]
[-0.3851662 -0.26422884 1.03784294 -0.44199909 0.83116801]
[-0.03563173 -0.66946972 0.45256832 0.17121998 0.2120946 ]]]
'''
c. 生成指定範圍的隨機數
# 生成指定範圍的隨機整數
np.random.randint(1, 7, 10) # 開區間,不可能取7 ,array([5, 3, 6, 1, 6, 1, 6, 5, 4, 3])
np.random.random_integers(1,7,10) # 閉區間,可以取到7 array([5, 1, 1, 7, 7, 2, 6, 5, 1, 2])
np.random.random() # [0,1)內隨機數 本例:0.3825271804785396
d. np.random.choice 隨機選擇
# 隨機選擇
np.random.choice(10,size=2) # array([7, 4])
np.random.choice(10,size=(3,4)) # 設定shape
'''
array([[4, 9, 5, 7],
[6, 6, 5, 1],
[6, 5, 5, 0]])
'''
np.random.choice(['a','b','c'],size=[2,2]) # 從給出的list中隨機選擇生成陣列
'''
array([['c', 'b'],
['c', 'b']],
dtype='<U1')
'''
二. 陣列的屬性
"""陣列的屬性"""
data = np.array([[1, 2, 3], [4, 5, 6]])
data.ndim # 陣列的維度 本例:2
data.shape # 陣列的形狀,返回一個元組,表示各個方向的長度,元組本身的長度等於陣列的維度. 本例:(2, 3)
data.size # 陣列的大小,等於shape元組內各個元素的乘積, 本例:6
data.dtype # 陣列內部元素的資料型別, 本例:dtype('int32')
data.itemsize # 陣列內元素佔計算機記憶體的大小(多少個Byte), 本例:4
data.data # 陣列在記憶體中的地址,一般很少用,本例:<memory at 0x00000000070AC1F8>
三. 陣列維度及元素型別轉換
1. 重新修改維度(常用)
### 重新修改維度(常用)
data_shaped1 = data.reshape(3,2)
print(data_shaped1)
'''
[[1 2]
[3 4]
[5 6]]
'''
# 利用arrange() 和 reshape() 生成多維陣列
data_shaped3 = np.arange(24).reshape(3, -1, 4) #如果某一個方向設為-1,則會自動計算
print(data_shaped3)
''' 可以看到,第二維度被自動計算為2
[[[ 0 1 2 3]
[ 4 5 6 7]]
[[ 8 9 10 11]
[12 13 14 15]]
[[16 17 18 19]
[20 21 22 23]]]
'''
### resize()
# 與 reshape()相似,不過resize()會修改原陣列
2. np陣列轉換為list
### 轉換為list
list_np = one_np.tolist() # [[1, 1], [1, 1], [1, 1]]
3. np.astype() 轉換元素型別
### 轉換型別 astype() 在轉換中,可能會損失精度
data2_int = data2.astype(int) # 元素資料型別,從原先的複數complex轉換為int
print(data2_int)
'''
[[ 1 3]
[ 5 7]
[ 9 11]]
'''
4. 陣列轉置
### 陣列轉置
np.transpose(data_shaped1)
'''
array([[1, 3, 5],
[2, 4, 6]])
'''
# 多維陣列轉置
np.transpose(data_shaped3) # 多維陣列轉置,就是座標的reverse,然後組成新陣列,注意資料交換
'''
array([[[ 0, 8, 16], d[0,0,0], d[1,0,0], d[2,0,0]
[ 4, 12, 20]], d[0,1,0], d[1,1,0], d[2,1,0]
[[ 1, 9, 17], d[0,0,1], d[1,0,1], d[2,0,1]
[ 5, 13, 21]], d[0,1,1], d[1,1,1], d[2,1,1]
[[ 2, 10, 18], d[0,0,2], d[1,0,2], d[2,0,2]
[ 6, 14, 22]], d[0,1,2], d[1,1,2], d[2,1,2]
[[ 3, 11, 19], d[0,0,3], d[1,0,3], d[2,0,3]
[ 7, 15, 23]]]) d[0,1,3], d[1,1,3], d[2,1,3]
'''
5. 轉換一維陣列
### ravel() 逐行取元素並返回一個一維陣列
print(np.ravel(data_shaped3)) # [ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23]
四. 陣列的完全不復制(全部引用)、指代(區域性引用)、深度複製
1. 完全不復制
### 1. 完全不復制
a = np.arange(4)
b = np.arange(4)
b is a # b和a現在還不是同一引用,為False
b = a
b is a # b和a是同一引用,為True
a.shape # 此時是(4,)
b.shape = (2,2) # 因為是同一引用,所以也會修改a的形狀
a.shape # 變為(2, 2)
2. 指代 (切片也是如此)
### 2. 指代 (切片也是如此)
a = np.arange(12,24).reshape(3,4)
b = a.view() # 返回一個指代,但還是引用了同一陣列
b is a # False
b[1,2] = 678
print(a)
''' 可以看到a[1,2]也變成了678
[[ 12 13 14 15]
[ 16 17 678 19]
[ 20 21 22 23]]
'''
3. 深度複製
### 3. 深度複製
a = np.arange(12,24).reshape(3,4)
b = a.copy()
b is a # False
b[1,1] = 111
print(b)
'''
[[ 12 13 14 15]
[ 16 111 18 19]
[ 20 21 22 23]]
'''
print(a)
''' a 沒變
[[12 13 14 15]
[16 17 18 19]
[20 21 22 23]]
'''
五. 陣列取值及分片
1. 多維陣列通過索引取某一元素
### 多維陣列通過索引取某一元素
c = np.arange(12).reshape(3, 4) # 建立一個3*4的二維陣列
print(c)
'''
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
'''
c[1,3] # 取第二行第四列的元素,本例:7
c[1][3] # 取第二行第四列的元素,本例:7
2. 一維陣列分片 arr[i:j:k]
i決定起始,j決定結束,k決定步進,都可以為負數,其中k為負則倒序取值
### 一維陣列分片 arr[i:j:k] i決定起始,j決定結束,k決定步進,都可以為負數,其中k為負則倒序取值
b = np.arange(1,20,2)
b # array([ 1, 3, 5, 7, 9, 11, 13, 15, 17, 19])
b[2] # 取第三項的元素, 本例:5
b[2:4] # 取第三、四項的子分片,本例:array([5, 7])
b[:5:2] # 在原陣列前五項中,步進為2的取陣列成新陣列,本例:array([1, 5, 9])
b[-3:1:-2] # 從倒數第三項('15')開始,從後向前步進2格取值,直到第二項('3',不包含此項) 本例:array([15, 11, 7])
## 注意:雖然語法相似,但不同於list的分片(建立副本),np的分片是不建立副本的,修改分片中的元素,會影響到原陣列的元素
sub_b = b[2:7:3] # sub_b = array([ 5, 11])
sub_b[1] = 1 # 此時sub_b變為了array([5, 1])
b # 同時原陣列也發生了變化:array([ 1, 3, 5, 7, 9, 1, 13, 15, 17, 19])
### 如果想建立絕對的副本,則要用copy()
b = np.arange(1,20,2)
duplicate_b = b[2:7:3].copy() # duplicate_b = array([ 5, 11]), 使用copy建立副本
duplicate_b[1] = 1 # 此時duplicate_b變為了array([5, 1])
b # 原陣列沒有變:array([ 1, 3, 5, 7, 9, 11, 13, 15, 17, 19])
4. 多維陣列分片
### 多維陣列分片
c = np.arange(12).reshape(3, 4) # 建立一個3*4的二維陣列
print(c)
'''
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
'''
sub_c = c[1:3,1:3] # 取2、3行和2、3列的子陣列
print(sub_c)
'''
[[ 5 6]
[ 9 10]]
'''
## 多維陣列倒序分片
sub_c = c[::-2,1:3] # 取所有行步進為-2(從後向前)和2、3列組成的子陣列
print(sub_c)
'''
[[ 9 10] # 先取的第三行的9,10
[ 1 2]] # 再取的第一行的1,2
'''
使用分片,批量修改元素及順序
### 使用分片,批量修改元素(因為只是引用)
b = np.arange(1,20,2) # array([ 1, 3, 5, 7, 9, 11, 13, 15, 17, 19])
b[1:9:2] += 1 # 從第2項到第9項,步進為2的數都自增1
b # array([ 1, 4, 5, 8, 9, 12, 13, 16, 17, 19])
### 拓展:重新排序
c = b[[0,2,1]]
print(c)
'''
[[10 11 12 13]
[18 19 20 21] 第二行和第三行交換了順序
[14 15 16 17]]
'''
d = b[:,[3,0,-2,1]]
print(d)
''' 列上交換了順序
[[13 10 12 11]
[17 14 16 15]
[21 18 20 19]]
'''
六. 高階索引
1. 整數索引
a. 陣列arr_index為索引,從一維陣列array中篩選出新的陣列
## 陣列arr_index為索引,從一維陣列array中篩選出新的陣列
a = np.arange(10,20) # a: array([10, 11, 12, 13, 14, 15, 16, 17, 18, 19])
arr_index = np.array(([3,4,5],[1,2,3]))
a_new = a[arr_index]
print(a_new)
''' 可以看到取的陣列的格式跟arr_index一致,每個元素為原陣列在索引arr_index[i]處的值
[[13 14 15] a[3]=13, a[4]=14, a[5]=15
[11 12 13]] a[1]=11, a[2]=12, a[3]=13
'''
b. 陣列arr_index為索引,從多維陣列array中篩選出新的陣列
## 陣列arr_index為索引,從多維陣列array中篩選出新的陣列
b = np.arange(10,22).reshape(3,4)
print(b)
"""
[[10 11 12 13]
[14 15 16 17]
[18 19 20 21]]
"""
arr_index = np.array([[0,2,1],[1,2,0]])
e = b[arr_index]
print(e)
''' 本質上與一維陣列和多維索引的結果類似,整體陣列的形狀還是按照索引arr_index的形狀,
只是這裡的索引搜出來的元素不是簡單資料,而是一維陣列,所以整個結果陣列擴充為了三維陣列
[[[10 11 12 13] b[0]=[10 11 12 13]
[18 19 20 21] b[2]=[18 19 20 21]
[14 15 16 17]] b[1]
[[14 15 16 17] b[1]
[18 19 20 21] b[2]
[10 11 12 13]]] b[0]
'''
c. 列表、元組list_index為索引,從多維陣列array中篩選出新的陣列
## 列表、元組list_index為索引,從多維陣列array中篩選出新的陣列
b = np.arange(10,22).reshape(3,4)
print(b)
'''
[[10 11 12 13]
[14 15 16 17]
[18 19 20 21]]
'''
list_index = ([0,2,1],[1,2,0]) # 注意這裡是list、tuple或者其他簡單序列,不是np.array
b_new = b[list_index]
print(b_new)
''' 通過索引來取元素組成新的結果
[11 20 14] a[0,1]=11, a[2,2]=20, a[1,0]=14
2. 布林索引
a. 布林索引的意義
### 布林索引 通過True/False的陣列,來索引陣列
m = np.random.rand(30).reshape(10, 3) # 建立一個10 * 3 的二維陣列
print(m)
'''
[[ 0.64877021 0.72179137 0.56534942]
[ 0.66861906 0.99436 0.09971997]
[ 0.71550022 0.39179217 0.69544798]
[ 0.76190541 0.71990526 0.5056289 ]
[ 0.09218896 0.20907768 0.53118 ]
[ 0.04977315 0.79410303 0.86770033]
[ 0.39507158 0.42490585 0.48495278]
[ 0.18026899 0.47850156 0.13560222]
[ 0.11837795 0.67394728 0.7619298 ]
[ 0.23708205 0.93920253 0.79138269]]
'''
m_select = m[[True,False,False,False,True,True,False,False,False,False]] # 總共10個Boolean值,對應m的10行
print(m_select)
''' 通過True值,篩選出第一行、第五、六行的資料組成子陣列
[[ 0.64877021 0.72179137 0.56534942]
[ 0.09218896 0.20907768 0.53118 ]
[ 0.04977315 0.79410303 0.86770033]]
'''
# 這樣乍一看覺得用布林值多此一舉,用數篩選更方便,但如果有兩個陣列,之間存在對應關係,布林值搜尋就十分方便
n = np.array(['小明','小紅','李白','小明','小明','李白','小紅','張偉','李白','張偉']) # n中有10個人,對應m的十行資料
# 現在我們要篩選出屬於'小紅'的資料
m_select2 = m[n=='小紅']
print(m_select2)
''' 篩選出屬於小紅的第二行、第七行資料
[[ 0.66861906 0.99436 0.09971997]
[ 0.39507158 0.42490585 0.48495278]]
'''
b. 布林索引判斷的拓展
## 還可以再用切片或者篩選
m_select3 = m[n=='小紅',1] # 篩選出第二列的資料
print(m_select3) # [ 0.99436 0.42490585]
## 與或非運算
m_select4 = m[n!='小紅',:2] # 篩選出不是小紅的前兩列資料
print(m_select4)
'''
[[ 0.64877021 0.72179137]
[ 0.71550022 0.39179217]
[ 0.76190541 0.71990526]
[ 0.09218896 0.20907768]
[ 0.04977315 0.79410303]
[ 0.18026899 0.47850156]
[ 0.11837795 0.67394728]
[ 0.23708205 0.93920253]]
'''
m_select5 = m[(n=='小紅')|(n=='李白')] # 篩選出小紅或者李白的資料
print(m_select5)
'''
[[ 0.66861906 0.99436 0.09971997]
[ 0.71550022 0.39179217 0.69544798]
[ 0.04977315 0.79410303 0.86770033]
[ 0.39507158 0.42490585 0.48495278]
[ 0.11837795 0.67394728 0.7619298 ]]
'''
七. 陣列元素替換和填充
1. 布林索引
"""陣列元素替換和填充 布林索引 + np.where() """
### 通過布林索引,實現元素替換與填充
x = np.array([[1,2],[np.nan, 3],[np.nan, np.nan]])
print(x)
'''
[[ 1. 2.]
[ nan 3.]
[ nan nan]]
'''
np.isnan(x)
'''
array([[False, False],
[ True, False],
[ True, True]], dtype=bool)
'''
x[np.isnan(x)] # 篩選出是nan元素組成新一維陣列, array([ nan, nan, nan])
x[np.isnan(x)] = 0 # 將x中是nan的元素,都替換為0,通過布林索引實現替換元素的功能
print(x)
'''
[[ 1. 2.]
[ 0. 3.]
[ 0. 0.]]
'''
x[~np.isnan(x)] # 篩選出是nan元素組成新一維陣列, array([ 1., 2., 3.])
2. 篩選函式where(condition, a, b)
condition元素為布林值,能起到和布林索引同樣的功能
### 篩選函式where(condition, a, b) condition元素為布林值,能起到和布林索引同樣的功能
a = np.arange(1,10,2) # array([1, 3, 5, 7, 9])
b = np.arange(2,11,2) # array([ 2, 4, 6, 8, 10])
f = np.array([True,True,False,True,False])
r = np.where(f, a, b) # 如果f某位置的值為True則取對應位置的a的值,否則取b的值
'''
array([ 1, 3, 6, 7, 10])
'''
# a/b可以不為陣列,用法如下
a = np.random.rand(12).reshape(3,4)
'''
array([[ 0.21251241, 0.28023144, 0.01282137, 0.24889945],
[ 0.55515714, 0.95901587, 0.34933226, 0.80928682],
[ 0.69281057, 0.63581292, 0.960359 , 0.7399842 ]])
'''
r = np.where(a>0.5, 1, 0)
'''
array([[0, 0, 0, 0],
[1, 1, 0, 1],
[1, 1, 1, 1]])
'''
r2 = np.where(a>0.5, a, 0)
'''
array([[ 0. , 0. , 0. , 0. ],
[ 0.55515714, 0.95901587, 0. , 0.80928682],
[ 0.69281057, 0.63581292, 0.960359 , 0.7399842 ]])
'''
## 與r2相同結果的方法實現
a[a<=0.5] = 0 # 將a中所有小於等於0.5的都設為0
print(a)
'''
[[ 0. 0. 0. 0. ]
[ 0.55515714 0.95901587 0. 0.80928682]
[ 0.69281057 0.63581292 0.960359 0.7399842 ]]
'''
八. 陣列的組裝拼接和分割
1. 拼接陣列
a = np.array([[1,2],[3,4]])
b = np.array([['A','B'],['C','D']])
### 豎直拼接
v = np.vstack((a,b))
print(v)
''' 有意思的是,數字也被轉換string格式了
[['1' '2']
['3' '4']
['A' 'B']
['C' 'D']]
'''
### 水平拼接
h = np.hstack((a,b))
print(h)
'''
[['1' '2' 'A' 'B']
['3' '4' 'C' 'D']]
'''
2. 分割陣列
### 分割陣列
d = np.arange(32).reshape(4,8)
prin