{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# [원문] [Crypto forecasting tutorial - carlos stein n brito](https://www.kaggle.com/cstein06/tutorial-to-the-g-research-crypto-competition)\n\nThis kernel is the Korean translation of [Crypto forecasting tutorial - carlos stein n brito](https://www.kaggle.com/cstein06/tutorial-to-the-g-research-crypto-competition). Please refer to the link for the original text.\n\n본 커널은 [Crypto forecasting tutorial - carlos stein n brito](https://www.kaggle.com/cstein06/tutorial-to-the-g-research-crypto-competition)의 한국어 번역본입니다. 원문을 보고 싶으시면 링크를 참고해주세요.\n\n---\n*원문을 번역하면서 부가 설명 및 설명을 위한 코드를 추가했습니다. 따라서 원문에는 없는 주석이나 내용이 포함되어 있을 수 있으니 참고 부탁드립니다.*","metadata":{}},{"cell_type":"markdown","source":"> Table of Contents\n```\n1. G-Research Crypto forecasting competition\n   1-1. The Cryptocurrency Market(암호화폐 시장)\n   1-2. Forecasting returns(수익 예측)\n2. Dataset description(데이터셋 소개)\n   2-1. Load the training set(학습 데이터 불러오기)\n   2-2. Data features\n   2-3. Candlestick charts(캔들 차트)\n3. Preprocessing(전처리)\n   3-1. Dealing with missing data(결측치 처리)\n   3-2. Data visualization\n   3-3. Log returns(로그 변환된 수익)\n   3-4. Correlation between assets\n4. Building your prediction model(예측 모델 구현)\n   4-1. Prediction targets and evaluation\n   4-2. Feature design\n   4-3. Preparing the data for building predictive models\n   4-4. Baseline model: Linear Regression(선형 회귀)\n```","metadata":{}},{"cell_type":"markdown","source":"## 1. G-Research Crypto forecasting competition","metadata":{}},{"cell_type":"markdown","source":"[G-Research Crypto forecasting competition](https://www.kaggleusercontent.com/kf/78721864/eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0..pLhgGXU4kw56ttAxAdOh2Q.Z3kMdL5jKOuvGqGPIDp272XUHrIC7OVW0MTjTi4IzYqJJqrPWJRInG6aKvMZJ_oDJ2N_ioNiR3OmnHU0MIoD99M-2TlPIkXtwVIqn6aXlKNoRQoLnUiHd583xLYosT1tcI2ThBsxigVMZQZf8iP4PgZz_ZRKHivlKpd63JtK_fmoLvPOGPrPgABA3Sq13_vxpM2htnVOc218zkrSV7189xeUl3r4PR-lG5r1Nqriyuht54us243c9Rwlh7yM0_Am29sq4NXkRoJbGSiHWC5VQnUzx1cqsJPhLG0t0P-AVbwZS2JixR29mqZ09bNGvIpSaEtDOn6Q7eXW7aIwzdJdq-O_FWEiUsd-lOFS_FfpIVShloTLL-zzWs-cc_IolYlrdVTI7JgED9IwvdcUxdySA6Neqzj55-jVwoYsla9gaT5tN_htxLgNkrBBL6Smo4yGrXfL59aEdWMxekTIhryN35d4Ayf7_L1_LOqyMoUskSC_HA8MSRnrJfBwg_bs8B8qB0dTOhFBe_CYbSohDV2hzlup3oPzjl5nb14sJArRjuScsgDr7w1cdpgRwQAJeVNl4Rnm_WxTvEWEhHwelvt5pbVv7zwaWxo2eKSYu2VGKDgF6nVyURq_kuBHk-ZfKyAKCnW_h8FtfoGir2VNIbFh9yzyCsLSKu1uUmXsHGf6JWT6dxT49DEm_thqtPBMIvKN.ew0nR2D55NdrgO0l-z3S3Q/link)에서 참가자들은 주요 암호화폐들에 대한 수익 예측에 도전합니다. 그리고 저희는 여러분의 원활한 참여를 돕기 위해 이번 예측 대회의 주요 사항들을 커버하는 튜토리얼 Notebook을 작성하기로 했습니다.","metadata":{}},{"cell_type":"markdown","source":"### 1-1. The Cryptocurrency Market(암호화폐 시장)","metadata":{}},{"cell_type":"markdown","source":"먼저, 암호화폐 세계에 대해 빠르게 소개하겠습니다. 암호화폐는 굉장히 인기가 많고 격한 시장으로, 투자자들에게 엄청난 수익(또는 그 반대)을 가져다 주기도 합니다. 많은 분들이 아실 비트코인이나 이더리움을 포함해 지금까지 수 천개의 암호화폐들이 만들어졌습니다.\n\nCryptoCompare에 따르면(2021년 7월 25일 기준) 암호화폐는 지난 한 해 동안 매일 평균 거래량이 410억 달러로, 거래소에서 광범위하게 거래되었습니다.\n\n암호 화폐들 간에 주요한 특징이 있다면, 가격 변동이 서로 연결되어 있다는 점입니다. 예를 들어 비트코인은 암호 화폐 시장의 가격 변동을 주로 이끌어왔지만, 다른 수 많은 코인도 이 추세와 함께 움직여왔습니다. 모든 코인이 서로 유기적으로 시장에 영향을 주고 있는 것입니다.","metadata":{}},{"cell_type":"markdown","source":"### 1-2. Forecasting returns(수익 예측)","metadata":{}},{"cell_type":"markdown","source":"금융 모델링의 가장 근본적인 목적은 가까운 미래에 가격이 어떻게 변할지 예측하기 위함입니다. 학습 데이터로 주어진, 과거부터 현재까지의 시계열 가격 데이터를 사용하여 가격이 상승할지 하락할지, 그리고 자산 수익률이 얼마나 될지 예측하고자 합니다.\n\n이번 대회에서 캐글러들은 머신러닝 모델을 만들고 14개 대표 암호화폐들의 수익을 '분'에서 '시간' 단위로 예측하게 됩니다. 여러분은 수백만 행의 분당 암호화폐 거래 데이터에 접근할 수 있으며 이를 통해 14개 자산 모두에 대한 예측 모델을 동시에 설계할 수 있습니다. 경쟁이 종료된 후 3개월의 평가 기간 동안 수집된 실제 시장 데이터와 여러분의 예측이 얼마나 상관관계가 있는지에 따라 평가가 진행될 것입니다.\n\n암호화폐 수익 예측은 여전히 풀리지 않은 문제입니다. 과한 변동성, 시계열 데이터의 비정상성, 시장과 밈 조작, 매우 빠르게 변화하는 시황과 자산간의 관계를 고려할 때 이는 매우 어려운 예측 작업입니다. 그러나 이렇게 어려운 문제인만큼 상당히 매력적인 머신러닝 영역이기도 합니다.","metadata":{}},{"cell_type":"markdown","source":"## 2. Dataset description(데이터셋 소개)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-12-12T03:03:46.417318Z","iopub.execute_input":"2021-12-12T03:03:46.41777Z","iopub.status.idle":"2021-12-12T03:03:46.454343Z","shell.execute_reply.started":"2021-12-12T03:03:46.417687Z","shell.execute_reply":"2021-12-12T03:03:46.453144Z"}}},{"cell_type":"markdown","source":"이제, 데이터를 살펴볼 것입니다. 먼저 대회 데이터셋을 불러와서 기본적인 요소들을 살펴보겠습니다.","metadata":{}},{"cell_type":"markdown","source":"### 2-1. Load the training set(학습 데이터 불러오기)","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nfrom datetime import datetime","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:41:59.482785Z","iopub.execute_input":"2021-12-13T08:41:59.484069Z","iopub.status.idle":"2021-12-13T08:41:59.488663Z","shell.execute_reply.started":"2021-12-13T08:41:59.484005Z","shell.execute_reply":"2021-12-13T08:41:59.487936Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"data_folder = \"../input/g-research-crypto-forecasting/\"\n\n# !는 쉘 명령어의 시작을 의미합니다. 쉘 명령에서는 $기호로 변수를 사용할 수 있습니다.\n!ls $data_folder","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:41:59.490438Z","iopub.execute_input":"2021-12-13T08:41:59.490701Z","iopub.status.idle":"2021-12-13T08:42:00.281903Z","shell.execute_reply.started":"2021-12-13T08:41:59.490669Z","shell.execute_reply":"2021-12-13T08:42:00.280866Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# pandas 라이브러리를 사용해 csv파일을 읽어옵니다.\n# 읽어온 데이터는 데이터프레임으로 변환되어 crypto_df에 저장됩니다.\ncrypto_df = pd.read_csv(data_folder + 'train.csv')","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:42:00.284602Z","iopub.execute_input":"2021-12-13T08:42:00.286021Z","iopub.status.idle":"2021-12-13T08:43:05.120878Z","shell.execute_reply.started":"2021-12-13T08:42:00.285945Z","shell.execute_reply":"2021-12-13T08:43:05.119813Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 데이터프레임에서 가장 상단 10개만 살펴봅니다.\ncrypto_df.head(10)","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:05.122256Z","iopub.execute_input":"2021-12-13T08:43:05.122491Z","iopub.status.idle":"2021-12-13T08:43:05.155670Z","shell.execute_reply.started":"2021-12-13T08:43:05.122464Z","shell.execute_reply":"2021-12-13T08:43:05.154789Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"데이터 세트의 각 행(row)에 유닉스 시간 타임스탬프 기준, 각 거래 데이터를 살펴볼 수 있습니다. 아래에서 더 자세히 살펴보겠습니다.","metadata":{}},{"cell_type":"markdown","source":"### 2-2. Data features","metadata":{}},{"cell_type":"markdown","source":"데이터셋에는 서로 다른 특성(여기서는 컬럼)이 있고, 이 특성들이 각 자산에 대한 정보를 보여주고 있습니다.\n- **timestamp** : 모든 타임스탬프는 초 단위 Unix 타임스탬프(1970-01-01 00:00:00.000 UTC 이후 경과된 '초')로 반환됩니다. 이 데이터 세트의 타임스탬프는 60의 배수로 표현되어 있습니다. 즉, '분' 단위 데이터를 나타냅니다.\n- **Asset_ID** : Asset ID는 각 암호화폐와 매핑되어 있습니다(예를 들어 Asset_ID 1은 비트코인). `asset_details.csv` 파일에서 매핑 정보를 상세히 볼 수 있습니다.\n- **Count** : 각 타임스탬프별 거래 수를 의미합니다(분 단위 총 거래 수).\n- **Open** : USD(달러) 단위 시가.\n- **High** : 매 분마다 도달한 USD(달러) 단위 고가.\n- **Low** : 매 분마다 도달한 USD(달러) 단위 저가.\n- **Close** : USD(달러) 단위 종가.\n- **Volume** : 매수, 매도 수량.\n- **VWAP** : VWAP는 거래량 가중 평균 가격(Volume Weighted Average Price)을 의미합니다. 즉, 시간 간격 동안 자산의 평균 가격으로, 거래량으로 가중된 수치를 보여줍니다.\n- **Target** : 15분 단위의 log변환된 잔여이익(residual returns)을 보여줍니다. *(여기서 잔여이익이란 수수료 등을 제외하고 얻은 순이익을 뜻합니다.)*\n\n처음 2개 컬럼(timestamp, Asset_ID)은 각 행마다 시간과 자산 인덱스를 표시하고 있습니다. 중간 6개 컬럼(Count, Open, High, Low, Close, Volume)은 분 단위 자산의 거래 정보를 담고 있습니다. 마지막 컬럼(Target)은 우리가 예측할 타겟 정보입니다.","metadata":{}},{"cell_type":"markdown","source":"다음으로 데이터셋에 포함된 모든 암호화폐 자산 목록을 매핑된 ID와 함께 평가 지표에서 상대적 중요성을 평가하는 데 사용된 각 자산의 가중치를 확인하겠습니다.\n\n","metadata":{}},{"cell_type":"code","source":"asset_details = pd.read_csv(data_folder + 'asset_details.csv')\nasset_details","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:05.157823Z","iopub.execute_input":"2021-12-13T08:43:05.158106Z","iopub.status.idle":"2021-12-13T08:43:05.178823Z","shell.execute_reply.started":"2021-12-13T08:43:05.158074Z","shell.execute_reply":"2021-12-13T08:43:05.177883Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"보기 쉽게 Asset_ID 순으로 정렬해주겠습니다.","metadata":{}},{"cell_type":"code","source":"# ignore_index를 True로 주지 않으면 현재의 인덱스가 컬럼으로 들어오게 됩니다.\n# 인덱스는 0부터 새롭게 만들고 기존 인덱스는 버릴 것이니 True 옵션을 주겠습니다.\nasset_details = asset_details.sort_values('Asset_ID', ignore_index=True)\nasset_details","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:05.180227Z","iopub.execute_input":"2021-12-13T08:43:05.180490Z","iopub.status.idle":"2021-12-13T08:43:05.202422Z","shell.execute_reply.started":"2021-12-13T08:43:05.180455Z","shell.execute_reply":"2021-12-13T08:43:05.201524Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"새롭게 정렬된 인덱스가 생겼으니 기존 인덱스 컬럼은 삭제합니다.","metadata":{"execution":{"iopub.status.busy":"2021-12-12T07:17:28.145699Z","iopub.execute_input":"2021-12-12T07:17:28.146358Z","iopub.status.idle":"2021-12-12T07:17:28.151502Z","shell.execute_reply.started":"2021-12-12T07:17:28.14632Z","shell.execute_reply":"2021-12-12T07:17:28.150609Z"}}},{"cell_type":"markdown","source":"### 2-3. Candlestick charts(캔들 차트)","metadata":{}},{"cell_type":"markdown","source":"일반적으로 거래 데이터는 시가, 고가, 저가, 종가가 합쳐진 형태로 보여지게 되고 트레이더는 이러한 데이터를 보고 기술적 분석을 할 수 있습니다. 우리도 파이썬을 사용해 캔들 바 형태로 차트를 그릴 수 있습니다. \n\n캔들의 길이는 하루 간 시가와 종가를 나타내고, 캔들이 붉은색이면 종가가 시가보다 낮다는 것을 의미합니다. 즉, 장이 시작할 때보다 마감했을 때 가격이 떨어졌다는 것입니다. 초록색이면 그 반대입니다. 이들은 강세 및 약세 촛대라고도합니다. 막대 위와 아래의 심지(막대를 벗어난 선 부분)는 해당 일자 거래의 높은 가격과 낮은 가격을 보여줍니다. 즉, 장 중에 시가와 종가 간격을 벗어난 부분입니다.\n\n파이썬 plotly 라이브러리를 사용하여 비트코인 가격의 각각 조각(캔들)을 시각화할 수 있습니다. 시각화 하단에는 그래프를 확대하는 데 사용할 수 있는 범위 슬라이더가 표시됩니다.","metadata":{}},{"cell_type":"markdown","source":"시각화 전에 잠시 데이터프레임에서 일부를 추출하는 방법을 배워보겠습니다. pandas는 기본적으로 2가지 방법을 제공합니다. \n\n1. 먼저 데이터프레임을 직접 조작하는 방식입니다.","metadata":{}},{"cell_type":"code","source":"# 예시를 위해 상위 5개 항목만 추출합니다.\ncrypto_df[crypto_df[\"Asset_ID\"]==1].head(5)","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:05.203983Z","iopub.execute_input":"2021-12-13T08:43:05.204588Z","iopub.status.idle":"2021-12-13T08:43:05.559982Z","shell.execute_reply.started":"2021-12-13T08:43:05.204538Z","shell.execute_reply":"2021-12-13T08:43:05.559028Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"2. 다른 방식으로는, 좀 더 직관적인 query문법을 사용할 수 있습니다.","metadata":{}},{"cell_type":"code","source":"# 예시를 위해 상위 5개 항목만 추출합니다.\ncrypto_df.query(\"Asset_ID==1\").head(5)","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:05.561621Z","iopub.execute_input":"2021-12-13T08:43:05.562107Z","iopub.status.idle":"2021-12-13T08:43:06.132842Z","shell.execute_reply.started":"2021-12-13T08:43:05.562065Z","shell.execute_reply":"2021-12-13T08:43:06.131843Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"정확히 같은 결과를 반환하니 익숙한 방식으로 사용하면 되겠습니다. 여기서는 가독성을 위해 query 문법을 주로 사용하겠습니다. \n\n이제 본격적으로 캔들차트를 그려보겠습니다.","metadata":{}},{"cell_type":"code","source":"# 여기서는 비트코인(Asset_ID == 1) 시세를 캔들 차트로 그려봅니다.\n# set_index 함수를 연결해 timestamp 컬럼을 인덱스로 사용하겠습니다.\nbtc = crypto_df.query(\"Asset_ID==1\").set_index(\"timestamp\")\n\n# 추출한 비트코인 시세 중 최근 200일치만 btc_mini 변수에 담습니다.\nbtc_mini = btc.iloc[-200:]\n# tail 함수를 사용하면 최근 5개 항목만 살펴볼 수 있습니다.\nbtc_mini.tail(5)","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:06.134359Z","iopub.execute_input":"2021-12-13T08:43:06.134596Z","iopub.status.idle":"2021-12-13T08:43:06.624662Z","shell.execute_reply.started":"2021-12-13T08:43:06.134567Z","shell.execute_reply":"2021-12-13T08:43:06.624092Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"일반적으로 시계열 데이터는 이렇게 시간축(여기서는 `timestamp` 컬럼)을 인덱스로 놓고 분석하게 됩니다.","metadata":{}},{"cell_type":"code","source":"import plotly.graph_objects as go\n\n# index(timestamp)를 x축으로 놓고 시가/고가/저가/종가 데이터를 입력해줍니다.\nfig = go.Figure(\n    data=[go.Candlestick(\n            x=btc_mini.index,\n            open=btc_mini['Open'],\n            high=btc_mini['High'],\n            low=btc_mini['Low'],\n            close=btc_mini['Close']\n        )]\n    )\n\nfig.show()","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:06.625790Z","iopub.execute_input":"2021-12-13T08:43:06.626512Z","iopub.status.idle":"2021-12-13T08:43:06.784397Z","shell.execute_reply.started":"2021-12-13T08:43:06.626478Z","shell.execute_reply":"2021-12-13T08:43:06.783362Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 3. Preprocessing(전처리)","metadata":{}},{"cell_type":"markdown","source":"### 3-1. Dealing with missing data(결측치 처리)","metadata":{}},{"cell_type":"markdown","source":"머신러닝 전처리 작업에서 결측치 처리는 매우 중요한 부분입니다. 데이터가 시계열 형태라면 꼭 머신러닝을 위해서가 아니더라도 반드시 결측치 처리를 해줘야 하는데, 시간 간격마다 온전하게 정보가 채워져있어야 정상적인 분석이 가능하기 때문입니다.\n\n이번에는 이더리움 가격 데이터를 가지고 결측치를 다뤄보도록 하겠습니다.","metadata":{}},{"cell_type":"code","source":"# 이더리움의 Asset_ID는 6번입니다.\neth = crypto_df.query(\"Asset_ID==6\").set_index(\"timestamp\")\n\n# 결측치 여부를 확인하기 위해 show_counts 옵션을 True로 지정하겠습니다.\n# 해당 옵션을 지정해줌으로써 아래 출력되는 값 중 Non-Null Count 부분이 보여지게 됩니다.\neth.info(show_counts=True)","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:06.786136Z","iopub.execute_input":"2021-12-13T08:43:06.786497Z","iopub.status.idle":"2021-12-13T08:43:07.373988Z","shell.execute_reply.started":"2021-12-13T08:43:06.786452Z","shell.execute_reply":"2021-12-13T08:43:07.372751Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"`Target` 컬럼을 자세히 보면 다른 컬럼들보다 숫자가 적다는 것을 알 수 있습니다. 하지만 보기 어려우니 결측치를 확인할 때에는 결측치 수만 따로 한번 더 확인해주는 것이 좋습니다.","metadata":{}},{"cell_type":"code","source":"eth.isna().sum()","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:07.375320Z","iopub.execute_input":"2021-12-13T08:43:07.375544Z","iopub.status.idle":"2021-12-13T08:43:07.417792Z","shell.execute_reply.started":"2021-12-13T08:43:07.375516Z","shell.execute_reply":"2021-12-13T08:43:07.416871Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"시계열 데이터의 경우 특정 시간대가 빠져있는 것도 주의해야 합니다. 비트코인과 이더리움의 시간 범위를 비교하여 차이가 있는지 확인해보겠습니다.\n\n그 전에, 유닉스 시간 표현은 사람이 인지하기 어려우니 년-월-일-시간으로 표시해볼텐데 아래와 같이 astype 함수를 사용하면 되겠습니다.","metadata":{"execution":{"iopub.status.busy":"2021-12-12T08:50:40.669122Z","iopub.execute_input":"2021-12-12T08:50:40.669379Z","iopub.status.idle":"2021-12-12T08:50:40.712116Z","shell.execute_reply.started":"2021-12-12T08:50:40.669351Z","shell.execute_reply":"2021-12-12T08:50:40.711154Z"}}},{"cell_type":"code","source":"btc.index[0].astype('datetime64[s]')","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:07.419125Z","iopub.execute_input":"2021-12-13T08:43:07.419342Z","iopub.status.idle":"2021-12-13T08:43:07.425978Z","shell.execute_reply.started":"2021-12-13T08:43:07.419315Z","shell.execute_reply":"2021-12-13T08:43:07.425077Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"이 방식으로 비트코인 가격이 기록된 시간 범위, 이더리움 가격이 기록된 시간 범위를 동시에 확인하겠습니다.","metadata":{}},{"cell_type":"code","source":"beg_btc = btc.index[0].astype('datetime64[s]')\nend_btc = btc.index[-1].astype('datetime64[s]')\n\nbeg_eth = eth.index[0].astype('datetime64[s]')\nend_eth = eth.index[-1].astype('datetime64[s]')\n\nprint('BTC : \\n', f'{beg_btc} ~ {end_btc}')\nprint('ETH : \\n', f'{beg_eth} ~ {end_eth}')\n\nif (beg_btc == beg_eth) and (end_btc == end_eth):\n    print(\">>Data intervals match.\")","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:07.431698Z","iopub.execute_input":"2021-12-13T08:43:07.432607Z","iopub.status.idle":"2021-12-13T08:43:07.441730Z","shell.execute_reply.started":"2021-12-13T08:43:07.432529Z","shell.execute_reply":"2021-12-13T08:43:07.440611Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"우선 시간 범위(시작~끝)는 괜찮은 것 같습니다. 다음으로 timestamp는 60초 단위로 기록되어야 하는데, 그렇지 않은 데이터 행이 있는지 여부를 파악해보겠습니다.\n\n아래와 같이 2번째 데이터부터 마지막 데이터, 그리고 1번째 데이터부터 마지막 바로 앞 데이터를 빼면 각 데이터가 몇 초 단위로 기록되어 있는지 확인할 수 있습니다.\n\n즉 `2번째 데이터 - 1번째 데이터`, `3번째 데이터 - 2번째 데이터`와 같은 식입니다.","metadata":{}},{"cell_type":"code","source":"eth.index[1:] - eth.index[:-1]","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:07.443187Z","iopub.execute_input":"2021-12-13T08:43:07.444413Z","iopub.status.idle":"2021-12-13T08:43:07.462092Z","shell.execute_reply.started":"2021-12-13T08:43:07.444295Z","shell.execute_reply":"2021-12-13T08:43:07.461217Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"이제 이를 확장할 수 있습니다. 두 데이터의 차이가 60초인 경우를 포함한 모든 경우의 수를 아래 코드로 카운트해볼 수 있습니다.","metadata":{}},{"cell_type":"code","source":"# value_counts() 함수는 pandas Series 타입 데이터에서 unique한 값을 카운트해줍니다.\n(eth.index[1:] - eth.index[:-1]).value_counts()","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:07.464587Z","iopub.execute_input":"2021-12-13T08:43:07.465140Z","iopub.status.idle":"2021-12-13T08:43:07.494172Z","shell.execute_reply.started":"2021-12-13T08:43:07.465092Z","shell.execute_reply":"2021-12-13T08:43:07.493250Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"시계열 데이터를 분석할 때에는 반드시 데이터의 시간 간격이 일정해야 하기 때문에 시간 간격이 일정하지 않은 부분을 1분 간격으로 채워줄 것입니다. 인덱스를 기존 인덱스의 시작과 끝을 동일하게 하되, 중간 간격을 1분(60초) 간격으로 맞춰줍니다.\n\n아래와 같이 range 함수를 사용하면 처음과 끝을 지정하고 '60' 간격으로 숫자 배열을 만들어낼 수 있습니다. 여기서는 10개만 간단히 보여주기 위해 count로 반복문을 제어합니다.","metadata":{}},{"cell_type":"code","source":"count = 0\nfor i in range(eth.index[0], eth.index[-1]+60, 60):\n    print(i)\n    count += 1\n    if count == 10:\n        break","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:07.495814Z","iopub.execute_input":"2021-12-13T08:43:07.496328Z","iopub.status.idle":"2021-12-13T08:43:07.506012Z","shell.execute_reply.started":"2021-12-13T08:43:07.496282Z","shell.execute_reply":"2021-12-13T08:43:07.504990Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"그런데, 이렇게 60 간격으로 모든 인덱스를 다시 맞춰주면, 기존에 없는 인덱스에 해당하는 값들은 모두 비워져있을 것입니다. reindex()함수에서 method 옵션을 `pad`로 주면 forward filling, 즉 앞쪽의 데이터를 사용해 빈 값을 채워주는 방식으로 null이 생기지 않도록 합니다.","metadata":{"execution":{"iopub.status.busy":"2021-12-12T11:38:56.262723Z","iopub.execute_input":"2021-12-12T11:38:56.263027Z","iopub.status.idle":"2021-12-12T11:38:56.271672Z","shell.execute_reply.started":"2021-12-12T11:38:56.262994Z","shell.execute_reply":"2021-12-12T11:38:56.271046Z"}}},{"cell_type":"code","source":"eth_fill_range = range(eth.index[0], eth.index[-1]+60, 60)\neth = eth.reindex(eth_fill_range, method='pad')\n\n# 60 간격의 데이터만 남아있는지 앞에서 진행했던 방법으로 다시 확인합니다.\n(eth.index[1:] - eth.index[:-1]).value_counts()","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:07.507714Z","iopub.execute_input":"2021-12-13T08:43:07.508030Z","iopub.status.idle":"2021-12-13T08:43:08.083154Z","shell.execute_reply.started":"2021-12-13T08:43:07.507989Z","shell.execute_reply":"2021-12-13T08:43:08.082267Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"마찬가지로 비트코인 가격 데이터도 비어있는 시간 간격을 확인하고, 채워주겠습니다.","metadata":{}},{"cell_type":"code","source":"# 우선 비어있는 부분을 확인해줍니다.\n(btc.index[1:] - btc.index[:-1]).value_counts()","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:08.084810Z","iopub.execute_input":"2021-12-13T08:43:08.085321Z","iopub.status.idle":"2021-12-13T08:43:08.114636Z","shell.execute_reply.started":"2021-12-13T08:43:08.085276Z","shell.execute_reply":"2021-12-13T08:43:08.114003Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 이더리움과 같은 방식으로 비트코인도 비어있는 구간을 채워줍니다.\nbtc_fill_range = range(btc.index[0], btc.index[-1]+60, 60)\nbtc = btc.reindex(btc_fill_range, method='pad')\n\n# 정상적으로 1분 간격 시계열 데이터가 구성되었는지 다시 확인합니다.\n(btc.index[1:] - btc.index[:-1]).value_counts()\n","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:08.116061Z","iopub.execute_input":"2021-12-13T08:43:08.116475Z","iopub.status.idle":"2021-12-13T08:43:08.609780Z","shell.execute_reply.started":"2021-12-13T08:43:08.116443Z","shell.execute_reply":"2021-12-13T08:43:08.608811Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### 3-2. Data visualization","metadata":{}},{"cell_type":"markdown","source":"비트코인, 이더리움 가격 데이터의 시간 축을 동일하게 구성하는데 성공했습니다. 이번에는 이렇게 만든 데이터로 두 암호화폐의 종가를 시각화해보겠습니다.","metadata":{}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\n# 시각화 공간을 15 x 4 크기로 만든다.\nf = plt.figure(figsize=(15,4))\n\n# 전체 시각화 공간을 1행 2열로 분할하고 그 중 1번째 공간을 변수 'ax1'에 할당하겠습니다.\nax1 = f.add_subplot(121)\n# 공간 할당된 ax1에 비트코인 종가 그래프를 그립니다.\nplt.plot(btc['Close'], label='BTC', color='green')\nplt.legend()\nplt.xlabel('Time')\nplt.ylabel('Bitcoin')\n\n# 앞에서 전체 시각화 공간을 1행 2열로 분할했었습니다. 그 중 2번째 공간을 변수 'ax2'에 할당하겠습니다.\nax2 = f.add_subplot(122)\n# 공간 할당된 ax2에 비트코인 종가 그래프를 그립니다.\nplt.plot(eth['Close'], label='ETH', color='blue')\nplt.legend()\nplt.xlabel('Time')\nplt.ylabel('Ethereum')\n\n# 두 그래프 사이 간격을 좁혀줍니다.\nplt.tight_layout()\n# 그래프를 출력합니다.\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:08.611268Z","iopub.execute_input":"2021-12-13T08:43:08.612227Z","iopub.status.idle":"2021-12-13T08:43:11.598690Z","shell.execute_reply.started":"2021-12-13T08:43:08.612185Z","shell.execute_reply":"2021-12-13T08:43:11.597776Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"두 그래프는 시계열상 차이가 꽤 있습니다. 우리는 두 그래프의 최근 상관관계를 자세히 살펴볼 것입니다.\n\n먼저, 유닉스 타임스탬프를 쉽게 다루려면 우리의 '시간 표현'을 유닉스 타임스탬프로 변환하는 방식을 배워야 합니다. 간단하게 datetime.strptime() 함수를 사용하여 문자열로 제공한 시간 표현을 파이썬의 datetime 타입으로 변환할 수 있습니다.","metadata":{}},{"cell_type":"code","source":"datetime.strptime('01/06/2021', \"%d/%m/%Y\")","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:11.600085Z","iopub.execute_input":"2021-12-13T08:43:11.600346Z","iopub.status.idle":"2021-12-13T08:43:11.609542Z","shell.execute_reply.started":"2021-12-13T08:43:11.600313Z","shell.execute_reply":"2021-12-13T08:43:11.608471Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"이렇게 datetime 타입으로 변환해주면 깔끔하게 정리된 형태로 아래와 같이 출력할 수 있습니다.","metadata":{}},{"cell_type":"code","source":"print(datetime.strptime('01/06/2021', \"%d/%m/%Y\"))","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:11.613080Z","iopub.execute_input":"2021-12-13T08:43:11.613605Z","iopub.status.idle":"2021-12-13T08:43:11.621894Z","shell.execute_reply.started":"2021-12-13T08:43:11.613558Z","shell.execute_reply":"2021-12-13T08:43:11.621274Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"이 함수는 2번째 인자의 의미를 잘 이해하면 사용하기 쉽습니다. 자세히 보면 2번째 인자는 1번째 인자, 즉 우리가 전달한 문자열이 어떤 형태로 날짜를 표시하고 있는지 format을 정의하고 있습니다. 그러니까 format만 정확히 지정해준다면 원하는 형태로 편하게 문자열을 던져줄 수 있습니다. ","metadata":{}},{"cell_type":"code","source":"print(datetime.strptime('01..06..2021', '%d..%m..%Y'))\nprint(datetime.strptime('01..//06..//2021', '%d..//%m..//%Y'))\nprint(datetime.strptime('01<<06<<2021', '%d<<%m<<%Y'))","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:11.622964Z","iopub.execute_input":"2021-12-13T08:43:11.623740Z","iopub.status.idle":"2021-12-13T08:43:11.637129Z","shell.execute_reply.started":"2021-12-13T08:43:11.623697Z","shell.execute_reply":"2021-12-13T08:43:11.636209Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"극단적으로 이상한 형태로 문자열을 넣어줘도 format 형식에 따라 잘 출력된 것을 볼 수 있습니다. format을 이해했다면 아래와 같이 일/월/연 순서를 지키지 않아도 괜찮다는 것을 알 수 있습니다.","metadata":{}},{"cell_type":"code","source":"print(datetime.strptime('06..01..2021', '%m..%d..%Y'))\nprint(datetime.strptime('2021..//06..//01', '%Y..//%m..//%d'))\nprint(datetime.strptime('2021<<01<<06', '%Y<<%d<<%m'))","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:11.638241Z","iopub.execute_input":"2021-12-13T08:43:11.638799Z","iopub.status.idle":"2021-12-13T08:43:11.650020Z","shell.execute_reply.started":"2021-12-13T08:43:11.638764Z","shell.execute_reply":"2021-12-13T08:43:11.649369Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"더 나아가서 datetime 타입은 timetuple() 함수를 사용할 수 있습니다. 이 함수를 사용하면 각 시간 단위를 마치 튜플처럼 나열할 수 있습니다.","metadata":{}},{"cell_type":"code","source":"time_sample = datetime.strptime('01/06/2021', \"%d/%m/%Y\")\ntime_sample.timetuple()","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:11.651397Z","iopub.execute_input":"2021-12-13T08:43:11.651634Z","iopub.status.idle":"2021-12-13T08:43:11.666870Z","shell.execute_reply.started":"2021-12-13T08:43:11.651605Z","shell.execute_reply":"2021-12-13T08:43:11.666078Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"마침내 이렇게 분할된 시간 정보를 time 라이브러리를 사용해 유닉스 시간으로 변경해줄 수 있습니다.","metadata":{"execution":{"iopub.status.busy":"2021-12-12T12:26:08.628155Z","iopub.execute_input":"2021-12-12T12:26:08.628617Z","iopub.status.idle":"2021-12-12T12:26:08.643158Z","shell.execute_reply.started":"2021-12-12T12:26:08.628576Z","shell.execute_reply":"2021-12-12T12:26:08.642392Z"}}},{"cell_type":"code","source":"import time\n\ntime.mktime(time_sample.timetuple())","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:11.668454Z","iopub.execute_input":"2021-12-13T08:43:11.668947Z","iopub.status.idle":"2021-12-13T08:43:11.680656Z","shell.execute_reply.started":"2021-12-13T08:43:11.668886Z","shell.execute_reply":"2021-12-13T08:43:11.679343Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"이렇게 얻은 유닉스 시간은 우리의 시계열 데이터를 구간 분할하는 데에 사용할 수 있습니다. 즉, 지금부터 우리의 입맛대로 인덱싱이 가능해졌다는 뜻입니다.\n\n2021년 6월 1일 하루간의 시계열 데이터만 추출해서 시각화해보겠습니다.","metadata":{}},{"cell_type":"code","source":"# 문자열을 유닉스 시간으로 변환하는 코드를 람다 함수로 만들겠습니다.\n# 문자열 포멧은 %d/%m/%Y 로 지정했습니다.\ntotimestamp = lambda s : np.int32(time.mktime(datetime.strptime(s, \"%d/%m/%Y\").timetuple()))","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:11.683413Z","iopub.execute_input":"2021-12-13T08:43:11.683727Z","iopub.status.idle":"2021-12-13T08:43:11.692053Z","shell.execute_reply.started":"2021-12-13T08:43:11.683690Z","shell.execute_reply":"2021-12-13T08:43:11.691236Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"btc_mini_2021 = btc.loc[totimestamp('01/06/2021'):totimestamp('01/07/2021')]\neth_mini_2021 = eth.loc[totimestamp('01/06/2021'):totimestamp('01/07/2021')]","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:11.693314Z","iopub.execute_input":"2021-12-13T08:43:11.694161Z","iopub.status.idle":"2021-12-13T08:43:11.705538Z","shell.execute_reply.started":"2021-12-13T08:43:11.694106Z","shell.execute_reply":"2021-12-13T08:43:11.704647Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 7x8 크기의 시각화 공간을 할당받습니다.\nf = plt.figure(figsize=(7,8))\n\n# 2행 1열로 시각화 공간을 나누고 그 중 1번째 공간을 ax1 변수로 제어합니다.\nax1 = f.add_subplot(211)\n# ax1 공간에 2021년 6월 1일 비트코인 가격 차트를 그립니다.\nplt.plot(btc_mini_2021['Close'], label='btc', color='green')\nplt.legend() # label을 표시합니다\nplt.xlabel('Time')\nplt.ylabel('Bitcoin Close')\n\n# 앞에서 2행 1열로 시각화 공간을 나눴습니다. 그 중 2번째 공간을 ax2 변수로 제어합니다.\nax2 = f.add_subplot(212)\nplt.plot(eth_mini_2021['Close'], label='eth', color='blue')\nplt.legend()\nplt.xlabel('Time')\nplt.ylabel('Ethereum Close')\n\nplt.tight_layout() # 두 그래프 사이 간격을 좁힙니다.\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:11.708861Z","iopub.execute_input":"2021-12-13T08:43:11.709175Z","iopub.status.idle":"2021-12-13T08:43:12.193622Z","shell.execute_reply.started":"2021-12-13T08:43:11.709135Z","shell.execute_reply":"2021-12-13T08:43:12.192798Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"짧은 간격으로 그려보니 시각적으로 상승 하락 추세가 유사한 지점들이 보입니다. 좀 더 자세히 확인하기 위해 우리는 수익을 계산해볼 수도 있습니다.","metadata":{}},{"cell_type":"markdown","source":"### 3-3. Log returns(로그 변환된 수익)","metadata":{}},{"cell_type":"markdown","source":"자산에 대한 가격 변동을 분석하기 위해 우리는 가격의 차이를 계산할 수도 있습니다. 그러나 서로 다른 자산(ex 비트코인과 이더리움)은 각각 다른 가격대에서 움직이기 때문에 정확히 비교하기 어렵습니다. 우리는 `수익률`이라고도 하는 `가격의 백분율 변화`를 대신 계산하여 이 문제를 해결할 수 있습니다. 우리가 투자했을 때 백분율로 표시되는 그것입니다.\n\n일반 수익률은 금융에서 널리 사용되지만 `로그 수익률`은 시간에 따라 누적되기 때문에 시계열의 수학적 모델링에서 선호합니다. 또한 일반 수익률은 -100% 아래로 떨어질 수 없지만 로그 수익률은 그 제한이 없습니다.\n\n로그 수익을 계산하기 위해 단순히 두 개의 연속된 가격 사이 비율을 로그를 취할 수 있습니다. 첫 번째 행은 이전 값을 알 수 없으므로 빈 반환값을 가지므로 해당 데이터 포인트는 제거할 것입니다.","metadata":{}},{"cell_type":"code","source":"# 로그 변환 처리를 위해 함수를 만들어줍니다.\ndef log_return(series, periods=1):\n    return np.log(series).diff(periods=periods)","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:12.194901Z","iopub.execute_input":"2021-12-13T08:43:12.195735Z","iopub.status.idle":"2021-12-13T08:43:12.200142Z","shell.execute_reply.started":"2021-12-13T08:43:12.195699Z","shell.execute_reply":"2021-12-13T08:43:12.199383Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"다음으로 두 자산에 대한 로그 수익률을 시각화하겠습니다. 이 시각화를 통해 로그 수익률이 기존 가격 시계열 그래프에 비해 drift가 적고, white noise 에 가깝게 그려지는 것을 볼 수 있습니다. (*white noise란 자기상관성이 없는 시계열 데이터를 말하며 drift는 y절편 혹은 편향을 의미합니다. 특정 추세나 방향이 없이 잔차 `e` 만 걸러낸 형태로 볼 수도 있습니다.*)","metadata":{}},{"cell_type":"code","source":"import scipy.stats as stats\n\n# log return btc --> lret_btc 로 표현했습니다.\n# log_return 함수는 우리가 위에서 미리 만들어둔 함수로, 내부적으로 diff 함수를 사용하기 때문에 첫번째 row는 버립니다.\n# 첫번째 row(행)를 버리는 이유는 해당 데이터 포인터는 null이 들어가 있기 때문입니다.(이전 데이터가 없으므로 null이 채워짐)\nlret_btc = log_return(btc_mini_2021['Close'])[1:]\nlret_eth = log_return(eth_mini_2021['Close'])[1:]","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:12.201549Z","iopub.execute_input":"2021-12-13T08:43:12.201803Z","iopub.status.idle":"2021-12-13T08:43:12.217671Z","shell.execute_reply.started":"2021-12-13T08:43:12.201775Z","shell.execute_reply":"2021-12-13T08:43:12.216857Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# lret_btc(Series 타입)의 이름은 'Close'인 상태입니다. 이름을 변경해줍니다.\nlret_btc.rename('lret_btc', inplace=True)\n# lret_eth(Series 타입)의 이름도 'Close'인 상태입니다. 이름을 변경해줍니다.\nlret_eth.rename('lret_eth', inplace=True)\n\n# 8x4 크기로 시각화 합니다.\nplt.figure(figsize=(8,4))\n# 한 공간에 2개 그래프를 겹쳐 그립니다.\nplt.plot(lret_btc)\nplt.plot(lret_eth)\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:12.219002Z","iopub.execute_input":"2021-12-13T08:43:12.219851Z","iopub.status.idle":"2021-12-13T08:43:12.478342Z","shell.execute_reply.started":"2021-12-13T08:43:12.219800Z","shell.execute_reply":"2021-12-13T08:43:12.477482Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### 3-4. Correlation between assets","metadata":{}},{"cell_type":"markdown","source":"우리는 암호화폐 자산 수익이 약간의 상관 관계를 보여준다는 가설을 세웠습니다. 지금부터 비트코인과 이더리움 간의 기간에 따른 상관관계를 시각화를 통해 확인해보겠습니다.","metadata":{}},{"cell_type":"code","source":"# 앞에서 했던 것과 같은 방식으로 로그 수익을 구해줄 것입니다.\n# 단, 이번에는 하루가 아닌 전체 기간을 처리해줍니다.\nlret_btc_long = log_return(btc['Close'])[1:]\nlret_eth_long = log_return(eth['Close'])[1:]\n\n# 시리즈 데이터 이름을 Close에서 각 데이터에 맞게 바꿔줍니다.\nlret_btc_long.rename('lret_btc', inplace=True)\nlret_eth_long.rename('lret_eth', inplace=True)\n\n# 두 데이터를 병합합니다. \ntwo_assets = pd.concat([lret_btc_long, lret_eth_long], axis=1)\ntwo_assets","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:12.479435Z","iopub.execute_input":"2021-12-13T08:43:12.479666Z","iopub.status.idle":"2021-12-13T08:43:12.541557Z","shell.execute_reply.started":"2021-12-13T08:43:12.479638Z","shell.execute_reply":"2021-12-13T08:43:12.540681Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"corr()함수를 사용하면 상관관계를 수치로 확인할 수 있습니다.","metadata":{}},{"cell_type":"code","source":"two_assets.corr()","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:12.542648Z","iopub.execute_input":"2021-12-13T08:43:12.542855Z","iopub.status.idle":"2021-12-13T08:43:12.602757Z","shell.execute_reply.started":"2021-12-13T08:43:12.542829Z","shell.execute_reply":"2021-12-13T08:43:12.601914Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"그러나 우리는 기간에 따라 상관관계가 어떻게 변하는지 확인해보고 싶습니다. 이럴 때는 groupby()함수로 시계열 구간을 나눠준 다음 corr()함수를 사용하면 되겠습니다.","metadata":{}},{"cell_type":"code","source":"# 타임스탬프는 60초 간격입니다. \n# 60의 배수(여기서는 600,000)로 나눈 나머지로 묶게 되면 시간순으로 구획별 상관도를 확인할 수 있습니다.\n# ex. 유닉스 시간이 60만보다 작은 1번째 구간, 60만보다 크고 120만보다 작은 2번째 구간 ...\ntwo_assets.groupby(two_assets.index//(60*10000)).corr()","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:12.604071Z","iopub.execute_input":"2021-12-13T08:43:12.604490Z","iopub.status.idle":"2021-12-13T08:43:12.846305Z","shell.execute_reply.started":"2021-12-13T08:43:12.604445Z","shell.execute_reply":"2021-12-13T08:43:12.845475Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"그래프로 표현하기 위해서는 시간에 따른 lret_btc와 lret_eth의 상관데이터가 필요합니다. 위 표에서 상관데이터가 교차로 중복해서 표현되어 있기 때문에(btc대eth, btc대btc, eth대btc, eth대eth) 그 중 btc대eth 혹은 eth대btc 하나만 가져오면 되겠습니다. 인덱싱은 아래와 같이 할 수 있습니다.","metadata":{}},{"cell_type":"code","source":"# lret_btc 열을 인덱싱하면 lret_btc와 lret_btc, lret_btc와 lret_eth 데이터가 추출됩니다.\n# 그 중 lret_eth 데이터만 한번 더 인덱싱하는 코드입니다.\ntwo_assets.groupby(two_assets.index//(60*10000)).corr().loc[:,\"lret_btc\"].loc[:,\"lret_eth\"]","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:12.847482Z","iopub.execute_input":"2021-12-13T08:43:12.847729Z","iopub.status.idle":"2021-12-13T08:43:13.075004Z","shell.execute_reply.started":"2021-12-13T08:43:12.847685Z","shell.execute_reply":"2021-12-13T08:43:13.074404Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"이제 시간에 따른 두 자산(비트코인, 이더리움)의 상관도를 그래프로 그려보겠습니다.","metadata":{}},{"cell_type":"code","source":"corr_time = two_assets.groupby(two_assets.index//(10000*60)).corr().loc[:,\"lret_btc\"].loc[:,\"lret_eth\"]\n\ncorr_time.plot()\nplt.xticks([])\nplt.ylabel(\"Correlation\")\nplt.title(\"Correlation between BTC and ETH over time\")\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:13.076267Z","iopub.execute_input":"2021-12-13T08:43:13.076682Z","iopub.status.idle":"2021-12-13T08:43:13.472840Z","shell.execute_reply.started":"2021-12-13T08:43:13.076650Z","shell.execute_reply":"2021-12-13T08:43:13.472159Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"위 그래프에서 상관관계가 크고, 또 가변적이라는 사실에 주목할 것입니다. 시간이 지남에 따라 상관관계가 역동적으로 변화하는 모습을 보여주고 있습니다. 따라서 이 시계열 문제, 즉 \"`비정상성` 시계열 데이터에서 어떻게 예측을 수행할 것인가\"가 본 대회의 핵심이 되겠습니다.(*`정상성`은 평균, 분산 및 자기상관과 같은 시계열 속성들이 시간이 지남에 따라 변하지 않는 것을 가정합니다. 반면에, `비정상성`은 시간이 지남에 따라 통계적 속성에 지속적인 변화가 있는 경우를 말합니다. 많은 유용한 분석 도구, 통계 테스트 및 모델이 `정상성`에 의존하기 때문에 `정상성`은 시계열 분석에서 매우 중요한 요소입니다.*)\n\n이번에는 상관 행렬을 시각화하여 자산 간의 상관 관계를 확인해보겠습니다. 일부 자산 쌍에서 상관 관계가 높게 나온다면 주의 깊게 살펴볼 필요가 있습니다. 여기서는 2021년 1월 1일 ~ 2021년 1월 4일까지의 데이터만 가지고 확인합니다.","metadata":{}},{"cell_type":"code","source":"# 빈 데이터프레임을 초기화합니다.\nall_assets_2021 = pd.DataFrame([])\n\nfor asset_id, asset_name in zip(asset_details['Asset_ID'], asset_details['Asset_Name']):\n    # 하나의 암호화폐 정보를 timestamp를 인덱스로 하여 추출합니다.\n    asset = crypto_df.query(f\"Asset_ID=={asset_id}\").set_index(\"timestamp\")\n    \n    # 기간은 2021년 1월 1일~4일까지만 가져옵니다.\n    asset = asset.loc[totimestamp('01/01/2021'):totimestamp('01/05/2021')]\n    \n    # 60일 간격으로 데이터를 맞춰주고, 빈 값은 바로 직전 값으로 채워줍니다.\n    asset_range = range(asset.index[0], asset.index[-1]+60, 60)\n    asset = asset.reindex(asset_range, method='pad')\n    \n    # 빈 값이 있을 수 있습니다. 오류가 나지 않도록 0으로 채워주고 로그 수익률로 변환합니다.\n    # 변환 후 첫번째 값은 NaN일 것이므로 제거합니다.(diff 계산이므로)\n    lret = log_return(asset['Close'].fillna(0))[1:]\n    \n    # pandas를 사용해 outer join 방식으로 데이터를 병합합니다.(outer join은 양쪽 데이터를 누락없이 모두 표시하는 방식입니다.)\n    # 추가된 열의 컬럼명에 asset_name, 즉 암호화폐 이름을 붙여줍니다.\n    all_assets_2021 = all_assets_2021.join(lret, rsuffix=asset_name, how='outer')","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:13.474252Z","iopub.execute_input":"2021-12-13T08:43:13.474680Z","iopub.status.idle":"2021-12-13T08:43:20.976282Z","shell.execute_reply.started":"2021-12-13T08:43:13.474647Z","shell.execute_reply":"2021-12-13T08:43:20.975683Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 병합한 데이터를 살펴보겠습니다.\nall_assets_2021.corr()","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:20.977882Z","iopub.execute_input":"2021-12-13T08:43:20.978459Z","iopub.status.idle":"2021-12-13T08:43:21.119749Z","shell.execute_reply.started":"2021-12-13T08:43:20.978415Z","shell.execute_reply":"2021-12-13T08:43:21.119160Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"위에서 join 함수의 동작과 옵션을 처음 봤더라도 만들어진 표 결과를 보면 이해할 수 있습니다. rsuffix 옵션을 통해 모든 열의 기존 컬럼 명인 `Close` 뒤에 이어서 asset_name이 붙었고, 순차적으로 각 열이 반복문을 통해 생성되며 오른쪽에 연결되었습니다.","metadata":{}},{"cell_type":"code","source":"plt.imshow(all_assets_2021.corr())\n\n# 위 표에서 asset 이름들 앞에 Close라는 단어가 모두 붙어있습니다. 표로 그릴 때는 기존의 Asset_Name만 잘 표시되게 아래와 같이 코드를 작성합니다.\nplt.yticks(asset_details['Asset_ID'].values, asset_details['Asset_Name'].values)\n\n# x축은 rotation 옵션을 주어서 글자가 겹치지 않게 90도로 틀어줍니다.\nplt.xticks(asset_details['Asset_ID'].values, asset_details['Asset_Name'].values, rotation='vertical')\n\n# 색상의 의미를 수치로 알려준 colorbar를 우측에 표시합니다.\nplt.colorbar()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:21.123830Z","iopub.execute_input":"2021-12-13T08:43:21.124189Z","iopub.status.idle":"2021-12-13T08:43:21.796640Z","shell.execute_reply.started":"2021-12-13T08:43:21.124160Z","shell.execute_reply":"2021-12-13T08:43:21.795506Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"본 튜토리얼에서의 통계분석은 여기까지 간단하게만 진행했습니다. 본 데이터가 `비정상성`을 띄는 점을 고려하여 자기 상관, 시계열 분해 및 정상성 테스트를 포함하여 데이터 세트를 더 잘 파악하기 위해 추가 통계 분석을 수행할 것을 권장합니다.","metadata":{}},{"cell_type":"markdown","source":"## 4. Building your prediction model(예측 모델 구현)","metadata":{}},{"cell_type":"markdown","source":"### 4-1. Prediction targets and evaluation","metadata":{}},{"cell_type":"markdown","source":"이번 예측 대회의 목표는 각각 암호화폐 자산 $a$에 대하여 가까운 미래의 가격 $P^a$에서의 수익을 예측하는 것입니다. 데이터셋의 각 행에서 보면 우리가 예측해야 하는 부분은 `Target`입니다.\n\n`Target`은 15분 단위의 로그 수익 ($R^a$)로 계산됩니다.\n\n$$R^a(t) = log (P^a(t+16)\\ /\\ P^a(t+1))$$\n \n각 암호화폐 자산 수익은 서로 상관관계가 높으며 이러한 특징은 전체 암호화폐 시장에 크게 영향을 줍니다. 따라서 여러분의 개별 자산에 대한 수익률 예측 능력을 평가하기 위해 `target`을 생성할 때 개별 자산 수익률에서 시장 신호를 제거하는 방식으로 linear residualization을 수행합니다. 더 자세히는, 만약 𝑀(𝑡)가 가중평균된 시장 수익이라면 `target`은 다음과 같이 계산됩니다.\n\n$$M(t) = \\frac{\\sum_a w^a R^a(t)}{\\sum_a w^a}  \\\\\n\\beta^a = \\frac{\\langle M \\cdot R^a \\rangle}{\\langle M^2 \\rangle} \\\\\n\\text{Target}^a(t) = R^a(t) - \\beta^a M(t)$$\n\n위 식에서 괄호  $\\langle .\\rangle$ 는 3750분 단위의 이동평균을 의미하며 평가 지표에 동일한 가중치($w^a$)를 사용했습니다.\n\n미래 가격의 누락된 값으로 인해 일부 행에서 null 값이 발생할 수 있습니다. null이 있는 행은 점수를 매기기 위해 무시될 것입니다.\n\n이번 대회에서 여러분이 제출한 예측 결과는 자산 세부 정보 파일(`asset_details.csv`)의 가중치 열(`Weight`)에 의해 주어진 가중치와 함께 가중치가 적용된 피어슨 상관 계수로 평가됩니다.\n\n본 튜토리얼에서는 문제를 조금 단순화하겠습니다. 가중치를 고려하지 않은 상관 관계를 사용하고, BTC와 ETH의 두 가지 자산만 고려합니다.","metadata":{}},{"cell_type":"markdown","source":"### 4-2. Feature design","metadata":{}},{"cell_type":"markdown","source":"먼저 모델에 입력할 몇 가지 관련 Feature들을 설계하겠습니다.","metadata":{"execution":{"iopub.status.busy":"2021-12-13T04:04:54.018076Z","iopub.execute_input":"2021-12-13T04:04:54.022098Z","iopub.status.idle":"2021-12-13T04:04:54.038888Z","shell.execute_reply.started":"2021-12-13T04:04:54.021932Z","shell.execute_reply":"2021-12-13T04:04:54.037064Z"}}},{"cell_type":"code","source":"# 거래데이터로부터 모델에 넣어줄 입력 피처들을 선정합니다.\n# 입력 피처는 4개입니다 : 5분 단위 로그 수익률, 1분 단위 로그 수익률의 절대값, 캔들 윗꼬리와 아랫꼬리\n\n# 먼저 윗꼬리, 아랫꼬리 피처를 생성해줄 함수를 만들겠습니다.\n# 윗꼬리는 고가에서 몸통 윗부분까지입니다. 따라서 시가와 종가 중 큰 값과 고가를 비교해주면 됩니다.\nupper_shadow = lambda asset: asset['High'] - np.maximum(asset['Close'], asset['Open'])\n# 아랫꼬리는 몸통 아랫부분부터 저가까지입니다. 따라서 시가와 종가 중 낮은 값과 저가를 비교해주면 됩니다.\nlower_shadow = lambda asset: np.minimum(asset['Close'], asset['Open']) - asset['Low']\n\n# 비트코인 : 선정한 4개 입력 피처를 데이터프레임으로 묶어줍니다.\nX_btc = pd.concat([\n    log_return(btc['VWAP'], periods=5),\n    log_return(btc['VWAP'], periods=1).abs(),\n    upper_shadow(btc),\n    lower_shadow(btc)\n], axis=1)\n\n# 이더리움 : 동일합니다.\nX_eth = pd.concat([\n    log_return(eth['VWAP'], periods=5),\n    log_return(eth['VWAP'], periods=1).abs(),\n    upper_shadow(eth),\n    lower_shadow(eth)\n], axis=1)\n\n# 마지막으로 비트코인과 이더리움 각각에 대해 예측할 y값을 변수에 담아주겠습니다.\ny_btc = btc['Target']\ny_eth = eth['Target']","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:21.798412Z","iopub.execute_input":"2021-12-13T08:43:21.798941Z","iopub.status.idle":"2021-12-13T08:43:22.067124Z","shell.execute_reply.started":"2021-12-13T08:43:21.798886Z","shell.execute_reply":"2021-12-13T08:43:22.066132Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"위 코드를 실행하면 아래와 같이 데이터셋이 구성됩니다.","metadata":{}},{"cell_type":"code","source":"X_btc","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:22.068690Z","iopub.execute_input":"2021-12-13T08:43:22.069078Z","iopub.status.idle":"2021-12-13T08:43:22.089784Z","shell.execute_reply.started":"2021-12-13T08:43:22.069033Z","shell.execute_reply":"2021-12-13T08:43:22.088405Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"y_btc","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:22.092442Z","iopub.execute_input":"2021-12-13T08:43:22.092830Z","iopub.status.idle":"2021-12-13T08:43:22.104423Z","shell.execute_reply.started":"2021-12-13T08:43:22.092781Z","shell.execute_reply":"2021-12-13T08:43:22.103816Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"비트코인의 경우, 우리는 X_btc 데이터로 y_btc를 예측하게 됩니다. 이더리움도 마찬가지입니다.","metadata":{}},{"cell_type":"markdown","source":"## 4-3. Preparing the data for building predictive models","metadata":{}},{"cell_type":"markdown","source":"우리는 지도학습 방식의 선형 회귀 모델을 학습시킬 것이므로 우선 학습 데이터와 평가 데이터를 나눠줘야합니다. X와 y의 크기를 고려해 두 개의 데이터셋으로 분리해보겠습니다.","metadata":{}},{"cell_type":"code","source":"# 먼저 분리할 지점, 즉 시간 범위를 지정해줍니다.\n# 여기서는 50:50으로 나눠줍니다. 학습데이터는 5월, 평가데이터는 6월로 지정합니다.\ntrain_window = [totimestamp(\"01/05/2021\"), totimestamp(\"30/05/2021\")]\ntest_window = [totimestamp(\"01/06/2021\"), totimestamp(\"30/06/2021\")]\n\n# 이제 지정한 시간 범위대로 비트코인, 이더리움 각각 학습/평가 데이터를 생성해줍니다.\n# 단, 여기서 우리의 목표는 아주 단순한 회귀 모델을 만드는 것이므로 window size는 1로 합니다.\n# (시계열 데이터에 window size는 매우 중요합니다. window size란 몇 개의 연결된 x 데이터(행)로 y값을 예측할 것인가에 대한 정보입니다.)\nX_btc_train = X_btc.loc[train_window[0]:train_window[1]].fillna(0).to_numpy() # 빈 값은 0으로 채워주고 numpy 배열로 변환해줍니다.\ny_btc_train = y_btc.loc[train_window[0]:train_window[1]].fillna(0).to_numpy()\n\nX_eth_train = X_eth.loc[train_window[0]:train_window[1]].fillna(0).to_numpy()\ny_eth_train = y_eth.loc[train_window[0]:train_window[1]].fillna(0).to_numpy()\n\nX_btc_test = X_btc.loc[test_window[0]:test_window[1]].fillna(0).to_numpy()\ny_btc_test = y_btc.loc[test_window[0]:test_window[1]].fillna(0).to_numpy()\n\nX_eth_test = X_eth.loc[test_window[0]:test_window[1]].fillna(0).to_numpy()\ny_eth_test = y_eth.loc[test_window[0]:test_window[1]].fillna(0).to_numpy()","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:22.105690Z","iopub.execute_input":"2021-12-13T08:43:22.106512Z","iopub.status.idle":"2021-12-13T08:43:22.122899Z","shell.execute_reply.started":"2021-12-13T08:43:22.106478Z","shell.execute_reply":"2021-12-13T08:43:22.122287Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"넘파이 배열로 변환했기 때문에 데이터를 출력해보면 이제 데이터프레임 형태는 볼 수 없습니다. 모델에 입력값으로 전달할 준비가 된 것입니다.","metadata":{}},{"cell_type":"code","source":"X_btc_train","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:22.124143Z","iopub.execute_input":"2021-12-13T08:43:22.124515Z","iopub.status.idle":"2021-12-13T08:43:22.137245Z","shell.execute_reply.started":"2021-12-13T08:43:22.124486Z","shell.execute_reply":"2021-12-13T08:43:22.136488Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"데이터 크기도 살펴보겠습니다. 입력 피처는 4개, 예측할 타겟은 1개이며 50:50으로 잘 분리되었습니다.","metadata":{}},{"cell_type":"code","source":"print(X_btc_train.shape, X_btc_test.shape)\nprint(X_eth_train.shape, X_eth_test.shape)\n\nprint(y_btc_train.shape, y_btc_test.shape)\nprint(y_eth_train.shape, y_eth_test.shape)","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:22.138451Z","iopub.execute_input":"2021-12-13T08:43:22.139001Z","iopub.status.idle":"2021-12-13T08:43:22.152103Z","shell.execute_reply.started":"2021-12-13T08:43:22.138967Z","shell.execute_reply":"2021-12-13T08:43:22.151097Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"이제 입력 데이터를 표준화(Standardization)할 것입니다. \n\n표준화는 데이터 스케일링 방식 중 하나로 동일한 척도(scale)에 서로 다른 변수를 함께 배치하기 위해 사용합니다. 회귀 분석에서는 독립 변수를 표준화하는 것이 매우 중요합니다. 표준화(혹은 정규화 방식을 사용할 수도 있습니다.)를 수행하지 않고 모델을 학습시킨다면 제대로된 결과가 나오지 않거나 잘못된 결과를 얻을 수 있습니다.","metadata":{}},{"cell_type":"code","source":"from sklearn.preprocessing import StandardScaler\n\n# 스케일링 인스턴스를 생성합니다.\nscaler = StandardScaler()\n\n# train 데이터에는 fit, trainsform 과정을 모두 수행해주고, test 데이터에는 transform만 적용합니다.\n# - fit은 스케일링을 위해 평균, 데이터 범위 등을 찾는 과정입니다.\n# - transform은 fit으로 찾은 통계정보를 가지고 실제 스케일링(정규화)을 수행하는 부분입니다.\n# - 만약, test 데이터에 fit을 적용하게 되면 이전에 train 데이터에서 찾은 통계정보가 test 데이터 기준으로 리셋되버립니다.\nX_btc_train_scaled = scaler.fit_transform(X_btc_train)\nX_btc_test_scaled = scaler.transform(X_btc_test)\n\nX_eth_train_scaled = scaler.fit_transform(X_eth_train)\nX_eth_test_scaled = scaler.transform(X_eth_train)","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:22.153937Z","iopub.execute_input":"2021-12-13T08:43:22.154811Z","iopub.status.idle":"2021-12-13T08:43:22.175753Z","shell.execute_reply.started":"2021-12-13T08:43:22.154759Z","shell.execute_reply":"2021-12-13T08:43:22.174442Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### 4-4. Baseline model: Linear Regression(선형 회귀)","metadata":{}},{"cell_type":"markdown","source":"우리는 앞서 설계한 피처들로 선형 회귀 모형을 만들어볼 것입니다. 그 전에 염두해야 할 것은 일반적으로 선형 회귀 모형이 시계열 분석에는 잘 활용되지 않는다는 점입니다. 회귀 모형은 주로 특정 시점에서 관측된 자료, 즉 횡단면 자료 분석을 위해 활용됩니다. 여기서는 튜토리얼 목적이므로 가장 단순한 회귀 모형을 사용합니다.\n\n두 개의 선형 회귀 모형을 비교해볼 것입니다. 하나는 각 자산을 독립적으로 학습하는 모형, 다른 하나는 모든 자산을 동시에 모델링하는 다중 입력 모형입니다.","metadata":{}},{"cell_type":"code","source":"from sklearn.linear_model import LinearRegression\n\n# 먼저, 하나의 자산만 예측하는 모형입니다.\n# 비트코인, 이더리움 각각에 대해 예측 모형을 만들어보겠습니다.\n\n# 1. 회귀 모형 인스턴스를 생성합니다.\nlr = LinearRegression()\n# 2. X(입력)과 y(출력)을 쌍으로 학습시킵니다.(지도학습)\nlr.fit(X_btc_train_scaled, y_btc_train)\n# 3. 학습시킨 회귀 모형으로 새로운 데이터(test셋)를 받아 예측합니다.\ny_pred_lr_btc = lr.predict(X_btc_test_scaled)\n\n# 예측 결과를 출력해보겠습니다.\nprint(y_pred_lr_btc)","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:43:22.178251Z","iopub.execute_input":"2021-12-13T08:43:22.178658Z","iopub.status.idle":"2021-12-13T08:43:22.211412Z","shell.execute_reply.started":"2021-12-13T08:43:22.178611Z","shell.execute_reply":"2021-12-13T08:43:22.210292Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 동일하게 이더리움 수익 예측도 진행합니다.\n\nlr.fit(X_eth_train_scaled, y_eth_train)\ny_pred_lr_eth = lr.predict(X_eth_test_scaled)\n\n# 예측 결과를 출력해보겠습니다.\nprint(y_pred_lr_eth)","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:44:14.714200Z","iopub.execute_input":"2021-12-13T08:44:14.714873Z","iopub.status.idle":"2021-12-13T08:44:14.736711Z","shell.execute_reply.started":"2021-12-13T08:44:14.714820Z","shell.execute_reply":"2021-12-13T08:44:14.735383Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"이렇게 예측한 결과는 실제값, 즉 y_btc_test, y_eth_test와 비교해서 모델 정확도를 평가하게 됩니다. 우선 평가 전에 비트코인, 이더리움 수익을 동시에 예측하는 `Multi Output Regressor`도 만들겠습니다.\n\n이 모델을 학습시키기 위해서는 데이터셋을 합치는 작업이 필요합니다.","metadata":{}},{"cell_type":"code","source":"# 기존에 비트코인, 이더리움 각각에 대한 피처 구성\nprint(X_btc_train_scaled.shape, X_eth_train_scaled.shape)","metadata":{"execution":{"iopub.status.busy":"2021-12-13T09:03:27.186621Z","iopub.execute_input":"2021-12-13T09:03:27.186910Z","iopub.status.idle":"2021-12-13T09:03:27.192605Z","shell.execute_reply.started":"2021-12-13T09:03:27.186880Z","shell.execute_reply":"2021-12-13T09:03:27.191528Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"비트코인, 이더리움 두 자산은 각각 4개의 피처에 대해 41761개 정보를 가지고 있습니다. 가로 방향으로 두 데이터를 병합하여 8개 피처를 가진 통합 데이터 X를 구성하겠습니다.","metadata":{}},{"cell_type":"code","source":"np.concatenate((X_btc_train_scaled, X_eth_train_scaled), axis=1).shape","metadata":{"execution":{"iopub.status.busy":"2021-12-13T09:02:45.765607Z","iopub.execute_input":"2021-12-13T09:02:45.766704Z","iopub.status.idle":"2021-12-13T09:02:45.775976Z","shell.execute_reply.started":"2021-12-13T09:02:45.766652Z","shell.execute_reply":"2021-12-13T09:02:45.775004Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"다음으로, y값 역시 2개 output으로 구성해줘야합니다. 단, y값은 데이터프레임이 아닌 1차원 배열이기 때문에 `axis=1` 옵션을 적용할 수 없습니다. 아래 두 데이터(X, y)의 구성을 보면 X와 달리 y는 행, 열이 없다는 것을 알 수 있습니다. 따라서 axis, 즉 축이 존재하지 않습니다.","metadata":{}},{"cell_type":"code","source":"X_btc_train_scaled","metadata":{"execution":{"iopub.status.busy":"2021-12-13T09:05:49.192359Z","iopub.execute_input":"2021-12-13T09:05:49.193357Z","iopub.status.idle":"2021-12-13T09:05:49.201713Z","shell.execute_reply.started":"2021-12-13T09:05:49.193293Z","shell.execute_reply":"2021-12-13T09:05:49.200643Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"y_btc_train","metadata":{"execution":{"iopub.status.busy":"2021-12-13T09:05:43.690350Z","iopub.execute_input":"2021-12-13T09:05:43.691222Z","iopub.status.idle":"2021-12-13T09:05:43.699863Z","shell.execute_reply.started":"2021-12-13T09:05:43.691161Z","shell.execute_reply":"2021-12-13T09:05:43.698504Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"그렇기 때문에 여기서는 데이터 병합을 위해 다른 방식을 사용합니다. numpy의 column_stack 함수는 2개 넘파이 배열을 마치 컬럼 2개를 가로로 이어붙인 것처럼 데이터셋을 재구성해줍니다.","metadata":{}},{"cell_type":"code","source":"np.column_stack((y_btc_train, y_eth_train))","metadata":{"execution":{"iopub.status.busy":"2021-12-13T09:10:10.836728Z","iopub.execute_input":"2021-12-13T09:10:10.837594Z","iopub.status.idle":"2021-12-13T09:10:10.846596Z","shell.execute_reply.started":"2021-12-13T09:10:10.837541Z","shell.execute_reply":"2021-12-13T09:10:10.845329Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(y_btc_train.shape)\nprint(y_eth_train.shape)\nprint(np.column_stack((y_btc_train, y_eth_train)).shape)","metadata":{"execution":{"iopub.status.busy":"2021-12-13T09:10:45.115516Z","iopub.execute_input":"2021-12-13T09:10:45.116153Z","iopub.status.idle":"2021-12-13T09:10:45.123811Z","shell.execute_reply.started":"2021-12-13T09:10:45.116089Z","shell.execute_reply":"2021-12-13T09:10:45.122845Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"위 방식을 사용해 본격적으로 데이터셋을 구성하고 `MultiOutputRegressor`를 학습시키겠습니다.","metadata":{}},{"cell_type":"code","source":"from sklearn.multioutput import MultiOutputRegressor\n\n# 2차원 행렬 병합 방식입니다. axis=1은 컬럼 기준(가로) 병합을 의미합니다. 기본값은 열(세로) 병합입니다.\nX_both_train = np.concatenate((X_btc_train_scaled, X_eth_train_scaled), axis=1)\nX_both_test = np.concatenate((X_btc_test_scaled, X_eth_test_scaled), axis=1)\n\n# 1차원 벡터 병합 방식입니다. axis 옵션 없이 기본값으로 컬럼 기준 병합을 수행합니다.\ny_both_train = np.column_stack((y_btc_train, y_eth_train))\ny_both_test = np.column_stack((y_btc_test, y_eth_test))\n\n# MultiOutputRegressor는 내부적으로 어떤 회귀모형을 사용할 것인지 입력받습니다.\n# 여기서는 선형회귀를 사용합니다.\nmlr = MultiOutputRegressor(LinearRegression())\nmlr.fit(X_both_train, y_both_train)\ny_pred_mlr_both = mlr.predict(X_both_test)","metadata":{"execution":{"iopub.status.busy":"2021-12-13T09:18:56.175801Z","iopub.execute_input":"2021-12-13T09:18:56.176254Z","iopub.status.idle":"2021-12-13T09:18:56.210486Z","shell.execute_reply.started":"2021-12-13T09:18:56.176215Z","shell.execute_reply":"2021-12-13T09:18:56.209458Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"y_pred_mlr_both","metadata":{"execution":{"iopub.status.busy":"2021-12-13T09:18:57.143334Z","iopub.execute_input":"2021-12-13T09:18:57.143623Z","iopub.status.idle":"2021-12-13T09:18:57.149591Z","shell.execute_reply.started":"2021-12-13T09:18:57.143592Z","shell.execute_reply":"2021-12-13T09:18:57.149049Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(y_pred_mlr_both.shape)","metadata":{"execution":{"iopub.status.busy":"2021-12-13T09:18:59.860740Z","iopub.execute_input":"2021-12-13T09:18:59.861504Z","iopub.status.idle":"2021-12-13T09:18:59.869064Z","shell.execute_reply.started":"2021-12-13T09:18:59.861437Z","shell.execute_reply":"2021-12-13T09:18:59.867930Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"2개 자산(비트코인, 이더리움) 예측을 동시에 수행한 것을 볼 수 있습니다.","metadata":{}},{"cell_type":"markdown","source":"### 4-5. Evaluation baselines(모델 평가)","metadata":{"execution":{"iopub.status.busy":"2021-12-13T09:16:42.156959Z","iopub.execute_input":"2021-12-13T09:16:42.157264Z","iopub.status.idle":"2021-12-13T09:16:42.165058Z","shell.execute_reply.started":"2021-12-13T09:16:42.157233Z","shell.execute_reply":"2021-12-13T09:16:42.163802Z"}}},{"cell_type":"markdown","source":"실제 대회에서는 가중 상관도를 평가지표로 사용하지만 여기서는 상관관계만으로 성능을 확인해볼 것입니다. \n\n회귀분석에서 상관관계는 피어슨 상관계수로 간단히 확인할 수 있습니다. numpy의 corrcoef()함수를 통해 비트코인 수익 예측 회귀선의 상관계수를 출력해보겠습니다.","metadata":{"execution":{"iopub.status.busy":"2021-12-13T08:56:54.061228Z","iopub.execute_input":"2021-12-13T08:56:54.061500Z","iopub.status.idle":"2021-12-13T08:56:54.068590Z","shell.execute_reply.started":"2021-12-13T08:56:54.061472Z","shell.execute_reply":"2021-12-13T08:56:54.067995Z"}}},{"cell_type":"code","source":"np.corrcoef(y_pred_lr_btc, y_btc_test)","metadata":{"execution":{"iopub.status.busy":"2021-12-13T09:24:50.280167Z","iopub.execute_input":"2021-12-13T09:24:50.280557Z","iopub.status.idle":"2021-12-13T09:24:50.290518Z","shell.execute_reply.started":"2021-12-13T09:24:50.280520Z","shell.execute_reply":"2021-12-13T09:24:50.289599Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"피어슨 상관계수는 -1에서 1 사이의 값으로 표현되며, 1에 가까울 수록 상관관계가 높다는 것을 의미합니다. 위 행렬에서 (0,0)은 예측값과 예측값의 상관계수, (1,1)은 실제값과 실제값의 상관계수입니다. 예측한 비트코인 수익과 실제 수익의 상관계수는 (1,0) 혹은 (0,1)에서 확인할 수 있고 그 값이 높지 않다는 것을 알 수 있습니다.\n\n이제, 이 방식을 사용해 두 모델(단일예측, 다중예측) 모두 평가해보겠습니다.","metadata":{"execution":{"iopub.status.busy":"2021-12-13T09:26:53.899935Z","iopub.execute_input":"2021-12-13T09:26:53.900234Z","iopub.status.idle":"2021-12-13T09:26:53.905641Z","shell.execute_reply.started":"2021-12-13T09:26:53.900204Z","shell.execute_reply":"2021-12-13T09:26:53.904754Z"}}},{"cell_type":"code","source":"print('Test score for LR baseline: BTC', f\"{np.corrcoef(y_pred_lr_btc, y_btc_test)[0,1]:.3f}\", \n                                ', ETH', f\"{np.corrcoef(y_pred_lr_eth, y_eth_test)[0,1]:.3f}\")\nprint('Test score for multiple output LR baseline: BTC', f\"{np.corrcoef(y_pred_mlr_both[:,0], y_btc_test)[0,1]:.3f}\", \n                                                ', ETH', f\"{np.corrcoef(y_pred_mlr_both[:,1], y_eth_test)[0,1]:.3f}\")","metadata":{"execution":{"iopub.status.busy":"2021-12-13T09:33:11.550632Z","iopub.execute_input":"2021-12-13T09:33:11.551028Z","iopub.status.idle":"2021-12-13T09:33:11.562710Z","shell.execute_reply.started":"2021-12-13T09:33:11.550990Z","shell.execute_reply":"2021-12-13T09:33:11.562089Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"사실, 비정상성을 띄는 시계열이기도 하고 학습이나 평가 지표를 세팅하는 데에 크게 노력을 기울이지 않았기 때문에 두 모델의 성능에 큰 차이가 있어보이지는 않습니다. 그럼에도 불구하고 이더리움 수익 예측에서 조금 더 나은 성능을 보여줬기 때문에 모델을 만든다면 여러 자산을 한번에 예측하는 것이 더 효과적이겠습니다.","metadata":{}},{"cell_type":"markdown","source":"### 4-6. Submission(제출)","metadata":{}},{"cell_type":"markdown","source":"본 대회는 Code Competition입니다. 따라서 참가자는 작성한 notebook을 제출해야 하며 제출할 Notebook은 제공된 파이썬 시계열 API를 사용해야합니다. API 및 제출 관련해서는 아래 문서를 참고해주세요.\n- [Code Competition Detailed API instructions](https://www.kaggle.com/sohier/detailed-api-introduction)\n- [Basic Submission Template](https://www.kaggle.com/sohier/basic-submission-template)","metadata":{}}]}