[NIPA] 데이터 분석 믹스앤 매치 아카데미 - Python - 활용필수 - 주가 예측 체험

2020. 11. 28. 04:09통계 & 데이터분석/데이터분석

728x90

과목구성 & 일정

  • 이 과정을 하면 정보통신산업진흥원(NIPA) 명의의 인증서를 발급해줍니다 !
  • 이 블로깅은 Python Pandas 사용법을 까먹지 않기 위해 작성되었습니다.

 

1. [활용필수] 인공지능 주가 예측 체험

<< 1-1. 파이썬 데이터 분석 기초 점검 및 복습 >>

1-1-1. Pandas Column 추출

import pandas as pd

# 코로나 엑셀 데이터 파일(.xlsx)이 저장된 경로입니다.
data_path = './data/corona_data.xlsx'

# pandas를 활용해 엑셀 파일을 불러오고, corona_data(변수)에 저장합니다.
corona_data = pd.read_excel(data_path)

# corona_data셋에서 '확진자' 데이터만 추출하여 출력해보세요
confirmed = corona_data['확진자']        #특정 Column 명

# pandas 데이터에서 특정 열을 출력할 때는 다음과 같은 명령어를 사용합니다.
# 데이터셋[열이름]
print(confirmed)

 

1-1-2. Pandas loc 사용 데이터 추출

import pandas as pd

# 코로나 데이터를 불러옵니다.
data_path = './data/corona_data.xlsx'
corona_data = pd.read_excel(data_path)

# .loc을 활용하여 7월 30일('2020-07-30')의 사망자 데이터를 추출해봅니다.
death_0730 = corona_data.loc[corona_data['날짜']=='2020-07-30', '사망자']

# 데이터의 특정 부분을 가져올 때는 .loc[행 지정, 열 지정]을 사용합니다.
# ex.
# df.loc[df['과일']=='사과', '가격']
# ‘과일’ 열 값이 ‘사과’인 행의 ‘가격’만 표시하게 되는 것이죠.
print(death_0730)

<< 1-2. 주식데이터의 이해와 분석 >>

1-2-1. 인공지능 / 머신러닝 개론

 

1-2-2. 주식이란 ?

 

주식 데이터는 시계열 데이터의 성질을 가지고 있습니다

 

Python Pandas DataFrame(데이터프레임) 기본 정보

from datetime import datetime #날짜와 시간을 쉽게 조작할 수 있게 하는 클래스 제공
import pandas as pd

# csv 형태의 주식 데이터 파일을 불러오는 코드
df = pd.read_csv('stock.csv') 

# 데이터프레임 출력(데이터프레임은 (헹 X 열)로 이루어진 표 형태의 특수한 데이터 타입)
print(df)


# --- 주식 데이터 살펴보기 --- #

print('\n주식 데이터의 형태를 출력')
print(df.shape)
# (105, 7)


print('\n주식 데이터의 정보를 출력')
print(df.info)

print('\n주식 데이터의 데이터 타입을 출력')
print(df.dtypes)
# 각 Column 등의 데이터 타입을 출력합니다
# Date         object
# High          int64
# Low           int64
# Open          int64
# Close         int64
# Volume        int64
# Adj Close     int64
# dtype: object


print('\n주식 데이터의 상단 5개 행을 출력')
print(df.head())

print('\n주식 데이터의 하단 5개 행을 출력')
print(df.tail())

print('\n주식 데이터의 모든 열을 출력')
print(df.columns)

print('\n주식 데이터의 요약 통계 자료 출력')
print(df.describe())
#                High           Low  ...        Volume     Adj Close
# count    105.000000    105.000000  ...  1.050000e+02    105.000000
# mean   51996.190476  50637.619048  ...  2.390007e+07  51310.476190
# std     3278.145108   3365.597879  ...  1.152018e+07   3331.829995
# min    43550.000000  42300.000000  ...  0.000000e+00  42500.000000
# 25%    49350.000000  48500.000000  ...  1.621493e+07  48800.000000
# 50%    51600.000000  50300.000000  ...  2.105442e+07  51200.000000
# 75%    54700.000000  53200.000000  ...  2.759696e+07  53800.000000
# max    60400.000000  59000.000000  ...  5.946293e+07  59000.000000
# [8 rows x 6 columns]

 

Python Pandas DataFrame(데이터프레임)에 시계열 이동 평균 값 insert(추가) 하기

from datetime import datetime #날짜와 시간을 쉽게 조작할 수 있게 하는 클래스 제공
import pandas as pd
import matplotlib.pyplot as plt
from elice_utils import EliceUtils
pd.set_option('display.max_columns', None)
elice_utils = EliceUtils()


# 주식 데이터 불러오기
df = pd.read_csv('stock.csv') 


# 수정 종가 이동평균(MA: Moving Average) 값 구하기
ma5 = df['Adj Close'].rolling(window=5).mean() # 수정 종가 5일 이동평균
ma20 = df['Adj Close'].rolling(window=20).mean() # 수정 종가 20일 이동평균
ma60 = df['Adj Close'].rolling(window=60).mean() # 수정 종가 60일 이동평균


# 이동평균 값 추가하기
df.insert(len(df.columns), "MA5", ma5) # 'MA5'라는 열 이름으로 ma5 값 추가
df.insert(len(df.columns), "MA20", ma20) # 'MA20'라는 열 이름으로 ma20 값 추가
df.insert(len(df.columns), "MA60", ma60) # 'MA60'라는 열 이름으로 ma60 값 추가


# 거래량 5일 이동평균 추가
vma5 = df['Volume'].rolling(window=5).mean() # 거래량의 5일 이동평균 구하기
df.insert(len(df.columns), "VMA5", vma5) # 'VMA5'라는 열 이름으로 vma5 값 추가


# --- 이격도 추가 --- #
# 수정 종가 데이터를 5일 이동평균 값으로 나눈 비율
disp5 = (df['Adj Close']/df['MA5'])*100

# 이격도 데이터를 'Disp5'라는 열 이름으로 추가
df.insert(len(df.columns), "Disp5", disp5) 



# 데이터 확인
print('이동평균 및 이격도가 추가된 주가 데이터')
print(df)

 

  • .rolling : 이동 평균 구할 때 사용
  • df.insert(행길이,열이름,열값): 데이터프레임에 새로운 열 삽입

 

 

Python Pandas DataFrame(데이터프레임)에 시각화 하기

from datetime import datetime #날짜와 시간을 쉽게 조작할 수 있게 하는 클래스 제공
import pandas as pd
import matplotlib.pyplot as plt
from elice_utils import EliceUtils
elice_utils = EliceUtils()


# 주식 데이터 불러오기
df = pd.read_csv('stock.csv') 
print('초기 데이터 확인')
print(df)


# 주식 데이터 전처리하기(이전 문제에서 실행했던 코드)
ma5 = df['Adj Close'].rolling(window=5).mean()
ma20 = df['Adj Close'].rolling(window=20).mean()
ma60 = df['Adj Close'].rolling(window=60).mean()

df.insert(len(df.columns), "MA5", ma5)
df.insert(len(df.columns), "MA20", ma20)
df.insert(len(df.columns), "MA60", ma60)

vma5 = df['Volume'].rolling(window=5).mean()
df.insert(len(df.columns), "VMA5", vma5)

##############################################################################

# 이동평균선의 시각화
plt.plot(df.index, df['MA5'], label = "MA5") # 이동평균선 시각화
plt.plot(df.index, df['Adj Close'], label='Adj Close') # 수정 종가 시각화


# 시각화 옵션 코드
# (시각화 강의에서 별도로 다루는 내용입니다)
plt.legend(loc='best')
plt.xticks(rotation = 45)
plt.grid()
plt.savefig("plot.png")
elice_utils.send_image("plot.png")

 

[Option] 주가 변동률 계산

from datetime import datetime #날짜와 시간을 쉽게 조작할 수 있게 하는 클래스 제공
import pandas as pd
import matplotlib.pyplot as plt
from elice_utils import EliceUtils
elice_utils = EliceUtils()
pd.set_option('display.max_columns', None)



# 주식 데이터 불러오기
df = pd.read_csv('stock.csv') 
    
#          Date   High    Low   Open  Close    Volume  Adj Close  \
# 0    2020-03-02  55500  53600  54300  55000  30403412      55000   
# 1    2020-03-03  56900  55100  56700  55400  30330295      55400       

# 당일 종가가 아니라 다음 날 종가를 새로운 컬럼으로 추가하기
# shift(-1) 옵션을 사용하여 데이터를 하루씩 밀어서 삽입
df['tomorrow Adj Close']= df['Adj Close'].shift(-1)


# 주가 변동 및 변동률(%) 추가하기 - 기대 수익률 계산 가능
df['Fluctuation'] = df['tomorrow Adj Close'] - df['Adj Close'] # 주가 변동 데이터(다음날 종가 - 오늘 종가)
df['Fluctuation Rate'] = df['Fluctuation'] / df['Adj Close'] # 주가 변동률 데이터(변동 / 오늘 종가)


#     tomorrow Adj Close  Fluctuation  Fluctuation Rate  
# 0               55400.0        400.0          0.007273  
# 1               57400.0       2000.0          0.036101  

# 데이터 보기 변동률의 시각화
plt.figure(figsize=(12,8)) # 표현할 그래프의 크기 설정
plt.plot(df.index, df['Fluctuation Rate']) # 변동률 데이터 시각화
plt.axhline( y = 0, color = 'red', ls = '--') # 변동률 폭을 관찰하기 위한 기준 수평선 추가


# 시각화 옵션 코드
# (시각화 강의에서 별도로 다루는 내용입니다)
plt.legend(loc='best')
plt.grid()
plt.savefig("plot.png")
elice_utils.send_image("plot.png")
  • .shift(n): 데이터를 행 단위로 n칸씩 밀어낸다는 뜻입니다. 하루씩 밀어낸 종가 데이터에서 원래의 종가 데이터를 빼기 위해 사용되었습니다.
  • 변동 = 다음날 종가 - 오늘 종가


    [Result]

    시각적으로 분포를 살펴보니 -0.01과 +0.00~0.01 등락율 분포가 가장 많습니다. -0.04 로 하락하거나 +0.04로 상승하는 경우는 거의 없음을 볼 수 있습니다.

    이는 이 기업의 주가에서 급락 또는 급상승이 거의 없다는 것을 파악할 수 있습니다.

 

[시각화] 히스토그램, KDE(커널밀도추정) 으로 분포 살펴보기

  • 히스토그램 : 값이 어떤 값에 치중되어 있는지, 이상치(outlier)가 있는지 등을 직관적으로 파악
  • KDE : 히스토그램을 부드럽게 표현
 히스토그램을 이용해 분포 살펴보기
df['Fluctuation Rate'].plot.hist()
plt.title('Fluctuation Rate Histogram')

plt.cla() #그래프를 그린 후 초기화

# 커널 밀도함수를 이용해 분포 살펴보기
df['Fluctuation Rate'].plot.kde()
plt.title('Fluctuation Rate KDE plot')


[Option] mplfinance Library를 활용한 캔들 차트

from datetime import datetime
import pandas as pd
import matplotlib.pyplot as plt
import mplfinance as mpf
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker 
import matplotlib.dates as mdates
import numpy as np
from elice_utils import EliceUtils
elice_utils = EliceUtils()


# 주식 데이터 불러오기
df = pd.read_csv('stock.csv', index_col = 0, parse_dates = True) 
df2 = pd.read_csv('stock.csv', index_col = 0, parse_dates = True) 

print('주가 데이터 출력')
print(df)


# mplfinance 라이브러리를 사용하면 캔들 차트를 간편하게 시각화할 수 있습니다.
mc = mpf.make_marketcolors(up='r',down='b')
s  = mpf.make_mpf_style(marketcolors=mc)
mpf.plot(df, type='candle', figscale=1.2, style=s)


# 시각화 함수
plt.savefig("plot.png")
elice_utils.send_image("plot.png")


<< 1-3. 주가 예측 위한 모델 만들기 >>

순서

  1. 파생변수 생성
  2. 결측치제거
  3. 스케일링
  4. train/test/validation 나누기
  5. 모델 적재

 

from datetime import datetime #날짜와 시간을 쉽게 조작할 수 있게 하는 클래스 제공
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
from sklearn.preprocessing import MinMaxScaler
from sklearn.preprocessing import StandardScaler
from tensorflow.keras import models
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dropout, Dense, Activation
from elice_utils import EliceUtils
elice_utils = EliceUtils()



# 주식 데이터 불러오기
df = pd.read_csv('stock.csv') 
print('주식 데이터 확인하기')
print(df)


# 주가의 중간값 계산하기
high_prices = df['High'].values # 고가
low_prices = df['Low'].values # 저가
mid_prices = (high_prices + low_prices) / 2 # 고가와 저가의 중간값
print('주가의 중간값:', mid_prices)


# 주가 데이터에 중간 값 요소 추가하기
df['Mid'] = mid_prices # 'Mid' 열을 새로 만들고 mid_prices 데이터를 넣습니다.
print(df)


# 종가의 이동평균값을 계산하고 및 주가 데이터에 추가합니다.
ma5 = df['Adj Close'].rolling(window=5).mean()
df['MA5'] = ma5 # 'MA5' 열을 새로 만들고 ma5 값을 넣습니다.


df = df.fillna(0) # 비어있는 값을 모두 0으로 바꾸기
print('전처리 전의 주가 데이터')
print(df)



# --- 데이터 전처리 --- #

# Date 열을 제거합니다.
df = df.drop('Date', axis = 1)


# 데이터 스케일링(MinMaxScaler 적용)
min_max_scaler = MinMaxScaler()
fitted = min_max_scaler.fit(df)
output = min_max_scaler.transform(df)
output = pd.DataFrame(output, columns=df.columns, index=list(df.index.values))


print('\n전처리 후의 주가 데이터')
print(output.head())


# --- 데이터셋 나누기 --- #

# 0~60% 지점까지를 트레인셋(학습 데이터)으로 설정(전체의 60%)
train_size = int(len(output)* 0.6) 

# 60-90% 지점까지를 테스트셋으로 설정(전체의 30%)
test_size = int(len(output)*0.3) + train_size


#train/test 학습 및 라벨 설정
#종가를 예측하기 위해 종가를 label로 설정
train_x = np.array(output[:train_size]) # 트레인셋의 독립변수
train_y = np.array(output['Close'][:train_size]) # 트레인셋의 종속변수
test_x =np.array(output[train_size:test_size]) # 테스트셋의 독립변수
test_y = np.array(output['Close'][train_size:test_size]) # 테스트셋의 종속변수
validation_x = np.array(output[test_size:]) # 트레인셋의 독립변수
validation_y = np.array(output['Close'][test_size:]) # 테스트셋의 종속변수

print('분할 전 전체 데이터의 길이: %s' % len(output))
print('학습 데이터의 길이: %s' % len(train_x))
print('테스트 데이터의 길이: %s' % len(test_x))
print('검증용 데이터의 길이: %s' % len(validation_x))



# --- Keras를 이용한 딥러닝 수행 --- #

model = Sequential() # Keras 모델을 생성합니다.

# Keras 딥러닝 모델 학습을 위한 파라미터(옵션값)을 설정합니다.
# 현재 단계에서 각 파라미터에 대한 세부적인 내용까지 알 필요는 없으므로, 너무 걱정하지 마세요.
learning_rate = 0.01
training_cnt = 1000
batch_size = 100 
input_size = 8 

# 생성된 딥러닝 모델에 학습용 데이터(train_x)를 넣습니다.
# 마찬가지로 구체적인 코드를 처음부터 모두 이해하고 외울 필요는 없습니다.
model.add(Dense(input_size, activation='tanh', input_shape=(train_x.shape[1],))) 
model.add(Dense(input_size * 3,  activation='tanh')) 
model.add(Dense(1, activation='tanh'))

# 데이터를 학습을 진행합니다.
model.compile(optimizer='sgd', loss='mse', metrics=['mae', 'mape','acc'])
model.summary()
history = model.fit(train_x, train_y, epochs=training_cnt,   
                    batch_size=batch_size, verbose=1)
val_mse, val_mae, val_mape, val_acc = model.evaluate(test_x, test_y, verbose=0)

    
# --- 학습 결과를 그래프로 확인해봅니다 --- #

# 학습된 모델로부터 테스트 데이터를 예측합니다.
pred = model.predict(test_x)

fig = plt.figure(facecolor='white', figsize=(8, 5))
ax = fig.add_subplot(111)
ax.plot(test_y, label='True') # 실제 주가
ax.plot(pred, label='Prediction') # 우리가 만든 딥러닝 모델이 예측한 주가
ax.legend()

# 현재까지 그려진 그래프를 시각화
plt.savefig("plot.png")
elice_utils.send_image("plot.png")
  • 입력피쳐(입력특성) 설계 : Input을 위한 데이터 구조 설계
    ← 여기서는 중간값, 이동평균값 이 해당 될 수 있겠죠
  • 데이터 전처리 : 데이터의 값이 너무 크거나 작으면 모델에 제대로 반영되지 않습니다
    - 여기서는 전처리 방법 중 하나인 스케일링 을 합니다.
  • 데이터 셋 분할 : 일반적으로 학습/테스트/검정을 6:3:1 로 나눕니다
  • 모델 적재
    -
    learning_rate(학습률): 학습 효율을 얼마나 좋게 할 것인지를 설정합니다. 효율이 크면 좋을 것 같지만, 이 경우 모델이 특정 데이터셋만 과하게 학습하기 때문에 모델이 보지 못한 새로운 데이터가 나타났을 때 제대로 예측하지 못합니다

    - training_cnt(반복횟수): 학습을 얼마나 오래 반복할 것인지를 설정합니다. 일반적으로 학습을 오래 반복할 수록 성능이 좋아지지만, 그만큼 학습 시간이 길어집니다.

    - batch_size(회당 학습량): 한 번에 얼마나 많은 데이터를 학습할지를 설정합니다. 높을 수록 좋지만, 그만큼 더 많은 컴퓨터 성능을 요구하며 학습 속도가 느려집니다.

  • 예측 : model.predict()