R 회귀분석에서 not defined because of singularities 해결
당신이 통계나 수학 전공자라면 원인을 대강 파악하고 직면한 문제를 해결하는 것에서 그치지 않고, 수리적인 증명까지 이해하는 것을 강하게 권한다.
에러
진단
Coefficients: (1 not defined because of singularities)
Estimate Std. Error t value Pr(>|t|)
(Intercept) 0.5723 0.1064 5.381 4.98e-05 ***
최고기온 -0.3528 0.1490 -2.368 0.030 *
최저기온 0.2982 0.1955 1.525 0.146
일교차 NA NA NA NA
R에서 회귀분석을 할 때 회귀계수에서 not defined because of singularities
라는 메세지와 함께 계수 추정이 되지 않는 문제다.
원인
- 디자인 매트릭스 $X$ 가 풀랭크를 가지지 않아 최소제곱해를 구할 때 $X^{T} X$ 의 역행렬이 존재하지 않아서 그렇다. 그래서 에러메세지에도 ‘singularities’가 언급되는 것이다.
- 쉽게 말해, 다중공선성이 있는 것이다.
- 더 쉽게 말해, 독립변수끼리 독립이 아닌 것이다.
$X^T X$ 의 역행렬이 존재하는 필요충분조건: $m \ge n$ 일 때, 행렬 $X \in \mathbb{R}^{m \times n}$ 의 역행렬이 존재하는 필요충분조건은 $X$ 가 풀 랭크를 가지는 것이다. $$ \exists \left( X^{T} X \right)^{-1} \iff \text{rank} X = n $$
이는 사실 교과서적으로 초보들이 저지르는 실수들로, 예시에서 나오듯 나름 머리를 써서 파생변수를 만들다가 처음 보게 되는 문제다. 이유를 듣고보면 수학적으로 너무 당연하지만 통계적인 직관이 이제 막 자리잡히는 학생들은 당연히 할 수 있는 실수다.
당신만이 겪은 실수가 아니라 이 사람들도 겪었고, 나도 겪었다. 중요한 것은 이제 이 문제를 해결한 뒤 수학―특히 행렬대수 및 선형대수가 중요하다는 팩트에 공감하고 철저한 이론 공부의 동기로 삼는 것이다.
전형적인 상황은 다음과 같은 예시를 상상해볼 수 있다.
> data = as.data.frame(matrix(runif(60),20,3))
> names(data) <- c("감기확률", "최고기온", "최저기온")
> lm(감기확률 ~ 최고기온 + 최저기온, data = data)
Call:
lm(formula = 감기확률 ~ 최고기온 + 최저기온, data = data)
Coefficients:
(Intercept) 최고기온 최저기온
0.5723 -0.3528 0.2982
가령 감기가 걸릴 확률을 설명할 데이터로써 기온에 관련된 데이터가 있다고 하자.
> data$일교차 <- (data$최고기온 - data$최저기온)
> out <- lm(감기확률 ~ 최고기온 + 최저기온 + 일교차, data = data)
그러나 상식으로써 널리 알려져있듯 감기가 유행하는 시기는 단순히 기온이 낮은 것보다 일교차가 클 때와 더 관련이 있다. 그래서 위와 같이 파생변수로써 일교차를 넣어주면 다음의 결과를 얻는다.
> summary(out)
Call:
lm(formula = 감기확률 ~ 최고기온 + 최저기온 + 일교차, data = data)
Residuals:
Min 1Q Median 3Q Max
-0.41562 -0.03316 0.00506 0.10834 0.35714
Coefficients: (1 not defined because of singularities)
Estimate Std. Error t value Pr(>|t|)
(Intercept) 0.5723 0.1064 5.381 4.98e-05 ***
최고기온 -0.3528 0.1490 -2.368 0.030 *
최저기온 0.2982 0.1955 1.525 0.146
일교차 NA NA NA NA
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 0.1988 on 17 degrees of freedom
Multiple R-squared: 0.2779, Adjusted R-squared: 0.193
F-statistic: 3.271 on 2 and 17 DF, p-value: 0.06281
해결법
가능하면 새로운 파생변수를 빼고, 정 남기고 싶다면 그 파생변수를 만들 때 사용했던 원래의 독립변수를 제거해야한다. 직관적으로 말이 된다면 비선형 함수를 취하는 등 파생변수를 만드는 방법 자체를 바꿔보는 것도 가능하다.
코드
data = as.data.frame(matrix(runif(60),20,3))
names(data) <- c("감기확률", "최고기온", "최저기온")
lm(감기확률 ~ 최고기온 + 최저기온, data = data)
data$일교차 <- (data$최고기온 - data$최저기온)
out <- lm(감기확률 ~ 최고기온 + 최저기온 + 일교차, data = data)
summary(out)
환경
- OS: Windows 11
- julia: v4.1.1