圖像處理總是與多維數(shù)組啸臀、多維矩陣離不開具被,python中用于處理數(shù)組的高效數(shù)據(jù)庫之一是Numpy,因此在此對Numpy的一些概念進(jìn)行鞏固,記錄numpy常見的操作斤吐,最后將廣播機制的常見使用進(jìn)行記錄站粟。
Numpy的本質(zhì)
numpy可以提供快速高效的多維數(shù)組對象ndarray,可以用于讀寫硬盤上基于數(shù)組的數(shù)據(jù)集工具曾雕,可以對數(shù)組進(jìn)行元素級別的計算奴烙;Numpy在數(shù)據(jù)分析方便便另一個重要作用就是成為算法之間傳遞數(shù)據(jù)的容器。
Numpy的ndarray底層實現(xiàn)是一個數(shù)據(jù)塊剖张,dtype類型切诀,表示數(shù)組形狀的元組以及一個跨度元組。
比如一個二維的的int32 的ndarray搔弄,因為int32占據(jù)4字節(jié)幅虑,因此這個數(shù)組底層一個60字節(jié)的內(nèi)存空間的數(shù)據(jù)塊,dtype類型為np.int32,表示數(shù)組形狀的元組(3,5)顾犹,以及一個跨度元組(20,4).
數(shù)據(jù)類型決定了ndarray將一塊內(nèi)存解釋為特定數(shù)據(jù)類型所需要的信息倒庵。數(shù)據(jù)類型是numpy強大靈活的原因,
如何理解跨度元組的概念炫刷?跨度元組中的每個整數(shù)是指為了前進(jìn)到當(dāng)前維度下一個元素需要跨過的字節(jié)數(shù)擎宝。一個 的數(shù)組,在第一個維度中有三個元素浑玛,每個元素是一個長度為5的int32型整數(shù)绍申,因此從當(dāng)前元素移動到下個元素需要跨過
字節(jié);而對于第二個維度顾彰,其移動到下個元素极阅,只需要移動一個整數(shù)長度,也就是4.
常用的數(shù)組操作
創(chuàng)建數(shù)組
如果沒有指定dtype涨享,默認(rèn)為float64
從已有的python數(shù)組生成
data1= [0,1,2,3,4,5]
arr1 = np.array(data1)
創(chuàng)建全1或者全0數(shù)組,empty,順序數(shù)組
np.zeros(10)
np.zeros((3,6))
np.ones((3,6))
np.empty((2,3,3))
np.arange(15)
獲得數(shù)組的屬性
arr.dtype
arr.shape
arr.ndim
改變數(shù)據(jù)類型
float轉(zhuǎn)為int會直接截斷小數(shù)部分
int_arr = arr.astype(np.int32)
字符串轉(zhuǎn)為整數(shù)筋搏,可以優(yōu)雅的進(jìn)行
numeric_string = np.array(['1.25','9.6'],dtype=np.string_)
numeric_string.astype(float)
改變數(shù)組形狀
reshape操作不會復(fù)制任何數(shù)據(jù),僅僅是改變其表示數(shù)組形狀的元組
arr.reshape((2,5))
將多維數(shù)組扁平化有兩個方法灰伟,其中ravel不會產(chǎn)生元數(shù)據(jù)的副本拆又,而flatten則會返回數(shù)據(jù)的副本
arr.ravel()
arr2 = arr.flatten()
數(shù)組和數(shù)組之間的算數(shù)運算
相同大小的數(shù)組和數(shù)組的算數(shù)運算都是元素級別的。數(shù)組和標(biāo)量的運算會將標(biāo)量值傳播到各個元素上栏账。大小不同的數(shù)組之間的運算叫廣播帖族。
廣播是一個值得單獨提出來的模塊。放在后面仔細(xì)講挡爵。
數(shù)組的合并和拆分
numpy.concatenate按照指定軸將數(shù)組連接在一起竖般,vstack等價于np.concatenate([arr1,arr2],axis=0),hstack等價于np.concatenate([arr1,arr2],axis=1)
arr1 = np.array([[1,2,3],[4,5,6]])
arr2 = np.array([7,8,9],[10,11,12])
np.concatenate([arr1,arr2],axis=0)
#將會得到一個(4,3)形狀的數(shù)組
np.concatenate([arr1,arr2],axis=1)
#將會得到一個(2,6)形狀的數(shù)組
數(shù)組的復(fù)制
想要得到數(shù)組的復(fù)制而不是視圖茶鹃,需要用copy()函數(shù)
arr[5:8].copy()
基本索引和切片
數(shù)組的切片反應(yīng)的是原始數(shù)組的視圖涣雕,數(shù)據(jù)并不會被復(fù)制艰亮,因此對切片上的所有改動都會直接反應(yīng)到原始數(shù)組上。
arr = np.arange(9).reshape((3,3))
# 索引
arr[2]
#得到的是np.array([7,8,9])
arr[0][2]
arr[0,2]
# 切片索引
#對于二維數(shù)組挣郭,可以進(jìn)行這些類型的切片索引
arr[:2] #默認(rèn)axis=0
arr[:2,1:]
arr[2,:1]
arr[:,:1]
# 布爾型索引
#bool型數(shù)組長度必須和被索引的軸長度一致迄埃。一般切片默認(rèn)axis=0.
#通過bool型選取的數(shù)組總是創(chuàng)建數(shù)組的副本,即使是返回一模一樣的數(shù)組也是如此兑障。
names = np.array(['bob','Joe','Will','Bob']
data = np.randn(4,4)
data[names=='bob']
data[-(names=='bob')]
data[names=='bob',2:]
mask = (names=='bob')|(names=='Will')
data[mask]
#和上面的bool索引不同侄非,通過bool型索引設(shè)置值會直接在原始的數(shù)組上進(jìn)行改動。
data[data<0]=0
# 花式索引
# 花式索引是利用整數(shù)數(shù)組進(jìn)行索引
#花式索引總是復(fù)制數(shù)據(jù)到新的數(shù)組
array = np.arange(32).reshape((8,4))
arr[[4,3,0,6]] #得到第4,3,0,6行
arr[[-5,-3,-7]]
arr[[1,5,7,2],[0,3,1,2]] #得到的是長度為4的一維數(shù)組
arr[[1,5,7,2],[:,0,3,1,2]] #得到的一個二維數(shù)組
數(shù)組的轉(zhuǎn)置和軸對換
轉(zhuǎn)置返回的是元數(shù)據(jù)的視圖流译,并不會進(jìn)行任何復(fù)制操作
# 屬性方式
arr.T
# 計算內(nèi)積
np.dot(arr.T,arr)
#高維情況
arr = np.arange(16).reshape((2,2,4))
arr.transpose((1,0,2))
arr.swapaxes(1,2)
通用函數(shù)
主要摘自參考文獻(xiàn)中的《通用函數(shù):快速的元素級數(shù)組函數(shù)》
一元通用函數(shù)
abs丶fabs 計算整數(shù)丶浮點數(shù)或復(fù)數(shù)的絕對值逞怨。對于非復(fù)數(shù)值,可以使用更快的fabs福澡。
sqrt:計算各元素的平方根叠赦。
In [36]: np.sqrt(4)
square:計算各元素的平方。
In [38]: np.square(4)
exp:計算各元素的指數(shù)(e^x)
In [40]: np.exp(2)
#python3還有個函數(shù)為exp2革砸,計算的是2的指數(shù)
In [41]: np.exp2(5)
Out[41]: 32.0
log丶log10丶log2丶log1p 分別為自然對數(shù)(底數(shù)為e)丶底數(shù)為10的log丶底數(shù)為2的log丶log(1+x)
In [42]: np.log(2)
Out[42]: 0.6931471805599453
sign:計算各元素的正負(fù)號:1(正數(shù))丶0(零)丶-1(負(fù)數(shù))
In [44]: np.sign(12)
ceil:計算各元素的ceiling值除秀,即大于等于該值的最小整數(shù)
In [47]: np.ceil(15.971)
Out[47]: 16.0
floor:計算各元素的floor值,即小于等于該值的最大整數(shù)
In [48]: np.floor(-1.564)
Out[48]: -2.0
rint:將各元素四舍五入到最接近的整數(shù)业岁,保留dtype
In [50]: np.rint(1.485)
Out[50]: 1.0
modf:將數(shù)組的小數(shù)和整數(shù)部分以兩個獨立數(shù)組的形式返回
In [52]: np.modf([1.5,2.9])
Out[52]: (array([0.5, 0.9]), array([1., 2.]))
isnan:返回一個表示“哪些值是NaN(這不是一個數(shù)字)”的布爾型數(shù)組
In [56]: np.isnan(NaN)
Out[56]: True
isfinite丶isinf:分別返回一個表示“哪些元素是有窮的(非inf鳞仙,非NaN)”或“哪些元素是無窮的”的布爾型數(shù)組
復(fù)制代碼
In [61]: np.isfinite(1/3)
Out[61]: True
cos丶cosh丶sin丶sinh:普通型和雙曲型三角函數(shù)
In [65]: np.sin(30)
Out[65]: -0.9880316240928618
tan丶tanh丶arccos丶arccosh丶arcsin丶arcsinh丶arctan丶arctanh:反三角函數(shù)
logical_not:計算各元素not x的真值。相當(dāng)于-arr
In [71]: np.logical_not(15)
Out[71]: False
二元通用函數(shù)ufunc
add:將數(shù)組中對應(yīng)的元素相加
subtract:從第一個數(shù)組中減去第二個數(shù)組中的元素
multiply:數(shù)組元素相乘
divide丶floor_divide:除法或向下圓整除法(丟棄余數(shù))
power:對第一個數(shù)組中的元素A笔时,根據(jù)第二個數(shù)組中的相應(yīng)元素B棍好,計算A^B
maximum丶fmax:元素級的最大值計算。fmax將忽略NAN
minimum丶fmin:元素級的最小值計算允耿。fmax將忽略NAN
mod:元素級的求模計算(除法的余數(shù))
copysign:將第二個數(shù)組中的值的符號復(fù)制給第一個數(shù)組中的值借笙。
greater丶greater_equal丶less丶less_equal丶equal丶not_equal:執(zhí)行元素級的比較運算,最終產(chǎn)生布爾型數(shù)組较锡。相當(dāng)于運算符>丶>=丶<丶<=丶==丶!=
logical_and丶logical_or丶logical_xor:執(zhí)行元素級的真值邏輯運算业稼。相當(dāng)于運算符&丶|丶^(與或異)
條件邏輯判斷轉(zhuǎn)為數(shù)組運算 where
#cond需要得到true/false的bool型數(shù)組
# x,y可以是大小相同的數(shù)組,也可以是標(biāo)量+數(shù)組
res = np.where(cond,x,y)
arr=np.random.randn(4,4)
np.where(arr>0,2,arr)
np.where(arr>0,2,-2)
數(shù)學(xué)統(tǒng)計方法
這個很常用mean/sum,std標(biāo)準(zhǔn)差,var方差蚂蕴,min低散,max,argmin,argmax,cumsum所有元素的累計和,cumprod所有元素的累計積
arr=np.random.randn(4,3)
arr.mean()
arr.sum()
arr.mean(axis=1)
#表示沿著該軸計算均值骡楼,比如這里沿著1軸計算熔号,最終是4個個元素
------>axis=1 mean
1, 2, 3 2
4, 5, 6 5
7, 8, 9 8
10,11,12 11
#cumsum和cumprod返回的是一個由中間結(jié)果組成數(shù)組
arr.cumsum(0)
| 1, 2, 3 2
| 4, 5, 6 5
| 7, 8, 9 8
\/ 10,11,12 11
axis=0
cumsum(0)
| 1, 2, 3
| 5, 7, 9
| 12,15,18
\/ 22,26,30
bool型數(shù)組可以使用sum()得到True的數(shù)量,可以使用any()和all()函數(shù)得到是否存在ture和是否都是True.
bools = np.array([True,False,True,False])
(bools>0).sum()
# 這兩個方法也可以用于非bools型數(shù)值數(shù)組鸟整,非0元素將被認(rèn)為是True
bools.any()
True
bools.all()
False
arr.sort()函數(shù)用于就地排序引镊,還可以傳入axis沿著指定軸排序.頂級方法np.sort()函數(shù)用于返回排序副本,并不會改變原始數(shù)組的結(jié)果
唯一化函數(shù)和其他集合邏輯函數(shù)
np.unique(arr)得到數(shù)組中的唯一元素的排序數(shù)組
intersect1d(x,y) 得到x,y中得到公共元素的排序數(shù)組
union1d(x,y) 得到xy的并集有序數(shù)組
in1d(x,y) 得到x每個元素是否包含于y的bool數(shù)組
setdiff1d(x,y)元素在x中不在y中的元素集合
setor1d(x,y)得到存在于一個數(shù)組但是不存在于另一個數(shù)組的元素集合,理解為異或
數(shù)組輸入輸出
np.save和np.load,存放在npy后綴的文件中弟头。
線性代數(shù)函數(shù)
直接對兩個數(shù)組*得到的對應(yīng)元素的積吩抓,當(dāng)需要計算矩陣點積的時候需要使用代數(shù)函數(shù)。
z = np.dot(x,y)
z = x.dot(y)
mat = x.T.dot(x)
from numpy.linalg import inv,qr
diag 方陣的對角線元素赴恨,一位數(shù)組方式
trace 對角線元素之和
dot 矩陣點積
det 矩陣行列式
eig 矩陣特征值和特征向量
inv 矩陣的逆
qr 矩陣的QR分解
svd矩陣的奇異值分解
廣播機制
廣播是值不同形狀的數(shù)組之間進(jìn)行算數(shù)運算的執(zhí)行方式疹娶。將標(biāo)量和數(shù)組合并行就會發(fā)生最簡單的廣播——將標(biāo)量數(shù)值廣播到所有的元素上。
廣播看起來很簡單伦连,但是也是有原則的蚓胸。原則就是:
- 如果兩個數(shù)組維度不一致,但是后緣維度的軸長相符除师,則可以廣播
- 如果兩個數(shù)組維度數(shù)量一致(比如都是三維),其中有一個軸為1扔枫,則含有1的這個數(shù)組會沿著1這個軸進(jìn)行廣播
具體來看,下面兩個數(shù)組的維度不一致(4,3),(3,)汛聚,但是從最后一個維度開始,arr1的第二維長度為3短荐,和arr2的維度相同倚舀。arr1和arr2的shape并不一樣,但是它們可以執(zhí)行相加操作忍宋,這就是通過廣播完成的痕貌,在這個例子當(dāng)中是將arr2沿著0軸進(jìn)行擴展。
arr1 = np.array([[0, 0, 0],[1, 1, 1],[2, 2, 2], [3, 3, 3]]) #arr1.shape = (4,3)
arr2 = np.array([1, 2, 3]) #arr2.shape = (3,)
arr_sum = arr1 + arr2
同理糠排,如果有一個數(shù)組是(3,4,2),另一個數(shù)組是(4,2)舵稠,那么雖然(3,4,2)和(4,2)的維度是不相同的,前者為3維入宦,后者為2維哺徊。但是它們后緣維度的軸長相同,都為(4,2)乾闰,所以可以沿著0軸進(jìn)行廣播落追。
但是如果一個數(shù)組是(4,3),另一個數(shù)組是(4,),那么因為后緣維度不一致,所以會報錯涯肩。這種情況需要將第二個數(shù)組reshape(4,1),讓其變成第二種情況轿钠。
對于一個數(shù)組是(4,3),另一個數(shù)組是(4,1)的,因為其第一軸長度相同病苗,第二軸上有1疗垛,因此第二個數(shù)組會沿著軸1進(jìn)行廣播。
arr = np.range(12).reshape((4,3))
mean = arr.mean(1) #mean.shape = (4,)
mean = mean.reshape((4,1))
demean = arr-mean
如何比較優(yōu)雅的廣播:上面的代碼中铅乡,使用了reshape()函數(shù)改變了數(shù)組的形狀继谚,numpy還有另一種通過索引機制插入周的特殊語法,通過np.newaxis屬性和全切片方式插入
arr = np.zeros((4,4))
arr_3d = arr[:,np.newaxis,:]
通過這樣發(fā)的方式,我們就可以根據(jù)需要廣播的軸定制需要廣播的數(shù)組花履。比如數(shù)組形狀(8,5,3)芽世,第二個數(shù)組為(8,5),想要沿著軸2廣播诡壁,則先將其形狀擴充為(8,5,1);第二個數(shù)組為(8,3),想要沿著軸1廣播济瓢,則需要將其形狀擴充為(8,1,3);第二個數(shù)組為(5,3),想要沿著軸0進(jìn)行廣播,則需要將其形狀擴充為(1,5,3).perfect妹卿,感覺自己棒棒噠旺矾!
到這里大致介紹了廣播的機制。
我們平時看見的標(biāo)量和數(shù)組的廣播中夺克,標(biāo)量沒有維度箕宙,因此會全部被廣播到各個維度。
如果有一個數(shù)組是(3,4,2),另一個數(shù)組是(1,2)铺纽,同時滿足了維度不一致和存在長度為1的維度柬帕,因此也可以進(jìn)行廣播,沿著軸0和軸1進(jìn)行廣播狡门。
arr= np.arange(24).reshape((2,3,4))
mean = np.array([1,2,3,4]).reshape((1,4))
arr-mean
在tensorflow中陷寝,會遇到對于三通道的圖片,需要對每個通道減去通道均值其馏,這時候用到的就是這種廣播機制凤跑。
主要參考資料
《利用python進(jìn)行數(shù)據(jù)分析》書籍第4章和第12章