오늘의 예제: CSV 파일 읽어서 데이타 불러들이기
전에 언급했듯이 이번 시리즈를 위해 @roostermine 님께서 http://www.cryptodatadownload.com/data/northamerican/ 라고 하는 훌륭한 사이트를 알아봐주셨습니다. 이 사이트에 가보면 여러 거래소의 거래 기록이 모아져 있습니다. 저는 이곳에서 북미의 모든 거래소의 BTCUSD 기록을, Daily와 1-hourly 모두 다 다운받았습니다. 파이쏜 프로그램으로 웹에 있는 링크에서 파일을 직접 다운받을 수도 있을 것 같은데, 그 부분은 본 시리즈의 범위를 넘어서므로 (지금은 제가 그거 어떻게 하는 지 몰라서) 넘어가도록 하겠습니다.
이 사이트에서 제공되는 파일의 형태가 바로 Comma Separated Values 즉 CSV 파일입니다. 이름이 거창하긴 한데, 사실 별 거 없습니다. 텍스트 파일인데, 자료의 값들 사이가 오직 Comma (",") 로만 이뤄어 졌다는 뜻입니다. 그래서 지난 글 #00을 조금만 수정하면 됩니다.
더하여, 이제 데이타가 많아졌으므로, 데이타는 "Data"라고 하는 서브 폴더에 저장하도록 하겠습니다.
아래는 위 사이트에서 다운받은 CSV 파일을 읽어들이는 부분을 함수로 만든 것입니다.
import sys
import numpy as np
from datetime import datetime, timedelta
def conv2date(dateinfo):
"""
Date Format: YYYY-MM-DD HH-AM (or PM)
"""
return datetime.strptime(dateinfo,'%Y-%m-%d %I-%p')
def load_csv_data(fname):
"""
Input: file name with directory information
Assume that we already know the structure of csv file
Output: Time info, Prices, and Volumes: All togerther in a dictionary
"""
print(fname.split('/')[-1])
dates=[]; prices=[]; volumes=[]
## Read Text CSV File
with open(fname,'r') as f:
for i,line in enumerate(f):
if i==0:
print(line)
elif i==1:
headers=line.strip().split(',')
num=len(headers)
print(num,headers)
else:
words=line.strip().split(',')
if num!=len(words):
print(num,len(words),words)
sys.exit("Data entry is incomplete.")
dates.append(conv2date(words[0]))
prices.append(list(map(float,words[2:6])))
volumes.append([float(x) for x in words[6:]])
dates=np.asarray(dates)[::-1]
prices=np.asarray(prices)[::-1,:]
volumes=np.asarray(volumes)[::-1,:]
print(dates.shape,prices.shape,volumes.shape)
print( "Done to read a CSV file!")
return {'date':dates, 'prices':prices, 'volumes':volumes}
몇 가지 설명
- "conv2date"라는 함수는 예전과 거의 같습니다. 시간/날짜 정보의 형태만 살짝 바뀌었습니다.
- "load_csv_data"라는 함수는 필요로 하는 것이 디렉토리 정보가 포함된 파일 이름밖에 없습니다. 즉, 모든 파일이 동일한 형태임을 가정하고 있고, 또 그 형태를 미리 알고 있다는 뜻입니다. 아무 CSV 파일을 다 이걸로 읽을 수 있는 건 아닙니다.
- 기본 구조는, 각 줄에서 날짜정보/가격정보/거래량 정보 각각을 List에 차곡차곡 쌓은 후 나중에 Numpy Array로 변환하고, 마지막에는 Dictionary 형태로 모두 합쳐서 값을 돌려줍니다.
- Dictionary의 장점은 "이름"으로 데이타에 접근할 수 있어서 직관적이라는 점이고, 단점은 그만큼 복잡해진다는 점입니다.
- 데이타 원본이 현재에서 과거 순이므로, 뒤집어서 과거에서 현재로 바꿔줬습니다.
- 텍스트 파일에서 읽어들인 숫자들은 문자열 형태이므로 숫자 형태(여기서는 float)로 바꿔줘야 합니다. 위에는 같은 내용을 2가지 방법으로 표현해 봤습니다. (내장 함수 map을 이용하는 방법 vs. in-line for를 이용하는 방법)
그리고 위 함수를 포함해서 프로그램은 아래와 같습니다. (read_csv_data.py3.py)
###--- Main ---###
## Input File
in_dir='./Data/'
in_csv_fn=in_dir+'Bitfinex_BTCUSD_1h.csv'
data=load_csv_data(in_csv_fn)
print(data['date'][0],data['prices'][0],data['volumes'][0])
print(data['date'][-1],data['prices'][-1],data['volumes'][-1])
위 프로그램을 실행하면 다음과 같이 나옵니다.
>> python3 read_csv_data.py3.py
Bitfinex_BTCUSD_1h.csv
Timestamps are UTC timezone,https://www.CryptoDataDownload.com
8 ['Date', 'Symbol', 'Open', 'High', 'Low', 'Close', 'Volume BTC', 'Volume USD']
(15521,) (15521, 4) (15521, 2)
Done to read a CSV file!
2017-10-09 09:00:00 [ 4575.4 4589.5 4568.6 4585.7] [ 6.28890000e+02 2.87889786e+06]
2019-07-18 01:00:00 [ 9617.5 9813.1 9560.3 9748.7] [ 5.45670000e+02 5.28719882e+06]
자료를 읽어들였으니 여기에 #01에서 다뤘던 캔들스틱 그래프 부분을 붙여볼 수도 있겠는데, 여기선 이 자료로 조금 더 놀아보도록 하겠습니다.
위의 2개의 함수 부분은 공통이구요, 아래 실행시키는 부분을 약간 변형했습니다. 여기서는 Bitfinex와 Coinbase 이렇게 거래소 2군데의 자료를 불러와 서로 비교해보도록 하겠습니다.
"read_csv_data+simple_plot.py3.py"
###--- Main ---###
## Parameters
c_name="BTCUSD"
Exchanges=['Bitfinex','Coinbase']
## Input File
in_dir='./Data/'
data=[]
for ex_name in Exchanges:
in_csv_fn=in_dir+'{}_{}_1h.csv'.format(ex_name,c_name)
data.append(load_csv_data(in_csv_fn))
print(ex_name)
print(data[-1]['date'][0],data[-1]['prices'][0],data[-1]['volumes'][0])
print(data[-1]['date'][-1],data[-1]['prices'][-1],data[-1]['volumes'][-1])
print('\n')
### Check consistency between data
if data[0]['date'][-1] != data[1]['date'][-1]:
print('Time stamp is inconsistent',data[0]['date'][-1],data[1]['date'][-1])
sys.exit()
### Cut for last 10 days
xdate= data[0]['date'][-240:]
## Coinbase price - Bitfinex price
yprice_delta= data[1]['prices'][-240:,:]-data[0]['prices'][-240:,:]
###--- Plotting
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from matplotlib.ticker import AutoMinorLocator
##-- Page Setup --##
fig = plt.figure()
fig.set_size_inches(8.5,5) # Physical page size in inches, (lx,ly)
fig.subplots_adjust(left=0.05,right=0.95,top=0.92,bottom=0.05,hspace=0.35,wspace=0.15) ### Margins, etc.
##-- Title for the page --##
suptit="{} Price Difference [{} - {}]".format(c_name,Exchanges[1],Exchanges[0])
fig.suptitle(suptit,fontsize=15) #,ha='left',x=0.,y=0.98,stretch='semi-condensed')
cc=['r','g','b','y']
lname=['Open','High','Low','Close']
##-- Set up an axis --##
ax1 = fig.add_subplot(1,1,1) # (# of rows, # of columns, indicater from 1)
for i in range(4):
ax1.plot(xdate,yprice_delta[:,i],color=cc[i],lw=0.7,ls='-',label=lname[i])
print("{}: Mean={:.2f}, RMSD={:.3f}".format(lname[i],yprice_delta[:,i].mean(),np.sqrt(np.power(yprice_delta[:,i],2).mean())))
ax1.axhline(y=0.,color='0.5',lw=0.5,ls='--')
ax1.xaxis.set_major_formatter(mdates.DateFormatter('%m-%d\n%Y'))
ax1.legend(loc='lower right',bbox_to_anchor=(0.97, 0.05),fontsize=11,ncol=2) # x and y relative location in ax1
##-- Seeing or Saving Pic --##
#- If want to see on screen -#
#plt.show()
#- If want to save to file
outdir = "./Pics/"
outfnm = outdir+"{}_price_diff.{}vs{}.png".format(c_name,Exchanges[1],Exchanges[0])
#fig.savefig(outfnm,dpi=100) # dpi: pixels per inch
fig.savefig(outfnm,dpi=180,bbox_inches='tight') # dpi: pixels per inch
몇 가지 설명
- 거래소 이름을 미리 적어놓고, 그 거래소 이름에 맞는 데이타 CSV 파일을 불러오고 있습니다. 이렇게 하면 나중에 여러 거래소의 자료를 불러올 때 편리하겠죠. 물론 CSV 파일의 이름 형식이 동일하기에 가능한 방법입니다.
- 여기서 "data"라는 변수는 List로 안에 Dictionary 2개가 들어있습니다. 각각의 Dictionary는 3가지의 데이타가 들어있고, 각각의 데이타는 Numpy Array입니다.
- 편의를 위해 최근 10일만 잘라서 썼습니다. Coinbase 가격에서 Bitfinex 가격을 빼서 그 차이를 보는 게 목적입니다.
- 중간에 평균과 RMSD를 화면에 출력해봤습니다. RMSD는 Root-Mean-Squared-Difference의 약자입니다.
이 프로그램을 실행하면 다음과 같은 결과가 나옵니다.
>> python3 read_csv_data+simple_plot.py3.py
Bitfinex_BTCUSD_1h.csv
Timestamps are UTC timezone,https://www.CryptoDataDownload.com
8 ['Date', 'Symbol', 'Open', 'High', 'Low', 'Close', 'Volume BTC', 'Volume USD']
(15521,) (15521, 4) (15521, 2)
Done to read a CSV file!
Bitfinex
2017-10-09 09:00:00 [ 4575.4 4589.5 4568.6 4585.7] [ 6.28890000e+02 2.87889786e+06]
2019-07-18 01:00:00 [ 9617.5 9813.1 9560.3 9748.7] [ 5.45670000e+02 5.28719882e+06]
Coinbase_BTCUSD_1h.csv
Timestamps are UTC timezone,https://www.CryptoDataDownload.com
8 ['Date', 'Symbol', 'Open', 'High', 'Low', 'Close', 'Volume BTC', 'Volume USD']
(17919,) (17919, 4) (17919, 2)
Done to read a CSV file!
Coinbase
2017-07-01 11:00:00 [ 2505.56 2513.38 2495.12 2509.17] [ 1.14600000e+02 2.87000320e+05]
2019-07-18 01:00:00 [ 9630. 9829.78 9627.08 9795.28] [ 7.17030000e+02 6.97910252e+06]
Open: Mean=15.69, RMSD=26.041
High: Mean=20.48, RMSD=31.632
Low: Mean=11.19, RMSD=33.040
Close: Mean=15.98, RMSD=26.180
관련 글들
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
#03 Read CSV data
sct천사의 보팅입니다.
앞으로도 좋은 활동 부탁드려요~~^^
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit
돌봐주셔서(?) 고맙습니다 ^^
Downvoting a post can decrease pending rewards and make it less visible. Common reasons:
Submit