ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [4장]ISL with R : 로지스틱회귀 LDA QDA 실습 in R(재무지표를 통한 상승주 분류)
    기초지식/ISL with R(통계학) 2024. 3. 14. 07:59
    반응형

    재무지표를 통한 부도 예측

    로지스틱회귀, LDA, QDA라는 툴을 투자에 적용하면 내가 찍은 주식이 오를지 떨어질지 예측하는데 쓸 수 있다. ISL with R 4장에서 살펴본 로지스틱회귀, LDA, QDA 모두 다양한 투입변수를 통해서 어떤 결과로 분류할 수 있는지 예측하는 모델이라는 점에서 다양한 투입변수를 통해서 상승할 종목인지, 하락할 종목인지 판단할 수 있는 것이다. 이때 유용한 투입변수가 될 수 있는 것이 각종 재무지표이다.( 주요 재무비율 통한 기업 분석 참조) 이번 분석에서는 2023년 3분기 매출원가율, 부채비율, 순이익증가율 이라는 세 가지 요소가 2024년 2월 말 주식의 상승 여부와 어떻게 작용하는지를 볼 것이다. 회사별 재무비율은 금융감독원에서 제공하는 전자공시시스템인 다트(https://dart.fss.or.kr/)를 통해서 아래와 같이 회사를 검색해서 확인할 수 있다.

    그림1: 기업재무비율 조회

    주가가 상승했는지 하락했는지에 대한 정보는 아래와 같이 KRX정보데이터시스템(http://data.krx.co.kr/contents/MDC/MAIN/main/index.cmd)을 통해서 확인할 수 있다. 코스피 전 종목에 대해서 2023년 3분기 종가와 2024년 2월말 종가를 아래 그림 2와 같이 조회해서 엑셀파일로 만들었다.

    그림2: 코스피종가정보 조회
    kospi_20230927.xlsx
    0.10MB
    kospi_20240229.xlsx
    0.10MB

     

    2023년 3분기 코스피지수대비 2024년 2월 코스피 지수가 7% 정도 상승했다는 점을 감안해서 10% 상승한 종목을 A종목, 그 외의 종목을 B종목으로 분류한다면, 위와 같이 조회한 자료를 바탕으로  각 기업의 2023년 3분기 재무비율 자료를 붙인 데이터를 아래 첨부파일과 같이 만들어 낼 수 있다.(편의상 매출원가율, 부채비율, 순이익증가율 중 하나라도 안 나왔다면 분석 대상에서 제외했다.)

    output.xlsx
    0.24MB

     

    Logistic 회귀분석

    output 엑셀파일 입수

    R의 readxl 패키지를 사용하면 쉽게 엑셀데이터를 읽어올 수 있는데, 아래와 같이 먼저 엑셀 패키지를 설치한 뒤

    install.packages("readxl")

    read_excel 함수를 사용해서 data 라는 변수에 위의 output 파일의 데이터를 저장할 수 있다.

    data <- read_excel("엑셀파일의 경로")

    R Studio 로 작업한다면 변수에 저장된 데이터가 아래 그림 3과 같이 우측 상단 칸에 표시가 되고, 이 변수를 클릭하면 좌측상단칸과 같이 변수에 저장된 실제 데이터를 확인할 수 있다.

    그림3: output.xlsx 데이터 입수

    data의 컬럼은 names(data)라는 명령어로 살펴볼 수 있는데 아래와 같은 결과가 나타날 것이다. (한 줄짜리 코드는 좌측하단 콘솔창에 바로 입력하면 된다.)

    > names(data)
     [1] "종목코드"       "종목명"         "종가_x"         "종가_y"         "비율"          
     [6] "분류"           "기관코드"       "매출원가율"     "매출총이익률"   "금융비용부담률"
    [11] "부채비율"       "총자산증가율"   "순이익증가율"

    그림 3에서 확인할 수 있는 것처럼 output.xlsx 데이터는 2023년 3분기 대비 2024년 2월 10% 이상 상승한 종목을 A종목, 그 외의 종목을 B종목으로 설정했다. 이를 코드화하기 위해서 아래와 같이 data에 "분류코드"컬럼을 추가하고 "분류"컬럼 데이터가 "A종목"이면 1 그 외의 경우 0 값을 부여해서 최종 데이터를 완성한다. 

    data$분류코드 <- ifelse(data$분류 == "A종목", 1, 0)

    Logistic 회귀분석 결과 분석하기

    데이터만 제대로 입수되었으면 분석 모델을 만드는 것은 간단하다. 아래와 같이 glm 이라는 R의 함수를 사용해서 glm 클래스의 fits라는 객체를 만들면 된다. 

    #  라이브러리 참조
    > library(MASS)
    # ex)
    # glm.fits = glm(결과값(종속변수)~투입값(독립변수), data=데이터명, family=로지스틱 회귀 모델에서는
    #               이항분포(binomial) 사용)
    > glm.fits = glm(분류코드~매출원가율+부채비율+순이익증가율, data=data, family=binomial)
    
    > summary(glm.fits)
    
    Call:
    glm(formula = 분류코드 ~ 매출원가율 + 부채비율 + 
        순이익증가율, family = binomial, data = data)
    
    Coefficients:
                   Estimate Std. Error z value Pr(>|z|)  
    (Intercept)  -0.3209685  0.4585976  -0.700   0.4840  
    매출원가율   -0.0106507  0.0057440  -1.854   0.0637 .
    부채비율      0.0004756  0.0003714   1.281   0.2003  
    순이익증가율  0.0003681  0.0002491   1.478   0.1394  
    ---
    Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
    
    (Dispersion parameter for binomial family taken to be 1)
    
        Null deviance: 709.75  on 637  degrees of freedom
    Residual deviance: 701.87  on 634  degrees of freedom
    AIC: 709.87
    
    Number of Fisher Scoring iterations: 4

    로지스틱 회귀분석은 결국 아래 식에서( [4장]ISL with R : 로지스틱 회귀(로지스틱 함수) 참조)

    식1: 로지스틱 회귀분석

    b0, b1 ... bp까지의 계수들을 찾아내는 하나의 과정이다. glm.fits = glm(분류코드~매출원가율+부채비율+순이익증가율, data=data, family=binomial)를 통해서 모델을 완성할 수 있고, summary(glm.fits)를 통해서 완성된 모델의 요약을 볼 수 있다. summary(glm.fits)을 통해서 알 수 있는 모델의 계수들의 값은 매출원가율: -0.0106507, 부채비율: 0.0004756, 순이익증가율: 0.0003681이다.각 계수들의 유의수준인 p 값은 각각 매출원가율: 0.0637, 부채비율: 0.2003, 순이익증가율: 0.1394을 보이는데, 보통 95% 신뢰 수준을 보통 가정한다면 유의 수준인 p는 5%보다는 낮아야 하므로 각 계수들의 신뢰 수준은 낮은 편이라고 할 수 있다. 

    Deviance 값은 관찰된 결과와 예측한 결과 간의 편차를 의미한다. Null deviance 는 아무런 입력변수 없이 절편값만 있는 상태와 실제 결과값의 차이로 계산한 편차이고, Residual deviance는 독립변수까지 모델에 넣었을 때 도출된 종속 변수와 실제 결과값의 차이로 계산한 편차이다. 모델이 유의미하려면 당연히 편차만 적용된 Null deviance 보다(여기서 709.75) 모델이 제대로 적용된 Residual deviance 값이(여기서 701.87) 더 작을 것이다.

    AIC는(Akaike Information Criterion) 모델의 적합도와 복잡성을 동시에 고려해서 나타내는 지표이다. 절대적인 값은 크게 의미가 없고 모델간 비교를 가능하게 하는데, 작을수록 모델이 더 적합하다고 한다.

    LDA(Linear Discriminant Analysis)

    그림 3의 output.xlsx 데이터는 주어진 기간동안 주가가 10% 이상 오른 A그룹 주식과 그 외의 B그룹 주식으로 나뉜다. LDA에서는 "특정 기업(매출원가율, 부채비율, 순이익증가율의 재무비율)이 주어졌을 때 A그룹에 혹은 B그룹에 속할 사후확률을 구하는 것"이 목적이다. 하지만 사후확률로 바로 접근하는 것은 어려우므로 베이즈 정리를 통해서 사후확률을 구하는 식을 "A(또는 B) 그룹이 나올 확률 * A(또는 B) 그룹 일 때 특정 기업 데이터일 확률  / 특정 기업이 A그룹 혹은 B그룹에 속할 확률"을 구하는 식으로 변형한다. 이를 바탕으로 도출한 판별함수를 통해서 특정 기업(매출원가율, 부채비율, 순이익증가율) 재무비율로 A그룹인지 B그룹인지 판별할 수 있게 되는 것이다.( [4장]ISL with R : LDA(선형판별분석) 참조)

    R 에서 LDA 자체는 아래와 같은 코드로 간단하게 수행할 수 있다. 

    #  라이브러리 참조
    > library(MASS)
    # ex)
    # lda.fits = lda(결과값(종속변수)~투입값(독립변수), data=데이터명)
    > lda.fits = lda(분류코드~매출원가율+부채비율+순이익증가율, data=data)
    > lda.fits
    
    Call:
    lda(분류코드 ~ 매출원가율 + 부채비율 + 순이익증가율, data = data)
    
    Prior probabilities of groups:
            0         1 
    0.7554859 0.2445141 
    
    Group means:
      매출원가율 부채비율 순이익증가율
    0   80.98230 110.7157    -95.23615
    1   78.28655 140.7248    -17.93937
    
    Coefficients of linear discriminants:
                          LD1
    매출원가율   -0.043845044
    부채비율      0.001516735
    순이익증가율  0.001069575

    lda.fits = lda(분류코드~매출원가율+부채비율+순이익증가율, data=data)를 통해서 lda의 결과를 lda 클래스의 fits 객체에 만들 수 있다. summary 함수는 사용하지 않고 단순히 lda.fits라는 새로운 객체를 다시 입력하면 (> lda.fits) 분석 결과를 볼 수 있다. Prior probabilities of groups는 각 그룹의 사전확률을 의미한다. 즉, 실제 데이터 상에서 A그룹(=1)은 24.45% 만큼 존재하고 B그룹(=2)은 75.55%만큼 존재한다는 의미이다. Group meanss는 각 그룹에서 매출원가율, 부채비율, 순이익증가율이라는 독립변수들의 평균을 의미한다. Group meanss 를 통해서 A그룹에 속한 기업이 매출원가는 낮고, 부채비율은 높고, 순이익증가율은 - 값이 더 작다는 것을 알 수 있다. Coefficients of linear discriminants는  [4장]ISL with R : LDA(선형판별분석) 참조에서 구해본 선형판별식의 각 계수들을 의미한다.

     

    QDA(Quadratic Discriminant Analysis)

    QDA는 LDA와 기본적으로 같은데 A그룹과 B그룹의 각 독립변수들(매출원가율, 부채비율, 순이익증가율)의 분산(엄밀하게 공분산 행렬) 이 다르다는 가정을 추가한다. 그 결과 판별함수도 비선형적인 형태를 띠게 된다.( [4장]ISL with R : QDA(이차판별분석) 참조) 아래와 같이 R을 이용한 분석을 하면, 이런 QDA의 특징으로 인해서 Coefficients of linear discriminants 값은 나오지 않는다.

    #  라이브러리 참조
    > library(MASS)
    # ex)
    # qda.fits = qda(결과값(종속변수)~투입값(독립변수), data=데이터명)
    > qda.fits = qda(분류코드~매출원가율+부채비율+순이익증가율, data=data)
    > qda.fits
    Call:
    qda(분류코드 ~ 매출원가율 + 부채비율 + 순이익증가율, 
        data = data)
    
    Prior probabilities of groups:
            0         1 
    0.7554859 0.2445141 
    
    Group means:
      매출원가율 부채비율 순이익증가율
    0   80.98230 110.7157    -95.23615
    1   78.28655 140.7248    -17.93937

    그 외에 Prior probabilities of groups, Group means 값은 데이터가 동일하기 때문에 LDA와 동일하게 나오지만 새로운 데이터로 qda.fits를 이용한 결과 예측을 수행하면, lda.fits와 다른 값을 나타낼 것이다. 

    반응형
Designed by Tistory.