더북(TheBook)

아이리스 데이터로부터 로지스틱 회귀 모델을 작성해보자. 예측값이 두 분류여야 하므로 virginica, versicolor라는 두 분류만 남긴다.

> d <- subset(iris, Species == "virginica" | Species == "versicolor")
> str(d)
'data.frame'   : 100 obs. of 5 variables:
 $ Sepal.Length: num  7 6.4 6.9 5.5 6.5 5.7 6.3 4.9 6.6 5.2 ...
 $ Sepal.Width : num  3.2 3.2 3.1 2.3 2.8 2.8 3.3 2.4 2.9 2.7 ...
 $ Petal.Length: num  4.7 4.5 4.9 4 4.6 4.5 4.7 3.3 4.6 3.9 ...
 $ Petal.Width : num  1.4 1.5 1.5 1.3 1.5 1.3 1.6 1 1.3 1.4 ...
 $ Species     : Factor w/ 3 levels "setosa","versicolor",..: 2 2 2 2 2 2 2 2 2 2 ...

위 결과에서 보듯이 subset( )을 적용하고 나면 데이터는 virginica, versicolor만 남도록 잘 걸러지지만 Species는 여전히 3개 레벨을 가질 수 있는 팩터다. 따라서 Species 컬럼에 새로 범주 레벨이 정해지도록 다시 한 번 factor( ) 함수를 거치게 한다.

> d$Species <- factor(d$Species)
> str(d)
'data.frame'   : 100 obs. of 5 variables:
 $ Sepal.Length: num  7 6.4 6.9 5.5 6.5 5.7 6.3 4.9 6.6 5.2 ...
 $ Sepal.Width : num  3.2 3.2 3.1 2.3 2.8 2.8 3.3 2.4 2.9 2.7 ...
 $ Petal.Length: num  4.7 4.5 4.9 4 4.6 4.5 4.7 3.3 4.6 3.9 ...
 $ Petal.Width : num  1.4 1.5 1.5 1.3 1.5 1.3 1.6 1 1.3 1.4 ...
 $ Species     : Factor w/ 2 levels "versicolor","virginica": 1 1 1 1 1 1 1 1 1 1 ...

그 결과 Species가 2개의 레벨로 잘 정리되었다. 모델은 glm( ) 함수에 family=“binomial”을 지정해 선형 회귀 모델에서 한 것처럼 만든다.

> (m <- glm(Species ~ ., data=d, family="binomial"))

Call: glm(formula = Species ~ ., family = "binomial", data = d)
Coefficients:
(Intercept)  Sepal.Length  Sepal.Width  Petal.Length  Petal.Width
    -42.638        -2.465       -6.681         9.429       18.286

Degrees of Freedom: 99 Total (i.e. Null); 95 Residual
Null Deviance:       138.6
Residual Deviance: 11.9 AIC: 21.9

모델이 적합된 값은 fitted( )를 사용해 알 수 있다.

> fitted(m)[c(1:5, 51:55)]
          51           52           53           54           55          101
1.171672e-05 4.856237e-05 1.198626e-03 4.220049e-05 1.408470e-03 1.000000e+00
         102          103          104          105
9.996139e-01 9.999990e-01 9.997188e-01 9.999999e-01

로지스틱 회귀 모델은 0 또는 1로 값을 예측하는 모델이다. 위 결과를 보면 virginica에 해당하는 1:5행은 0, versicolor에 해당하는 51:55행은 1로 잘 예측된 것을 알 수 있다.

예측값이 0.5 이하인 경우 virginica, 0.5보다 큰 경우 versicolor라고 하고, 이를 실제 데이터와 비교해보자. 아래 코드에서 as.numeric( )은 팩터를 숫자를 저장한 벡터로 변환한다. R에서 팩터의 레벨은 1, 2, 3, …처럼 1부터 값이 부여되기 시작한다. 따라서 as.numeric( )으로 팩터를 변환한 뒤 1을 빼주어야 로지스틱 회귀 분석의 결과에 맞게 0 또는 1의 값을 갖게 된다.

> f <- fitted(m)
> as.numeric(d$Species)
 [1] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
[37] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
[73] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
> ifelse(f > .5, 1, 0) == as.numeric(d$Species) - 1
  51    52    53    54    55    56    57    58    59    60    61    62    63
TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
  64    65    66    67    68    69    70    71    72    73    74    75    76
TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
   77    78    79    80    81    82    83   84    85    86    87    88    89
TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE
  90    91    92    93    94    95    96    97    98    99   100   101   102
TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
 103   104   105   106   107   108   109   110   111   112   113   114   115
TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
 116   117   118   119   120   121   122   123   124   125   126   127   128
TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
 129   130   131   132   133   134   135   136   137   138   139   140   141
TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
 142   143   144   145   146   147   148   149   150
TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE

예측된 분류와 실제 분류가 일치한 경우 TRUE, 일치하지 않은 경우 FALSE가 표시되었다. 눈으로 확인하기에 불편한 점이 있으므로 위 결과의 TRUE 개수를 코드를 사용해 세어보자.

> is_correct <- (ifelse(f > .5, 1, 0) == as.numeric(d$Species) - 1)
> sum(is_correct)
[1] 98
> sum(is_correct) / NROW(is_correct)
[1] 0.98

sum( ) 함수는 TRUE를 1, FALSE를 0으로 취급하므로 sum( )은 전체에서 TRUE의 개수를 반환한다. NROW( )는 데이터의 전체 개수를 반환한다. 따라서 sum(is_correct) / NROW(is_correct)는 정확한 분류의 비율이 되며 이 경우에는 98%였다.

새로운 데이터에 대한 예측은 predict( ) 함수를 사용한다. 이 예에서는 설명의 편의를 위해 미리 테스트할 데이터를 제외시켜놓지 않았기 때문에 모델을 만들 때 사용한 데이터를 재사용한 예를 보인다. 그러나 실제 모델을 만들 때는 9장에서 설명한 것처럼 훈련 데이터와 테스트 데이터가 분리되어 있도록 하는 것이 올바른 방법이다.

type을 response로 지정하고 예측을 수행하면 0 ~ 1 사이의 확률을 구해준다.

> predict(m, newdata=d[c(1, 10, 55),], type="response")
           1           10           55
2.220446e-16 2.220446e-16 1.000000e+00

predict( )는 일반 함수Generic Function므로 주어진 인자에 따라 다른 메서드를 호출한다. predict( ) 함수가 호출하는 메서드의 목록은 methods(“predict”)로 볼 수 있으며, glm의 경우 predict.glm( )이 자동으로 호출된다.

로지스틱 회귀 모델에는 이외에도 8장의 선형 회귀에서 살펴본 다양한 함수가 적용 가능하니 확인해보기 바란다.

신간 소식 구독하기
뉴스레터에 가입하시고 이메일로 신간 소식을 받아 보세요.