2017-05-31 66 views
5

什麼是計算像快速的方法來計算條件函數

# here x is just a number 
def f(x): 
    if x >= 0: 
     return np.log(x+1) 
    else: 
     return -np.log(-x+1) 

一種可能的方法功能的最快捷的方法是:

# here x is an array 
def loga(x) 
    cond = [x >= 0, x < 0] 
    choice = [np.log(x+1), -np.log(-x+1) 
    return np.select(cond, choice) 

,但似乎numpy的由元素經過數組元素。 有什麼辦法可以使用概念上類似於np.exp(x)的東西來獲得更好的性能?

+0

發佈的解決方案是否適用於您? – Divakar

+0

@Divakar對不起,我還沒有可能檢查,如果還沒有。我會在最近的時間嘗試並給出答案 – ichernob

回答

6
def f(x): 
     return (x/abs(x)) * np.log(1+abs(x)) 
+0

好!這工作。數學上的好工作。再加上'numexpr'和/或重新使用'abs(x)'應該是贏家。 – Divakar

+2

我建議使用'numpy.sign(x)'而不是'x/abs(x)',以避免可能的零除。 'np.copysign(np.log(1 + abs(x)),x)'是另一種方式。 –

+0

這一切都很不錯。但像abs(),np.sign()這樣的函數可以返回0.標記解決方案非常好,但是,我希望找到比np.tanh(x)更快的解決方案。可能嗎? – ichernob

3

在這樣的情況下,masking幫助 -

def mask_vectorized_app(x): 
    out = np.empty_like(x) 
    mask = x>=0 
    mask_rev = ~mask 
    out[mask] = np.log(x[mask]+1) 
    out[mask_rev] = -np.log(-x[mask_rev]+1) 
    return out 

介紹numexpr module有助於我們進一步。

import numexpr as ne 

def mask_vectorized_numexpr_app(x): 
    out = np.empty_like(x) 
    mask = x>=0 
    mask_rev = ~mask 

    x_masked = x[mask] 
    x_rev_masked = x[mask_rev] 
    out[mask] = ne.evaluate('log(x_masked+1)') 
    out[mask_rev] = ne.evaluate('-log(-x_rev_masked+1)') 
    return out 

通過@user2685079's post啓發,然後使用logarithmetic屬性:log(A**B) = B*log(A),我們可以在簽入日誌計算推,這讓我們做更多的工作numexpr的計算表達式,像這樣 -

s = (-2*(x<0))+1 # np.sign(x) 
out = ne.evaluate('log((abs(x)+1)**s)') 

計算sign使用比較給了我們s以另一種方式 -

s = (-2*(x<0))+1 

最後,我們可以把這個到numexpr評估表達 -

def mask_vectorized_numexpr_app2(x): 
    return ne.evaluate('log((abs(x)+1)**((-2*(x<0))+1))') 

運行測試

糊塗的做法進行比較 -

def loopy_app(x): 
    out = np.empty_like(x) 
    for i in range(len(out)): 
     out[i] = f(x[i]) 
    return out 

時序和驗證 -

In [141]: x = np.random.randn(100000) 
    ...: print np.allclose(loopy_app(x), mask_vectorized_app(x)) 
    ...: print np.allclose(loopy_app(x), mask_vectorized_numexpr_app(x)) 
    ...: print np.allclose(loopy_app(x), mask_vectorized_numexpr_app2(x)) 
    ...: 
True 
True 
True 

In [142]: %timeit loopy_app(x) 
    ...: %timeit mask_vectorized_numexpr_app(x) 
    ...: %timeit mask_vectorized_numexpr_app2(x) 
    ...: 
10 loops, best of 3: 108 ms per loop 
100 loops, best of 3: 3.6 ms per loop 
1000 loops, best of 3: 942 µs per loop 

使用@user2685079's solution使用np.sign以取代第一部分,然後與不numexpr評價 -

In [143]: %timeit np.sign(x) * np.log(1+abs(x)) 
100 loops, best of 3: 3.26 ms per loop 

In [144]: %timeit np.sign(x) * ne.evaluate('log(1+abs(x))') 
1000 loops, best of 3: 1.66 ms per loop 
+0

你能把我的時間添加到你的時間嗎? – piRSquared

+1

@piRSquared我沒有'numba'。介意加入我的你的? – Divakar

+0

添加了最新的'app2' – piRSquared

1

您可以通過使用np.where代替np.select小幅提升你的第二個解決方案的速度:

def loga(x): 
    cond = [x >= 0, x < 0] 
    choice = [np.log(x+1), -np.log(-x+1)] 
    return np.select(cond, choice) 

def logb(x): 
    return np.where(x>=0, np.log(x+1), -np.log(-x+1)) 

In [16]: %timeit loga(arange(-1000,1000)) 
10000 loops, best of 3: 169 µs per loop 

In [17]: %timeit logb(arange(-1000,1000)) 
10000 loops, best of 3: 98.3 µs per loop 

In [18]: np.all(loga(arange(-1000,1000)) == logb(arange(-1000,1000))) 
Out[18]: True 
2

Using numba

Numba爲您提供加速應用程序的能力h高性能函數直接用Python編寫。通過一些註釋,面向數組和麪向數學的Python代碼可以即時編譯爲本地機器指令,與C,C++和Fortran的性能類似,無需切換語言或Python解釋器。

Numba通過使用LLVM編譯器基礎結構在導入時,運行時或靜態生成優化的機器代碼(使用包含的pycc工具)來工作。Numba支持編譯Python以運行在CPU或GPU硬件上,並且旨在與Python科學軟件棧集成。

Numba項目由Continuum Analytics和Gordon和Betty Moore基金會(Grant GBMF5423)提供支持。

from numba import njit 
import numpy as np 

@njit 
def pir(x): 
    a = np.empty_like(x) 
    for i in range(a.size): 
     x_ = x[i] 
     _x = abs(x_) 
     a[i] = np.sign(x_) * np.log(1 + _x) 
    return a 

精度

np.isclose(pir(x), f(x)).all() 

True 

時序

x = np.random.randn(100000) 

# My proposal 
%timeit pir(x) 
1000 loops, best of 3: 881 µs per loop 

# OP test 
%timeit f(x) 
1000 loops, best of 3: 1.26 ms per loop 

# Divakar-1 
%timeit mask_vectorized_numexpr_app(x) 
100 loops, best of 3: 2.97 ms per loop 

# Divakar-2 
%timeit mask_vectorized_numexpr_app2(x) 
1000 loops, best of 3: 621 µs per loop 

函數定義

from numba import njit 
import numpy as np 

@njit 
def pir(x): 
    a = np.empty_like(x) 
    for i in range(a.size): 
     x_ = x[i] 
     _x = abs(x_) 
     a[i] = np.sign(x_) * np.log(1 + _x) 
    return a 

import numexpr as ne 

def mask_vectorized_numexpr_app(x): 
    out = np.empty_like(x) 
    mask = x>=0 
    mask_rev = ~mask 

    x_masked = x[mask] 
    x_rev_masked = x[mask_rev] 
    out[mask] = ne.evaluate('log(x_masked+1)') 
    out[mask_rev] = ne.evaluate('-log(-x_rev_masked+1)') 
    return out 

def mask_vectorized_numexpr_app2(x): 
    return ne.evaluate('log((abs(x)+1)**((-2*(x<0))+1))') 


def f(x): 
    return (x/abs(x)) * np.log(1+abs(x)) 
+0

我最近的app2有點不同:)對不起。你能更新嗎?不要以爲你需要再次列出這些。 – Divakar

+1

@Divakar沒有麻煩:-)。最新的一個非常快。 – piRSquared