【Task02】Numpy學習打卡
六、副本與檢視
前言
在學習本章之前,我們先回顧一下Python中引用和物件的概念:
Python 中,一切皆物件。每個物件由:標識(identity)、型別(type)、value(值)
組成。
- 標識用於唯一標識物件,通常對應於物件在計算機記憶體中的地址。使用內建函式 id(obj) 可返回物件 obj 的標識。
- 型別用於表示物件儲存的“資料”的型別。型別可以限制物件的取值範圍以及可執行的 操作。可以使用 type(obj)獲得物件的所屬型別。
- 值表示物件所儲存的資料的資訊。使用 print(obj)可以直接列印出值。
舉例說明:
【例六、0-1】python原生的物件和引用
#自定義輸出函式printInfo
>>> def printInfo(obj):
>>> print("id:%s type:%s"%(id(obj),type(obj)))
>>> a = 2
>>> b = "datawhale"
>>> print("a的資訊",end='')
>>> printInfo(a)
a的資訊id:4503882768 type:<class 'int'>
>>> print("b的資訊",end='')
>>> printInfo(b)
b的資訊id:4571412400 type:<class 'str'>
如上圖所示,右邊分別是兩個不同型別的物件,變數a和b分別引用了這兩個物件。在python中,變數也成為:物件的引用。變數儲存的就是物件的地址。變數通過地址引用了“物件”。
【例六、0-2】python改變變數的引用
>>> data = [x for x in range(5)]
>>> data2 = [x for x in range(5,10)]
>>> print(data,data2)
[0, 1, 2, 3, 4] [5, 6, 7, 8, 9]
>>> a = data
>>> print(a,id(a),data,id(data))
[0, 1, 2, 3, 4] 4570869056 [0, 1, 2, 3, 4] 4570869056
>>> a[0] = 100
>>> print(a,id(a),data,id(data))
[100, 1, 2, 3, 4] 4570869056 [100, 1, 2, 3, 4] 4570869056
>>> a = data2
>>> print(a,id(a),data,id(data),data2,id(data2))
[5, 6, 7, 8, 9] 4654437248 [100, 1, 2, 3, 4] 4570869056 [5, 6, 7, 8, 9] 4654437248
如上圖,我們可以看到這樣的過程,開始時,我們可以通過變數a修改data的資料,變數a的引用從data轉向data2後,a的id和資料都發生了改變,與data2保持一致。
1.numpy中的引用
類比前言中python的引用例子,我們也可以看一下對numpy的資料型別ndarray進行引用會發生什麼:
【六、例1】引用並修改ndarray變數
>>> data = np.arange(5)
>>> a = data
>>> print(a,id(a),data,id(data))
>>> print(a is data)
[0 1 2 3 4] 4659785120 [0 1 2 3 4] 4659785120
True
>>> a[0] = 100
>>> print(a,id(a),data,id(data))
>>> print(a is data)
True
a is data
也可以用來判斷2個變數所引用的物件id是否相同,id也叫做同一性運算子。
我們通過上面的例子可以看出,目前為主,numpy和python原生沒有太大區別。
2.檢視
檢視是資料的一個別稱或引用,通過該別稱或引用亦便可訪問、操作原有資料,但原有資料不會產生拷貝。如果我們對檢視進行修改,它會影響到原始資料,實體記憶體在同一位置。
檢視一般發生在:
- numpy 的切片操作返回原資料的檢視。
- 呼叫 ndarray 的 view() 函式產生一個檢視。
【六、例2-1】使用numpy切片操作返回檢視
#自定義輸出函式printInfo
>>> def printInfo(obj):
>>> print(obj,end=" ")
>>> print("id:%s type:%s"%(id(obj),type(obj)))
>>> data = np.arange(5)
>>> print("data:",end='')
>>> printInfo(data)
data:[0 1 2 3 4] id:4656357504 type:<class 'numpy.ndarray'>
>>> a = data[:2]
>>> print("a:",end=' ')
>>> printInfo(a)
a: [0 1] id:4661896176 type:<class 'numpy.ndarray'>
>>> b = data[:-1]
>>> print("b:",end=' ')
>>> printInfo(b)
b: [0 1 2 3] id:4661897776 type:<class 'numpy.ndarray'>
>>> a[0] = 100
>>> b[-1] = 200
>>> print(data)
[100 1 2 200 4]
>>> a.shape = (2,1)
>>> print(a)
>>> print(data)
[[100]
[ 1]]
[100 1 2 200 4]
a和b均由ndarray資料型別切片而來,可以看到,雖然data、a、b三個變數所引用的id不同,但是改變a和b的行為均在data上體現,但是改變a的shape並不會對data的shape產生影響。
【六、例2-2】呼叫 ndarray 的 view() 函式產生檢視
#自定義輸出函式printInfo
>>> def printInfo(obj):
>>> print(obj,end=" ")
>>> print("id:%s type:%s"%(id(obj),type(obj)))
>>> data = np.arange(5)
>>> a = data.view()
>>> print("data:",end='')
>>> printInfo(data)
data:[0 1 2 3 4] id:4679609280 type:<class 'numpy.ndarray'>
>>> print("a:",end=' ')
>>> printInfo(a)
a: [0 1 2 3 4] id:4661896976 type:<class 'numpy.ndarray'>
>>> a[0] = 100
>>> print(data)
[100 1 2 3 4]
>>> a.shape = (5,1)
>>> print(a)
>>> print(data)
[[100]
[ 1]
[ 2]
[ 3]
[ 4]]
[100 1 2 3 4]
通過view()方法生成檢視,變數a和變數data的id仍不相同,但是改變a仍然可以改變data,另外,用這種檢視生成方式改變a的shape,data的shape仍然不會改變。
那麼可以把檢視理解成半相關,部分相關(資料),部分無關(shape等)。
3、副本
副本是一個資料的完整的拷貝,如果我們對副本進行修改,它不會影響到原始資料,實體記憶體不在同一位置。
副本一般發生在:
- Python 序列的切片操作,呼叫deepCopy()函式。
- 呼叫 ndarray 的 copy() 函式產生一個副本。
【例六、3-1】Python 序列的切片生成副本
>>> a = [x for x in range(5)]
>>> b = a[:-1]
>>> print(a is b)
False
>>> print(a,b)
[0, 1, 2, 3, 4] [0, 1, 2, 3]
>>> b[0] = 100
>>> print(a,b)
[0, 1, 2, 3, 4] [100, 1, 2, 3]
可以看到通過python List的切片操作,生成的b和a的id不同,改變b中的資料也並不會改變a的資料。
【例六、3-2】呼叫 ndarray 的 copy() 函式生成副本
>>> a = np.arange(5)
>>> b = a.copy()
>>> print(a is b)
False
>>> print(a,b)
[0 1 2 3 4] [0 1 2 3 4]
>>> b[0] = 100
>>> print(a,b)
[0 1 2 3 4] [100 1 2 3 4]
numpy.ndarray.copy()
函式建立一個副本。 對副本資料進行修改,不會影響到原始資料,它們實體記憶體不在同一位置。
七、索引與切片
前言
ndarray物件的內容可以通過索引或切片來訪問和修改,與 Python 中 list 的切片操作一樣。
1.整數索引
【七、例1】通過整數索引訪問陣列
>>> a = np.random.randint(0,10,5)
>>> print(a,a[2])
[2 7 3 6 6] 3
>>> b = np.random.randint(0,10,(5,5))
>>> print(b,b[3][1],b[3,1])
[[3 7 0 7 9]
[1 6 9 3 1]
[2 7 5 0 7]
[6 2 6 1 0]
[5 5 6 8 1]] 2 2
>>> c = np.random.randint(0,10,(3,3,3))
>>> print(c,c[0][0][0],c[0,0,0])
[[[6 1 5]
[9 5 9]
[2 3 5]]
[[1 1 5]
[2 1 5]
[1 5 4]]
[[3 5 9]
[8 9 6]
[7 3 3]]] 6 6
這裡用了np.random.randint()方法生成了隨機ndarray陣列,然後利用整數索引進行訪問。
注:[x][y][z] 與 [x,y,z]的效果相同
顧名思義,我們可以把切片理解成一系列切蛋糕的行為,第一個引數是第一刀,第二個引數是最後一刀後面的位置,第三個引數是刀與刀之間的距離,那麼整數索引在二維陣列就相當於與橫縱都僅切1刀,且刀寬為1。
2.切片索引
我們在上一章知道,python切片獲得原來資料的副本(深拷貝),而ndarray資料型別切片獲得原資料的檢視。
ndarray 陣列可以基於 0 - n 的下標進行索引,切片物件可以通過內建的 slice 函式,並設定 start, stop 及 step 引數進行,從原陣列中切割出一個新陣列,也可以通過冒號分隔切片引數 start:stop:step 來進行切片操作。
【七、例2-1】單維度切片
#一維陣列
>>> a = np.random.randint(0,10,5)
>>> print(a)
>>> print(a[:])
>>> print(a[:-2])
>>> print(a[1:])
>>> print(a[::2])
[8 6 4 8 6]
[8 6 4 8 6]
[8 6 4]
[6 4 8 6]
[8 4 6]
#二維陣列
>>> a = np.random.randint(0,10,(5,5))
>>> print(a)
[[1 7 5 8 3]
[1 7 8 7 3]
[2 1 7 3 7]
[3 3 4 1 4]
[6 4 3 8 6]]
#列印倒數前2行之前的陣列(不包括倒數第二行)
>>> print(a[:-2])
[[1 7 5 8 3]
[1 7 8 7 3]
[2 1 7 3 7]]
#步長為2列印整個陣列
>>> print(a[::2])
[[1 7 5 8 3]
[2 1 7 3 7]
[6 4 3 8 6]]
#列印倒數前2列之前的陣列(不包括倒數第二列)
>>> print(a[...,:-2])
[[1 7 5]
[1 7 8]
[2 1 7]
[3 3 4]
[6 4 3]]
#列印倒數前2行之前的陣列(不包括倒數第二行)
>>> print(a[:-2,...])
[[1 7 5 8 3]
[1 7 8 7 3]
[2 1 7 3 7]]
二維陣列單維度切片示意圖如下:
上面的案例僅以一維、二維陣列為例進行單維度切片(在二維陣列中要麼是行,要麼是列),上升到多維陣列後,行列就要換成第幾維去描述。
我們注意到a[:-2]
其實是和a[:-2,...]
是等價的,那麼就此引出dots:
NumPy 允許使用
...
表示足夠多的冒號來構建完整的索引列表。
那麼在上述案例中,:::
其實就和...
是等價。對於其他維度的陣列,...
可以起到一個補全的效果,我們接下來會在多維度切片中進行演示:
【七、例2-2】多維度切片
#二維陣列
>>> a = np.random.randint(0,10,(5,5))
>>> print(a)
[[7 7 9 8 0]
[7 9 9 9 9]
[2 1 4 7 7]
[4 9 7 3 8]
[2 1 8 2 8]]
#第2行與第3、4列交匯的資料
>>> print(a[1,2:4])
[9 9]
#第2列與第3、4行交匯的資料
>>> print(a[2:4,1])
[1 9]
#第3、4行與第3、4列交匯的資料
>>> print(a[2:4,2:4])
[[4 7]
[7 3]]
#從第一行開始以2為步長的行與從第一行開始以2為步長的列交匯的資料
>>> print(a[::2,::2])
[[7 9 0]
[2 4 7]
[2 8 8]]
#去掉最後一列
>>> print(a[...,:-1])
[[7 7 9 8]
[7 9 9 9]
[2 1 4 7]
[4 9 7 3]
[2 1 8 2]]
二維陣列多維度切片示意圖:
複雜版:
在這裡比較好奇,如果是一行一列,返回的結果是怎樣的呢?
>>> print(a[0,0])
7
是一個整數,切片切多了,忘了[0,0]其實就是整數索引 —_-!
3.整數陣列索引
先簡單看一個案例:
【七、例3-1】單維度整數陣列索引
>>> a = np.random.randint(0,10,(5,5))
>>> print(a)
[[0 3 2 8 0]
[2 6 8 7 9]
[4 0 5 0 5]
[8 5 9 0 0]
[1 9 8 8 5]]
>>> s = [0,2,4]
#取a的第1,3,5行
>>> print(a[s])
[[0 3 2 8 0]
[4 0 5 0 5]
[1 9 8 8 5]]
整數陣列解決的是不連續切片且間隔是隨機非等差的問題,上面的案例是操作行,我們也可以同時操作行和列:
【七、例3-2】多維度整數陣列索引
>>> a = np.random.randint(0,10,(5,5))
>>> print(a)
[[6 2 9 7 2]
[8 5 4 5 9]
[1 4 3 3 9]
[9 8 0 8 8]
[7 1 6 5 2]]
>>> s = [0,2,3]
>>> p = [1,2,4]
>>> q = [1,2]
>>> print(a[s,p])
[2 3 8]
>>> print(a[s,q])
IndexError: shape mismatch: indexing arrays could not be broadcast together with shapes (3,) (2,)
最後一句程式碼出現問題,原因是傳入的兩個index陣列的數量必須相等,怎樣理解,就是將整數索引重複n遍,那麼兩個index陣列當然要相等。
二維陣列多維度整數陣列切片示意圖:
4.布林索引
與其稱為布林索引,不如理解成條件索引:
【七、例4-1】控制最小值及型別的布林索引
#控制最小值
>>> a = np.random.randint(0,10,(5,5))
>>> print(a)
[[5 9 6 6 1]
[0 0 4 0 0]
[0 8 2 5 7]
[8 0 2 0 0]
[3 4 0 1 6]]
>>> b = a > 5
>>> print(b)
[[False True True True False]
[False False False False False]
[False True False False True]
[ True False False False False]
[False False False False True]]
>>> print(a[b])
[9 6 6 8 7 8 6]
#控制型別,找到不是nan的資料
>>> c = np.array([np.nan,9,8,7,np.nan,6,5])
>>> d = np.logical_not(np.isnan(c))
>>> print(d)
[False True True True False True True]
>>> print(c[d])
[9. 8. 7. 6. 5.]
我們其實可以看出,最終帶入的就是一個布林陣列b,然後利用a[b]去生成滿足條件的陣列。
【七、例4-2】布林索引在matplotlib上的應用
未完待續
八、陣列迭代
前言
除了for迴圈,Numpy 還提供另外一種更為優雅的遍歷方法。
apply_along_axis(func1d, axis, arr)
Apply a function to 1-D slices along the given axis.
第一個引數是要執行的函式,第二個引數是遍歷的維度,第三個引數是要遍歷的資料
1.採用系統函式迭代
>>> a = np.random.randint(0,10,(5,5))
>>> print(a)
[[6 8 7 0 5]
[3 4 4 3 7]
[6 0 4 2 4]
[3 9 8 4 8]
[8 9 3 5 7]]
>>> b = np.apply_along_axis(np.sum, 0, x)
>>> print(b)
[25 12 28 12 28]
>>> b = np.apply_along_axis(np.sum, 1, x)
>>> print(b)
[28 19 16 24 18]
這裡解釋一下axis = 0的含義,以二維陣列距離,當axis = 0時,是指遍歷的方向是行,也就是說列是固定的,比如第一列6+3+6+3+8=25,就是結果陣列的第一個數,以此類推。axis = 1的含義是,遍歷的方向是列,示意圖如下:
2.採用自定義函式迭代
也就是更改第一個引數,如下:
>>> def xiao_func(x):
>>> return (x[0] + x[3])
>>> b = np.apply_along_axis(xiao_func, 0, a)
>>> print(b)
[14 8 10 8 12]
相關文章
- 【Task01】Numpy學習打卡
- 【Task04】Numpy學習打卡
- 天池python學習-task02打卡第五天Python
- Task02:Numpy常用函式函式
- 每日學習打卡
- Numpy學習(2)numpy向量化、numpy操作
- Numpy學習筆記筆記
- NumPy 學習(1): ndarrays
- numpy 學習總結
- 第50天學習打卡(JavaScript)JavaScript
- 學習打卡 第六天
- 組隊打卡學習csappAPP
- 【Task03】Pandas學習打卡
- Numpy學習筆記 1筆記
- Numpy學習 Day1
- Numpy學習筆記(1)筆記
- python_numPy學習Python
- Python之numpy學習Python
- Python學習 day01打卡Python
- numpy的學習筆記\pandas學習筆記筆記
- numpy學習筆記 – numpy陣列的常見用法筆記陣列
- Python NumPy學習總結Python
- 【Numpy學習08】陣列迭代陣列
- Numpy與Pandas學習網站學習網站
- python——numpy學習筆記Python筆記
- NumPy 學習(3): 通用函式函式
- 程式設計學習打卡記錄貼程式設計
- 【numpy學習筆記】矩陣操作筆記矩陣
- 【numpy學習筆記】 Array processing筆記
- Numpy學習第二天
- 【Numpy學習12】邏輯函式函式
- NumPy 學習(2): 陣列的操作陣列
- 補打卡學習go第一天Go
- 強化學習組隊學習task02——馬爾可夫決策過程及表格型方法強化學習馬爾可夫
- 【Numpy學習】np.count_nonzero()用法解析
- 機器學習之numpy和matplotlib學習(四)機器學習
- python學習 day018打卡 反射Python反射
- 學習打卡2-藝術畫筆見乾坤