一篇文章搞懂Numpy的Python學習指北(文末附應用舉例)

大家在剛開始學python時,有沒有pip install numpy呢?下面我們一起來學習一下:

Numpy是NumericalPython的簡稱,是Python中高性能科學計算和數據分析的基礎包。ndarray數組是Numpy中的基礎數據結構式,它具有矢量算術運算和複雜廣播的能力,可以實現快速的計算並且能節省存儲空間。

在Python中使用list列表可以非常靈活的處理多個元素的操作,但是其效率卻比較低。ndarray數組相比於Python中的list列表更加方便,下面我們做一下對比學習:

  • ndarray數組中所有元素的數據類型是相同的,數據地址是連續的,批量操作數組元素時速度更快;list列表中元素的數據類型可以不同,需要通過尋址方式找到下一個元素
  • ndarray數組中實現了比較成熟的廣播機制,矩陣運算時不需要寫for循環
  • Numpy底層是用c語言編寫的,內置了並行計算功能,運行速度高於純Python代碼

下面一起來看看使用list列表和ndarray數組完成相同的任務,兩種方式在代碼上的區別,下面說一下我的代碼結構:

import numpy as np

def method_1():  #list列表
    a1 = [1, 2, 3, 4, 5]
    b1 = [1, 2, 3, 4, 5]

def method_2():  #ndarray數組
    a2 = np.array([1, 2, 3, 4, 5])
    b2 = np.array([1, 2, 3, 4, 5])

def main():
    method_1()
    method_2()

if __name__ == '__main__':
    main()

方法1裏使用list列表,變量a1、b1表示方法1裏的列表;
方法2裏使用ndarray數組,變量a2、b2表示方法2裏的數組。

下面的代碼中只展示method_1與method_2裏的代碼,而main函數內容不變,故省略

ndarray數組和list列表分別完成對每個元素同時進行加減乘除的計算

def method_1():
    a1 = [1, 2, 3, 4, 5]
    b1 = [1, 2, 3, 4, 5]
    for i in range(5):
        a1[i] = a1[i] + 1
        b1[i] = b1[i] * 2
    print("a1:",a1)
    print("b1:",b1)

在這裏插入圖片描述

def method_2():
    a2 = np.array([1, 2, 3, 4, 5])
    b2 = np.array([1, 2, 3, 4, 5])
    a2 = a2 + 1
    b2 = b2 * 2
    print("a2:",a2)
    print("b2:",b2)

在這裏插入圖片描述

ndarray數組和list列表對應元素分別完成相加計算

在列表裏,我們直接相加:
在這裏插入圖片描述
這樣的效果不是我們想要的,看來,我們還是要用循環:

def method_1():
    a1 = [1, 2, 3, 4, 5]
    b1 = [1, 2, 3, 4, 5]
    c1 = []
    for i in range(5):
        c1.append(a1[i] + b1[i])
    print("c1:",c1)

在這裏插入圖片描述

def method_2():
    a2 = np.array([1, 2, 3, 4, 5])
    b2 = np.array([1, 2, 3, 4, 5])
    c2 = a2 + b2
    print("c2:",c2)

在這裏插入圖片描述
從上面的示例中可以看出,使用ndarray數組不需要寫for循環,就可以非常方便的完成數學計算,在操作矢量或者矩陣時,可以像操作普通的數值變量一樣編寫程序,使得代碼極其簡潔。

另外,ndarray數組還提供了廣播機制,它會按一定規則自動對數組的維度進行擴展以完成計算,如下面例子所示,1維數組和2維數組進行相加操作,ndarray數組會自動擴展1維數組的維度,然後再對每個位置的元素分別相加:

def method_2():
    # 二維數組維度 2x5
    a2 = np.array([[1, 2, 3, 4, 5],
                   [6, 7, 8, 9, 10]])
    # 一維數組維度 1x5
    b2 = np.array([1, 2, 3, 4, 5])
    c2 = a2 + b2
    print("c2:\n",c2)

在這裏插入圖片描述
既然使用數組可以提高我們的體驗感,那麼下面我們就來看看它的創建方法

ndarray數組的創建方法

我們可以把列表轉換成數組:

def method_1():
    a1 = [1, 2, 3, 4, 5]
    print("a1:",a1)
    a2 = np.array(a1)
    print("a2:",a2)

在這裏插入圖片描述
使用np.arange,通過指定start, stop , interval來產生一個1維的ndarray:

def method_2():
    a2 = np.arange(0, 20, 2)
    print("a2:",a2)

在這裏插入圖片描述
我們還可以創建一個全零的或者是全1的數組:

def method_2():
    a2 = np.zeros([3,3])
    print("a2:\n",a2)

在這裏插入圖片描述

def method_2():
    a2 = np.ones([3,3])
    print("a2:\n",a2)

在這裏插入圖片描述

數組有屬於自己的屬性,下面一起來看看查詢它的方法

查看ndarray數組的屬性

ndarray的屬性包括形狀shape、數據類型dtype、元素個數size和維度ndim等:

  • 數組的數據類型 ndarray.dtype
  • 數組的形狀 ndarray.shape,1維數組(N, ),二維數組(M, N),三維數組(M, N, K)
  • 數組的維度大小,ndarray.ndim, 其大小等於ndarray.shape所包含元素的個數
  • 數組中包含的元素個數 ndarray.size,其大小等於各個維度的長度的乘積
def method_2():
    a2 = np.ones([3,3])
    print("a2:\n",a2)
    print('dtype: {}, shape: {}, ndim: {}, size: {}'.format(a2.dtype, a2.shape, a2.ndim, a2.size))

在這裏插入圖片描述
創建ndarray之後,可以對其數據類型進行更改,或者對形狀進行調整

改變ndarray數組的數據類型和形狀

def method_2():
    a2 = np.ones([3,3])
    print("a2:\n",a2)
    print('dtype: {}, shape: {}, ndim: {}, size: {}'.format(a2.dtype, a2.shape, a2.ndim, a2.size))
    # 轉化數據類型
    b2 = a2.astype(np.int64)
    print("b2:\n",b2)
    print('b2, dtype: {}, shape: {}'.format(b2.dtype, b2.shape))
    # 改變形狀
    c2 = a2.reshape([1, 9])
    print("c2:\n",c2)
    print('c2, dtype: {}, shape: {}'.format(c2.dtype, c2.shape))

在這裏插入圖片描述
ndarray數組的基本運算

ndarray數組可以像普通的數值型變量一樣進行加減乘除操作

標量和ndarray數組之間的運算在開頭已經講過了,這裏就不過多去闡述了,下面來看看兩個ndarray數組之間的運算

def method_2():
    a2 = np.ones([2,5])
    b2 = np.array([[1, 2, 3, 4, 5],
                   [6, 7, 8, 9, 10]])

兩個數組對應位置的元素相減:

	c2 = b2 -a2
    print(c2)

在這裏插入圖片描述
兩個數組對應位置的元素相乘:

	c2 = b2 * a2
    print(c2)

在這裏插入圖片描述
數組開根號,將每個位置的元素都開根號:

	c2 = b2 ** 0.5
    print(c2)

在這裏插入圖片描述
在程序中,通常需要訪問或者修改ndarray數組某個位置的元素,也就是要用到ndarray數組的索引;有些情況下可能需要訪問或者修改一些區域的元素,則需要使用數組的切片。

ndarray數組的索引和切片

索引和切片的使用方式與Python中的list類似,ndarray數組可以基於 -n ~ n-1 的下標進行索引,切片對象可以通過內置的 slice 函數,並設置 start, stop 及 step 參數進行,從原數組中切割出一個新數組。

def method_2():
    a2 = np.arange(30)
    b2 = np.array([[1, 2, 3, 4, 5],
                   [6, 7, 8, 9, 10]])
    print(a2)
    print("a2[0]:",a2[0])
    print("b2[0][1]:",b2[0][1])

在這裏插入圖片描述

	print("a2[5:10]:",a2[5:10])
    print("b2[1:2]:",b2[1:2])

在這裏插入圖片描述
將一個標量值賦值給一個切片時,該值會自動傳播到整個選區:

def method_2():
    a2 = np.arange(15)
    print(a2)
    a2[5:10] = 20
    print(a2)

在這裏插入圖片描述
除此之外,視圖上的任何修改都會直接反映到源數組上:

def method_2():
    a2 = np.arange(15)
    print(a2)
    b2 = a2[5:10]
    b2[1] = 20
    print("a2:",a2)
    print("b2:",b2)

在這裏插入圖片描述
要想避免這種情況,我們可以通過copy()給新數組創建不同的內存空間:

def method_2():
    a2 = np.arange(15)
    print(a2)
    b2 = a2[5:10]
    b2 = np.copy(b2)
    b2[1] = 20
    print("a2:",a2)
    print("b2:",b2)

在這裏插入圖片描述
多維數組的索引和切片:

def method_2():
    a2 = np.arange(6)
    b2 = a2.reshape(3, 2, 1)
    print("a2:",a2)
    print("b2:\n",b2)

在這裏插入圖片描述
只有一個索引指標時,會在第0維上索引,後面的維度保持不變:

print("b2[0]:\n",b2[0])

在這裏插入圖片描述
兩個索引指標:

	print("b2[0,1]:\n",b2[0,1])
    print("b2[0][1]:\n",b2[0][1])

在這裏插入圖片描述
我們還可以使用python中的for語法對數組切片:

def method_2():
    a2 = np.arange(24)
    a2 = a2.reshape([6, 4])
    b2 = [a2[k:k+2] for k in range(0, 6, 2)] 
    print("a2:",a2)
    print("b2:\n",b2)

k in range(0, 6, 2) 決定了k的取值可以是0, 2, 4:
在這裏插入圖片描述

ndarray數組的統計運算

計算均值,使用arr.mean() 或 np.mean(arr),二者是等價的:

def method_2():
    a2 = np.array([[1,2,3], [4,5,6], [7,8,9]])
    print("a2:",a2.mean())
    print("a2:",np.mean(a2))

在這裏插入圖片描述
除此之外還有:

  • 求和 arr.sum(), np.sum(arr)
  • 最大值 arr.max(), np.max(arr)
  • 求最小值 arr.min(), np.min(arr)
  • 找出最大元素的索引 arr.argmax(), arr.argmax(axis=0), arr.argmax(axis=1)
  • 找出最小元素的索引 arr.argmin(), arr.argmin(axis=0), arr.argmin(axis=1)
  • 計算標準差 arr.std()
  • 計算方差 arr.var()

這裏面可以指定參數:

def method_2():
    a2 = np.array([[1,2,3], [4,5,6], [7,8,9]])
    print("a2:",a2.mean(axis = 1))
    print("a2:",a2.sum(axis=0))

在這裏插入圖片描述
沿着第1維求平均,也就是將[1, 2, 3]取平均等於2,[4, 5, 6]取平均等於5,[7, 8, 9]取平均等於8;
沿着第0維求和,也就是將[1, 4, 7]求和等於12,[2, 5, 8]求和等於15,[3, 6, 9]求和等於18。

設置隨機數種子

如果設置隨機數種子,那麼產生的隨機數就不會變即每次運行結果一致:

def method_2():
    for i in range(3):
        np.random.seed(20)
        a2 = np.random.rand(3, 3)
        print("a2:",a2)

在這裏插入圖片描述
下面是不設置隨機數種子生成的隨機數:
在這裏插入圖片描述
隨機打亂ndarray數組順序

def method_2():
    a2 = np.random.rand(3, 3)
    print('before random shuffle: \n', a2)
    np.random.shuffle(a2)
    print('after random shuffle: \n', a2)

在這裏插入圖片描述
這是1維數組,下面看看2維數組的:

def method_2():
    a2 = np.arange(0, 30)
    a2 = a2.reshape(6, 5)
    print('before random shuffle: \n', a2)
    np.random.shuffle(a2)
    print('after random shuffle: \n', a2)

在這裏插入圖片描述
隨機打亂1維數組順序時,發現所有元素位置都改變了;隨機打亂二維數組順序時,發現只有第行的順序被打亂了,列的順序保持不變。

隨機選取元素

def method_2():
    a2 = np.arange(0, 15)
    b2 = np.random.choice(a2, size=5)
    print("a2:",a2)
    print("b2:",b2)

在這裏插入圖片描述
Numpy中實現了線性代數中常用的各種操作,並形成了numpy.linalg線性代數相關的模塊:

線性代數

矩陣相乘:

def method_2():
    a2 = np.arange(12)
    b2 = a2.reshape([3, 4])
    c2 = a2.reshape([4, 3])
    print("b2:\n",b2)
    print("c2:\n",c2)
    # 矩陣b的第二維大小,必須等於矩陣c的第一維大小
    d2 = b2.dot(c2) # 等價於 np.dot(b, c)
    print("d2:\n",d2)

在這裏插入圖片描述
學過線性代數應該知道,矩陣b與c相乘時矩陣b的第二維大小,必須等於矩陣c的第一維大小

	e2 = np.diag(d2)
    f2 = np.diag(e2)
    print("e2:\n",e2)
    print("f2:\n",f2)

使用diag 可以用一維數組的形式返回方陣的對角線(或非對角線)元素,或將一維數組轉換爲方陣(非對角線元素爲0):
在這裏插入圖片描述
除此之外,還有:

  • trace 計算對角線元素的和
  • det 計算矩陣行列式
  • eig 計算方陣的特徵值和特徵向量
  • inv 計算方陣的逆

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

Numpy保存和導入文件

Numpy提供了save和load接口,直接將數組保存成文件(保存爲.npy格式),或者從.npy文件中讀取數組:

def method_2():
    a2 = np.random.rand(3,3)
    print("a2:\n",a2)
    np.save('a.npy', a2)
    b2 = np.load('a.npy')
    print("b2:\n",b2)

在這裏插入圖片描述
下面是AI Studio上的應用舉例:

Numpy應用舉例1——計算激活函數

使用ndarray數組可以很方便的構建數學函數,而且能利用其底層的矢量計算能力快速實現計算。神經網絡中比較常用激活函數是Sigmoid和ReLU,其定義如下

Sigmoid激活函數:
在這裏插入圖片描述
ReLU激活函數:
在這裏插入圖片描述
下面使用numpy和matplotlib計算函數值並畫出圖形:

# ReLU和Sigmoid激活函數示意圖
import numpy as np
%matplotlib inline
import matplotlib.pyplot as plt
import matplotlib.patches as patches

#設置圖片大小
plt.figure(figsize=(8, 3))

# x是1維數組,數組大小是從-10. 到10.的實數,每隔0.1取一個點
x = np.arange(-10, 10, 0.1)
# 計算 Sigmoid函數
s = 1.0 / (1 + np.exp(- x))

# 計算ReLU函數
y = np.clip(x, a_min = 0., a_max = None)

#########################################################
# 以下部分爲畫圖程序

# 設置兩個子圖窗口,將Sigmoid的函數圖像畫在左邊
f = plt.subplot(121)
# 畫出函數曲線
plt.plot(x, s, color='r')
# 添加文字說明
plt.text(-5., 0.9, r'$y=\sigma(x)$', fontsize=13)
# 設置座標軸格式
currentAxis=plt.gca()
currentAxis.xaxis.set_label_text('x', fontsize=15)
currentAxis.yaxis.set_label_text('y', fontsize=15)

# 將ReLU的函數圖像畫在左邊
f = plt.subplot(122)
# 畫出函數曲線
plt.plot(x, y, color='g')
# 添加文字說明
plt.text(-3.0, 9, r'$y=ReLU(x)$', fontsize=13)
# 設置座標軸格式
currentAxis=plt.gca()
currentAxis.xaxis.set_label_text('x', fontsize=15)
currentAxis.yaxis.set_label_text('y', fontsize=15)

plt.show()

在這裏插入圖片描述
Numpy應用舉例2——圖像翻轉和裁剪

圖像是由像素點構成的矩陣,其數值可以用ndarray來表示:

import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
# 讀入圖片
image = Image.open('2007_003267.jpg')
image = np.array(image)
# 查看數據形狀,其形狀是[H, W, 3],
# 其中H代表高度, W是寬度,3代表RGB三個通道
image.shape

在這裏插入圖片描述

image2 = image[::-1, :, :]
plt.imshow(image2)

垂直方向翻轉,這裏使用數組切片的方式來完成,相當於將圖片最後一行挪到第一行,倒數第二行挪到第二行,…, 第一行挪到倒數第一行,對於行指標,使用::-1來表示切片,負數步長表示以最後一個元素爲起點,向左走尋找下一個點,對於列指標和RGB通道,僅使用:表示該維度不改變
在這裏插入圖片描述

# 水平方向翻轉
image3 = image[:, ::-1, :]
plt.imshow(image3)

在這裏插入圖片描述

# 保存圖片
im3 = Image.fromarray(image3)
im3.save('im3.jpg')

在這裏插入圖片描述

#  高度方向裁剪
H, W = image.shape[0], image.shape[1]
# 注意此處用整除,H_start必須爲整數
H1 = H // 2 
H2 = H
image4 = image[H1:H2, :, :]
plt.imshow(image4)

在這裏插入圖片描述

#  寬度方向裁剪
W1 = W//6
W2 = W//3 * 2
image5 = image[:, W1:W2, :]
plt.imshow(image5)

在這裏插入圖片描述

# 兩個方向同時裁剪
image5 = image[H1:H2, \
               W1:W2, :]
plt.imshow(image5)

在這裏插入圖片描述

# 調整亮度
image6 = image * 0.5
plt.imshow(image6.astype('uint8'))

在這裏插入圖片描述

# 調整亮度
image7 = image * 2.0
# 由於圖片的RGB像素值必須在0-255之間,
# 此處使用np.clip進行數值裁剪
image7 = np.clip(image7, \
        a_min=None, a_max=255.)
plt.imshow(image7.astype('uint8'))

在這裏插入圖片描述

#高度方向每隔一行取像素點
image8 = image[::2, :, :]
plt.imshow(image8)

在這裏插入圖片描述

#寬度方向每隔一列取像素點
image9 = image[:, ::2, :]
plt.imshow(image9)

在這裏插入圖片描述

#間隔行列採樣,圖像尺寸會減半,清晰度變差
image10 = image[::2, ::2, :]
plt.imshow(image10)
image10.shape

在這裏插入圖片描述
tanh也是神經網絡中常用的一種激活函數,下面我們使用numpy計算tanh激活函數:
在這裏插入圖片描述

import numpy as np
import matplotlib.pyplot as plt

#設置圖片大小
plt.figure(figsize=(8, 3))

# x是1維數組,數組大小是從-10. 到10.的實數,每隔0.1取一個點
x = np.arange(-10, 10, 0.1)
# 計算 tanh函數
t = (np.exp(x) - np.exp(- x)) / (np.exp(x) + np.exp(- x))

# 畫出函數曲線
plt.plot(x, t, color='r')
# 添加文字說明
plt.text(-5., 0.9, r'$y=\tanh(x)$', fontsize=13)
# 設置座標軸格式
currentAxis=plt.gca()
currentAxis.xaxis.set_label_text('x', fontsize=15)
currentAxis.yaxis.set_label_text('y', fontsize=15)

plt.show()

在這裏插入圖片描述
下面,我們統計一下隨機生成矩陣中有多少個元素大於0:

這裏我用了最簡單的方法,即使用兩個for循環

import numpy as np

arr = np.random.randn(10,10)
print(arr)
arr[arr < 0] = 0
print(arr)
resdata = []
for i in range(len(arr)):
    for j in range(len(arr)):
        if arr[i][j] > 0:
            resdata.append(arr[i][j])
print(len(resdata))

在這裏插入圖片描述
當然,這裏肯定還有更加便捷的算法,歡迎大家在評論區交流討論!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章