HDF5 파일 만드는 방법 - LOPES-HUFS/Korea_Stocks_for_HDF5 GitHub Wiki
준비작업 - hdf5r 패키지 설치
먼저 R에서 h5 파일을 다룰 수 있게 해주는 "hdf5r" 이란 패키지의 설치가 필요합니다. 이 패키지는 h5 형식의 파일을 열고 작성할 수 있게 해주는 핵심적인 역할을 합니다.
패키지의 설치 방법: 아래의 코드를 R 콘솔에서 실행하면 됩니다.
install.packages("hdf5r")
library(hdf5r)
R에서 주식 종목 데이터를 읽어와 h5 형식으로 만들기
전체 주식 종목들을 h5 파일로 만들 경우 약 400mb 정도의 파일이 나오기 때문에, 삼성전자(005930)와 LG전자(066570)의 데이터를 예시로 h5 파일을 만들어 보겠습니다.
1. 주식 종목 데이터 읽어서 확인하기
먼저 R의 기본 함수인 read.csv()
함수를 이용해 삼성전자 데이터를 R로 읽어와 줍니다. 깃허브에서 데이터를 읽어온 다음 head()
함수로 확인해 줍니다. 확인해보면 삼성전자 종목의 날짜 정보가 담긴 Date 열이 첫 번째로 나타납니다. 그리고 2~7번째 열에 시가(Open), 고가(High), 저가(Low), 종가(Close), 거래량(Volume), 수정종가(Adj.Close)에 대한 데이터를 확인할 수 있습니다.
stock_005930 <- read.csv(file = "https://raw.githubusercontent.com/LOPES-HUFS/Korea_Stocks_for_HDF5/master/stocks/005930.csv", header = TRUE, stringsAsFactors=FALSE)
head(stock_005930)
2. 새로운 h5 파일 만들고, 그 안에 그룹 만들기
H5File$new()
메소드를 사용해서 새로운 파일을 만들 수 있습니다. 함수를 사용할 때 () 안에 만들 h5 파일의 이름을 입력합니다. 여기에서는 "Sample.h5"로 이름을 입력 해줍니다. 그리고, H5File$new()
메소드의 mode 인자를 "w"(쓰기 모드)로 설정하면 새로운 h5 파일을 만들 수 있습니다. 이 방법으로 만들어진 Sample.h5 파일은 R 콘솔의 기본 디렉토리에 저장됩니다. dir()
함수를 통해 Sample.h5 파일이 만들어진걸 확인해볼 수 있습니다. 이제 그룹을 만들 차례입니다. 그룹을 만들기에 앞서 그룹의 이름을 정해야합니다. 그룹의 이름은 중복을 허용하지 않기 때문에 고유한 것으로 설정해야합니다. 주식의 종목코드가 고유한 번호이므로 "_005930"과 같이 삼성전자의 종목코드를 그룹의 이름으로 사용합니다. 만약 그룹의 이름이 중복된다면, 에러가 나고 그룹이 생성되지 않습니다. h5 파일 내 그룹은 file.h5$create_group()
메소드를 사용하여 만들 수 있습니다. 메소드 안에 name 인자를 통해 그룹의 이름을 지정할 수 있습니다. 만약 그룹의 이름을 잘못 입력했을 경우 file.h5$link_delete("그룹이름")
메소드를 사용해 지울 수 있습니다.
file.h5 <- H5File$new("sample.h5", mode="w")
dir()
stock.grp <- file.h5$create_group(name= "_005930")
3. 그룹 안에 데이터 입력하기
이제 가장 중요한 과정인 데이터의 입력 과정이 남아 있습니다. 데이터를 입력하려면 h5의 폴더 안의 파일 역할을 하는 계층을 만들어야 합니다. 계층이란 그룹 안에 데이터를 입력받는 위치입니다. 비유하자면 폴더 안에 파일이라 말할 수 있습니다. 저희가 입력할 데이터는 2가지 입니다. 삼성전자의 데이터 중 날짜 데이터와 시가와 종가를 비롯한 종목의 가격에 대한 데이터 2가지를 입력할 것입니다. 데이터를 나누어 입력하는 이유는 데이터의 형식을 한가지로 맞추기 위해서 입니다. 물론 데이터의 형식을 하나로 통일하지 않고 HDF5파일이 지원하는 compound 형식 즉 복합 형식으로 데이터를 입력할 수도 있습니다. 그러나 이렇게 할 경우 파일을 읽어올 때 굉장히 오랜 시간이 걸리거나, 혹은 호환성 문제가 일어날 수 있습니다. 예를 들어 R에서 만든 compound 형식의 h5 파일을 Python에서 열려고 할 때 R쪽에서는 쉽게 열리지만, 파이썬에서는 굉장히 오랜 시간이 지나도 쉽게 열리지 않는 경우가 발생할 수 있습니다. 그렇기 때문에 compound 형식을 사용하기 보다는 문자열(string), 정수형(int)과같이 단일한 형식으로 맞춰서 데이터를 입력하는 것이 좋습니다.
이제 삼성전자의 시가(Open)부터 수정종가(Adj.Close)까지 데이터를 matrix()
함수로 변환해서 삼성전자 그룹의 'data'란 이름의 계층으로 넣어줍니다. 마찬가지로 삼성전자의 날짜 데이터인 Date 열도 matrix()
함수로 변환해서 'index'란 계층으로 넣어줍니다. 이외에도 데이터를 분리해서 입력하고 싶다면 그룹 변수에 계층이름 방식으로 새로운 계층을 만들 수 있습니다. 계층의 이름은 앞서 그룹의 이름과 마찬가지로 중복이 되어서는 안됩니다. 물론 같은 그룹이 아니라 다른 그룹이라면 계층이름이 같아도 상관이 없습니다. 만든 계층을 지우는 방법은 그룹을 지우는 것과 똑같은 방식으로 file.h5['그룹이름'](/LOPES-HUFS/Korea_Stocks_for_HDF5/wiki/'그룹이름')$link_delete("계층이름")
와 같은 코드로 삭제할 수 있습니다. 또한, 데이터를 입력할 때 Dataframe
이 아닌 matrix
함수를 사용하는 이유는 compound 형식으로 입력되는 것을 방지하기 위해서 입니다. 이런 방식으로 데이터에서 열 이름을 제외하면 입력하는 데이터의 형식을 한 가지로 맞추기가 편합니다. 이렇게 제외된 열 이름들은 계층의 속성으로 따로 입력할 수 있기 때문에 데이터가 유실될 걱정은 할 필요가 없습니다.
stock.grp['data'](/LOPES-HUFS/Korea_Stocks_for_HDF5/wiki/'data') <- as.matrix(stock_005930[,2:7])
stock.grp['index'](/LOPES-HUFS/Korea_Stocks_for_HDF5/wiki/'index') <- as.matrix(stock_005930[,1])
3.1 데이터를 입력하는 또 다른 방법과 압축하는 방법
그룹에 데이터를 입력하는 또 다른 방법은 stock.grp$create_dataset()
메소드를 사용하는 것입니다. 이 방법을 사용하면 기존의 방식과 달리 파일의 압축과 관련된 옵션들을 설정할 수 있습니다. 파일의 압축은 HDF5 형식이 가진 장점으로, HDF5 파일이 여러 데이터를 하나로 저장하는 만큼 커지는 파일의 크기를 줄여주는 기능입니다. 물론 create_dataset()
메소드를 사용하지 않고 그냥 데이터를 입력할 경우에도 압축이 이루어집니다. 이 경우 압축은 자동적으로 이루어지기 때문에 최적화된 것이 아닙니다. 따라서 최적화 된 파일 크기로 압축을 진행하려면, create_dataset()
메소드를 사용하여 옵션을 수동으로 조절해주어야 합니다. create_dataset()
메소드에는 여러가지 인자가 있지만 압축과 직접적으로 연관된 인자는 두 가지로 chunk_dims, gzip_level
입니다.
먼저 chunk_dims 인자가 있습니다. 이 인자는 입력 데이터의 크기와 연관되는 인자입니다. 보통은 NULL로 설정되어 있지만 따로 입력한다면, 입력 데이터의 크기와 같은 크기로 지정해줍니다. 그러나 데이터의 크기를 잘 모르겠다면 'auto'를 입력해주어도 됩니다. gzip_level 인자는 압축 단계를 나타내는 인자로 숫자를 입력 받습니다. 단 이 숫자는 chunk_dims 인자를 입력했을 때만 효과가 있습니다. 이외에 입력하는 데이터 타입을 지정하는 dtype 인자가 있습니다. 이 인자는 설정하지 않는다면 메소드를 사용할 수 없습니다. 만약 입력하지 않고 메소드를 사용하려면 입력할 데이터를 줄 경우 자동으로 그것을 통해 dtype이 정해집니다.
dtype은 보통 단순한 수준에서 보면 int, float, string 수준으로 이루어져 있습니다. 그러나 확장해보면 int32bit, int64bit, string도 인코딩과 r 내부에서 "c or fortran" 어느 것으로 스트링 타입을 입력할 것인지 등 엄청 복잡하고 다양합니다. 따라서 어떤 타입으로 입력해야 최적일지 모르는 경우가 많습니다. 이 때 guess_dtype("입력할 데이터")
이란 함수를 사용하면 입력할 데이터에 알맞는 dtype을 찾아 줍니다. 찾아 준 데이터 타입은 dtype인자에 h5types$
메소드에서 찾아서 입력할 수 있습니다. 다음 인자로는 space 인자가 있습니다. space 인자는 입력하는 데이터의 크기와 최대 크기를 입력하는 인자입니다. 이 인자를 입력하지 않으면 그냥 inf 무한으로 입력됩니다. 무한으로 입력되면 파일의 크기가 커지기 때문에 입력 데이터의 크기를 dim()을 통해 찾아서 입력해주고, 최대 크기는 이후 데이터 추가를 생각한다면 행의 경우만 inf로 설정하면 됩니다.
위와 같이 수동으로 옵션을 조절하여 압축을 해 파일을 만들면, 전체 주식 종목을 하나의 파일로 만들 경우 자동으로 압축하는 것보다 약 50mb 가량의 크기를 줄일 수 있습니다. 그리고, 압축을 설정할 때 주의할 점은 입력 데이터의 크기가 1000000 보다 크고 압축 단계가 높다면, 파일을 읽고 쓰는데 시간이 굉장이 오래 걸릴 수 있다는 점입니다. 따라서 한 번에 1000000 개의 데이터를 입력하기 보다는 여러개로 쪼개서 하나의 데이터 셋에 입력하는 행과 열의 곱이 1000000이 넘지 않도록 하는 것이 중요합니다. 아래는 압축에 대한 인자를 조절하는 코드 예시입니다.
data_ds <- stock.grp$create_dataset(name = "data",robj = as.matrix(stock_005930[,2:7]),chunk_dims = c(NROW(stock_005930), 6),gzip_level = 9,space = H5S$new(dims = c(NROW(stock_005930), 6), maxdims = c(Inf, 6)))
index_ds <- stock.grp$create_dataset(name = "data",robj = as.matrix(stock_005930[,1]),chunk_dims = c(NROW(stock_005930), 1),gzip_level = 9,space = H5S$new(dims = c(NROW(stock_005930), 1), maxdims = c(Inf, 1)))
4. 그룹 안에 입력된 데이터의 정보 추가하기
이제 기존에 삼성전자 종목 데이터에 있던 열 이름에 대한 정보를 추가해줄 차례입니다. 위에서 "data"와 "index"란 두 개의 계층에 담긴 데이터의 열 이름은 h5attr()
함수를 사용해 각 계층에 열 이름이나 행 이름에 대한 정보를 추가할 수 있습니다. 아래의 코드 중 "colnames" 대신 "rownames"를 입력할 경우 계층에 입력된 데이터의 행 이름도 입력할 수 있습니다. 만약 정보를 잘못 입력 했다면 file.h5["그룹이름/계층이름"](/LOPES-HUFS/Korea_Stocks_for_HDF5/wiki/"그룹이름/계층이름")$attr_delete("")
메소드를 이용해 열 이름이나 행 이름을 지울 수 있습니다.
h5attr(stock.grp["data"](/LOPES-HUFS/Korea_Stocks_for_HDF5/wiki/"data"), "colnames") <- colnames(stock_005930)[2:7]
h5attr(stock.grp["index"](/LOPES-HUFS/Korea_Stocks_for_HDF5/wiki/"index"), "colnames") <- colnames(stock_005930)[1]
이렇게 h5 파일에 삼성전자의 데이터 입력이 완료되었습니다.
5. 반복되는 과정을 함수로 만들기
본래 h5 파일에 데이터의 입력이 완료되면 입력된 자료가 수정되지 않도록 파일을 닫아야 합니다. 그러나, 저희는 LG전자 데이터도 입력할 것이기 때문에 아직 h5 파일을 닫는 것은 이릅니다. 위와 같은 과정을 LG전자의 데이터로 반복하면 됩니다. 그러나 같은 작업이기 때문에 h5 파일에 데이터를 입력하는 과정을 하나의 함수로 만들어 적용해 볼 것입니다. 아래의 코드는 위의 반복되는 작업 과정을 함수로 만든 것입니다.
create_h5_file <- function(stocknumber){
file_path <- paste("https://raw.githubusercontent.com/LOPES-HUFS/Korea_Stocks_for_HDF5/master/stocks/",stocknumber,".csv",sep="")
stock_temp <- read.csv(file = file_path, header = TRUE, stringsAsFactors=FALSE)
data_name <- paste("_",stocknumber,sep = "")
stock.grp <- file.h5$create_group(data_name)
stock.grp['data'](/LOPES-HUFS/Korea_Stocks_for_HDF5/wiki/'data') <- as.matrix(stock_temp[,2:7])
stock.grp['index'](/LOPES-HUFS/Korea_Stocks_for_HDF5/wiki/'index') <- as.matrix(stock_temp[,1])
h5attr(stock.grp["data"](/LOPES-HUFS/Korea_Stocks_for_HDF5/wiki/"data"), "colnames") <- colnames(stock_temp)[2:7]
h5attr(stock.grp["index"](/LOPES-HUFS/Korea_Stocks_for_HDF5/wiki/"index"), "colnames") <- colnames(stock_temp)[1]
}
위의 코드로 만들어진 함수는 데이터를 읽고, 새로운 그룹을 만들고, 그룹 안에 데이터를 입력하고, 입력된 데이터의 정보를 추가하는 과정을 반복합니다. 이 함수는 주식의 종목코드를 입력받아서 작동합니다. 위의 함수에서 h5 파일에 데이터를 입력하는 과정에서 새로운 h5 파일을 만드는 부분만 빠졌습니다. 기존에 만든 h5 파일에 새로운 데이터만 추가하면 되기 때문에 새로운 h5 파일을 만들 필요가 없습니다.
6. 함수 적용해보기
이제 만들어진 함수에 LG전자의 종목코드를 넣어 h5 파일에 추가로 데이터를 입력 해보겠습니다. LG전자의 종목코드인 066570을 함수에 넣고 실행합니다. 에러가 발생하지 않으므로 만든 함수가 무사히 작동한 것으로 보입니다. 만약 에러가 발생한다면, csv를 못 읽어오는 경우일 것입니다. 이럴 경우 인터넷을 재확인하고 제대로 된 종목코드를 입력한 것인지 다시 한번 확인해주시기 바랍니다. 이제 h5 파일에 데이터 입력을 마쳤으니 파일을 닫아 입력된 데이터를 수정할 수 없게 만들어야 합니다. file.h5$close_all()
메소드로 파일을 닫아줍니다. 이 다음에는 작성한 파일을 열어 종목 데이터가 제대로 입력된 것인지 다시 한번 확인할 것입니다.
create_h5_file("066570")
file.h5$close_all()
만들어진 h5 파일 사용하기
1. 작성한 h5 파일 R로 읽어오기
위에서 입력한 데이터의 확인하기 위해 h5 파일을 읽어와 줍니다. h5 파일을 읽을 때 역시 작성할 때와 똑같은 함수인 H5File$new()
메소드를 사용합니다. 단지 mode
인자를 읽기 모드인 "r+" 로 바꾼다 점 외에는 차이가 없습니다. 파일을 읽어온 후 file.h5$ls()
메소드로 삼성전자와 LG전자 두 개의 그룹의 정보를 확인해 줍니다. h5 파일을 읽어올 때 주의해야할 점은 h5파일의 사용여부 입니다. 만약 다른 플랫폼에서 h5 파일을 열어서 사용하고 있다면 해당 h5 파일을 읽어올 수 없습니다. 또한, 같은 h5 파일이 닫히기 전이면 해당 파일을 읽어 올 수 없습니다. 만약 파일을 새롭게 읽어오고 싶다면 먼저 읽어온 파일을 close_all
메소드로 닫아주고 다시 읽어 올 수 있습니다.
file.h5 <- H5File$new("Sample.h5", mode="r+")
file.h5$ls()
2. 그룹 안에 입력된 데이터 확인하기
먼저 삼성전자 그룹의 시가와 종가를 비롯한 데이터들에 접근해보겠습니다. 그룹 안의 계층으로 입력된 데이터에 접근하려면 "_005930/data" 와 같이 "그룹이름/계층이름"의 방식으로 접근해야 합니다. 즉 file.h5["_005930/data"](/LOPES-HUFS/Korea_Stocks_for_HDF5/wiki/"_005930/data")
로 데이터의 위치를 지정하고 [,]
로 해당 위치의 데이터를 가져옵니다. "data" 계층에 있는 데이터를 가져왔으면 해당 데이터의 정보 가져와야 합니다. 데이터에 대한 열 정보를 입력했을 때와 마찬가지 방식으로 h5attr()
메소드를 사용해서 열 정보를 가져옵니다. 만약 계층에 입력된 정보가 없다면, h5attr()
메소드를 사용할 경우 에러가 발생할 수 있습니다. 참고로 미리 그룹 안의 정보를 file.h5['그룹이름'](/LOPES-HUFS/Korea_Stocks_for_HDF5/wiki/'그룹이름')$ls()
메소드로 확인할 수 있습니다.
samsung_data <- file.h5["_005930/data"](/LOPES-HUFS/Korea_Stocks_for_HDF5/wiki/"_005930/data")[,]
colnames(samsung_data) <- h5attr(file.h5["_005930/data"](/LOPES-HUFS/Korea_Stocks_for_HDF5/wiki/"_005930/data"),"colnames")
head(samsung_data)
3. 날짜 데이터 읽어와 전체 데이터 학인하기
위에서 시가와 종가 등의 데이터를 읽어온 방식과 마찬가지로 날짜 열도 읽어와 기존의 시가와 종가 등의 주가 데이터와 병합시켜 줍니다. h5 파일에서 읽어온 데이터로 새로 만든 삼성전자 데이터를 head()
함수로 확인합니다. LG전자의 경우도 같은 방식으로 그룹의 이름만 바꾸어 데이터를 확인하실 수 있습니다.
samsung_index <- file.h5["_005930/index"](/LOPES-HUFS/Korea_Stocks_for_HDF5/wiki/"_005930/index")[,]
stock_samsung <- data.frame(Date=as.Date(samsung_index),samsung_data)
head(stock_samsung)
h5 파일에서 읽어온 종목데이터를 이용해 캔들 차트(candlestick chart) 그려보기
주식 종목 데이터는 다양하게 활용할 수 있지만, 간단하게 h5 파일에서 뽑아서 만든 삼성전자 데이터를 가지고 캔들 차트를 그려보는 것을 소개하겠습니다. 주식 종목 데이터로 캔들 차트 그리기 위해서는 "plotly" 나 "tidyquant"와 같은 패키지가 필요합니다. 여기서는 "plotly" 패키지를 이용할 것입니다. 캔들 차트를 그리는 방법은 간단합니다. 다음은 plotly 함수를 이용해 캔들 차트를 그리는 코드와 그 결과입니다.
install.packages("plotly")
library(plotly)
plot_ly(tail(stock_samsung,30),x=~Date,type="candlestick",
open = ~Open, close = ~Adj.Close,
high = ~High, low = ~Low) %>% layout(title = "Samsung month candle chart",xaxis=list(title = "Date"),yaxis=list(title = "Adj.Close"))