개인작품

주식데이터 저장 + 추세선 그리기(2)

집못가는프로그래머 2021. 9. 14. 08:53

출연 : hive 3.1.2, hadoop 3.2.2, sklearn(LinearRegression), matplotlib, etc.

 

줄거리 : 본격적으로 테이블에 저장된 정보를 바탕으로 sklearn모델을 이용하여 추세선을 그린다.

 


1.

import pandas as pd
import numpy as np
from hdfs import InsecureClient
from pyhive import hive
from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearAegression
import matplotlib.pyplot as plt
from datetime import datetime
from sklearn.model_selection import train_test_split

 

이번에 사용된 파이썬 모듈들...

 

 

2.

try :
    conn = hive.Connection(host = '127.0.0.1', port = 10000, username = 'hdoop')
    cursor = conn.cursor()
except Exception as e :
    print('Error : ', e)

1편에서 하이브 커넥션을 만들어줬던 세팅과 같다.(1편에서 try문을 아예 이용하지 않을걸 뒤늦게 알고 2편에는 추가해봤다)

참고로 주피터 노트북을 통해 작성하였기 때문에 셀마다 try문이 적혀있을 수 있다..(개인적으로 보기 불편함을 느낀다..)

 

 

 

3.

# 조회할 테이블 선택

try :
    cursor.execute('use stockDB')
    cursor.execute('show tables')
    
    tables = cursor.fetchall()
except Exception as e :
    print('Error : ', e)



for num, tbl_name in enumerate(tables) :
    print(f'{num+1} : {tbl_name[0]}')

while True :
    sel = int(input('\n조회할 테이블 번호 입력 : ')) 
    if 1 <= sel <= len(tables) : break;
    print(f'!!!!! <1 ~ {len(tables)} 사이 숫자만 입력 가능>')

tbl_name = tables[sel-1][0]
print(f'선택한 테이블 : {tbl_name}')

Output

1편에서 만들었던 stockDB에 있는 테이블을 조회한다. 나는 현재 주식 테이블이 4개 들어있다. 테이블 이름만 봐도 어떤 주식에 대한 내용인지, 어느 날짜부터 조회한 기록인지 보기 편해서 개인적으로 맘에든다.

show tables를 써서 모든 테이블명을 가져오고 사용자에게 보여준다.

사용자는 조회하고자 하는 테이블의 번호를 입력하고 (e.g. 1번 : 구글 2011-1월-1일부터의 데이터)

tbl_name에 사용자가 선택한 테이블명을 저장한다.(이 변수또한 유용하게 사용된다)

while문이 사용된 이유는 사용자가 화면에 표시된 테이블 번호 범위 밖의 번호를 입력 방지하기 위함이다.

 

 

4.

# 테이블 -> 데이터프레임 

try :
    # 테이블 컬럼명
    cursor.execute(f'desc {tbl_name}')
    columns = list(map(lambda x : x[0],cursor.fetchall()))

    #테이블 조회
    cursor.execute(f'select * from {tbl_name}')
    df = pd.DataFrame(data = cursor.fetchall(), columns = columns)
except Exception as e :
    print('Error : ',e)
    
#데이터 프레임 생성

df['date'] = pd.to_datetime(df['date'])
df.set_index('date', inplace=True)

print('< 테이블 조회 >')
print(tbl_name)
display(df.head(10))

Output

사용자가 선택한 테이블에서 컬럼명, 데이터를 가져오기 위한 과정이다. try문을 급작스럽게 작성하는 바람에 범위가 이상한 감이 없지않다..

'desc 테이블명'을 이용해 테이블의 컬럼명을 가져온다. (desc로 테이블을 조회하면 테이블의 구조를 볼 수 있는데 그중 0번째 값이 컬럼명이다. 이를 lambda함수를 이용해 리스트로 만들었다)

테이블 조회는 select * from 테이블명을 통해 가져온다.

(1번에서 언급했지만 사용자가 원하는 테이블명을 tbl_name이라는 변수에 저장했기 때문에 사용자가 테이블을 선택할 수 있었고 선택한 테이블의 조회, 컬럼명과 데이터 저장이 모두 용이했다...물론 더 편한 방법이 있을 터..)

이렇게 불러온 데이터를 데이터 프레임으로 저장한다. 테이블 내용에는 날짜도 포함되어 있는데 저장된 type이 string이다.

데이터프레임에 저장 후 type을 datetime으로 변환하였다(이유는 나중에 나옴) 그리고 인덱스로 지정.

 

 

5.

# 테이블의 주식 차트 그리기
plt.figure(figsize = (12,8)) 
plt.plot(df['adj close'])
plt.show()

Output

테이블의 내용을 데이터프레임에 저장하고 그래프화 시켜본 것이다.

추세선을 그릴때 이용된 값은 adj close(수정 종가)와 날짜이다. 즉 날짜에 따른 수정 종가를 나타낸 것이다.

 

 

6.

# 날짜 변화에 따른 수정 종가(adj close) 
days = np.array((range(1, len(df)+1)))

이것은 그냥 사전에 미리 만든것인데 나는 날짜에 따른 수정 종가의 추세선을 다룰거기 때문에 시작 날짜로부터 몇일이 지났는지 일수를 담고있는 배열 days를 만들었다. (days의 길이는 테이블이 담고있던 정보의 전체 길이와 동일하므로 len(df)가 마지막 날짜가 된다)

 

 

7.

# 최대 결정계수 및 차수 구하기
pf = PolynomialFeatures()

r2_result = []
for i in range(1, 15):
    # 테스트, 검증 데이터 생성, random_state = 5
    x_train, x_test, y_train, y_test = train_test_split(days.reshape(-1,1), df['adj close'], random_state = 5)
    pf.degree = i
    x_train_poly = pf.fit_transform(x_train)
    model = LinearRegression()
    #테스트 데이터 기반 모델 생성
    model.fit(x_train_poly, y_train)
    
    #검증용 데이터 기반 결정계수 계산
    x_test_poly = pf.fit_transform(x_test)
    r2 = model.score(x_test_poly, y_test)
    
    print('R-Squared(degree=%s): %.4f' %(i, r2))
    #결정계수 List
    r2_result.append(r2)
    
r2_max = np.argmax(r2_result)+1
print('%d차 함수일 때,  최대 결정계수 : %s' % (r2_max, r2_result[r2_max-1]))

Output

음..위 내용은 최대 결정계수를 구하는 것인데 사실 추세선을 그릴때 최대 결정계수는 필요 없다. 물론 나는 주린이지만.. 내가 아는한 추세선이란 주식이 상향 그래프인지 하향 그래프인지 나타내는 것이므로 1차 직선으로 표현된다.

그런데 최대 결정계수(R2)를 구한 이유는 나중에 주식 차트를 그릴때 주식 차트의 동선을 좀 더 매끄럽게 보기 위함이다(자세한 이유는 나중에 나옴)

train_test_split을 이용해 테스트, 검증용 데이터를 만들고 1~15차수 중에 어떤 값이 최대 결정계수인지 구한다.

각 차수마다의 결정 계수를 r2_result라는 배열에 담고 배열의 최대값을 보면 최대 결정계수를 구할 수 있다.

이렇게 구해진 최대 결정계수의 차수는 r2_max에 저장하였다.

 

 

8.

# x값을 7차항으로 변환하고 모델 재생성
pf = PolynomialFeatures(degree = r2_max)
days_poly = pf.fit_transform(days.reshape(-1,1))
model1 = LinearRegression()
model1.fit(days_poly, df['adj close'])

위에서 구한 최대 결정계수를 통해 Polynomial(다항식)을 만들고 모델(model1)을 생성한다.

나는 날짜에 따른 변화를 보는것이기 때문에 days(날짜)를 다항식으로 변환하고 model1에 adj close(수정 종가)와 함께 입력으로 넣어준다.

 

 

9.

model2 = LinearRegression()
model2.fit(days.reshape(-1,1), df['adj close'])

위는 추세선을 구하기 위한 모델(model2)이다.

추세선은 직선이기 때문에 polynomial이 필요 없다.

 

 

10.

# 그래프 작성
predicted = model1.predict(days_poly)
trend = model2.predict(days.reshape(-1,1))

plt.figure(figsize = (12,8))
plt.plot(df.index, predicted, c = 'k', label = 'Prediction', linewidth=2)
plt.plot(df.index, trend, c = 'b', label = 'trend', linewidth=2)
plt.plot(df.index, df['adj close'], c = 'y', label = 'Stock data', linewidth=2)
plt.ylabel('Dollar($)')
plt.xlabel(f'Year')
plt.legend(loc = 'best')
plt.show()

Output

지금까지 모든 데이터를 수집하고 어떤 데이터를 이용할지 선택하고 선택한 데이터를 바탕으로 모델까지 생성하였다.

이제 그래프화 시켜주는 작업을 끝으로 마무리된다.

첫째로 predicted는 주식 차트의 전체적인 흐름을 model1을 통해 구한 값들을 담은 것이다.

둘째로 trend가 주식에 대한 추세선을 model2를 통해 구하고 값들을 담은 것이다.

그래프에서 파란줄이 trend(추세선)을 의미하고 검은줄이 predicted(주식 흐름)이다. 노란색은 위에서도 한번 그려봤던 주식 그래프이다.

전에 df 데이터프레임에 date를 datetime형태로 변환한 이유가 여기서 나타나는데, x축(year)를 보면 간단하게 년도별로 나타난다. 이는 x축에 df.index를 주었고 그 값들이 표로 구현될때 이렇게 자동으로 보기 편한 날짜의 값으로 변형되어 표시되기 때문이다.

만약 표현하고자 하는 날짜가 저렇게 10년치가 아닌 1년치라면 아마 1월~12월 까지로 표시될 것이다.

 

 

이상으로 개인적으로 성취감 있는 작품 설명을 마무리한다.