【Task04】Numpy學習打卡

Xiao_Spring 發表於 2020-10-28

十、向量化和廣播

1.向量化

我們先看一個numpy中向量化的例子:

【十、例1-1】numpy向量化之加法

>>> a = np.arange(1,5)
>>> b = np.arange(1,5)
>>> print(a,b,a+b)
[1 2 3 4] [1 2 3 4] [2 4 6 8]
>>> print([a[i]+b[i] for i in range(4)])
[2, 4, 6, 8]
>>> c = [x for x in range(1,5)]
>>> d = [x for x in range(1,5)]
>>> print(c,d,c+d)
[1, 2, 3, 4] [1, 2, 3, 4] [1, 2, 3, 4, 1, 2, 3, 4]
>>> print([c[i]+d[i] for i in range(4)])
[2, 4, 6, 8]

我們可以看到a+b返回的是a和b內元素分別相加之後再組合起來的結果,相比下python原生中c+d代表兩個list直接相加。

更多地,numpy還有其他關於向量化的用法,如:

【十、例1-2】numpy向量化舉例其他

>>> a = np.arange(1,5)
>>> b = np.arange(1,5)
#其他運算
>>> print(a-b)
>>> print(a*b)
>>> print(a/b)
>>> print(a**b)
[0 0 0 0]
[ 1  4  9 16]
[1. 1. 1. 1.]
[  1   4  27 256]
#乘以標量
>>> print(a*3)
[ 3  6  9 12]
#與、或、補碼
>>> print(a&b)
[1 2 3 4]
#短路 a若為True跳過後面
>>> print(a|b)
[1 2 3 4]
>>> print(~a)
[-2 -3 -4 -5]
>>> print(a&True)
[1 0 1 0]
>>> print(a&False)
[0 0 0 0]
#比較
>>> print(a > np.e)
>>> print(a >= np.e)
>>> print(a < np.e)
>>> print(a <= np.e)
>>> print(a == np.e)
>>> print(a != np.e)
[False False  True  True]
[ True  True False False]
[ True  True False False]
[False False False False]
[ True  True  True  True]
#索引陣列 布林切片
>>> print(a[a < 3])
[1 2]
# +=與+的區別
>>> c = np.arange(1,5)
>>> d = c
>>> c += c
>>> print(d)
[2 4 6 8]
>>> c = np.arange(1,5)
>>> d = c
>>> c = c+c
>>> print(d)
[1 2 3 4]

這裡重點說一下+=與+的區別,c += c改變了原地址的值,而c=c+c只改變了c的引用,並沒有改變原來的值,因為後者在c+c時,返回的地址就已經不是原來的地址了。

2.廣播

當運算中的 2 個陣列的形狀不同時,numpy 將自動觸發廣播機制。

廣播的規則:

  • 讓所有輸入陣列都向其中形狀最長的陣列看齊,形狀中不足的部分都通過在前面加 1 補齊。
  • 輸出陣列的形狀是輸入陣列形狀的各個維度上的最大值。
  • 如果輸入陣列的某個維度和輸出陣列的對應維度的長度相同或者其長度為 1 時,這個陣列能夠用來計算,否則出錯。
  • 當輸入陣列的某個維度的長度為 1 時,沿著此維度運算時都用此維度上的第一組值。

【十、例2-1】numpy廣播例項

# 4,和4,4運算
a = np.arange(4)
print(a,a.shape)
[0 1 2 3] (4,)
b = np.zeros([4,4])
print(b)
[[0. 0. 0. 0.]
 [0. 0. 0. 0.]
 [0. 0. 0. 0.]
 [0. 0. 0. 0.]]
print(a+b)
[[0. 1. 2. 3.]
 [0. 1. 2. 3.]
 [0. 1. 2. 3.]
 [0. 1. 2. 3.]]

# 4,和4,1運算
a = np.arange(4)
print(a)
[0 1 2 3]
b = np.zeros([4,1])
print(b)
[[0.]
 [0.]
 [0.]
 [0.]]
print(a+b)
[[0. 1. 2. 3.]
 [0. 1. 2. 3.]
 [0. 1. 2. 3.]
 [0. 1. 2. 3.]]

#3,和4,運算
a = np.arange(3)
print(a)
b = np.arange(4)
print(b)
print(a+b)
ValueError: operands could not be broadcast together with shapes (3,) (4,) 

簡單概括就是,如果shape不匹配,要麼其中一個是1,要麼可以補維度,需要滿足下面的情況:

在這裡插入圖片描述
否則就會報錯。

十一、數學函式

前言

不把廣播效應算在內,本章的內容很少會改變原始資料本身的shape,所以著重點在於弄明白每個數學函式的使用方法。

1.算數運算

算數運算函式主要有:

  • add():加
  • subtract():減
  • multiply():乘
  • divide():除
  • floor_divide():整除
  • power():a的b次冪
  • sqrt():開根號
  • square():a的平方

舉例說明它們的用法:

【十一、例1】算數運算函式例項

>>> a = np.random.randint(1,10,[3,3])
>>> print(a)
[[7 7 2]
 [6 6 7]
 [9 5 7]]
>>> b = np.random.randint(1,10,3)
>>> print(b)
[7 3 7]
#加
>>> print(np.add(a,b))
[[14 10  9]
 [13  9 14]
 [16  8 14]]
#減
>>> print(np.subtract(a,b))
[[ 0  4 -5]
 [-1  3  0]
 [ 2  2  0]]
#乘
>>> print(np.multiply(a,b))
[[49 21 14]
 [42 18 49]
 [63 15 49]]
#除
>>> print(np.divide(a,b))
[[1.         2.33333333 0.28571429]
 [0.85714286 2.         1.        ]
 [1.28571429 1.66666667 1.        ]]
#整除
>>> print(np.floor_divide(a,b))
[[1 2 0]
 [0 2 1]
 [1 1 1]]
#a的b次冪
>>> print(np.power(a,b))
[[ 823543     343     128]
 [ 279936     216  823543]
 [4782969     125  823543]]
#根
>>> print(np.sqrt(a))
[[2.64575131 2.64575131 1.41421356]
 [2.44948974 2.44948974 2.64575131]
 [3.         2.23606798 2.64575131]]
#平方
>>> print(np.square(a))
[[49 49  4]
 [36 36 49]
 [81 25 49]]

2.三角函式

三角函式主要有:

  • sin():正弦
  • cos():餘弦
  • tan():正切
  • arcsin():反正弦
  • arccos():反餘弦
  • arctan():反正切

以及numpy.degrees()是用來將弧度轉為角度,舉例說明它們的使用方法:

【十一、例2】三角函式例項

>>> import matplotlib.pyplot as plt
>>> a = np.linspace(0,2 * np.pi,20)
>>> b = np.sin(a)
>>> print(b)
[ 0.00000000e+00  3.24699469e-01  6.14212713e-01  8.37166478e-01
  9.69400266e-01  9.96584493e-01  9.15773327e-01  7.35723911e-01
  4.75947393e-01  1.64594590e-01 -1.64594590e-01 -4.75947393e-01
 -7.35723911e-01 -9.15773327e-01 -9.96584493e-01 -9.69400266e-01
 -8.37166478e-01 -6.14212713e-01 -3.24699469e-01 -2.44929360e-16]
>>> plt.plot(a,b)

在這裡插入圖片描述

>>> b = np.cos(a)
>>> plt.plot(a,b)

在這裡插入圖片描述

>>> a = np.linspace(-0.49*np.pi,0.49*np.pi,100)
>>> b = np.tan(a)
>>> plt.plot(a,b)

在這裡插入圖片描述
這裡注意tan()的範圍在(-pi/2,pi/2)之間,所以我們有0.49*pi來限定範圍。

3.指數和對數

直接上例子說明:

【十一、例3】指數和對數函式例項

>>> a = np.random.randint(1,10,5)
>>> print(a)
[6 5 4 6 3]
#exp e的x次方
>>> print(np.exp(a))
[403.42879349 148.4131591   54.59815003 403.42879349  20.08553692]
#log 以e為底x的對數
>>> print(np.log(a))
[1.79175947 1.60943791 1.38629436 1.79175947 1.09861229]
#log2 以2為底x的對數
>>> print(np.log2(a))
[2.5849625  2.32192809 2.         2.5849625  1.5849625 ]
#log10 以10為底x的對數
>>> print(np.log10(a))
[0.77815125 0.69897    0.60205999 0.77815125 0.47712125]
#exp2 2的x次方
>>> print(np.exp2(a))
[64. 32. 16. 64.  8.]

這裡容易混淆的是np.exp2(),因為np.exp()是e的x次方,所以容易把前者跟e聯絡起來,要注意。

4.加法函式和乘法函式

直接上例子說明:

【十一、例4】加法函式和乘法函式例項

>>> a = np.random.randint(1,10,[4,4])
>>> print(a)
[[2 6 1 9]
 [8 5 2 5]
 [7 6 2 2]
 [5 2 4 2]]
#整和
>>> print(np.sum(a))
68
#沿行求和(列和)
>>> print(np.sum(a,axis=0))
[22 19  9 18]
#沿列求和(行和)
>>> print(np.sum(a,axis=1))
[18 20 17 13]
#累積和(扁平化後)
>>> print(np.cumsum(a))
[ 2  8  9 18 26 31 33 38 45 51 53 55 60 62 66 68]
#沿行求累積和(列累積和)
>>> print(np.cumsum(a,axis=0))
[[ 2  6  1  9]
 [10 11  3 14]
 [17 17  5 16]
 [22 19  9 18]]
#沿列求累積和(行累積和)
>>> print(np.cumsum(a,axis=1))
[[ 2  8  9 18]
 [ 8 13 15 20]
 [ 7 13 15 17]
 [ 5  7 11 13]]
#整積
>>> print(np.prod(a))
580608000
#沿行求積(列積)
>>> print(np.prod(a,axis=0))
[560 360  16 180]
#沿列求積(行積)
>>> print(np.prod(a,axis=1))
[108 400 168  80]
#累積(扁平化後)
>>> print(np.cumprod(a))
[        2        12        12       108       864      4320      8640
     43200    302400   1814400   3628800   7257600  36288000  72576000
 290304000 580608000]
#沿行求累積(列累積)
>>> print(np.cumprod(a,axis=0))
[[  2   6   1   9]
 [ 16  30   2  45]
 [112 180   4  90]
 [560 360  16 180]]
#沿列求累積(行累積)
>>> print(np.cumprod(a,axis=1))
[[  2  12  12 108]
 [  8  40  80 400]
 [  7  42  84 168]
 [  5  10  40  80]]
#沿列求差(相鄰行之差)
>>> print(np.diff(a))
[[ 4 -5  8]
 [-3 -3  3]
 [-1 -4  0]
 [-3  2 -2]]
#沿行求差(相鄰列之差)
>>> print(np.diff(a,axis=0))
[[ 6 -1  1 -4]
 [-1  1  0 -3]
 [-2 -4  2  0]]

注:上述所有的axis=0其實是沿著行去遍歷,但求的是列之間的關係,比如sum中求的是列和,同理axis=1其實是沿著列去遍歷,是在求行之間的關係。

5.四捨五入函式

直接上例子說明:

【十一、例5】四捨五入函式例項

>>> a = np.random.rand(3,3)*10
>>> print(a)
[[1.16944655 7.50128062 2.21134653]
 [8.33939104 9.46452031 4.58700646]
 [5.15796925 4.75485535 6.39179018]]
#四捨五入
>>> print(np.around(a))
[[1. 8. 2.]
 [8. 9. 5.]
 [5. 5. 6.]]
#向上取整
>>> print(np.ceil(a))
[[ 2.  8.  3.]
 [ 9. 10.  5.]
 [ 6.  5.  7.]]
#向下取整
>>> print(np.floor(a))
[[1. 7. 2.]
 [8. 9. 4.]
 [5. 4. 6.]]

6.統計函式

NumPy 提供了很多統計函式,用於從陣列中查詢最小元素,最大元素,百分位標準差和方差等。包括:

  • amin():沿指定軸的最小值
  • amax():沿指定軸的最大值
  • ptp():沿指定軸的最小值與最大值之差
  • percentile():統計中使用的度量 【TODO】
  • median():計算陣列 a 中元素的中位數
  • mean():返回陣列中元素的算術平均值
  • average():根據在另一個陣列中給出的各自的權重計算陣列中元素的加權平均值 【TODO】
  • std():標準差
  • var():方差

【十一、例6】統計函式例項

>>> a = np.random.randint(1,10,[5,5])
>>> print(a)
[[2 8 3 2 6]
 [6 8 7 4 8]
 [2 3 7 9 4]
 [1 2 1 9 7]
 [9 7 3 6 2]]
#沿第一維度的最小值
>>> print(np.amin(a,axis=0))
[1 2 1 2 2]
#沿第一維度的最大值
>>> print(np.amax(a,axis=0))
[9 8 7 9 8]
#沿第一維度的最小值與最大值之差
>>> print(np.ptp(a,axis=0))
[8 6 6 7 6]
#沿第一維度中位數
>>> print(np.median(a,axis=0))
[2. 7. 3. 6. 6.]
#沿第一維度算術平均值
>>> print(np.mean(a,axis=0))
[4.  5.6 4.2 6.  5.4]
#沿第一維度標準差
>>> print(np.std(a,axis=0))
[3.03315018 2.57681975 2.4        2.75680975 2.15406592]
#沿第一維度方差
>>> print(np.var(a,axis=0))
[9.2  6.64 5.76 7.6  4.64]

7.雜項

np.clip是用來替換的,將小於a_min的值換成a_min,將大於a_max的值換成a_max。

np.abs和np.absolute的功能類似,返回元素的絕對值。

np.sign類似於符號函式,負數返回-1,正數返回1,零返回0。

【十一、例7】clip等例項

>>> a = np.random.randint(-5,5,[5,5])
>>> print(a)
>>> print(np.clip(a,a_min=-2,a_max=3))
>>> print(np.abs(a))
>>> print(np.absolute(a))
>>> print(np.sign(a))

十二、邏輯函式

前言

關於邏輯函式的內容,我們在之前11章或多或少都穿插過一些,這裡我們就做一個簡單的整理。

1.真值測試

真值測試包含np.all()np.any()兩種方法:

  • np.all()判斷是否全為真:如果是就返回True;否則返回False。
  • np.any()判斷是否至少有一個為真:如果有就返回True;否則返回False。

舉例:

【十二、例1】真值測試舉例

>>> a = np.random.randint(0,5,10)
>>> b = np.random.randint(0,5,10)
>>> print(a)
[4 1 0 2 4 0 1 0 4 4]
>>> print(b)
[0 2 1 4 4 2 4 2 0 0]
>>> print(np.all(a==a))
True
>>> print(np.all(a==b))
False
>>> print(np.any(a==b))
True
>>> print(np.all([2,np.nan]))
True

其實a==a,a==b只是一個前置環節,目的是為了拿到布林型別的陣列便於真值判斷。

而真值判斷的不僅僅是True和False,0和非0等。如上面最後一個例子,numpy中的nan並不是空物件,其實際上是numpy.float64物件,所以我們不能誤認為其是空物件,從而用bool(np.nan)去判斷是否為空值,這是不對的。

2.陣列內容

如上,nan型別並不能通過np.all()區分,那麼需要有一種方法判斷一個元素是否為nan,所以numpy提供了np.isnan()來判斷:

【十二、例2】isnan()舉例

>>> print(np.isnan([2,3.0,np.nan,4]))
[False False  True False]

3.邏輯運算

我們之前已經見過一些常見的邏輯運算,現在讓我們來歸納一下:

  • not 邏輯非
  • and 邏輯與
  • or 邏輯或
  • xor 邏輯異或

【十二、例3】邏輯運算舉例

>>> a = np.random.randint(0,5,10)>3
>>> print(a)
[False False False  True False  True False False False  True]
>>> b = np.random.randint(0,5,10)<2
>>> print(b)
[False False  True False  True False False False  True  True]
>>> print(np.logical_not(a))
[ True  True  True False  True False  True  True  True False]
>>> print(np.logical_and(a,b))
[False False False False False False False False False  True]
>>> print(np.logical_or(a,b))
[False False  True  True  True  True False False  True  True]
>>> print(np.logical_xor(a,b))
[False False  True  True  True  True False False  True False]

4.對照

  • greater: >
  • greater_equal: >=
  • equal: ==
  • not_equal: !=
  • less: <
  • less_equal: <=
  • isclose:是否接近
  • allclose:是否全部接近

其中它們返回的都是布林型別的ndarray資料型別。

【十二、例4】對照舉例

>>> a = np.random.randint(0,5,10)
>>> print(a)
[3 1 4 1 0 0 4 1 0 4]
>>> print(np.greater(a,2))
[ True False  True False False False  True False False  True]
>>> print(np.greater_equal(a,2))
[ True False  True False False False  True False False  True]
>>> print(np.less(a,3))
[False  True False  True  True  True False  True  True False]
>>> print(np.less_equal(a,3))
[ True  True False  True  True  True False  True  True False]
>>> print(np.equal(a,2))
[False False False False False False False False False False]
>>> print(np.not_equal(a,2))
[ True  True  True  True  True  True  True  True  True  True]
>>> print(np.isclose([1e10,1e-8], [1.0001e10,1e-9]))
[False  True]

allclose等同於isclose+logical_and的功能,isclose(a,b)返回True的前提是:

absolute(a - b) <= (atol + rtol * absolute(b))

而atol,rtol有預設值:

在這裡插入圖片描述
分別是1e-05和1e-08。

注意,上面的例子np.isclose([1e10,1e-8], [1.0001e10,1e-9])特別容易把[1e10,1e-8]和[1.0001e10,1e-9]分別各自計算,而忘了這是numpy的向量化特性,應該a1和b1計算,a2和b2計算,而不是交叉。

參考文章

1.https://blog.csdn.net/weixin_38287297/article/details/81430733

2.https://www.runoob.com/numpy/numpy-binary-operators.html