2017-10-28 5 views
3

NumPy (또는 Pandas)를 사용하여 Arnaud Legoux Moving Average를 계산하는 벡터화 된 버전의 코드를 작성하고 싶습니다. 이걸 좀 도와 주실 수 있나요? 감사.Arnaud Legoux 이동 평균 및 numpy

비 벡터화 버전은 다음과 같습니다 (아래 참조).

def NPALMA(pnp_array, **kwargs) : 
    ''' 
    ALMA - Arnaud Legoux Moving Average, 
    http://www.financial-hacker.com/trend-delusion-or-reality/ 
    https://github.com/darwinsys/Trading_Strategies/blob/master/ML/Features.py 
    ''' 
    length = kwargs['length'] 
    # just some number (6.0 is useful) 
    sigma = kwargs['sigma'] 
    # sensisitivity (close to 1) or smoothness (close to 0) 
    offset = kwargs['offset'] 

    asize = length - 1 
    m = offset * asize 
    s = length/sigma 
    dss = 2 * s * s 

    alma = np.zeros(pnp_array.shape) 
    wtd_sum = np.zeros(pnp_array.shape) 

    for l in range(len(pnp_array)): 
     if l >= asize: 
      for i in range(length): 
       im = i - m 
       wtd = np.exp(-(im * im)/dss) 
       alma[l] += pnp_array[l - length + i] * wtd 
       wtd_sum[l] += wtd 
      alma[l] = alma[l]/wtd_sum[l] 

    return alma 

답변

2

우리는 제 1 축을 따라 슬라이딩 윈도우를 생성하고 합 감축 wtd 값의 범위 텐서 곱을 사용하여 접근을

시작.

구현은 다음처럼 보일 것이다 -

# Get all wtd values in an array 
wtds = np.exp(-(np.arange(length) - m)**2/dss) 

# Get the sliding windows for input array along first axis 
pnp_array3D = strided_axis0(pnp_array,len(wtds)) 

# Initialize o/p array 
out = np.zeros(pnp_array.shape) 

# Get sum-reductions for the windows which don't need wrapping over 
out[length:] = np.tensordot(pnp_array3D,wtds,axes=((1),(0)))[:-1] 

# Last element of the output needed wrapping. So, do it separately. 
out[length-1] = wtds.dot(pnp_array[np.r_[-1,range(length-1)]]) 

# Finally perform the divisions 
out /= wtds.sum() 

기능을 슬라이딩 윈도우를 얻을 : strided_axis0here에서입니다.

부스트

그 승산 값 wtds 후 회선 1D 그들의 합계 감소는 기본적으로 제 1 축을 따라 컨볼 루션이다. 따라서 axis=0을 따라 scipy.ndimage.convolve1d을 사용할 수 있습니다. 거대한 슬라이딩 윈도우를 만들지 않으므로 메모리 효율성을 고려하면 훨씬 빠릅니다.

구현 될 것이다 - 비제 행인 따라서

from scipy.ndimage import convolve1d as conv 

avgs = conv(pnp_array, weights=wtds/wtds.sum(),axis=0, mode='wrap') 

, out[length-1:]avgs[:-length+1]와 동일하다.

실제로는 작은 커널 번호 wtds에서 작업하는 경우 약간의 정밀도 차이가있을 수 있습니다. 따라서이 convolution 메소드를 사용하는 경우이를 염두에 두십시오.

런타임 테스트

접근 -

def original_app(pnp_array, length, m, dss): 
    alma = np.zeros(pnp_array.shape) 
    wtd_sum = np.zeros(pnp_array.shape) 

    for l in range(len(pnp_array)): 
     if l >= asize: 
      for i in range(length): 
       im = i - m 
       wtd = np.exp(-(im * im)/dss) 
       alma[l] += pnp_array[l - length + i] * wtd 
       wtd_sum[l] += wtd 
      alma[l] = alma[l]/wtd_sum[l] 
    return alma 

def vectorized_app1(pnp_array, length, m, dss): 
    wtds = np.exp(-(np.arange(length) - m)**2/dss) 
    pnp_array3D = strided_axis0(pnp_array,len(wtds)) 
    out = np.zeros(pnp_array.shape) 
    out[length:] = np.tensordot(pnp_array3D,wtds,axes=((1),(0)))[:-1] 
    out[length-1] = wtds.dot(pnp_array[np.r_[-1,range(length-1)]]) 
    out /= wtds.sum() 
    return out 

def vectorized_app2(pnp_array, length, m, dss): 
    wtds = np.exp(-(np.arange(length) - m)**2/dss) 
    return conv(pnp_array, weights=wtds/wtds.sum(),axis=0, mode='wrap') 

타이밍 -

In [470]: np.random.seed(0) 
    ...: m,n = 1000,100 
    ...: pnp_array = np.random.rand(m,n) 
    ...: 
    ...: length = 6 
    ...: sigma = 0.3 
    ...: offset = 0.5 
    ...: 
    ...: asize = length - 1 
    ...: m = np.floor(offset * asize) 
    ...: s = length/sigma 
    ...: dss = 2 * s * s 
    ...: 

In [471]: %timeit original_app(pnp_array, length, m, dss) 
    ...: %timeit vectorized_app1(pnp_array, length, m, dss) 
    ...: %timeit vectorized_app2(pnp_array, length, m, dss) 
    ...: 
10 loops, best of 3: 36.1 ms per loop 
1000 loops, best of 3: 1.84 ms per loop 
1000 loops, best of 3: 684 µs per loop 

In [472]: np.random.seed(0) 
    ...: m,n = 10000,1000 # rest same as previous one 

In [473]: %timeit original_app(pnp_array, length, m, dss) 
    ...: %timeit vectorized_app1(pnp_array, length, m, dss) 
    ...: %timeit vectorized_app2(pnp_array, length, m, dss) 
    ...: 
1 loop, best of 3: 503 ms per loop 
1 loop, best of 3: 222 ms per loop 
10 loops, best of 3: 106 ms per loop 
+0

내가 1D 회선과의 두 번째 방법을 확인했습니다. 그리고 거기에 뭔가 잘못된 것처럼 보입니다. 그러나 나는 정확히 무엇을 얻을 수 없습니다. 내 예가 : 가 pnp_array = np.array ([3924.00752506가 5774.30335369가 5519.40734814가 4931.71344059]) 가 0.85 시그마 = 6 길이 = 3 m = 1.7 DSS = 0.5 예상되는 결과가 0 [되어야 오프셋 0, 5594.17030842, 5115.59420056]. 그러나 두 번째 응용 프로그램은 [0, 0, 5693.3358598, 5333.61073335]를 반환합니다. 따라서 누적 오류는 -317.182084168입니다. 언급 한대로 작은 커널 번호가 있기 때문입니까? – Prokhozhii

+0

@Prokhozhii'wtds = np.exp (- (np.arange (length) - m) ** 2/dss)'에서'wtds '의 값은 무엇입니까? – Divakar

+0

그들은 두 번째 응용 프로그램에서 올바른 : 배열 ([0.00308872, 0.3753111, 0.83527021]) – Prokhozhii