4-Numpy通用函數(shù)

numpy 對數(shù)組的操作效率

NumPy數(shù)組上的計算可能非常快甜刻,也可能非常慢≌眨快速實現(xiàn)的關(guān)鍵是使用矢量化操作得院,通常通過NumPy的通用函數(shù)(ufuncs)實現(xiàn)。

慢循環(huán)

 
Python的默認實現(xiàn)(CPython)執(zhí)行某些操作的速度非常慢昭齐。這是由于語言的動態(tài)尿招,解釋性所致:
類型具有靈活性,因此無法像C和Fortran這樣的語言將操作序列編譯成有效的機器代碼。最近就谜,人們進行了各種嘗試來解決這一弱點:著名的例子是PyPy項目怪蔑,
它是Python的實時編譯實現(xiàn)。 Cython項目丧荐,該項目將Python代碼轉(zhuǎn)換為可編譯的C代碼缆瓣;還有Numba項目,該項目將Python代碼段轉(zhuǎn)換為快速LLVM字節(jié)碼虹统。
每種方法都有其優(yōu)點和缺點弓坞,但是可以肯定地說,這三種方法都沒有超越標準CPython引擎的范圍和普及性车荔。
  • Python的相對呆板緩慢的操作渡冻,通常可以體現(xiàn)在一些重復的小操作中忧便,下面展示
In [1]: import numpy as np
In [2]: np.random.seed(0)
In [3]: def compute_rec(values):
   ...:     output=np.empty(len(values))
   ...:     for i in range(len(values)):
   ...:         output[i]=1.0/values[i]
   ...:     return output
   ...: 

In [4]: values = np.random.randint(1, 10, size=5)
In [5]: compute_rec(values)
Out[5]: array([0.16666667, 1.        , 0.25      , 0.25      , 0.125     ])
# 小數(shù)組可以看到耗時很小只有12.2 μs左右
In [6]: %timeit  compute_rec(values)
12.2 μs ± 198 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
# 這里我們創(chuàng)建一個大數(shù)組來看下
In [7]: big_arr = np.random.randint(1, 100, size=1000000)
# 當隨著數(shù)組變大竟然耗時2.52 s左右族吻,這相對其他靜態(tài)語言實在太慢了
In [8]: %timeit compute_rec(big_arr)
2.52 s ± 235 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

計算這百萬個操作并存儲結(jié)果需要幾秒鐘!甚至現(xiàn)在的手機的處理速度都以Giga-FLOPS衡量時(即每秒數(shù)十億次數(shù)字運算)珠增。
不過事實證明超歌,這里的瓶頸不是操操作系統(tǒng)作本身,而是CPython在循環(huán)的每個循環(huán)中必須執(zhí)行的類型檢查和函數(shù)分派蒂教。
每次計算倒數(shù)時巍举,Python都會首先檢查對象的類型,并動態(tài)查找要用于該類型的正確函數(shù)凝垛。如果我們使用的是已編譯的代碼(靜態(tài)語言的優(yōu)勢)懊悯,則在代碼執(zhí)行之前便會知道此類型規(guī)范,并且可以更有效地計算結(jié)果梦皮。

那我們有什么辦法可以再這種情況下提高執(zhí)行效率嗎定枷? 當然,這里我們就用到了numpy的Ufuncs 操作

Ufunc

對于許多類型的操作届氢,NumPy僅為此類靜態(tài)類型的已編譯例程提供了方便的接口。這稱為向量化操作覆旭。這可以通過簡單地對數(shù)組執(zhí)行操作來實現(xiàn)退子,然后將其應用于每個元素。這種矢量化方法旨在將循環(huán)推入NumPy底層的編譯層型将,從而大大提高了執(zhí)行速度寂祥。

比較下面兩種操作:

In [9]: compute_rec(values)
Out[9]: array([0.16666667, 1.        , 0.25      , 0.25      , 0.125     ])

In [10]: 1.0/values
Out[10]: array([0.16666667, 1.        , 0.25      , 0.25      , 0.125     ])
···
說明可以直接除法操作可以直接作用再數(shù)組上,那我們再比較下對大數(shù)組操作的耗時時間

```py
In [15]: %timeit (1.0 / big_arr)
5.25 ms ± 129 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)
  • 執(zhí)行時間幾乎降低了三個數(shù)量級

NumPy中的矢量化操作是通過ufunc實現(xiàn)的七兜,其主要目的是對NumPy數(shù)組中的值快速執(zhí)行重復的操作丸凭。 Ufunc非常靈活–在我們看到標量和數(shù)組之間的操作之前.我們也可以在兩個數(shù)組之間進行操作:

In [18]: np.arange(5) / np.arange(1,6)
# 每個對應的元素想除,要保證兩個數(shù)組size保持一致
Out[18]: array([0.        , 0.5       , 0.66666667, 0.75      , 0.8       ])

而且ufunc操作不僅限于一維數(shù)組-它們還可以作用于多維數(shù)組:

In [26]: x = np.arange(9).reshape((3, 3))
    ...: 2 ** x
Out[26]: 
array([[  1,   2,   4],
       [  8,  16,  32],
       [ 64, 128, 256]], dtype=int32)

通過ufunc使用矢量化的計算幾乎總是比使用Python循環(huán)實現(xiàn)的計算效率更高,尤其是隨著數(shù)組大小的增加惜犀。每當在Python腳本中看到這樣的循環(huán)時铛碑,都應該考慮是否可以將其替換為向量化表達式。

Ufuncs 更多應用

Ufunc有兩種形式:一元ufunc(在單個輸入上運行)和二元ufunc(在兩個輸入上運行)虽界。我們將在這里看到這兩種功能的示例汽烦。

數(shù)組算術(shù)

NumPy的ufunc使用起來非常自然,因為它們利用了Python的本機算術(shù)運算符莉御∑餐蹋可以使用標準的加,減礁叔,乘和除法:

In [27]: x = np.arange(4)
    ...: print("x     =", x)
    ...: print("x + 5 =", x + 5)
    ...: print("x - 5 =", x - 5)
    ...: print("x * 2 =", x * 2)
    ...: print("x / 2 =", x / 2)
    ...: print("x // 2 =", x // 2)  # floor division
x     = [0 1 2 3]
x + 5 = [5 6 7 8]
x - 5 = [-5 -4 -3 -2]
x * 2 = [0 2 4 6]
x / 2 = [0.  0.5 1.  1.5]
x // 2 = [0 0 1 1]

我們甚至可以將數(shù)組當作變量參與運算

In [30]: (x+2)*3
Out[30]: array([ 6,  9, 12, 15])

這些便捷的操作符很多都時依賴相應的方法牍颈,如下

In [31]: x+2
Out[31]: array([2, 3, 4, 5])

In [32]: np.add(x,2)
Out[32]: array([2, 3, 4, 5])

下面時numpy的操作符對應的方法

+   np.add  Addition (e.g., 1 + 1 = 2)
-   np.subtract     Subtraction (e.g., 3 - 2 = 1)
-   np.negative     Unary negation (e.g., -2)
*   np.multiply     Multiplication (e.g., 2 * 3 = 6)
/   np.divide   Division (e.g., 3 / 2 = 1.5)
//  np.floor_divide     Floor division (e.g., 3 // 2 = 1)
**  np.power    Exponentiation (e.g., 2 ** 3 = 8)
%   np.mod  Modulus/remainder (e.g., 9 % 4 = 1)

絕對值

In [33]: x = np.array([-2, -1, 0, 1, 2])
    ...: abs(x)
Out[33]: array([2, 1, 0, 1, 2])
  • 這里的abs就是np.absolute的別名,也可以使用np.absolute(x)或者np.abs(x)
    當數(shù)組為復數(shù)時琅关,絕對值則取的時復數(shù)的模(大兄笏辍)
In [36]: np.abs(x)
Out[36]: array([2.23606798, 3.60555128, 6.70820393])

三角函數(shù)

NumPy提供了大量有用的函數(shù),三角函數(shù)是對數(shù)據(jù)科學家最有用的一些函數(shù)死姚。我們將從定義一個角度數(shù)組開始:
從0-pi 截取三個點

In [45]: theta = np.linspace(0, np.pi, 3)
In [46]: theta
Out[46]: array([0.        , 1.57079633, 3.14159265])
# 計算 o pi/2 和 pi 的sin cos tan 值
In [47]: print("sin(theta) = ", np.sin(theta))
    ...: print("cos(theta) = ", np.cos(theta))
    ...: print("tan(theta) = ", np.tan(theta))
sin(theta) =  [0.0000000e+00 1.0000000e+00 1.2246468e-16]
cos(theta) =  [ 1.000000e+00  6.123234e-17 -1.000000e+00]
tan(theta) =  [ 0.00000000e+00  1.63312394e+16 -1.22464680e-16]

同樣也可以計算反三角函數(shù) np.arcsin(x) np.arccos(x) np.arctan(x)

指數(shù)和對數(shù)

In [49]: x = [1, 2, 3]
    ...: print("x     =", x)
    ...: print("e^x   =", np.exp(x))
    ...: print("2^x   =", np.exp2(x))
    ...: print("3^x   =", np.power(3, x))
x     = [1, 2, 3]
e^x   = [ 2.71828183  7.3890561  20.08553692]
2^x   = [2. 4. 8.]
3^x   = [ 3  9 27]
## 對數(shù)函數(shù)
In [52]: x = [1, 2, 4, 10]
    ...: print("x        =", x)
    ...: print("ln(x)    =", np.log(x))
    ...: print("log2(x)  =", np.log2(x))
    ...: print("log10(x) =", np.log10(x))
x        = [1, 2, 4, 10]
ln(x)    = [0.         0.69314718 1.38629436 2.30258509]
log2(x)  = [0.         1.         2.         3.32192809]
log10(x) = [0.         0.30103    0.60205999 1.        ]
# 當輸入的x 數(shù)值很小時人乓,下面特殊的方法可以提供更精確的結(jié)果
In [53]: x = [0, 0.001, 0.01, 0.1]
    ...: print("exp(x) - 1 =", np.expm1(x))
    ...: print("log(1 + x) =", np.log1p(x))
exp(x) - 1 = [0.         0.0010005  0.01005017 0.10517092]
log(1 + x) = [0.         0.0009995  0.00995033 0.09531018]

特別用處的ufuncs

NumPy具有更多可用的ufunc,包括雙曲三角函數(shù)都毒,按位算術(shù)等等色罚。通過查看NumPy文檔,可以發(fā)現(xiàn)很多功能账劲。

子模塊scipy.special是另一個更專業(yè)和晦澀的功能戳护。如果要在數(shù)據(jù)上計算一些晦澀的數(shù)學函數(shù),可在scipy.special中實現(xiàn)它瀑焦。有太多函數(shù)無法列出所有功能腌且,但以下代碼片段顯示了可能在統(tǒng)計上下文中出現(xiàn)的幾個功能:

##伽瑪函數(shù)(廣義階乘)和相關(guān)函數(shù)

In [56]: x = [1, 3, 4]
    ...: print("gamma(x)     =", special.gamma(x))
    ...: print("ln|gamma(x)| =", special.gammaln(x))
    ...: print("beta(x, 2)   =", special.beta(x, 2))
gamma(x)     = [1. 2. 6.]
ln|gamma(x)| = [0.         0.69314718 1.79175947]
beta(x, 2)   = [0.5        0.08333333 0.05      ]

#誤差函數(shù)(高斯積分)
#它的補數(shù)及其反數(shù)

In [58]: x = np.array([0, 0.3, 0.7, 1.0])
    ...: print("erf(x)  =", special.erf(x))
    ...: print("erfc(x) =", special.erfc(x))
    ...: print("erfinv(x) =", special.erfinv(x))
erf(x)  = [0.         0.32862676 0.67780119 0.84270079]
erfc(x) = [1.         0.67137324 0.32219881 0.15729921]
erfinv(x) = [0.         0.27246271 0.73286908        inf]
  • NumPy和scipy.special中都有很多可用的ufunc,可以查閱官方文檔

其他Ufunc功能

指定輸出

# x數(shù)組乘以2 輸出到y(tǒng)數(shù)組,此時x數(shù)組還是原來的值
In [61]: x = np.arange(4)
    ...: y = np.empty(4)
    ...: np.multiply(x, 2, out=y)
    ...: print(y)
[0. 2. 4. 6.]

In [85]: x = np.arange(5)
# 輸出本身
In [86]: y = np.zeros(10)
    ...: np.power(2, x, out=y[::2])
Out[86]: array([ 1.,  2.,  4.,  8., 16.])

如果我們改為寫y [:: 2] 改成 2 ** x榛瓮,這將導致創(chuàng)建一個臨時數(shù)組來保存2 ** x的結(jié)果铺董,然后執(zhí)行第二次操作,將這些值復制到y(tǒng)數(shù)組中禀晓。對于這么小的計算精续,這并沒有太大的區(qū)別,但是對于非常大的數(shù)組粹懒,謹慎使用out參數(shù)可以節(jié)省大量內(nèi)存重付。

聚合

對于二進制ufunc,可以直接從對象中計算出一些有趣的聚合凫乖。例如确垫,如果我們想通過特定操作來簡化數(shù)組弓颈,則可以使用任何ufunc的reduce方法。將給定操作删掀,重復應用于數(shù)組元素翔冀,直到僅保留單個結(jié)果為止。如下在add ufunc上調(diào)用reduce會返回數(shù)組中所有元素的總和

# 相加聚合
In [98]: x = np.arange(5)
    ...: np.add.reduce(x)
Out[98]: 10
# 也可以相乘聚合
In [100]: x = np.arange(1,5)
     ...: np.multiply.reduce(x)
Out[100]: 24
# 也可以保留執(zhí)行的中間過程
In [101]: np.add.accumulate(x)
Out[101]: array([ 1,  3,  6, 10], dtype=int32)

In [102]: np.multiply.accumulate(x)
Out[102]: array([ 1,  2,  6, 24], dtype=int32)
  • 注意爬迟,對于這些特殊情況橘蜜,有專用的NumPy函數(shù)來計算結(jié)果(np.sum,np.prod付呕,np.cumsum计福,np.cumprod),可在聚合中進行探討:最小值徽职,最大值和介于兩者之間的所有值象颖。

外部的方法

任何ufunc都可以使用外部方法來計算兩個不同輸入的所有對的輸出。這樣一來姆钉,就可以執(zhí)行創(chuàng)建乘法表之類的操作:

# 相當于矩陣相乘(1说订,2,3潮瓶,4陶冷,5)*(1,2毯辅,3埂伦,4,5)
In [103]: x = np.arange(1, 6)
     ...: np.multiply.outer(x, x)
Out[103]: 
array([[ 1,  2,  3,  4,  5],
       [ 2,  4,  6,  8, 10],
       [ 3,  6,  9, 12, 15],
       [ 4,  8, 12, 16, 20],
       [ 5, 10, 15, 20, 25]])

ufuncs的另一個極其有用的功能是能夠在不同大小和形狀的數(shù)組之間進行操作的能力思恐,這是一組稱為廣播的操作沾谜。下一節(jié)討論

文檔鏈接 numpyscipy
更新github

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市胀莹,隨后出現(xiàn)的幾起案子基跑,更是在濱河造成了極大的恐慌,老刑警劉巖描焰,帶你破解...
    沈念sama閱讀 222,865評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件媳否,死亡現(xiàn)場離奇詭異,居然都是意外死亡荆秦,警方通過查閱死者的電腦和手機逆日,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,296評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來萄凤,“玉大人,你說我怎么就攤上這事搪哪∶遗” “怎么了?”我有些...
    開封第一講書人閱讀 169,631評論 0 364
  • 文/不壞的土叔 我叫張陵,是天一觀的道長惑朦。 經(jīng)常有香客問我兽泄,道長,這世上最難降的妖魔是什么漾月? 我笑而不...
    開封第一講書人閱讀 60,199評論 1 300
  • 正文 為了忘掉前任病梢,我火速辦了婚禮,結(jié)果婚禮上梁肿,老公的妹妹穿的比我還像新娘蜓陌。我一直安慰自己,他們只是感情好吩蔑,可當我...
    茶點故事閱讀 69,196評論 6 398
  • 文/花漫 我一把揭開白布钮热。 她就那樣靜靜地躺著,像睡著了一般烛芬。 火紅的嫁衣襯著肌膚如雪隧期。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,793評論 1 314
  • 那天赘娄,我揣著相機與錄音仆潮,去河邊找鬼。 笑死遣臼,一個胖子當著我的面吹牛性置,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播暑诸,決...
    沈念sama閱讀 41,221評論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼蚌讼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了个榕?” 一聲冷哼從身側(cè)響起篡石,我...
    開封第一講書人閱讀 40,174評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎西采,沒想到半個月后凰萨,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,699評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡械馆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,770評論 3 343
  • 正文 我和宋清朗相戀三年胖眷,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片霹崎。...
    茶點故事閱讀 40,918評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡珊搀,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出尾菇,到底是詐尸還是另有隱情境析,我是刑警寧澤囚枪,帶...
    沈念sama閱讀 36,573評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站劳淆,受9級特大地震影響链沼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜沛鸵,卻給世界環(huán)境...
    茶點故事閱讀 42,255評論 3 336
  • 文/蒙蒙 一括勺、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧曲掰,春花似錦疾捍、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,749評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至底哥,卻和暖如春咙鞍,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背趾徽。 一陣腳步聲響...
    開封第一講書人閱讀 33,862評論 1 274
  • 我被黑心中介騙來泰國打工续滋, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人孵奶。 一個月前我還...
    沈念sama閱讀 49,364評論 3 379
  • 正文 我出身青樓疲酌,卻偏偏與公主長得像,于是被迫代替她去往敵國和親了袁。 傳聞我的和親對象是個殘疾皇子朗恳,可洞房花燭夜當晚...
    茶點故事閱讀 45,926評論 2 361

推薦閱讀更多精彩內(nèi)容