지난 시간에 Import, 즉
크롤링을 통한 데이터 수집과정에 대해 살펴보았다면,
이번에는 Tidy와 Transform 즉
데이터 정리 및 변형하기에 대해 알아보도록 하겠습니다.
데이터 가공에는 R의 기본함수를 사용해도 되지만,
데이터 과학에서 가장 애용되는 dplyr 패키지를 사용할 경우
매우 편리하면서도 빠르게 데이터를 정리할 수 있습니다.
따라서 해당 글에서는 dplyr 패키지의 대표적인 함수를 이용하여
금융 데이터를 정리하는 예시들을 살펴보도록 하겠습니다.
dplyr 패키지의 더욱 상세한 내용은
헤들리 위컴의 'R을 활용한 데이터과학'을 살펴보시길 권합니다.
data_sector = read.csv('data_sector.csv', row.names = 1, stringsAsFactors = FALSE)
data_value = read.csv('data_value.csv', row.names = 1, stringsAsFactors = FALSE)
data_value = read.csv('data_value.csv', row.names = 1, stringsAsFactors = FALSE)
먼저 지난시간 크롤링을 통해 다운로드 받은 csv 파일들을 읽어오도록 합니다.
readr 패키지의 read_csv() 함수를 이용하는 것이 더욱 효과적이지만,
한글의 인코딩이 계속 깨지는 문제로 인해
기본 함수인 read.csv()를 이용하도록 하겠습니다.
row.names=1 를 통해 첫번째 행을 행이름으로,
stringsAsFactors = FALSE 를 통해
데이터가 팩터 형태로 변형되는 것을 막아줍니다.
> head(data_sector)
시장구분 종목코드 종목명 산업분류 시가총액.원.
1 코스피 030720 동원수산 어업 41884245000
2 코스피 007160 사조산업 어업 249250000000
3 코스피 006040 동원산업 어업 682729600000
4 코스피 004970 신라교역 어업 206400000000
5 코스피 003580 넥스트사이언스 광업 137311128100
6 코스피 280360 롯데제과 음식료품 635574402000
시장구분 종목코드 종목명 산업분류 시가총액.원.
1 코스피 030720 동원수산 어업 41884245000
2 코스피 007160 사조산업 어업 249250000000
3 코스피 006040 동원산업 어업 682729600000
4 코스피 004970 신라교역 어업 206400000000
5 코스피 003580 넥스트사이언스 광업 137311128100
6 코스피 280360 롯데제과 음식료품 635574402000
> head(data_value)
종목코드 PER PBR 배당수익률
1 016670 34.29 1.01 0.00
2 043090 319.29 6.48 0.00
3 044180 - 0.97 0.00
4 051160 - 6.52 0.00
5 068240 55.99 2.23 0.31
6 114120 - 0.41 0.00
종목코드 PER PBR 배당수익률
1 016670 34.29 1.01 0.00
2 043090 319.29 6.48 0.00
3 044180 - 0.97 0.00
4 051160 - 6.52 0.00
5 068240 55.99 2.23 0.31
6 114120 - 0.41 0.00
> dim(data_sector)
[1] 2227 5
> dim(data_value)
[1] 2187 4
[1] 2227 5
> dim(data_value)
[1] 2187 4
두 데이터의 행렬 갯수를 확인해보면,
섹터 데이터는 행이 2227,
밸류 데이터는 행이 2187개로
섹터 데이터가 40개 종목이 더 많습니다.
이에 대해서는 뒤에서 자세히 다루도록 하겠습니다.
library(dplyr)
# join
data_market.left = left_join(data_sector, data_value, by = '종목코드')
data_market.inner = inner_join(data_sector, data_value, by = '종목코드')
# join
data_market.left = left_join(data_sector, data_value, by = '종목코드')
data_market.inner = inner_join(data_sector, data_value, by = '종목코드')
dplyr 패키지를 연 후, left_join()과 inner_join() 함수를 이용해
두개 데이터를 합쳐보도록 하겠습니다.
by는 데이터를 합칠 기준을 의미하며,
'종목코드'를 기준으로 데이터를 합치도록 합니다.
그렇다면 두 join은 어떠한 차이가 있을까요?
sql을 해보신 분이라면 join의 차이에 대해 잘 알것입니다.
먼저 left_join()은 왼쪽의 x를 기준으로 데이터를 맞추며,
x에는 있지만 y에는 없는 데이터는 NA로 표시됩니다.
반면 inner_join()은 교집합에 해당하는 부분으로 데이터를 맞추며,
x와 y 두곳 모두에 존재하는 데이터를 합칩니다.
> dim(data_market.left)
[1] 2227 8
> dim(data_market.inner)
[1] 2187 8
[1] 2227 8
> dim(data_market.inner)
[1] 2187 8
따라서 두 조인의 결과를 확인해보면
left_join()은 data_sector를 기준으로 데이터를 합쳤으므로,
해당 데이터와 행의 갯수가 같습니다. (2227개)
그러나 inner_join()은 교집합을 기준으로 데이터를 합쳤으므로,
데이터가 더 작은 data_value와 행의 갯수가 같습니다. (2187개)
그렇다면 과연 이 40개 종목의 차이는 무엇일까요?
여집합을 뜻하는 anti_join()을 통해 확인이 가능합니다.
> anti_join(data_sector, data_value, by = '종목코드')$종목명
[1] "엘브이엠씨홀딩스" "한국패러랠" "한국ANKOR유전" "맵스리얼티1"
[5] "맥쿼리인프라" "하나니켈2호" "하나니켈1호" "베트남개발1"
[9] "신한알파리츠" "이리츠코크렙" "모두투어리츠" "하이골드12호"
[13] "하이골드8호" "바다로19호" "하이골드3호" "케이탑리츠"
[17] "트러스제7호" "에이리츠" "동북아13호" "동북아12호"
[21] "컬러레이" "JTC" "뉴프라이드" "윙입푸드"
[25] "글로벌에스엠" "크리스탈신소재" "씨케이에이치" "차이나그레이트"
[29] "골든센츄리" "오가닉티코스메틱" "GRT" "로스웰"
[33] "헝셩그룹" "이스트아시아홀딩스" "에스앤씨엔진그룹" "차이나하오란"
[37] "SBI핀테크솔루션즈" "잉글우드랩" "코오롱티슈진" "엑세스바이오"
[1] "엘브이엠씨홀딩스" "한국패러랠" "한국ANKOR유전" "맵스리얼티1"
[5] "맥쿼리인프라" "하나니켈2호" "하나니켈1호" "베트남개발1"
[9] "신한알파리츠" "이리츠코크렙" "모두투어리츠" "하이골드12호"
[13] "하이골드8호" "바다로19호" "하이골드3호" "케이탑리츠"
[17] "트러스제7호" "에이리츠" "동북아13호" "동북아12호"
[21] "컬러레이" "JTC" "뉴프라이드" "윙입푸드"
[25] "글로벌에스엠" "크리스탈신소재" "씨케이에이치" "차이나그레이트"
[29] "골든센츄리" "오가닉티코스메틱" "GRT" "로스웰"
[33] "헝셩그룹" "이스트아시아홀딩스" "에스앤씨엔진그룹" "차이나하오란"
[37] "SBI핀테크솔루션즈" "잉글우드랩" "코오롱티슈진" "엑세스바이오"
data_sector에는 존재하지만
data_value에는 존재하지 않는 데이터의 종목명을 살펴보면
선박펀드, 광물펀드, 해외종목 등
일반적이지 않은 종목들이 포함되어 있습니다.
이러한 종목은 제외하는 것이 일반적이므로,
inner_join()을 이용한 데이터를 사용하도록 하겠습니다.
data_market = inner_join(data_sector, data_value, by = '종목코드')
rm(data_market.inner, data_market.left)
rm(data_market.inner, data_market.left)
data_market에 해당 결과를 저장해주며,
rm() 함수를 이용하여 data_market.inner, data_market.left 데이터를
삭제해주도록 합니다.
# glimpse
glimpse(data_market)
glimpse(data_market)
Observations: 2,187
Variables: 8
$ 시장구분 <chr> "코스피", "코스피", "코스피", "코스피", "코스피", "코스피", "코스피", "코스...
$ 종목코드 <chr> "030720", "007160", "006040", "004970", "003580", "2...
$ 종목명 <chr> "동원수산", "사조산업", "동원산업", "신라교역", "넥스트사이언스", "롯데제과", ...
$ 산업분류 <chr> "어업", "어업", "어업", "어업", "광업", "음식료품", "음식료품", "음식료품"...
$ 시가총액.원. 4.188425e+10, 2.492500e+11, 6.827296e+11, 2.064000e+1...
$ PER <chr> "6.83", "5.63", "4.09", "7.79", "-", "313.28", "...
$ PBR <chr> "0.96", "0.65", "0.75", "0.43", "1.88", "0.77", ...
$ 배당수익률 0.00, 0.40, 1.97, 3.88, 0.00, 0.19, 0.50, 1.23, 3.32,...
$ 시장구분 <chr> "코스피", "코스피", "코스피", "코스피", "코스피", "코스피", "코스피", "코스...
$ 종목코드 <chr> "030720", "007160", "006040", "004970", "003580", "2...
$ 종목명 <chr> "동원수산", "사조산업", "동원산업", "신라교역", "넥스트사이언스", "롯데제과", ...
$ 산업분류 <chr> "어업", "어업", "어업", "어업", "광업", "음식료품", "음식료품", "음식료품"...
$ 시가총액.원. 4.188425e+10, 2.492500e+11, 6.827296e+11, 2.064000e+1...
$ PER <chr> "6.83", "5.63", "4.09", "7.79", "-", "313.28", "...
$ PBR <chr> "0.96", "0.65", "0.75", "0.43", "1.88", "0.77", ...
$ 배당수익률 0.00, 0.40, 1.97, 3.88, 0.00, 0.19, 0.50, 1.23, 3.32,...
glimpse() 함수는 데이터를 대략 살펴보는 함수이며,
기본함수인 str()과 그 역할이 비슷합니다.
names(data_market)
[1] "시장구분" "종목코드" "종목명" "산업분류"
[5] "시가총액.원." "PER" "PBR" "배당수익률"
[5] "시가총액.원." "PER" "PBR" "배당수익률"
먼저 names() 함수를 통해 열이름을 살펴보면
시가총액.원 으로 된 이름이 있으며,
이를 시가총액 으로 변경하고자 합니다.
열 이름 변경은 rename() 함수를 사용하면 됩니다.
data_market = data_market %>%
rename(시가총액 = 시가총액.원.)
names(data_market)
[1] "시장구분" "종목코드" "종목명" "산업분류" "시가총액"
[6] "PER" "PBR" "배당수익률"
rename(시가총액 = 시가총액.원.)
names(data_market)
[1] "시장구분" "종목코드" "종목명" "산업분류" "시가총액"
[6] "PER" "PBR" "배당수익률"
rename(A = B) 형식을 사용하며,
A는 변경하고자 하는 이름, B는 현재 이름을 입력하면 됩니다.
이름이 변경된 것을 확인할 수 있습니다.
# distinct
data_market %>%
distinct(산업분류) %>% c()
data_market %>%
distinct(산업분류) %>% c()
$산업분류
[1] "어업" "광업" "음식료품" "섬유의복"
[5] "종이목재" "화학" "의약품" "비금속광물"
[9] "철강금속" "기계" "전기전자" "의료정밀"
[13] "운수장비" "기타제조" "유통업" "전기가스업"
[17] "건설업" "운수창고업" "통신업" "금융업"
[21] "서비스업" "기타서비스" "농림업" "전기,가스,수도"
[25] "건설" "유통" "숙박·음식" "운송"
[29] "금융" "오락·문화" "통신방송서비스" "IT S/W & SVC"
[33] "IT H/W" "음식료·담배" "섬유·의류" "종이·목재"
[37] "출판·매체복제" "제약" "비금속" "금속"
[41] "기계·장비" "일반전기전자" "의료·정밀기기" "운송장비·부품"
[45] "기타 제조"
[1] "어업" "광업" "음식료품" "섬유의복"
[5] "종이목재" "화학" "의약품" "비금속광물"
[9] "철강금속" "기계" "전기전자" "의료정밀"
[13] "운수장비" "기타제조" "유통업" "전기가스업"
[17] "건설업" "운수창고업" "통신업" "금융업"
[21] "서비스업" "기타서비스" "농림업" "전기,가스,수도"
[25] "건설" "유통" "숙박·음식" "운송"
[29] "금융" "오락·문화" "통신방송서비스" "IT S/W & SVC"
[33] "IT H/W" "음식료·담배" "섬유·의류" "종이·목재"
[37] "출판·매체복제" "제약" "비금속" "금속"
[41] "기계·장비" "일반전기전자" "의료·정밀기기" "운송장비·부품"
[45] "기타 제조"
distinct() 함수는 중복값을 제거하고 고유값만을 보여주며,
기본 함수인 unique()와 동일한 기능을 합니다.
총 45개의 산업분류가 존재함이 확인됩니다.
# select
data_market %>%
select(종목명) %>% head()
data_market %>%
select(종목명) %>% head()
종목명
1 동원수산
2 사조산업
3 동원산업
4 신라교역
5 넥스트사이언스
6 롯데제과
1 동원수산
2 사조산업
3 동원산업
4 신라교역
5 넥스트사이언스
6 롯데제과
select()는 원하는 열을 선택하는 함수입니다.
data_market 중 종목명 부분만 선택이 됨이 확인됩니다.
data_market %>%
select(종목명, PER, PBR) %>% head()
종목명 PER PBR
1 동원수산 6.83 0.96
2 사조산업 5.63 0.65
3 동원산업 4.09 0.75
4 신라교역 7.79 0.43
5 넥스트사이언스 - 1.88
6 롯데제과 313.28 0.77
select(종목명, PER, PBR) %>% head()
종목명 PER PBR
1 동원수산 6.83 0.96
2 사조산업 5.63 0.65
3 동원산업 4.09 0.75
4 신라교역 7.79 0.43
5 넥스트사이언스 - 1.88
6 롯데제과 313.28 0.77
한번에 여러 열을 선택할 수도 있습니다.
data_market %>%
select(starts_with('시')) %>% head()
select(starts_with('시')) %>% head()
시장구분 시가총액
1 코스피 41884245000
2 코스피 249250000000
3 코스피 682729600000
4 코스피 206400000000
5 코스피 137311128100
6 코스피 635574402000
data_market %>%
select(ends_with('R')) %>% head()
PER PBR
1 6.83 0.96
2 5.63 0.65
3 4.09 0.75
4 7.79 0.43
5 - 1.88
6 313.28 0.77
data_market %>%
select(contains('종목')) %>% head()
종목코드 종목명
1 030720 동원수산
2 007160 사조산업
3 006040 동원산업
4 004970 신라교역
5 003580 넥스트사이언스
6 280360 롯데제과
1 코스피 41884245000
2 코스피 249250000000
3 코스피 682729600000
4 코스피 206400000000
5 코스피 137311128100
6 코스피 635574402000
data_market %>%
select(ends_with('R')) %>% head()
PER PBR
1 6.83 0.96
2 5.63 0.65
3 4.09 0.75
4 7.79 0.43
5 - 1.88
6 313.28 0.77
data_market %>%
select(contains('종목')) %>% head()
종목코드 종목명
1 030720 동원수산
2 007160 사조산업
3 006040 동원산업
4 004970 신라교역
5 003580 넥스트사이언스
6 280360 롯데제과
select() 함수에는 다양한 응용버전도 존재합니다.
starts_with()를 입력할 경우 원하는 문자로 시작하는 열을,
ends_with()를 입력할 경우 원하는 문자로 끝나는 열을,
contains()를 입력할 경우 원하는 문자가 포함된 열을
선택할 수 있습니다.
data_market = data_market %>%
mutate(PBR = as.numeric(PBR),
PER = as.numeric(PER),
ROE = PBR / PER,
ROE = round(ROE, 4),
style = ifelse(PBR < median(PBR, na.rm = TRUE),
'value', 'growth')
)
mutate(PBR = as.numeric(PBR),
PER = as.numeric(PER),
ROE = PBR / PER,
ROE = round(ROE, 4),
style = ifelse(PBR < median(PBR, na.rm = TRUE),
'value', 'growth')
)
mutate() 함수는 데이터를 변형 후 저장합니다.
먼저 문자 형태인 PBR과 PER 열을 as.numeric() 함수를 이용하여
숫자 형태로 변형해줍니다.
또한 ROE는 PBR / PER로 나타낼 수 있으므로,
이를 ROE 열에 새로 저장해주며,
round() 함수를 이용하여 반올림을 해주도록 합니다.
또한 style 열에는 ifelse() 함수를 이용하여
PBR이 median 값보다 작을 경우 value를,
그렇지 않을 경우에는 growth를 입력합니다.
data_market %>% head()
시장구분 종목코드 종목명 산업분류 시가총액 PER PBR
1 코스피 030720 동원수산 어업 41884245000 6.83 0.96
2 코스피 007160 사조산업 어업 249250000000 5.63 0.65
3 코스피 006040 동원산업 어업 682729600000 4.09 0.75
4 코스피 004970 신라교역 어업 206400000000 7.79 0.43
5 코스피 003580 넥스트사이언스 광업 137311128100 NA 1.88
6 코스피 280360 롯데제과 음식료품 635574402000 313.28 0.77
배당수익률 ROE style
1 0.00 0.1406 value
2 0.40 0.1155 value
3 1.97 0.1834 value
4 3.88 0.0552 value
5 0.00 NA growth
6 0.19 0.0025 value
시장구분 종목코드 종목명 산업분류 시가총액 PER PBR
1 코스피 030720 동원수산 어업 41884245000 6.83 0.96
2 코스피 007160 사조산업 어업 249250000000 5.63 0.65
3 코스피 006040 동원산업 어업 682729600000 4.09 0.75
4 코스피 004970 신라교역 어업 206400000000 7.79 0.43
5 코스피 003580 넥스트사이언스 광업 137311128100 NA 1.88
6 코스피 280360 롯데제과 음식료품 635574402000 313.28 0.77
배당수익률 ROE style
1 0.00 0.1406 value
2 0.40 0.1155 value
3 1.97 0.1834 value
4 3.88 0.0552 value
5 0.00 NA growth
6 0.19 0.0025 value
데이터를 확인해보면 mutate() 함수가 잘 적용되어 있습니다.
data_market_ROE = data_market %>%
transmute(ROE = PBR / PER)
data_market_ROE %>% head()
ROE
1 0.140556369
2 0.115452931
3 0.183374083
4 0.055198973
5 NA
6 0.002457865
transmute(ROE = PBR / PER)
data_market_ROE %>% head()
ROE
1 0.140556369
2 0.115452931
3 0.183374083
4 0.055198973
5 NA
6 0.002457865
mutate() 함수와 비슷한 함수로 transmute() 함수가 있습니다.
mutate() 함수는 기존 데이터가 유지되는 반면,
transmute() 함수는 새로 생성된 데이터만이 저장됩니다.
# filter
data_market %>%
select(종목명, PBR) %>%
filter(PBR < 1) %>% head()
종목명 PBR
1 동원수산 0.96
2 사조산업 0.65
3 동원산업 0.75
4 신라교역 0.43
5 롯데제과 0.77
6 사조오양 0.62
data_market %>%
select(종목명, PBR) %>%
filter(PBR < 1) %>% head()
종목명 PBR
1 동원수산 0.96
2 사조산업 0.65
3 동원산업 0.75
4 신라교역 0.43
5 롯데제과 0.77
6 사조오양 0.62
filter() 함수는 조건에 해당하는 행을 찾아주는 함수입니다.
먼저 종목명과 PBR 열을 선택해준 후,
PBR이 1 미만인 데이터를 필터링 해주면
이에 해당하는 결과를 보여줍니다.
data_market %>%
select(종목명, PER, PBR, ROE) %>%
filter(PBR < 1 & PER < 20 & ROE > 0.1 ) %>% head()
종목명 PER PBR ROE
1 동원수산 6.83 0.96 0.1406
2 사조산업 5.63 0.65 0.1155
3 동원산업 4.09 0.75 0.1834
4 사조오양 3.82 0.62 0.1623
5 선진 3.66 0.84 0.2295
6 팜스코 7.00 0.90 0.1286
select(종목명, PER, PBR, ROE) %>%
filter(PBR < 1 & PER < 20 & ROE > 0.1 ) %>% head()
종목명 PER PBR ROE
1 동원수산 6.83 0.96 0.1406
2 사조산업 5.63 0.65 0.1155
3 동원산업 4.09 0.75 0.1834
4 사조오양 3.82 0.62 0.1623
5 선진 3.66 0.84 0.2295
6 팜스코 7.00 0.90 0.1286
조건을 여러개 입력할 수도 있습니다.
PBR이 1 미만,
PER가 20 미만,
ROE가 10% 이상을 조건으로 주면
이에 해당하는 종목들을 보여줍니다.
data_market %>%
filter(substr(종목명, nchar(종목명), nchar(종목명)) %in% c('우') |
substr(종목명, nchar(종목명) - 1, nchar(종목명)) %in% c('우B', '우C') |
grepl('스팩', 종목명)) %>%
select(종목명) %>% c()
$종목명
[1] "크라운제과우" "삼양사우" "하이트진로2우B" "대한제당3우B"
[5] "CJ씨푸드1우" "CJ제일제당 우" "롯데칠성우" "서울식품우"
[9] "남양유업우" "대한제당우" "대상우" "신원우"
[13] "BYC우" "깨끗한나라우" "SK케미칼우" "한화3우B"
[17] "동원시스템즈우" "코오롱인더우" "LG하우시스우" "노루페인트우"
[21] "아모레퍼시픽우" "LG화학우" "LG생활건강우" "금호석유우"
[25] "S-Oil우" "한화케미칼우" "덕성우" "NPC우"
[29] "넥센타이어1우B" "한화우" "신풍제약우" "일양약품우"
[33] "JW중외제약2우B" "JW중외제약우" "유유제약2우B" "유유제약1우"
[37] "유한양행우" "동양3우B" "동양2우B" "동양우"
[41] "성신양회3우B" "성신양회2우B" "성신양회우" "쌍용양회우"
[45] "금강공업우" "동부제철우" "남선알미우" "현대비앤지스틸우"
[49] "계양전기우" "대덕전자1우" "코리아써키트2우B" "DB하이텍1우"
[53] "LG전자우" "성문전자우" "삼성전기우" "코리아써우"
[57] "삼성SDI우" "대원전선우" "삼성전자우" "삼성중공우"
[61] "현대차3우B" "현대차2우B" "현대차우" "태양금속우"
[65] "삼성물산우B" "호텔신라우" "포스코대우" "SK네트웍스우"
[69] "진흥기업2우B" "금호산업우" "진흥기업우B" "태영건설우"
[73] "동부건설우" "코오롱글로벌우" "현대건설우" "대림산업우"
[77] "세방우" "대한항공우" "SK디스커버리우" "미래에셋대우2우B"
[81] "롯데지주우" "한진칼우" "SK이노베이션우" "대상홀딩스우"
[85] "GS우" "SK우" "크라운해태홀딩스우" "넥센우"
[89] "LG우" "아모레G우" "코오롱우" "CJ우"
[93] "노루홀딩스우" "하이트진로홀딩스우" "삼양홀딩스우" "대신증권2우B"
[97] "한국금융지주우" "미래에셋대우우" "미래에셋대우" "NH투자증권우"
[101] "대신증권우" "한화투자증권우" "유안타증권우" "유화증권우"
[105] "한양증권우" "신영증권우" "SK증권우" "부국증권우"
[109] "삼성화재우" "흥국화재2우B" "흥국화재우" "두산2우B"
[113] "두산우" "대교우B" "녹십자홀딩스2우" "소프트센우"
[117] "키움제5호스팩" "엔에이치스팩13호" "미래에셋대우스팩2호" "신영스팩4호"
[121] "한국제8호스팩" "삼성머스트스팩3호" "대신밸런스제6호스팩" "SK4호스팩"
[125] "교보8호스팩" "하나머스트제6호스팩" "DB금융스팩6호" "IBKS제10호스팩"
[129] "삼성스팩2호" "대신밸런스제5호스팩" "IBKS제9호스팩" "하나금융11호스팩"
[133] "한국제7호스팩" "유안타제3호스팩" "대신밸런스제3호스팩" "한국제6호스팩"
[137] "동부스팩5호" "한화에이스스팩4호" "IBKS제7호스팩" "신한제4호스팩"
[141] "하나금융10호스팩" "엔에이치스팩12호" "한국제5호스팩" "하나금융9호스팩"
[145] "교보7호스팩" "한화수성스팩" "IBKS제6호스팩" "미래에셋대우스팩1호"
[149] "한화에이스스팩3호" "엔에이치스팩10호" "케이비제11호스팩" "대신밸런스제4호스팩"
[153] "신한제3호스팩" "엔에이치스팩11호" "IBKS제5호스팩" "SK3호스팩"
[157] "케이비제10호스팩" "한국4호스팩" "미래에셋제5호스팩" "연우"
[161] "대호피앤씨우" "루트로닉3우C"
filter(substr(종목명, nchar(종목명), nchar(종목명)) %in% c('우') |
substr(종목명, nchar(종목명) - 1, nchar(종목명)) %in% c('우B', '우C') |
grepl('스팩', 종목명)) %>%
select(종목명) %>% c()
$종목명
[1] "크라운제과우" "삼양사우" "하이트진로2우B" "대한제당3우B"
[5] "CJ씨푸드1우" "CJ제일제당 우" "롯데칠성우" "서울식품우"
[9] "남양유업우" "대한제당우" "대상우" "신원우"
[13] "BYC우" "깨끗한나라우" "SK케미칼우" "한화3우B"
[17] "동원시스템즈우" "코오롱인더우" "LG하우시스우" "노루페인트우"
[21] "아모레퍼시픽우" "LG화학우" "LG생활건강우" "금호석유우"
[25] "S-Oil우" "한화케미칼우" "덕성우" "NPC우"
[29] "넥센타이어1우B" "한화우" "신풍제약우" "일양약품우"
[33] "JW중외제약2우B" "JW중외제약우" "유유제약2우B" "유유제약1우"
[37] "유한양행우" "동양3우B" "동양2우B" "동양우"
[41] "성신양회3우B" "성신양회2우B" "성신양회우" "쌍용양회우"
[45] "금강공업우" "동부제철우" "남선알미우" "현대비앤지스틸우"
[49] "계양전기우" "대덕전자1우" "코리아써키트2우B" "DB하이텍1우"
[53] "LG전자우" "성문전자우" "삼성전기우" "코리아써우"
[57] "삼성SDI우" "대원전선우" "삼성전자우" "삼성중공우"
[61] "현대차3우B" "현대차2우B" "현대차우" "태양금속우"
[65] "삼성물산우B" "호텔신라우" "포스코대우" "SK네트웍스우"
[69] "진흥기업2우B" "금호산업우" "진흥기업우B" "태영건설우"
[73] "동부건설우" "코오롱글로벌우" "현대건설우" "대림산업우"
[77] "세방우" "대한항공우" "SK디스커버리우" "미래에셋대우2우B"
[81] "롯데지주우" "한진칼우" "SK이노베이션우" "대상홀딩스우"
[85] "GS우" "SK우" "크라운해태홀딩스우" "넥센우"
[89] "LG우" "아모레G우" "코오롱우" "CJ우"
[93] "노루홀딩스우" "하이트진로홀딩스우" "삼양홀딩스우" "대신증권2우B"
[97] "한국금융지주우" "미래에셋대우우" "미래에셋대우" "NH투자증권우"
[101] "대신증권우" "한화투자증권우" "유안타증권우" "유화증권우"
[105] "한양증권우" "신영증권우" "SK증권우" "부국증권우"
[109] "삼성화재우" "흥국화재2우B" "흥국화재우" "두산2우B"
[113] "두산우" "대교우B" "녹십자홀딩스2우" "소프트센우"
[117] "키움제5호스팩" "엔에이치스팩13호" "미래에셋대우스팩2호" "신영스팩4호"
[121] "한국제8호스팩" "삼성머스트스팩3호" "대신밸런스제6호스팩" "SK4호스팩"
[125] "교보8호스팩" "하나머스트제6호스팩" "DB금융스팩6호" "IBKS제10호스팩"
[129] "삼성스팩2호" "대신밸런스제5호스팩" "IBKS제9호스팩" "하나금융11호스팩"
[133] "한국제7호스팩" "유안타제3호스팩" "대신밸런스제3호스팩" "한국제6호스팩"
[137] "동부스팩5호" "한화에이스스팩4호" "IBKS제7호스팩" "신한제4호스팩"
[141] "하나금융10호스팩" "엔에이치스팩12호" "한국제5호스팩" "하나금융9호스팩"
[145] "교보7호스팩" "한화수성스팩" "IBKS제6호스팩" "미래에셋대우스팩1호"
[149] "한화에이스스팩3호" "엔에이치스팩10호" "케이비제11호스팩" "대신밸런스제4호스팩"
[153] "신한제3호스팩" "엔에이치스팩11호" "IBKS제5호스팩" "SK3호스팩"
[157] "케이비제10호스팩" "한국4호스팩" "미래에셋제5호스팩" "연우"
[161] "대호피앤씨우" "루트로닉3우C"
filter() 함수를 이용하여
우선주 및 스팩 종목을 제거할 수 있습니다.
1. 종목명 마지막 글자가 '우'
2. 종목명 마지막 글자가 '우B' 나 '우C'
3. 종목명에 '스팩'이 들어가는
종목들을 찾아주면 위와 같습니다.
위의 조건에 해당하지 않은 종목을 찾기 위해,
not(A or B), 즉 not A and not B를 응용하면 다음과 같습니다.
data_market = data_market %>%
filter(!substr(종목명, nchar(종목명), nchar(종목명)) %in% c('우') &
!substr(종목명, nchar(종목명) - 1, nchar(종목명)) %in% c('우B', '우C') &
!grepl('스팩', 종목명))
filter(!substr(종목명, nchar(종목명), nchar(종목명)) %in% c('우') &
!substr(종목명, nchar(종목명) - 1, nchar(종목명)) %in% c('우B', '우C') &
!grepl('스팩', 종목명))
nrow(data_market)
[1] 2025
[1] 2025
종목수가 기존 2187 개에서 162개가 줄어든 2025개가 되었습니다.
# summarize
market_PBR =
data_market %>%
summarize(PBR.med = median(PBR, na.rm = TRUE)) %>%
as.numeric()
market_PBR =
data_market %>%
summarize(PBR.med = median(PBR, na.rm = TRUE)) %>%
as.numeric()
summarize() 함수는 원하는 통계값을 계산해주는 함수입니다.
PBR.med 변수에 PBR 열의 median 값들을 저장해주도록 합니다.
market_PBR
[1] 1.08
[1] 1.08
국내 종목들의 평균(median) PBR은 1.08 정도임이 확인됩니다.
# arrange
data_market %>%
arrange(PBR) %>%
select(PBR) %>%
head(5)
PBR
1 0.08
2 0.10
3 0.12
4 0.14
5 0.16
data_market %>%
arrange(desc(ROE)) %>%
select(ROE) %>%
head(5)
ROE
1 9.4286
2 6.0000
3 5.2500
4 3.5263
5 3.4444
data_market %>%
arrange(PBR) %>%
select(PBR) %>%
head(5)
PBR
1 0.08
2 0.10
3 0.12
4 0.14
5 0.16
data_market %>%
arrange(desc(ROE)) %>%
select(ROE) %>%
head(5)
ROE
1 9.4286
2 6.0000
3 5.2500
4 3.5263
5 3.4444
arrange() 함수는 데이터를 정렬해주는 역할을 합니다.
기본적으로는 오름차순으로 데이터를 정리하며,
desc()함수를 사용할 경우 내림차순으로 정리합니다.
# mutate + arrange
data_market = data_market %>%
mutate(시총비중 = 시가총액 / sum(시가총액)) %>%
arrange(desc(시가총액))
mutate(시총비중 = 시가총액 / sum(시가총액)) %>%
arrange(desc(시가총액))
시가총액 비중을 구한 후, 비중 순으로 정리해보도록 하겠습니다.
먼저 mutate() 함수를 통해 시총비중을 구하며,
arrange() 내에 desc() 함수를 통해
시총 비중이 큰 순서부터 내림차순을 정리해주도록 합니다.
data_market %>%
select(종목명, 시가총액) %>%
head(10)
종목명 시가총액
1 삼성전자 2.310306e+14
2 SK하이닉스 4.404414e+13
3 셀트리온 2.791399e+13
4 삼성바이오로직스 2.557277e+13
5 현대차 2.531968e+13
6 LG화학 2.449554e+13
7 SK텔레콤 2.176097e+13
8 한국전력 2.124901e+13
9 POSCO 2.118640e+13
10 NAVER 2.010723e+13
select(종목명, 시가총액) %>%
head(10)
종목명 시가총액
1 삼성전자 2.310306e+14
2 SK하이닉스 4.404414e+13
3 셀트리온 2.791399e+13
4 삼성바이오로직스 2.557277e+13
5 현대차 2.531968e+13
6 LG화학 2.449554e+13
7 SK텔레콤 2.176097e+13
8 한국전력 2.124901e+13
9 POSCO 2.118640e+13
10 NAVER 2.010723e+13
종목명과 시가총액을 뽑아 상위 10종목을 확인해보면
시총 순서대로 정리되었음이 확인됩니다.
data_market %>%
group_by(시장구분) %>% str()
group_by(시장구분) %>% str()
Classes ‘grouped_df’, ‘tbl_df’, ‘tbl’ and 'data.frame': 2025 obs. of 13 variables:
$ 시장구분 : chr "코스피" "코스피" "코스피" "코스피" ...
$ 종목코드 : chr "005930" "000660" "068270" "207940" ...
$ 종목명 : chr "삼성전자" "SK하이닉스" "셀트리온" "삼성바이오로직스" ...
$ 산업분류 : chr "전기전자" "전기전자" "의약품" "의약품" ...
$ 시가총액 : num 2.31e+14 4.40e+13 2.79e+13 2.56e+13 2.53e+13 ...
$ EPS : int 5997 15073 3150 NA 14993 25367 36582 2023 34464 5320 ...
$ PER : num 6.45 4.01 70.63 NA 7.9 ...
$ BPS : int 28126 46449 19041 60099 242062 206544 220967 111660 501600 28936 ...
$ PBR : num 1.38 1.3 11.69 6.43 0.49 ...
$ 배당수익률: num 2.2 1.65 0 0 3.38 1.73 3.71 2.39 3.29 0.24 ...
$ ROE : num 0.214 0.324 0.166 NA 0.062 ...
$ style : chr "growth" "growth" "growth" "growth" ...
$ 시총비중 : num 0.1526 0.0291 0.0184 0.0169 0.0167 ...
- attr(*, "vars")= chr "시장구분"
- attr(*, "drop")= logi TRUE
- attr(*, "indices")=List of 2
..$ : int 27 51 62 63 67 80 85 91 95 109 ...
..$ : int 0 1 2 3 4 5 6 7 8 9 ...
- attr(*, "group_sizes")= int 1259 766
- attr(*, "biggest_group_size")= int 1259
- attr(*, "labels")='data.frame': 2 obs. of 1 variable:
..$ 시장구분: chr "코스닥" "코스피"
..- attr(*, "vars")= chr "시장구분"
..- attr(*, "drop")= logi TRUE
$ 시장구분 : chr "코스피" "코스피" "코스피" "코스피" ...
$ 종목코드 : chr "005930" "000660" "068270" "207940" ...
$ 종목명 : chr "삼성전자" "SK하이닉스" "셀트리온" "삼성바이오로직스" ...
$ 산업분류 : chr "전기전자" "전기전자" "의약품" "의약품" ...
$ 시가총액 : num 2.31e+14 4.40e+13 2.79e+13 2.56e+13 2.53e+13 ...
$ EPS : int 5997 15073 3150 NA 14993 25367 36582 2023 34464 5320 ...
$ PER : num 6.45 4.01 70.63 NA 7.9 ...
$ BPS : int 28126 46449 19041 60099 242062 206544 220967 111660 501600 28936 ...
$ PBR : num 1.38 1.3 11.69 6.43 0.49 ...
$ 배당수익률: num 2.2 1.65 0 0 3.38 1.73 3.71 2.39 3.29 0.24 ...
$ ROE : num 0.214 0.324 0.166 NA 0.062 ...
$ style : chr "growth" "growth" "growth" "growth" ...
$ 시총비중 : num 0.1526 0.0291 0.0184 0.0169 0.0167 ...
- attr(*, "vars")= chr "시장구분"
- attr(*, "drop")= logi TRUE
- attr(*, "indices")=List of 2
..$ : int 27 51 62 63 67 80 85 91 95 109 ...
..$ : int 0 1 2 3 4 5 6 7 8 9 ...
- attr(*, "group_sizes")= int 1259 766
- attr(*, "biggest_group_size")= int 1259
- attr(*, "labels")='data.frame': 2 obs. of 1 variable:
..$ 시장구분: chr "코스닥" "코스피"
..- attr(*, "vars")= chr "시장구분"
..- attr(*, "drop")= logi TRUE
group_by() 함수는 원하는 기준대로 데이터를 묶는 함수로써,
함수를 사용하는 것만으로는 변화를 알아차리기 힘듭니다.
data_market %>%
group_by(산업분류) %>%
summarize(n())
group_by(산업분류) %>%
summarize(n())
# A tibble: 45 x 2
산업분류 `n()`
<chr> <int>
1 IT H/W 296
2 IT S/W & SVC 138
3 건설 24
4 건설업 31
5 광업 3
6 금속 62
7 금융 43
8 금융업 112
9 기계 41
10 기계·장비 85
# ... with 35 more rows
산업분류 `n()`
<chr> <int>
1 IT H/W 296
2 IT S/W & SVC 138
3 건설 24
4 건설업 31
5 광업 3
6 금속 62
7 금융 43
8 금융업 112
9 기계 41
10 기계·장비 85
# ... with 35 more rows
group_by() 함수를 이용하여 산업분류 별로 그룹화 한 후,
summarize() 함수 내에 n() 함수를 사용하여
각 섹터 별 종목수를 구할 수 있습니다.
data_market %>%
group_by(시장구분) %>%
summarize(PBR.mkt = median(PBR, na.rm = TRUE))
# A tibble: 2 x 2
시장구분 PBR.mkt
<chr>
1 코스닥 1.36
2 코스피 0.8
data_market %>%
group_by(산업분류) %>%
summarize(PBR.sector = median(PBR, na.rm = TRUE))
# A tibble: 45 x 2
산업분류 PBR.sector
<chr>
1 IT H/W 1.17
2 IT S/W & SVC 1.83
3 건설 0.84
4 건설업 0.8
5 광업 1.88
6 금속 0.79
7 금융 0.88
8 금융업 0.56
9 기계 0.9
10 기계·장비 1.32
# ... with 35 more rows
group_by(시장구분) %>%
summarize(PBR.mkt = median(PBR, na.rm = TRUE))
# A tibble: 2 x 2
시장구분 PBR.mkt
<chr>
1 코스닥 1.36
2 코스피 0.8
data_market %>%
group_by(산업분류) %>%
summarize(PBR.sector = median(PBR, na.rm = TRUE))
# A tibble: 45 x 2
산업분류 PBR.sector
<chr>
1 IT H/W 1.17
2 IT S/W & SVC 1.83
3 건설 0.84
4 건설업 0.8
5 광업 1.88
6 금속 0.79
7 금융 0.88
8 금융업 0.56
9 기계 0.9
10 기계·장비 1.32
# ... with 35 more rows
각 시장별과 섹터별 PBR의 median 값을 구하는 것도 가능합니다.
data_market %>%
group_by(시장구분, 산업분류) %>%
summarize(cap = sum(시가총액)) %>%
mutate(cap = cap / sum(data_market$시가총액),
cap = round(cap, 5)) %>%
arrange(desc(cap))
# A tibble: 47 x 3
# Groups: 시장구분 [2]
시장구분 산업분류 cap
<chr> <chr>
1 코스피 전기전자 0.218
2 코스피 금융업 0.167
3 코스피 화학 0.0836
4 코스피 서비스업 0.0727
5 코스피 운수장비 0.0609
6 코스피 의약품 0.0536
7 코스피 유통업 0.0441
8 코스피 철강금속 0.0286
9 코스닥 IT H/W 0.0257
10 코스피 통신업 0.0249
# ... with 37 more rows
group_by(시장구분, 산업분류) %>%
summarize(cap = sum(시가총액)) %>%
mutate(cap = cap / sum(data_market$시가총액),
cap = round(cap, 5)) %>%
arrange(desc(cap))
# A tibble: 47 x 3
# Groups: 시장구분 [2]
시장구분 산업분류 cap
<chr> <chr>
1 코스피 전기전자 0.218
2 코스피 금융업 0.167
3 코스피 화학 0.0836
4 코스피 서비스업 0.0727
5 코스피 운수장비 0.0609
6 코스피 의약품 0.0536
7 코스피 유통업 0.0441
8 코스피 철강금속 0.0286
9 코스닥 IT H/W 0.0257
10 코스피 통신업 0.0249
# ... with 37 more rows
위에서 배웠던 함수들을 활용하여
각 섹터 별 시가총액 비중을 구하도록 하겠습니다.
먼저 group_by() 함수를 이용해
시장과 섹터별로 그룹화를 해줍니다.
그 후, summarize() 함수를 이용하여
각 그룹 별 시가총액의 합을 구해줍니다.
mutate() 함수를 통해 그룹별 시총 / 전체 시총을 계산하여
이를 cap에 저장해 주며,
round() 함수를 이용해 반올림 해주도록 합니다.
이를 cap에 저장해 주며,
round() 함수를 이용해 반올림 해주도록 합니다.
마지막으로 arrange()와 desc() 함수를 이용하여
내림차순으로 정리를 해주면
각 그룹별 시가총액이 높은 순부터 정리됩니다.
결과를 확인해보면 삼성전자가 포함된 코스피-전기전자 비중이
단연 높음이 확인됩니다.
내림차순으로 정리를 해주면
각 그룹별 시가총액이 높은 순부터 정리됩니다.
결과를 확인해보면 삼성전자가 포함된 코스피-전기전자 비중이
단연 높음이 확인됩니다.
크롤링한 데이터를 바탕으로 데이터 정리 및 변형에 대해
알아보았습니다.
다음에는 마지막으로 시각화 및 모델링, 즉
퀀트를 이용한 종목선택에 대해 알아보도록 하겠습니다.
댓글 없음:
댓글 쓰기