手把手Numpy入門教程【二】——數組與切片

本文始發於個人公衆號:TechFlow,原創不易,求個關注


今天是Numpy專題的第二篇,我們來進入正題,來看看Numpy的運算。

上一篇文章當中曾經提到過,同樣大小的數據,使用Numpy的運算速度會是我們自己寫循環來計算的上百倍甚至更多。並且Numpy的API非常簡單,通常只要簡單幾行代碼就可以完成非常複雜的操作。


計算與廣播


在Python中的數組無論是什麼類型,我們是無法直接對其中所有的元素進行計算的。想要做到這一點,必須要通過map這樣的方式操作。而Numpy當中,我們可以很方便地對一整個數組或者是矩陣進行各式的計算

首先,我們先定義一個Numpy的數組:

arr = np.array([[1,2,3],[2,2,3]])

image-20200516161939969

首先而我們來看一下基本的四則運算:

image-20200516162021455

這張圖中我們可以看出兩點,首先是Numpy當中的數組重載了四則運算符,我們可以直接通過加減乘除進行計算。第二點是Numpy自動替我們做了映射,雖然我們運算操作的對象是數組本身,但是Numpy自動替我們映射到了其中的每一個元素。

如果你不喜歡直接運算,想要使用Numpy的api進行調用,也是一樣可以的。Numpy當中也爲加減乘除提供了api。

image-20200516162427254

我們甚至還可以比較兩個數組的大小,得到的結果是一個bool型的數組,代表其中每一個元素的大小關係。

image-20200516162534018

除了列舉的這些之外,Numpy當中還提供了許多其他的api來進行各種計算,幾乎囊括了所有常見的數學計算公式。比如log、exp、pow、開方、三角函數等等計算,基本上api的名稱和math當中的一樣,大家也沒有必要都記住,基本上可以根據英文猜出來,一般來說記住常用的,其他的可以等到使用的時候再查閱。


廣播


理解了Numpy中的基本操作之後,接下來要介紹一個非常重要的概念,叫做廣播。如果這個概念理解不到位,那麼後來在使用的過程當中,會遇到很多頭疼的問題,或者是總是看不懂別人的代碼。

廣播的英文叫做broadcasting,這個思想應用的範圍很廣,比如分佈式消息中間件等很多領域都有化用。在Numpy計算當中,廣播指的是將一個小的數據應用在大數據的計算上。這個概念其實很形象,我們來看個例子。

比如我們想要對Numpy中的數組每一位的元素都加上3,我們當然可以創造出一個同樣大小的數組來,然後再把它們相加。但是大可不必這麼麻煩,我們直接用原數組加上3即可,Numpy內部會發現3和我們的數據大小不一致,然後自動幫我們把3拓充到和我們的數據一樣大小的數組再進行計算:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-d5OM0glu-1589762176089)(https://tva1.sinaimg.cn/large/007S8ZIlgy1geudfdqm24j30lg04iaa7.jpg)]

它其實等價於:

np.full_like(arr, 3) + arr

如果你能理解了上面這個操作,那麼同樣的,我們要對所有的元素平方或者是開方也都不在話下了:

image-20200516163141477

廣播並不是只可以用在數組和一個整數之間,還可以用在數組和另外一個規模更小的數組當中,但是會對兩者的shape有所要求。Numpy規定,兩個數組的shape必須相等或者其中一個爲1纔可以執行廣播操作。

比如說剛纔我們創建的arry數組的shape是(3, 2),我們可以讓它和一個大小是(1, 2)或者是(3, 1)的“小數組”進行運算,這同樣是支持的。

如果你看不明白上面的計算過程, 我下面用一張圖做一下演示。

image-20200516164211467

從圖中可以看到左邊的數組shape是(2, 3),右邊的數組shape是(2, 1),滿足Numpy對於廣播機制的要求。Numpy會自動對右邊數組shape爲1的維度進行廣播,也就是將它複製若干份使得它們的shape相等。如果你把左邊的數組看成是若干個聽廣播的人,右側的數組看成是消息的話,那麼廣播機制就是把消息複製若干份,讓每一個聽廣播的人聽到同樣的內容。所以這個名字還是很形象的。


切片


Python中數組爲人稱道的很重要的一點就是它的切片操作非常方便,Numpy作爲依託於Python的計算包,自然也繼承了這一點,所以在Numpy當中,我們也可以很方便地使用切片功能。切片的使用方法和Python基本是一樣的。

我們用上下標加上冒號來表示我們想要切片的範圍, 和Python一樣,這是一個左閉右開的區間。

我們也可以省略其中的一個範圍,只提供上界或者是下界:

image-20200516165031942

我們還可以上下界都省略,表示全部都要,以及倒序切片的方法也和Python是一樣的。

image-20200516165127699

但是有一點不太一樣,Numpy中的切片和golang中的切片比較像,它代表原數組一段區間的引用,而不是拷貝。也就是說我們修改切片中的內容是會影響原數組的,我們對一個切片賦值,明顯可以發現原數組的對應位置發生了改變。

image-20200516165245162

這麼設計的原因和golang是一樣的,因爲Numpy是爲了大數據計算而誕生的,大數據計算顯然性能是一個非常重要的考量指標。如果這裏不是設計成引用,而是拷貝的話,那麼當一個大的切片產生的時候,必然會涉及到大量拷貝的操作。不僅非常消耗內存,並且也會佔用大量計算資源。如果使用引用可以非常快速地返回結果。

golang當中如此設計,也是一樣的道理。

那問題來了,如果我們想要拷貝出一份切片出來,而不是獲得一個切片應該怎麼辦?答案也很簡單,我們可以調用copy方法,獲取一份拷貝。

arr[3:10].copy()

索引


理解了切片的用法之後,我們接下來看看索引。索引也是Numpy當中非常重要的概念,應用也非常普遍。

Numpy當中的索引對應數組中的維度,比如一個二維的數組,當我們用下標訪問的時候,獲得的其實是一個一維的數組。所以如果我們想要訪問一個具體的元素的時候,能做的就是繼續往下指定下標:

image-20200516171154055

這個很好理解,和Python當中的多維數組的用法是一樣的。上面我們用了兩個方括號去鎖定一個元素的位置,爲了寫起來方便,我們還可以用逗號分隔查詢。友情提醒,Python原生的數組並不支持這樣的操作,不要搞混哦。

同樣的道理,如果是多維的數組也是一樣的,我們依次寫出從0到k維的座標來獲取一個固定的元素。如果我們給出的座標信息較少,那麼則會獲得一個數組。

拿3維數組舉例,如果我們訪問的時候只用一個下標,那麼我們獲得的是一個二維數組。如果使用兩個下標,則獲得的是一個一維數組。對於更高的維度也是同樣。


結尾


今天的文章我們一起了解了Numpy當中常見的計算api以及廣播和索引機制,關於索引的使用今天只是開了個頭,還有很多非常靈活的用法,由於篇幅的限制,我們分成了多篇文章,會在之後的文章當中一一介紹。

今天介紹的也是Numpy的基礎內容,除了廣播機制稍稍需要思考一下之外,其餘的應該都非常簡單,我相信大家都能看明白。Numpy之所以普及,除了速度快之外,api簡單易用,學習成本低也是很大的特點。

各位看官,喜歡本文的話點個關注唄

在這裏插入圖片描述

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