ndarray基礎
NumPy(Numerical Python) 是 Python 語言的一個擴展程序庫,支持大量的維度數組與矩陣運算,此外也針對數組運算提供大量的數學函數庫。是在學習機器學習、深度學習之前應該掌握的一個非常基本且實用的Python庫。
初見ndarray對象
ndarray基本概念
ndarray是numpy庫中的一個處於核心地位的數據結構,全稱爲N-Dimension Array
,從字面上就表明了它是一個N維數組。要注意的是,ndarray是同質的,就是說其中的元素都必須是同一種數據類型(PS: python中的list列表是異質的)
ndarray實例化後,包含一些基本屬性,如shape、ndim、size、dtype等。例如現在有一個3行5列的矩陣(ndarray)如下:
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]])
那麼該ndarray的shape值爲(3,5)
(元組類型,表示3行5列)
ndim爲2(表示矩陣維度爲2)
size爲15(矩陣總共有15個元素)
dtype爲int32(因爲矩陣中的元素都是32位整數)
# 導入numpy並取別名爲np
import numpy as np
# 構造ndarray
a = np.arange(15).reshape(3, 5)
# 打印a的shape,ndim,size,dtype
print(a.shape)
print(a.ndim)
print(a.size)
print(a.dtype)
ndarray對象實例化的方法
實例化ndarray的最常用的方法就是使用array函數,用list對象作爲參數傳遞,返回以list中的內容爲初始值的ndarray對象:
import numpy as np
# 使用列表作爲初始值,實例化ndarray對象a
a = np.array([2,3,4])
# 打印ndarray對象a
print(a)
除了array以外比較常用的實例化函數還有zeros、ones、empty,這三個函數的參數都是表示返回ndarray對象形狀的元組
zeros返回一個全爲0的ndarray對象:
import numpy as np
# 實例化ndarray對象a,a是一個3行4列的矩陣,矩陣中元素全爲0
a = np.zeros((3, 4))
# 打印ndarray對象a
print(a)
ones與上類似,返回一個全爲1的ndarray對象:
import numpy as np
# 實例化ndarray對象a,a是一個3行4列的矩陣,矩陣中元素全爲1
a = np.ones((3, 4))
# 打印ndarray對象a
print(a)
empty則是返回一個元素的值沒有經過初始化的ndarray:
import numpy as np
# 實例化ndarray對象a,a是一個2行3列的矩陣,矩陣中元素全爲隨機值
a = np.empty((2, 3))
# 打印ndarray對象a
print(a)
ndarray形狀操作
ndarray的數組與一般的數組列表的一個不同之處就是它可以變換形狀,這使得一些數據處理的操作變得尤爲方便。最直接粗暴的變換方法就是修改shape屬性的值
import numpy as np
a = np.zeros((3, 4))
# 直接修改shape屬性
a.shape = [4, 3]
但這樣做不符合編程規範,更優雅的方法是使用ndarray對象提供的方法reshape。該方法(及其他大部分方法都)有兩種調用方式,一種是直接作爲函數調用,參數爲ndarray對象及形狀元組;另一種是作爲ndarray實例的成員函數來調用。
面向對象風格
import numpy as np
a = np.zeros((3, 4))
# 調用a的成員函數reshape將3行4列改成4行3列
a = a.reshape((4, 3))
面向過程風格
import numpy as np
a = np.zeros((3, 4))
# 調用reshape函數將a變形成4行3列
a = np.reshape(a, (4, 3))
另外,reshape有一個非常方便的功能就是可以自動判斷某一維上的長度。例如要將一個6行8列的二維數組變化成2列,那麼只要在未知的維上傳入參數-1,reshape就會自動得出變化後的數組應爲24行
import numpy as np
a = np.zeros((6, 8))
# 行的維度上填-1,會讓numpy自己去推算出行的數量,很明顯,行的數量應該是24
a = a.reshape((-1, 2))
不過最多隻有一個維度能填-1,如果超過一個維度是-1就會拋出異常
PS:要注意reshape並不會改變原ndarray的形狀,而只是返回變化後的ndarray對象,所以需要將其賦值給原變量。
如果想要直接改變原ndarray的形狀,除了之前的直接修改shape屬性,可以使用resize方法:
import numpy as np
a = np.zeros((3, 4))
# 將a從3行4列的二維數組變成一個有12個元素的一維數組
a.resize(12)
ndarray基礎操作
作爲數據處理的利器,ndarray自然提供了常用的數學的處理功能。
基本運算
ndarray可以直接和基本的數字類型變量運算,返回該運算作用到數組中所有元素上的結果。
import numpy as np
a = np.array([0, 1, 2, 3])
# a中的所有元素都加2,結果爲[2, 3, 4, 5]
b = a + 2
# a中的所有元素都減2,結果爲[-2, -1, 0, 1]
c = a - 2
# a中的所有元素都乘以2,結果爲[0, 2, 4, 6]
d = a * 2
# a中的所有元素都平方,結果爲[0, 1, 4, 9]
e = a ** 2
# a中的所有元素都除以2,結果爲[0, 0.5, 1, 1.5]
f = a / 2
# a中的所有元素都與2比,結果爲[True, True, False, False]
g = a < 2
ndarray與ndarray之間也可以運算,返回兩個數組中對應位置元素相互作用的結果。
import numpy as np
a = np.array([[0, 1], [2, 3]])
b = np.array([[1, 1], [3, 2]])
# a與b逐個元素相加,結果爲[[1, 2], [5, 5]]
c = a + b
# a與b逐個元素相減,結果爲[[-1, 0], [-1, 1]]
d = a - b
# a與b逐個元素相乘,結果爲[[0, 1], [6, 6]]
e = a * b
# a的逐個元素除以b的逐個元素,結果爲[[0., 1.], [0.66666667, 1.5]]
f = a / b
# a與b逐個元素做冪運算,結果爲[[0, 1], [8, 9]]
g = a ** b
# a與b逐個元素相比較,結果爲[[True, False], [True, False]]
h = a < b
PS:當運算的兩個ndarray形狀不同時,會觸發Numpy的廣播(broadcasting)機制嘗試將兩個ndarray自動拓展成相同形狀。關於廣播機制將在下一篇文章中講解。
上述運算都是逐個元素進行運算,如果想要進行數學中的矩陣乘這種運算,可以使用Numpy提供的運算符@或函數dot。
import numpy as np
A = np.array([[1, 1], [0, 1]])
B = np.array([[2, 0], [3, 4]])
# @表示矩陣乘法,矩陣A乘以矩陣B,結果爲[[5, 4], [3, 4]]
print(A @ B)
# 面向對象風格,矩陣A乘以矩陣B,結果爲[[5, 4], [3, 4]]
print(A.dot(B))
# 面向過程風格,矩陣A乘以矩陣B,結果爲[[5, 4], [3, 4]]
print(np.dot(A, B))
簡單統計
在一般的數組中進行統計需要手動用循環遍歷整個數組,而ndarray爲了解放程序員們的雙手,提供了sum、min、max、argmax、argmin等方法實現簡單的統計功能。
import numpy as np
a = np.array([[-1, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 13]])
# 計算a中所有元素的和,結果爲67
print(a.sum())
# 找出a中最大的元素,結果爲13
print(a.max())
# 找出a中最小的元素,結果爲-1
print(a.min())
# 找出a中最大元素在a中的位置,由於a中有12個元素,位置從0開始計,所以結果爲11
print(a.argmax())
# 找出a中最小元素在a中位置,結果爲0
print(a.argmin())
當需要按某一個方向/軸來進行統計時,可以向這些方法傳入axis參數。例如有一個學生成績表如下,要統計出每一名學生的總分:
姓名 | 語文 | 數學 | 英語 | 物理 | 化學 | 政治 | 歷史 |
---|---|---|---|---|---|---|---|
張三 | 90 | 112 | 104 | 84 | 89 | 77 | 80 |
李四 | 105 | 109 | 114 | 81 | 81 | 87 | 92 |
王五 | 93 | 103 | 97 | 79 | 75 | 80 | 79 |
用ndarray存儲如下
a=np.array([[ 90, 112, 104, 84, 89, 77, 80],
[ 105, 109, 114, 81, 81, 87, 92],
[ 93, 103, 97, 79, 75, 80, 79]])
那麼要做的就是統計每一行的和,沿着橫軸把元素相加,axis爲所沿軸的序號
其餘方法以及更高維的數組同理。
PS:當沒有修改axis時,axis的值默認爲None。意思是在統計時會把ndarray對象中所有的元素都考慮在內。
隨機數生成
簡單隨機數生成
NumPy的random模塊下提供了許多生成隨機數的函數,如果對於隨機數的概率分佈沒有什麼要求,則通常可以使用random_sample、choice、randint等函數來實現生成隨機數的功能。這些函數的功能都和Python標準庫random 中的類似,只不過是作用在ndarray對象上或返回ndarray。
random_sample:用於生成區間爲的隨機數,參數size爲生成ndarray的形狀。
import numpy as np
'''
結果可能爲[[0.32343809, 0.38736262, 0.42413616]
[0.86190206, 0.27183736, 0.12824812]]
'''
print(np.random.random_sample(size=[2, 3]))
choice:從給定範圍的數字中隨機挑選生成ndarray。參數a是一維數組或整數,給定一維數組時生成ndarray中的元素將從中隨機選取,給定整數時則從np.arange(a)中隨機選取;參數size爲生成ndarray形狀;參數replace決定是否能有重複元素,默認值爲True即允許重複。
import numpy as np
'''
模擬擲5次骰子
擲骰子時可能出現的點數爲1, 2, 3, 4, 5, 6,所以a=[1,2,3,4,5,6]
模擬5次擲骰子所以size=5
骰子點數可以重複出現所以replace=True
結果可能爲 [4 3 1 4 3]
'''
print(np.random.choice(a=[1, 2, 3, 4, 5, 6], size=5,replace=True))
PS:當生成ndarray中元素數量多於a且replace=False時會拋出異常ValueError: Cannot take a larger sample than population when ‘replace=False’
randint:生成選定區間內的整數隨機選取產生的ndarray。參數low、high爲區間的上下限,區間爲半開半閉區間;參數size爲生成數組形狀。
import numpy as np
'''
模擬擲5次骰子
擲骰子時可能出現的點數爲1, 2, 3, 4, 5, 6,所以low=1,high=7
模擬5此擲骰子所以size=5
結果可能爲 [6, 4, 3, 1, 3]
'''
print(np.random.randint(low=1, high=7, size=5)
概率分佈隨機數生成
如果對於產生的隨機數的概率分佈有特別要求,NumPy同樣提供了從指定的概率分佈中採樣得到的隨機值的接口。在這裏主要介紹正態分佈。
正態分佈又稱爲高斯分佈,其分佈圖形如下:
要根據正態分佈產生隨機數,可以使用normal函數。該函數的參數有loc、scale、size,分別是正態分佈的均值、方差和生成數組的形狀。默認情況下loc=0,scale=1,即標準正態分佈。
隨機種子
衆所周知計算機無法產生真正的隨機數,所有計算機產生的隨機數本質都是僞隨機數,其結果由隨機種子決定。Numpy中手動設定隨機種子的方法就是使用seed函數,其參數爲seed。
import numpy as np
# 設置隨機種子爲233
np.random.seed(seed=233)
data = [1, 2, 3, 4]
# 隨機從data中挑選數字,結果爲4
print(np.random.choice(data))
# 隨機從data中挑選數字,結果爲4
print(np.random.choice(data))