오늘의 예제: 이동 평균 선 추가하기
파이쏜에서 이동평균은 어떻게 계산할 수 있을까요?
일단 이동평균 정의는 이렇습니다.
n개의 항목이 있는 시계열 time series의 경우,
P1, P2, ..., Pn
길이 2m+1인 이동 평균은
MAk= (Pk-m, Pk-m+1, ..., Pk, ..., Pk+m-1, Pk+m)/(2m+1)
포트란이나 C처럼 좀 더 원초적인 프로그래밍에 익숙한 사람이라면 이런 알고리즘이 떠오를 거에요
Loop j in (1, n)
Loop i in (-m, m)
ma(j)=ma(j)+P(j+i)
End Loop
End Loop
그런데 Matlab과 유사한 Numpy의 경우 원소 하나하나를 직접 방문하며 계산하는 게 비효율적 입니다. 최대한 Matrix 형태를 유지한 채로 계산하는 게 좋죠. 그럼 어떻게 해야 할까요. 혹시 Numpy에서 지원하는 이동평균에 특화된 함수가 있는 지 한 번 찾아봤습니다.
있네요. 당연히 누군가 이미 질문 했고, 누군가 이미 답을 달았습니다.
https://stackoverflow.com/questions/14313510/how-to-calculate-moving-average-using-numpy
여기서 가져온 답은 아래와 같습니다.
def moving_average(a, n=3) :
ret = np.cumsum(a, dtype=float)
ret[n:] = ret[n:] - ret[:-n]
return ret[n - 1:] / n
Numpy에 이미 정의된 함수 중에 "cumsum"을 이용했습니다. Cumulative Sum 누적합계를 계산하는 함수입니다. 예를 들면,
>> np.cumsum([1,2,3,4,5])
이렇게 계산하면 출력은
[1,3,6,10,15]
이렇게 나오는 거죠.
참고로, 누적합계를 이용한 방법이 Numpy이기 때문에 가능한, 그런 특별한 건 아니구요, 프로그래밍 언어에 관계없이 알고리즘 상으로 더 효율적인 방법입니다. 위에 루프 2번 돌리는 것 보다 시간을 많이 절약할 수 있죠. (이렇게 더 효율적인 알고리즘을 연구하는 학문이 Computational Science 계산과학입니다.)
그런데 가격을 이용한 이동평균선은 내일의 가격을 모르니까 앞 뒤로 몇 개의 값들을 이용해 평균을 하지 않고, 어떤 시점에서 과거 몇 개의 가격을 이용해서 평균을 계산합니다. 그래서 위 함수를 살짝 고쳤습니다.
def moving_average(a, n=3): ### "n=3" indicates the default value
ret = np.cumsum(a, dtype=float)
ret[n:] = ret[n:] - ret[:-n]
ret/=n
### Masking initial a few values to prevent weird values
ret[:n-1]=ret[n-1]
return ret
이렇게 하면 주어진 시계열 자료와 길이가 똑같은 이동평균 시계열 자료가 나오겠죠. 처음 몇 값들은 Dummy이긴 하지만요.
그리고 중간에 "ret[:-n]" 이라는 표현이 어려울 것 같은데, 파이쏜에서 기본적으로 "-1"이라는 인덱스는 List이든 Numpy Array이든 가장 마지막 값을 가리킵니다. "-2"는 끝에서 2번째 값을 가리키구요. 그래서 "a[:-1]" 이라는 표현이 있다면 이것은 a라고 하는 변수 값들 중 마지막 값을 제외한 나머지라는 뜻이 됩니다.
자, 이제 정리해봅시다.
저번 시간에서 작성했던 "read_txt+plot_candle.py3.py"를 새 이름으로 저장합니다. 새 이름은 "read_txt+plot_candle+ma.py3.py" 입니다.
위쪽의 함수들 모아놓은 곳에 2개의 함수를 추가합니다.
def moving_average(a, n=3): ### "n=3" indicates the default value
ret = np.cumsum(a, dtype=float)
ret[n:] = ret[n:] - ret[:-n]
ret/=n
### Masking initial a few values to prevent weird values
ret[:n-1]=ret[n-1]
return ret
def draw_ma_line(ax1,dates,ma,c='b',label=''):
### Line Plot
line1=ax1.plot(dates, ma, color=c, linewidth=0.7, linestyle='-', alpha=0.7, label=label)
return line1
본문 중간에 "ini_date= dates[0]" 줄 위에 다음 부분을 끼워 넣습니다.
### Add Moving Average
## Here, MA is based on the closing price
ma10= moving_average(prices[3,:],n=10)
ma50= moving_average(prices[3,:],n=50)
### Cut only for targeted period
이후 캔들스틱 그리고 나서 그림 저장하기 전에 다음 부분을 끼워 넣습니다.
line10=draw_ma_line(ax1,dates,ma10,c='b',label='10-day MA')
line50=draw_ma_line(ax1,dates,ma50,c='r',label='50-day MA')
ax1.legend(loc='upper left',bbox_to_anchor=(0.06, 0.9),fontsize=11) # x and y relative location in ax1
이렇게 해서 다음과 같은 결과를 얻었습니다.
다음 시간에는 @roostermine 님이 알려주신,
http://www.cryptodatadownload.com/data/northamerican/
이 사이트에서 받을 수 있는 CSV 파일을 파이쏜에서 다뤄보도록 하겠습니다.

관련 글들
Matplotlib List
[Matplotlib] 00. Intro + 01. Page Setup
[Matplotlib] 02. Axes Setup: Subplots
[Matplotlib] 03. Axes Setup: Text, Label, and Annotation
[Matplotlib] 04. Axes Setup: Ticks and Tick Labels
[Matplotlib] 05. Plot Accessories: Grid and Supporting Lines
[Matplotlib] 06. Plot Accessories: Legend
[Matplotlib] 07. Plot Main: Plot
[Matplotlib] 08. Plot Main: Imshow
[Matplotlib] 09. Plot Accessary: Color Map (part1)
[Matplotlib] 10. Plot Accessary: Color Map (part2) + Color Bar
F2PY List
[F2PY] 01. Basic Example: Simple Gaussian 2D Filter
[F2PY] 02. Basic Example: Moving Average
[F2PY] 03. Advanced Example: Using OpenMP
Scipy+Numpy List
[SciPy] 1. Linear Regression (Application to Scatter Plot)
[SciPy] 2. Density Estimation (Application to Scatter Plot)
[Scipy+Numpy] 3. 2D Histogram + [Matplotlib] 11. Plot Main: Pcolormesh
Road to Finance
#00 Read Text File
#01 Draw CandleSticks
#02 Moving Average