EDA - 시계열 분석(Forecast)

BlackLabel·2023년 10월 19일
0

EDA, 크롤링 기초 

목록 보기
10/13

시계열(Forecast) 분석

  • 시간의 흐름에 대해 특정 패턴과 같은 정보를 가지고 있는 경우
  • 머신러닝에서는 시계열 데이터를 다루지 않는 경우가 많다
  • 머신러닝에서는 '시간'이라는 것이 특성으로 잡히는 경우가 많지 않다
  • 딥러닝 부분에서 다시 다룰 예정이다
  • 통계학에서 많이 언급되는 부분이다
  • 개요 정도의 레벨에서 접근하는 시계열은 대부분 forecast이다
  • 주기성을 가지고 있는 데이터를 다루는 경우를 Seasonal Time Series라고 한다
    먼저 트랜드를 찾는다
  • 트랜드를 뺀 주기적 특성을 찾는다.

함수의 기초

전역 변수

a = 1

def edit_a(i):
  global a
  a = i

edit_a(2)
a
  • 함수내에서의 변수와 밖에서의 변수는 같은 이름이어도 같은 것이 아니다

삼각함수

$$ y = asin(2\pi ft + t_0) + b $$

1

  • $$ $$ 안에 수식 넣으면 함수 식으로 바뀜
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline
def plotSinWave(amp, freq, endTime,sampleTime,startTime, bias):
    
    """
    plot sine wave
    y = a sin(2 pi t + t_0) + b
    """
    time = np.arange(startTime, endTime, sampleTime) # 시작, 끝, 간격
    result = amp * np.sin(2 * np.pi * freq * time + startTime) + bias
    
    plt.figure(figsize=(12,6))
    plt.plot(time, result)
    plt.grid(True)
    plt.xlabel('time')
    plt.ylabel('sin')
    plt.title(str(amp) + '*sin(2*pi' + str(freq) + '*t+' + str(startTime) + ')' + str(bias))
    
    plt.show()
plotSinWave(2,1,10,0.01,0.5,0)

  • """ 으로 묶인 부분은 나중에 shift+tab(독스트링)하면 나오는 설명부분을 작성한 것이다

  • 1번같은 경우 함수에 필요한 인자가 많아서 나중에 사용하기에 순서가 헷갈릴 수 있다

2

def plotSinWave(**kwargs):
    
    """
    plot sine wave
    y = a sin(2 pi t + t_0) + b
    """
    
    endTime = kwargs.get('endTIme',1)
    sampleTime = kwargs.get('sampleTime',0.01)
    amp = kwargs.get('amp',1)
    freq = kwargs.get('freq',1)
    startTime = kwargs.get('startTime',0)
    bias = kwargs.get('bias',0)
    figsize = kwargs.get('figsize',(12,6))
    
    time = np.arange(startTime, endTime, sampleTime) # 시작, 끝, 간격
    result = amp * np.sin(2 * np.pi * freq * time + startTime) + bias
    
    plt.figure(figsize=(12,6))
    plt.plot(time, result)
    plt.grid(True)
    plt.xlabel('time')
    plt.ylabel('sin')
    plt.title(str(amp) + '*sin(2*pi' + str(freq) + '*t+' + str(startTime) + ')' + str(bias))
    plt.show()
plotSinWave(amp=2, freq=0.5, endTime=10)

  • **kwargs: 인자들의 기본값(디폴트값)을 사용자가 지정 및 수정할 수 있음(별 두개 다음 오는 단어는 상관없음)

내가 만든 함수 import

  • drawSinWave.py
%%writefile ./drawSinWave.py

import numpy as np 
import matplotlib.pyplot as plt 

def plotSinWave(**kwargs):
    """
    plot sine wave 
    y = a sin(2 pi f t + t_0) + b
    """
    endTime = kwargs.get("endTime", 1)
    sampleTime = kwargs.get("sampleTime", 0.01)
    amp = kwargs.get("amp", 1)
    freq = kwargs.get("freq", 1)
    startTime = kwargs.get("startTime", 0)
    bias = kwargs.get("bias", 0)
    figsize = kwargs.get("figsize", (12, 6))
    
    time = np.arange(startTime, endTime, sampleTime)
    result = amp * np.sin(2 * np.pi * freq * time + startTime) + bias 
    
    plt.figure(figsize=(12, 6))
    plt.plot(time, result)
    plt.grid(True)
    plt.xlabel("time")
    plt.ylabel("sin")
    plt.title(str(amp) + "*sin(2*pi" + str(freq) + "*t+" + str(startTime) + ")+" + str(bias))
    plt.show()
    
if __name__ == "__main__":
    print("hello world~!!")
    print("this is test graph!!")
    plotSinWave(amp=1, endTime=2)
Overwriting ./drawSinWave.py

if name == "main":

  • 직접 실행된 것이냐
  • 혹은 다른 코드에서 import되어 불러와진 것이냐를 물어보는 코드
  • 이 파이썬 파일이 어디서 실행되었느냐(drawSinWave.py)
import drawSinWave as dS

dS.plotSinWave()

그래프 한글 설정

%%writefile ./set_matplotlib_hangul.py

import platform
import matplotlib.pyplot as plt 
from matplotlib import font_manager, rc

path = "c:/Windows/Fonts/malgun.ttf"

if platform.system() == "Darwin":
    print("Hangul OK in your MAC!!!")
    rc("font", family="Arial Unicode MS")
elif platform.system() == "Windows":
    font_name = font_manager.FontProperties(fname=path).get_name()
    print("Hangul OK in your Windows!!!")
    rc("font", family=font_name)
else:
    print("Unknown system.. sorry~~~")
    
plt.rcParams["axes.unicode_minus"] = False 
plt.title("한글")


-> 한글로 잘 나온다

  • 위 한글 설정 코드를 set_matplotlib_hangul.py로 저장 시키고 그래프 그릴때 import 해서 사용 하자
 import set_matplotlib_hangul

fbprophet 기초

fbprophet 설치

설치

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

# 데이터 준비
time = np.linspace(0,1, 365*2) # 0부터 1까지 730등분
result = np.sin(2*np.pi*12*time)
ds = pd.date_range('2017-01-01', periods=365*2, freq='D') # freq='5D'이면 5일씩
df = pd.DataFrame({
    'ds':ds,
    'y':result
})
df.head()

df['y'].plot(figsize=(10,6));

1

from prophet import Prophet
# 학습
m = Prophet(yearly_seasonality=True, daily_seasonality=True)
m.fit(df) 

# 이후 30일간의 데이터 예측
future = m.make_future_dataframe(periods=30)
forecast = m.predict(future)

m.plot(forecast)

  • 기존의 데이터를 가지고 학습을 한다
  • 학습한 내용을 토대로 이후의 30일간의 예측을 그래프에 표시한다
  • 점선기존의 데이터이고 실선예측한 데이터이다

2

time = np.linspace(0,1, 365*2)
result = np.sin(2*np.pi*12*time) + time
ds = pd.date_range('2017-01-01', periods=365*2, freq='D')
df = pd.DataFrame({
    'ds':ds,
    'y':result
})

df['y'].plot(figsize=(10,6));

m = Prophet(yearly_seasonality=True, daily_seasonality=True)
m.fit(df) # 학습
future = m.make_future_dataframe(periods=30) 
forecast = m.predict(future)
m.plot(forecast)

3

time = np.linspace(0,1, 365*2)
result = np.sin(2*np.pi*12*time) + time + np.random.randn(365*2)/4
ds = pd.date_range('2017-01-01', periods=365*2, freq='D')
df = pd.DataFrame({
    'ds':ds,
    'y':result
})

df['y'].plot(figsize=(10,6));

m = Prophet(yearly_seasonality=True, daily_seasonality=True)
m.fit(df)
future = m.make_future_dataframe(periods=30)
forecast = m.predict(future)
m.plot(forecast)

웹 유입량 데이터 분석

import pandas as pd 
import pandas_datareader as web 
import numpy as np 
import matplotlib.pyplot as plt 

from fbprophet import Prophet
from datetime import datetime 

%matplotlib inline 
pinkwink_web = pd.read_csv(
    "../data/05_PinkWink_Web_Traffic.csv",
    encoding="utf-8",
    thousands=",",
    names=["date", "hit"],
    index_col=0
)

pinkwink_web = pinkwink_web[pinkwink_web["hit"].notnull()]
pinkwink_web.head()

# 전체 데이터 그려보기
pinkwink_web['hit'].plot(figsize=(12,4), grid=True)

  • 구간이 짧은 영역을 가지는 주기성 데이터가 있고
  • 전반적으로 있는 주기성 데이터가 있는 것으로 보인다
  • 자연상에 있는 데이터들은 여러 주파수 대역에 하모니로 연결되어 있기 때문에 하나의 주파수만 가지는 데이터는 거의 없다
# trend 분석을 시각화하기 위한 x축 값을 만들기
time = np.arange(0,len(pinkwink_web))
traffic = pinkwink_web['hit'].values
fx = np.linspace(0, time[-1], 1000)

# 에러를 계산할 함수
def error(f,x,y):
  return np.sqrt(np.mean(f(x) - y) ** 2) # 에러의 제곱의 평균에 루트를 씌운 것
  • 에러 : trend를 만든 다음 그 트랜드가 얼마나 원 데이터를 잘 반영하는지에 대한 정량적 평가 지표
fp1 = np.ployfit(time, traffic, 1) # 계수
f1 = np.poly1d(fp1) # 함수

f2p = np.ployfit(time, traffic, 2) # 계수
f2 = np.poly1d(f2p) # 함수

f3p = np.ployfit(time, traffic, 3) # 계수
f3 = np.poly1d(f3p) # 함수

f15p = np.ployfit(time, traffic, 15) # 계수
f15 = np.poly1d(f15p) # 함수
  • time과 treffic을 가지고 1차,2차,3차,15차 함수를 만들어라
  • x값을 집어넣으면 y값이 나오는 함수

  • 1차,2차,3차 함수까지는 에러의 큰 차이가 없다
    그럼 1차식 혹은 15차식 중에 고르는게 맞는거같다.
plt.figure(figsize=(12,4))
plt.scatter(time, traffic, s=10)
plt.plot(fx,f1(fx),lw=4, label='f1')
plt.plot(fx,f2(fx),lw=4, label='f2')
plt.plot(fx,f3(fx),lw=4, label='f3')
plt.plot(fx,f15(fx),lw=4, label='f15')

plt.grid(True, linestyle='-', color='0.75')
plt.legend(loc=2)

  • 몇차식을 고를지는 코더 마음이지만 강의에서는 1차식을 선택
  • 자세히 분석해야한다면 15차식
df = pd.DataFrame({'ds':pinkwink_web.index, 'y': pinkwink_web['hit']})
df.reset_index(inplace=True)
df['ds'] = pd.to_datetime(df['ds'], format='%y. %m. %d.')
del df['date']
df.head()

  • 데이터를 prophet에서 사용하기 좋게 바꾼다
m = Prophet(yearly_seasonality=True, daily_seasonality=True)
m.fit(df) # prophet에 적용


future = m.make_future_dataframe(periods=60)
future.tail()

  • 60일에 해당하는 데이터 예측
 # 예측 결과는 상한/하한의 범위를 포함해서 얻어진다
forecast = m.predict(future)
forecast[['ds','yhat','yhat_lower','yhat_upper']].tail()

m.plot(forecast);

  • 내가 준 데이터가 주기성이 좋을수록 예측결과가 좋아진다
m.plot_components(forecast)
  • 각 주제에 맞는 데이터들이 출력된다

  • trend 분석

  • 요일별 특성
  • trend 대비 방문객 수는 월,화,수에 많다

  • 연간 데이터
  • 4월, 6월, 11월 경에 데이터가 많다, 특히 4,6월
  • 원인을 분석해보면 이때쯤이 대학생들 시험시간이다라고 예측해볼 수 있다

주식 데이터 분석

# 데이터 추출 : 네이버
req = Request(
    "https://finance.yahoo.com/quote/035420.KS/history?p=035420.KS&guccounter=1",
    headers={"User-Agent" : "Chrome"},
)
page = urlopen(req).read()

soup = BeautifulSoup(page, 'html.parser')
table = soup.find("table")
df_raw = pd.read_html(str(table))[0]
df_raw.head(3)

# fbprophet 형식에 맞추고 nan 제외
# 종가(Close*) 사용
df_tmp = pd.DataFrame({'ds' : df_raw['Date'], 'y' : df_raw["Close*"]})
df_target = df_tmp[:-1]
df_target.head()

# 날짜 형식 변경
df = df_target.copy()
df['ds'] = pd.to_datetime(df_target['ds'], format="%b %d, %Y")
df.head()

# 정보 확인
df.info()

# y컬럼 데이터 타입 변경
df["y"] = df['y'].astype("float")

# 예측
m = Prophet(yearly_seasonality=True, daily_seasonality=True)
m.fit(df);
future = m.make_future_dataframe(periods=30)
forecast = m.predict(future)
forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].tail()

# 실제 데이터와 예측 데이터 비교
plt.figure(figsize=(12, 6))
plt.plot()
plt.plot(df['ds'], df['y'], label = 'real')
plt.plot(forecast['ds'], forecast['yhat'], label = 'forecast')
plt.grid(True)
plt.show();

# 예측 그래프
m.plot(forecast);

# 트렌드 분석
m.plot_components(forecast);

yfinance 모듈

  • data_reader를 통해 주가 정보를 얻는 기능이 있었지만 안정성 문제로 동작하지 않는다.
  • 우회적으로 야후의 기능을 복구시켜주는 모듈
    pip install yfinance
# 테스트 : 대한항공(003490)
start_date = "2010-03-01"
end_date = "2018-02-28"
KoreaAir = data.get_data_yahoo("003490.KS", start_date, end_date)
KoreaAir.head()

# 종가 그래프
KoreaAir["Close"].plot(figsize=(12, 6), grid=True);

# 2017-11-30 잘라내기 : 예측값과 비교해보기 위해
KoreaAir_trunc = KoreaAir[:"2017-11-30"]
KoreaAir_trunc.head()

# 데이터 정리
df = pd.DataFrame({'ds' : KoreaAir_trunc.index, 'y':KoreaAir_trunc["Close"]})
df.reset_index(inplace=True)
del df["Date"]
df.head()

# 예측
m = Prophet(yearly_seasonality=True, daily_seasonality=True)
m.fit(df)
future = m.make_future_dataframe(periods=90)
forecast = m.predict(future)
forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].tail()

# 실제와 예측값 비교 그래프
plt.figure(figsize=(12, 6))
plt.plot(KoreaAir.index, KoreaAir["Close"], label="real")
plt.plot(forecast["ds"], forecast["yhat"], label="forecast")
plt.grid(True)
plt.legend()
plt.show();

# 예측 그래프
m.plot(forecast);

# 트렌드 분석
m.plot_components(forecast);

비트코인 데이터 분석

비트코인 데이터 https://bitcoincharts.com/charts/bitstampUSD#rg60ztgSzm1g10zm2g25zv

  • symbol, time period에 따라 주소값이 변경된다.
  • time에 따라 rg일수로 변경됨을 확인(2달 rg60, 2년 rg730)
# 웹 페이지 접근
# 2년 URL 주소
driver = webdriver.Chrome('../driver/chromedriver.exe')
driver.get("https://bitcoincharts.com/charts/bitstampUSD#rg730ztgSzm1g10zm2g25zv")

# raw_data 메뉴가 가려지기 때문에 스크롤 조정
xpath = '''//*[@id="content_chart"]/div/div[2]/a'''
variable = driver.find_element_by_xpath(xpath)
driver.execute_script('return arguments[0].scrollIntoView();', variable)
variable.click()

# 데이터 추출
html = driver.page_source

soup = BeautifulSoup(html, 'html.parser')
table = soup.find("table", id='chart_table')
table

  • 데이터프레임 생성 : read_html
# read_html로 데이터프레임 생성
df = pd.read_html(str(table))
bitcoin = df[0]
bitcoin.head()

# 셀레니움 종료
driver.close()

# 종가 그래프
bitcoin["Close"].plot(figsize=(12, 6), grid=True);

# 종가로 Prophet 적용
df = pd.DataFrame({'ds' : bitcoin["Timestamp"], 'y' : bitcoin["Close"]})

# 예측
m = Prophet(yearly_seasonality=True, daily_seasonality=True)
m.fit(df);
future = m.make_future_dataframe(periods=30)
forecast = m.predict(future)
forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].tail()

# 실제, 예측 비교 그래프
plt.figure(figsize=(12, 6))
plt.plot(df['ds'], df['y'], label='real')
plt.plot(forecast['ds'], forecast['yhat'], label='forecast')
plt.grid(True)
plt.legend(loc=2)
plt.show();

# 예측 그래프
m.plot(forecast);

# 트렌드 분석
m.plot_components(forecast);

profile
+database

0개의 댓글