Chapter 7 데이터 정리하기
앞 CHAPTER에서는 API와 크롤링을 통해 주가, 재무제표, 가치지표를 수집하는 방법을 배웠습니다. 이번 CHAPTER에서는 각각 csv 파일로 저장된 데이터들을 하나로 합친 후 저장하는 과정을 살펴보겠습니다.
7.1 주가 정리하기
주가는 data/KOR_price 폴더 내에 티커_price.csv 파일로 저장되어 있습니다. 해당 파일들을 불러온 후 데이터를 묶는 작업을 통해 하나의 파일로 합치는 방법을 알아보겠습니다.
library(stringr)
library(xts)
library(magrittr)
= read.csv('data/KOR_ticker.csv', row.names = 1)
KOR_ticker $'종목코드' =
KOR_tickerstr_pad(KOR_ticker$'종목코드', 6, side = c('left'), pad = '0')
= list()
price_list
for (i in 1 : nrow(KOR_ticker)) {
= KOR_ticker[i, '종목코드']
name =
price_list[[i]] read.csv(paste0('data/KOR_price/', name,
'_price.csv'),row.names = 1) %>%
as.xts()
}
= do.call(cbind, price_list) %>% na.locf()
price_list colnames(price_list) = KOR_ticker$'종목코드'
head(price_list[, 1:5])
## X X005930 X000660 X035420 X207940
## 1 2018-11-05 43800 70500 113500 399000
## 2 2018-11-06 43750 70100 117000 401000
## 3 2018-11-07 44000 70800 115500 400000
## 4 2018-11-08 44050 72900 116500 384500
## 5 2018-11-09 44300 72900 115000 368000
## 6 2018-11-12 45200 74500 115000 285500
tail(price_list[, 1:5])
## X X005930 X000660 X035420 X207940
## 737 2021-10-29 69800 103000 407000 871000
## 738 2021-11-01 69900 106500 408500 874000
## 739 2021-11-02 71500 107500 410000 874000
## 740 2021-11-03 70400 105500 402500 874000
## 741 2021-11-04 70600 106000 411500 865000
## 742 2021-11-05 70600 107000 409000 856000
- 티커가 저장된 csv 파일을 불러온 후 티커를 6자리로 맞춰줍니다.
- 빈 리스트인 price_list를 생성합니다.
- for loop 구문을 이용해 종목별 가격 데이터를 불러온 후
as.xts()
를 통해 시계열 형태로 데이터를 변경하고 리스트에 저장합니다. do.call()
함수를 통해 리스트를 열 형태로 묶습니다.- 간혹 결측치가 발생할 수 있으므로,
na.locf()
함수를 통해 결측치에는 전일 데이터를 사용합니다. - 행 이름을 각 종목의 티커로 변경합니다.
해당 작업을 통해 개별 csv 파일로 흩어져 있던 가격 데이터가 하나의 데이터로 묶이게 됩니다.
write.csv(data.frame(price_list), 'data/KOR_price.csv')
마지막으로 해당 데이터를 data 폴더에 KOR_price.csv 파일로 저장합니다. 시계열 형태 그대로 저장하면 인덱스가 삭제되므로 데이터 프레임 형태로 변경한 후 저장해야 합니다.
7.2 재무제표 정리하기
재무제표는 data/KOR_fs 폴더 내 티커_fs.csv 파일로 저장되어 있습니다. 주가는 하나의 열로 이루어져 있어 데이터를 정리하는 것이 간단했지만, 재무제표는 각 종목별 재무 항목이 모두 달라 정리하기 번거롭습니다.
library(stringr)
library(magrittr)
library(dplyr)
= read.csv('data/KOR_ticker.csv', row.names = 1)
KOR_ticker $'종목코드' =
KOR_tickerstr_pad(KOR_ticker$'종목코드', 6, side = c('left'), pad = '0')
= list()
data_fs
for (i in 1 : nrow(KOR_ticker)){
= KOR_ticker[i, '종목코드']
name = read.csv(paste0('data/KOR_fs/', name,
data_fs[[i]] '_fs.csv'), row.names = 1)
}
위와 동일하게 티커 데이터를 읽어옵니다. 이를 바탕으로 종목별 재무제표 데이터를 읽어온 후 리스트에 저장합니다.
= data_fs[[1]] %>% rownames()
fs_item length(fs_item)
## [1] 237
print(head(fs_item))
## [1] "매출액" "매출원가" "매출총이익" "판매비와관리비"
## [5] "인건비" "유무형자산상각비"
다음으로 재무제표 항목의 기준을 정해줄 필요가 있습니다. 재무제표 작성 항목은 각 업종별로 상이하므로, 이를 모두 고려하면 지나치게 데이터가 커지게 됩니다. 또한 퀀트 투자에는 일반적이고 공통적인 항목을 주로 사용하므로 대표적인 재무 항목을 정해 이를 기준으로 데이터를 정리해도 충분합니다.
따라서 기준점으로 첫 번째 리스트, 즉 삼성전자의 재무 항목을 선택하며, 총 237개 재무 항목이 있습니다. 해당 기준을 바탕으로 재무제표 데이터를 정리하며, 전체 항목에 대한 정리 이전에 간단한 예시로 첫 번째 항목인 매출액 기준 데이터 정리를 살펴보겠습니다.
= lapply(data_fs, function(x) {
select_fs # 해당 항목이 있을시 데이터를 선택
if ( '매출액' %in% rownames(x) ) {
which(rownames(x) == '매출액'), ]
x[
# 해당 항목이 존재하지 않을 시, NA로 된 데이터프레임 생성
else {
} data.frame(NA)
}
})
= bind_rows(select_fs)
select_fs
print(head(select_fs))
## X2018.12 X2019.12 X2020.12 NA.
## 매출액...1 2437714 2304009 2368070 NA
## 매출액...2 404451 269907 319004 NA
## 매출액...3 55869 43562 53041 NA
## 매출액...4 5358 7016 11648 NA
## 매출액...5 24170 30701 41568 NA
## 매출액...6 281830 273531 300765 NA
먼저 lapply()
함수를 이용해 모든 재무 데이터가 들어 있는 data_fs 데이터를 대상으로 함수를 적용합니다. %in%
함수를 통해 만일 매출액이라는 항목이 행 이름에 있으면 해당 부분의 데이터를 select_fs 리스트에 저장하고, 해당 항목이 없는 경우 NA로 이루어진 데이터 프레임을 저장합니다.
그 후, dplyr 패키지의 bind_rows()
함수를 이용해 리스트 내 데이터들을 행으로 묶어줍니다. rbind()
에서는 리스트 형태를 테이블로 묶으려면 모든 데이터의 열 개수가 동일해야 하는 반면, bind_rows()
에서는 열 개수가 다를 경우 나머지 부분을 NA로 처리해 합쳐주는 장점이 있습니다.
합쳐진 데이터를 살펴보면, 먼저 열 이름이 . 혹은 NA.인 부분이 있습니다. 이는 매출액 항목이 없는 종목의 경우 NA 데이터 프레임을 저장해 생긴 결과입니다. 또한 연도가 순서대로 저장되지 않은 경우가 있습니다. 이 두 가지를 고려해 데이터를 클렌징합니다.
= select_fs[!colnames(select_fs) %in%
select_fs c('.', 'NA.')]
= select_fs[, order(names(select_fs))]
select_fs rownames(select_fs) = KOR_ticker[, '종목코드']
print(head(select_fs))
## X2018.12 X2019.12 X2020.12
## 5930 2437714 2304009 2368070
## 660 404451 269907 319004
## 35420 55869 43562 53041
## 207940 5358 7016 11648
## 35720 24170 30701 41568
## 51910 281830 273531 300765
!
와%in%
함수를 이용해, 열 이름에 . 혹은 NA.가 들어가지 않은 열만 선택합니다.order()
함수를 이용해 열 이름의 연도별 순서를 구한 후 이를 바탕으로 열을 다시 정리합니다.- 행 이름을 티커들로 변경합니다.
해당 과정을 통해 전 종목의 매출액 데이터가 연도별로 정리되었습니다. for loop 구문을 이용해 모든 재무 항목에 대한 데이터를 정리하는 방법은 다음과 같습니다.
= list()
fs_list
for (i in 1 : length(fs_item)) {
= lapply(data_fs, function(x) {
select_fs # 해당 항목이 있을시 데이터를 선택
if ( fs_item[i] %in% rownames(x) ) {
which(rownames(x) == fs_item[i]), ]
x[
# 해당 항목이 존재하지 않을 시, NA로 된 데이터프레임 생성
else {
} data.frame(NA)
}
})
# 리스트 데이터를 행으로 묶어줌
= bind_rows(select_fs)
select_fs
# 열이름이 '.' 혹은 'NA.'인 지점은 삭제 (NA 데이터)
= select_fs[!colnames(select_fs) %in%
select_fs c('.', 'NA.')]
# 연도 순별로 정리
= select_fs[, order(names(select_fs))]
select_fs
# 행이름을 티커로 변경
rownames(select_fs) = KOR_ticker[, '종목코드']
# 리스트에 최종 저장
= select_fs
fs_list[[i]]
}
# 리스트 이름을 재무 항목으로 변경
names(fs_list) = fs_item
위 과정을 거치면 fs_list에 총 237개 리스트가 생성됩니다. 각 리스트에는 해당 재무 항목에 대한 전 종목의 연도별 데이터가 정리되어 있습니다.
saveRDS(fs_list, 'data/KOR_fs.Rds')
마지막으로 해당 데이터를 data 폴더 내에 저장합니다. 리스트 형태 그대로 저장하기 위해 saveRDS()
함수를 이용해 KOR_fs.Rds 파일로 저장합니다.
Rds 형식은 파일을 더블 클릭한 후 연결 프로그램을 R Studio로 설정해 파일을 불러올 수 있습니다. 혹은 readRDS()
함수를 이용해 파일을 읽어올 수도 있습니다.
7.3 가치지표 정리하기
가치지표는 data/KOR_value 폴더 내 티커_value.csv 파일로 저장되어 있습니다. 재무제표를 정리하는 방법과 거의 동일합니다.
library(stringr)
library(magrittr)
library(dplyr)
= read.csv('data/KOR_ticker.csv', row.names = 1)
KOR_ticker $'종목코드' =
KOR_tickerstr_pad(KOR_ticker$'종목코드', 6, side = c('left'), pad = '0')
= list()
data_value
for (i in 1 : nrow(KOR_ticker)){
= KOR_ticker[i, '종목코드']
name =
data_value[[i]] read.csv(paste0('data/KOR_value/', name,
'_value.csv'), row.names = 1) %>%
t() %>% data.frame()
}
먼저 티커에 해당하는 파일을 불러온 후 for loop 구문을 통해 가치지표 데이터를 data_value 리스트에 저장합니다. 단, csv 내에 데이터가 7.1와 같이 행의 형태로 저장되어 있으므로, t() 함수를 이용해 열의 형태로 바꿔주며, 데이터 프레임 형태로 저장합니다.
value | x |
---|---|
PER | Number 1 |
PBR | Number 2 |
PCR | Number 3 |
PSR | Number 4 |
= bind_rows(data_value)
data_value print(head(data_value))
## PER PBR PCR PSR X1
## x...1 16.15 1.527 6.456 1.780 NA
## x...2 16.23 1.487 6.266 2.419 NA
## x...3 67.45 8.188 46.707 12.744 NA
## x...4 237.48 12.445 283.190 49.135 NA
## x...5 372.09 7.795 59.620 13.928 NA
## x...6 108.79 2.968 9.548 1.854 NA
bind_rows()
함수를 이용하여 리스트 내 데이터들을 행으로 묶어준 후 데이터를 확인해보면 PER, PBR, PCR, PSR 열 외에 불필요한 NA로 이루어진 열이 존재합니다. 해당 열을 삭제한 후 정리 작업을 하겠습니다.
= data_value[colnames(data_value) %in%
data_value c('PER', 'PBR', 'PCR', 'PSR')]
= data_value %>%
data_value mutate_all(list(~na_if(., Inf)))
rownames(data_value) = KOR_ticker[, '종목코드']
print(head(data_value))
## PER PBR PCR PSR
## 005930 16.15 1.527 6.456 1.780
## 000660 16.23 1.487 6.266 2.419
## 035420 67.45 8.188 46.707 12.744
## 207940 237.48 12.445 283.190 49.135
## 035720 372.09 7.795 59.620 13.928
## 051910 108.79 2.968 9.548 1.854
write.csv(data_value, 'data/KOR_value.csv')
- 열 이름이 가치지표에 해당하는 부분만 선택합니다.
- 일부 종목은 재무 데이터가 0으로 표기되어 가치지표가 Inf로 계산되는 경우가 있습니다.
mutate_all()
내에na_if()
함수를 이용해 Inf 데이터를 NA로 변경합니다. - 행 이름을 티커들로 변경합니다.
- data 폴더 내에 KOR_value.csv 파일로 저장합니다.