열렬히.뛰기

12. 스플라인 기법

수학 & 통계 > 탐색적 자료분석 > 탐색적 자료분석 : 목차 > 12. 스플라인 기법

개요: 선형과 비선형

일반적인 모델이 다음과 같이 있다고 치자.

\begin{align*} Y_i &= f(x_i) + \epsilon_i \\ &= \beta_0 +\beta_1x_i \\ &= \beta_0 +\beta_1x_i + \beta_1x_i^2 \end{align*}

보통 그래프를 그리면 각 점을 따라서 모델이 잘 맞을 가능성이 크다.

그런데 데이터의 패턴이 이상하게 꺾인다면?

물론 비선형 모델을 도입할 수 있지만, 이거 말고도 방법이 있다.

Spline model

  • 구간을 나눠서 직선을 피팅하는 방법

  • 단점1: 미분이 안된다.

  • 단점2: 각 구간별 직선이 로컬(local) 하게 적용된다.

    즉, knot (갈라지는 점)에 따라 너무 모양이 많이 달라진다.

그래서, Spline을 강화시켜야 한다.

  • knot의 갯수를 잘 조절해야 한다.
  • 이를 smoothing scatterplot (산점도 평활화) 이라고 한다.

loess procedures

  • local regression의 약자.
  • 데이터의 어떤 점을 기준으로 그 근방의 값(local)에 대해 회귀
  • g를 추정하는 방법 : 모수와 SSE를 이용한다.
y_i = g(x_i) + \epsilon
  • 대표적인 산점도 평활화 기법.
    1. 윈도우를 이용해 구간을 설정
    2. 함수를 피팅 (윈도우가 클 수록 차수가 늘어난다)
    3. 피팅된 선들의 평균을 구한다.
    4. 평균들을 연결!

그런데 윈도우가 너무 크면, 이상한 직선이 생길 수 있다.

이를 피하는 방법 : 가중치를 적게 주면 가능하다.

점 A와 가까운 곳 : 가중치를 늘린다.

점 A와 먼 곳 : 가중치를 줄인다.

가중치 = 거리의 절댓값의 역수.

knot (x_0)을 계속 이동시켜 전체적인 curve를 그리는 것이 목표

  • 정확한 추정을 위해서.

기준과 만들기

  • 좋은 선 : SSE가 최소화되는 선
  • \beta_0, \beta_1의 관점에서 SSE 최소화
  • 두가지 파라미터: \alpha, \lambda
  • \alpha : 몇 개의 이웃을 반영할 것인지 (local의 영역을 결정)
    • 이 값이 클수록 그래프가 smooth해진다.
    • 너무 작으면 overfitting의 문제가 발생
  • \lambda : 다항식의 차수 결정.
    • 보통 1이 기본값이다.
  • 자료의 양에 따라 두 파라미터를 조정하는 방법이 달라진다.

가중치함수

한 이웃에 있는 모든 점들은 knot (x_0)으로의 거리를 이용해 가중치를 가진다.

  • 거리의 역수 = 가중치
  • 거리가 멀수록 가중치가 낮아진다.

이때, 각 가중치를 만드는 가중치함수(weight function)가 있다.

W_i(x_0) = W \bigg(\dfrac{|x_0 - x_i|} {d_k(x_0)}\bigg)

W는 다음과 같은 성질을 가진다.

\begin{align*} 1.&~W(x) > 0 \text{ for } |x| < 1\\ 2.&~W(-x) = W(x)\\ 3.&~W(x) \text{ is a nonincreasing}\\ 4.&~W(x) = 0 \text{ for } |x| \le 1 \end{align*}

tri-cube weight function은 다음과 같다.

  • 정규분포를 반으로 자른 모양이랑 비슷하다.

각 가중치와 SSE를 곱한 뒤, 전부 더한다.

\sum_{i=1}^{k}~w_i\epsilon_i = \sum_{i=1}^{k}~w_i(x_0)~ (y_i-\beta_0-\beta_1x_i)^2

loess의 단점

  1. 관측값 내에서만 그릴 수 있다. extrapolation(외삽법) 불가.

  2. x와 y의 연관성을 파악하기 힘들다. 검정이 안됨.

  3. 전체적인 신뢰구간을 찾을 수 없다. 구간추정이 안됨.

    (지역적인 신뢰구간은 구할 수 있다.)

  4. 변수가 많아지면 만들고, 보기 힘들다.

→ 말 그대로 탐색을 위한 기법!

예시문제 1: environmental

데이터 준비하기

데이터를 준비하고, loess을 만들어보자.

해답
r
load("environmental.Rdata")
ls()
str(data)
attach(data)

# loess fit
x = 2/3
y = 1
data_loess = loess(Ozone~Temperature, 
                   span=x, degree=y)

scatterplot 그리기

해답
r
# 산점도 그리기
windows()
plot(Temperature, Ozone, pch=16, 
     main="Environmental Data", 
     xlab="온도(F)", ylab="Ozone(PPB)", 
     axes=F, xlim=c(55, 100), ylim=c(0, 180))
axis(1, seq(55, 100, by=5), seq(55, 100, by=5))
axis(2, seq(0, 180, by=20), seq(0, 180, by=20))
ord_temp = order(Temperature)
lines(Temperature[ord_temp], fitted(data_loess)[ord_temp], lwd=2)
detach(data)

결과해석 : 직선 fit vs loess fit

  1. loess fit
    • 일반적인 직선보다 더 분포를 잘 반영한다.
    • 단, loess는 정렬을 하지 않으면 데이터가 엉망이 된다.
  2. 이웃의 넓이에 따른 loess fit의 차이
    • 구간이 넓으면 좀 더 smooth한 곡선이 그려진다.

loess의 신뢰구간

  • 잭나이프법이나 부트스트래핑을 이용, 신뢰구간을 구하는 것
  • 완벽한 신뢰구간을 구할 수 없는 loess 기법에서 자주 사용한다.
  • 아래는 y값의 평균에 대한 신뢰구간이다.
r
sample_data = sample(data_loess$y, 30, replace=TRUE)
n = length(sample_data) # 반복횟수
x = 50000 # 부트스트랩 반복횟수

resamples = matrix(sample(sample_data, n*x, replace=TRUE))

means = apply(resamples, 1, mean) # 샘플별 평균

quantile(means, c(0.025, 0.975))

예시문제 2: galaxy

데이터 준비하기

데이터를 준비하고, loess을 만들어보자.

해답
r
##### environmental data
### 데이터 준비
dev.off()
rm(list=ls())
load("galaxy.Rdata")
str(data)
attach(data)

# loess fit
galaxy_loess = loess(Velocity~EastWest*NorthSouth, span=0.25, degree=2)

조건부 산점도

  • conditional plot
  • coplot()을 사용한다.
  • 변수가 두 개 이상일 때 사용한다.
해답
r
# NorthSouth를 조건부로 loess fit 그리기
t1 = "East-West Coordinate (arcsec)"
t2 = "Given: North-South Coordinate (arcsec)"
windows()
coplot(Velocity~EastWest|NorthSouth, 
       panel = function(x, y, col, pch){
           idx = order(x)
           points(x, y, pch=pch, col=col)
           lines(x[idx], predict(loess(y~x))[idx], lwd=2, col=4)
       }, xlab=c(t1, t2), ylab="Velocity")

등고선 그리기

  • 등고선(contour plot): 변수가 두 개 이상일 때 사용한다.
해답
r
# 등고선 그리기
sort_ew = sort(EastWest)
sort_ns = sort(NorthSouth)
pred_val = predict(galaxy_loess, expand.grid(data.frame(EastWest=sort_ew, NorthSouth=sort_ns)))

contour(sort_ew, sort_ns, pred_val, 
        nlevels=15, main="Contour plot of loess fit to Velocity", xlab=t1, ylab=t2)

detach(data)