[Python]속도개선 - Recursive pandas
- Python/알고리즘
- 2021. 9. 18.
판다스로 단순 이평선을 구할 때는 판다스 내에 rolling을 이용한 방식으로 구합니다.
그런데 커스텀하게 만든다면 결국 recursive 알고리즘을 쓰게 되어있습니다. 왜냐하면 주식데이터는 시간과 값으로 이루어져 있고 시간에 따른 변화를 분석하는 것이 보통 금융데이터 분석을 하는 목적이기 때문입니다.
결론적으로 Recursive를 써야하니 속도 개선이 필요합니다.
이번 포스팅에서는 판다스를 이용할 때의 속도 개선입니다.
Exponentially weighted moving average(EWMA)라는 지수가중평균선을 만들어 분석하는 시간이 얼마나 드는지 확인해보겠습니다. 판다스로 EWMA를 바로 만들 수 있지만 임의로 만들어서 해보겠습니다.
EWMA
EWMA에 대한 자세한 설명은 나중에 기회가 되면 따로 하겠습니다.
지수가중평균을 요약하자면 시간에 따라 가중시키기는 이동평균선입니다. 변동성 측면에서 시장은 시시각각 변하기 때문에 최근 동향을 반영하여 산출해내는 것이 더 정확합니다. 이런 시세 반영을 위해 EWMA를 씁니다. 그래서 smoothing factor인 alpha를 곱해 시세 반영을 합니다.
식은 다음과 같습니다.
시간에 따라 움직이니 시간 $t \in {0,...,T} $에 대하여, $ x_{t}$ 는 데이터라고 할 때,
$$ EWMA_{0} = x_{0} $$
$$ EWMA_{t} = \alpha x_{t} +(1-\alpha)EWMA_{t-1} $$
속도 개선
Python
앞선 포스팅에서 Recursive는 파이썬으로는 느리다는 얘기를 했었는데 여지없이 그럴겁니다.
애플의 가격 데이터를 가지고 확인을 해보겠습니다. 제가 쓰는 알파값은 Python for finance라는 책에 있는 값으로 했습니다. 주식에서 실제로 EWMA를 구현할때 쓰는 수는 아니니 참고해주셨으면 합니다.
import FinanceDataReader as fdr
import pandas as pd
sym = 'AAPL'
alpha= 0.25
df = fdr.DataReader('AAPL')
df_close = pd.DataFrame(df.Close)
data = df_close
data
시간 데이터 하나만 필요해서 종가만 가져와 종가와 EWMA로 이루어진 데이터프레임을 만들었습니다.
현재 EWMA는 종가와 똑같습니다.
이제 EWMA를 계산하도록 하겠습니다.
%%time
for t in zip(data.index,data.index[1:]):
data.loc[t[1],'EWMA'] = (alpha*data.loc[t[1],'Close']+(1-alpha)*data.loc[t[0],'EWMA'])
EWMA를 만드는데에만 약 4초정도 걸렸습니다.
잘 만들었는지 확인합니다.
data.head()
종가와 EWMA 값이 다른 걸 볼 수 있습니다. 잘 만들어진 것 같습니다.
Numpy
data의 column으로 된 Close는 여러개의 데이터로 되어있으니 numpy로 다룬다면 벡터 계산으로 빠르게 계산할 수 있으니 numpy를 이용해 해보겠습니다. ewma_np라는 함수를 만들어 numpy로 ewma를 한꺼번에 계산하겠습니다.
import numpy as np
def ewma_np(x, alpha):
y = np.zeros_like(x)
y[0] = x[0]
for i in range(1,len(x)):
y[i] = alpha*x[i]+(1-alpha)*y[i-1]
return y
return을 ndarray로 나타내겠습니다.
실제 시간을 재보면 파이썬보다는 빨라진 걸 볼 수 있습니다.
%timeit ewma_np(data['Close'],alpha)
생각해보면 data['Close']는 Series로 이루어져 있습니다. 그래서 index와 value를 분리할 수 있습니다.
어짜피 ndarray로 return을 할 것이기 때문에 value로 분리해서 계산한다면 더 빠를 것이라고 생각됩니다.
%timeit ewma_np(data['Close'].values,alpha)
실제로 속도가 현저히 빨라지는 걸 볼 수 있습니다.
Numda
numda로 하면 더 빨라집니다. 애초에 numpy를 반영한 함수를 만들었기 때문에 numpy+numba가 됩니다.
그런데 데이터프레임 자체를 다 쓰면 그리 빨라지진 않습니다.
import numba as nb
ewma_nb = nb.jit(ewma_np)
ewma_nb(data['Close'],alpha)
%timeit ewma_nb(data['Close'],alpha)
하지만 value값만 가져온다면 굉장히 빨라집니다.
%timeit ewma_nb(data['Close'].values,alpha)
마이크로초 수준으로 나오네요!
Cython
Cython을 이용해 진행합니다. numba만큼만 나온다면 Cython에서도 속도면에서는 성공입니다.
numba와 비교하기 위해 numpy를 Cython 안에서 도입하여 만듭니다.
decorator를 추가하였는데 boundscheck는 말그대로 bounds check하는 기능이고
wraparound는 마이너스로 이루어진 인덱스를 체크하고 접근하는 기능입니다.
둘다 현 데이터에서는 해당되지 않으니 False로 해서 속도를 높여봅니다.
%load_ext cython
%%cython
import numpy as np
cimport cython
@cython.boundscheck(False)
@cython.wraparound(False)
def ewma_cy(double[:] x, float alpha):
cdef int i
cdef double[:] y = np.empty_like(x)
y[0] = x[0]
for i in range(1,len(x)):
y[i] = alpha*x[i] + (1-alpha)*y[i-1]
return y
numba보다는 약간 느리지만 비슷한 성능을 가져오는 걸 볼 수 있습니다.
%timeit ewma_cy(data['Close'].values,alpha)
마무리
값을 어찌어찌하여 구한 후 pandas에 넣기만 하면 모든 데이터 처리는 끝나게 됩니다.
data['EWMA_cy'] = ewma_cy(data['Close'].values,alpha)
data.head()
커스텀한 지표를 만들어 pandas에서 분석을 해보았습니다.
요약하자면 numba가 가장 빠르고 Cython이 그 다음으로 빠릅니다.
numba를 잘 사용한다면 좋은 성능으로 손쉽고 빠르게 원하는 값을 계산해내지 않을까 싶습니다.
관련포스팅
[Python/알고리즘] - [Python] 속도 개선 방법
Python/알고리즘] - [Python] 속도 개선 -Loop편
[Python/알고리즘] - [Python] 속도 개선 - 알고리즘편
[Python/알고리즘] - [Python] 속도개선 -알고리즘편(Recursive)
[Python/Pandas] - Pandas Series 특징
참고문헌
Python for finance
'Python > 알고리즘' 카테고리의 다른 글
[파이썬 자료구조] HashTable 충돌 시 적용 알고리즘 (2) | 2022.12.25 |
---|---|
[파이썬 자료구조] 해쉬테이블 구현해보기 (0) | 2022.12.22 |
[Python] 속도개선 -알고리즘편(Recursive) (0) | 2021.09.16 |
[Python] 속도 개선 - 알고리즘편 (0) | 2021.09.12 |
[Python] 속도 개선 -Loop편 (0) | 2021.09.09 |