[기초] 보간법(Interpolation)

반응형
    반응형

    보간법은 데이터만으로 그래프를 만들어갈 때 쓰는 방법입니다.

    생각해보면 얻어낸 데이터 연속적일 수 없습니다.

    왜냐하면 x축을 시간이라고 보면 시간은 연속이지만 저는 특정 시간마다 데이터를 추출해오기 때문이죠.

    그렇지 않다면 규칙이 확실히 이미 정해진 데이터라고 볼 수 있습니다.

    규칙이 있으니 함수로 수식으로 나타낼 수 있기 때문에 데이터를 굳이 받아올 필요가 없습니다.

    어쨋든 늘 빈 곳이 존재합니다.

    한정된 데이터를 가지고 연속적인 그래프로 나타내야 하는데 사이사이를 나름의 규칙을 정해서 메꾸는 작업을 하는게 보간법입니다.

     

    사실 보간법 전체로 보면 머신러닝에 가깝진 않습니다. 

    보간법도 근사하는 방식으로 접근을 하기 때문에 그 부분만 강조해서 보고 regression 기반인 머신러닝과 비교를 위해서 포스팅을 합니다.

    수학적인 내용은 추후에 포스팅을 하겠습니다. 이번 포스팅에서는 코딩으로 하는 방법만 보여드리겠습니다.

     

    스플라인 보간법

    보간하는 방법은 함수를 다항식으로 결정지은 다음 그에 맞게 점을 껴맞춰가는 방식으로 하는데 범위가 길면 정확성이 떨어지게 됩니다. 또한 n차원으로 가면 갈수록 계산량이 어마어마해집니다. 그래서 적은 차원에서 계산을 편하게 하기 위해서 스플라인 보간법을 씁니다.

    스플라인은 piecewise 방식으로 각 데이터값의 범위마다 점을 접근시키고 연결하는 방식으로 하게 됩니다. 

    보통 3차원인 cubic 스플라인을 많이 씁니다. 

    그래서 코딩을 하려면 recursive하게 식을 써서 진행시키면 됩니다.

    다행히도 파이썬에는 보간법을 할 수 있는 라이브러리가 따로 있습니다. 

    scipy 라는건데 이를 통해서 간편하게 보간을 할 수 있습니다.

     

     

    코드 구현

    대부분의 코드는 Python for finance라는 책에서 가져왔습니다.

     

    보간을 하기 전에 plot을 그릴 준비를 해놓겠습니다.

    항상 같은 방식으로 그림을 그리기 때문에 함수로 만듭니다.

    x,y, style(선색), label,axlabels(x,y축 이름) 으로 변수를 두었습니다.

    import scipy.interpolate as spi
    import numpy as np
    import matplotlib.pyplot as plt
    
    plt.style.use('seaborn')
    
    #plot 그리기
    def create_plot(x,y,styles, labels,axlabels):
        plt.figure(figsize=(10,6))
        for i in range(len(x)):
            plt.plot(x[i],y[i],styles[i],label=labels[i])
            plt.xlabel(axlabels[0])
            plt.ylabel(axlabels[1])
        plt.legend(loc=0)

     

    함수 f(x)에서 점을 가져와서 스플라인 방식으로 그렸을 때 얼마나 근사하는지를 보겠습니다.

    #x값
    x= np.linspace(-2*np.pi,2*np.pi,50)
    
    #f(x) 정의
    def f(x):
        return np.sin(x)+0.5*x
    
    #스플라인 곡선을 만듦(k=1 이면 linear 스플라인, 3이면 cubic 스플라인)
    ipo = spi.splrep(x,f(x),k=1)
    
    #스플라인 곡선에서 값 가져오기
    iy = spi.splev(x,ipo)
    
    #근사인지 확인(True or False)
    np.allclose(f(x),iy)
    
    #plot 그리기
    create_plot([x,x],[f(x),iy],['b','ro'],['f(x)','interpolation'],['x','f(x)'])

     

     

    어느정도 근사가 되었다면 np.allclose에서 True라고 나옵니다. 실제로 그림을 그려도 f(x)와 비슷하게 그려집니다.

    지금의 x값은 점이 50개밖에 없어서 그렇지만 적당히 많아지면 안 맞습니다. 

    xd로 범위와 갯수를 바꿔보겠습니다.

     

    xd = np.linspace(1.0,5.0,100)
    iyd = spi.splev(xd,ipo)
    create_plot([xd,xd],[f(xd),iyd],['b','ro'],['f(x)','interpolation'],['x','f(x)'])

     

    현재 1차원의 식으로 스플라인 곡선을 만들어놓았기 때문에 굴곡진 곳에서 약간 안 맞습니다.

    이를 해결하기 위해 차원을 높입니다.

    가장 많이 쓰이는 3차원으로 바꿔보겠습니다.

    ipo = spi.splrep(x,f(x),k=3)
    iyd = spi.splev(xd,ipo)
    create_plot([xd,xd],[f(xd),iyd],['b','ro'],['f(x)','interpolation'],['x','f(x)'])

     

    그러면 좀 더 smooth하게 나오는 점이 메꿔지는 걸 볼 수 있습니다.

    이런 식으로 보간법을 사용할 수 있습니다. 편리하다는 장점은 있습니다. 어쨋든 데이터를 가지고 함수와 근사되었으니 성공입니다. 하지만 고차원 다항식으로 보간한다면 계산량이 많아질 겁니다. 복잡한 데이터를 보간법으로 한다면 여러 측면에서 다른 시도를 하긴 해야할겁니다.

     

     

     

    전체 코드

    import scipy.interpolate as spi
    import numpy as np
    import matplotlib.pyplot as plt
    
    plt.style.use('seaborn')
    
    #plot 그리기
    def create_plot(x,y,styles, labels,axlabels):
        plt.figure(figsize=(10,6))
        for i in range(len(x)):
            plt.plot(x[i],y[i],styles[i],label=labels[i])
            plt.xlabel(axlabels[0])
            plt.ylabel(axlabels[1])
        plt.legend(loc=0)
    
    #x값
    x= np.linspace(-2*np.pi,2*np.pi,50)
    
    #f(x) 
    def f(x):
        return np.sin(x)+0.5*x
    
    #스플라인 곡선 만들기(k=1,2,3,4.5 가능)
    ipo = spi.splrep(x,f(x),k=1)
    #스플라인 곡선에서의 y값
    iy = spi.splev(x,ipo)
    
    #그리기
    create_plot([x,x],[f(x),iy],['b','ro'],['f(x)','interpolation'],['x','f(x)'])

     

     

    관련 포스팅

    [Python/Numpy] - ndarray 생성하기

    [Python/그래프 그리기] - [matplotlib]plt 라벨(label) 위치설정

    [수학] - [수치해석] Spline interpolation(스플라인 보간법)

    댓글

    Designed by JB FACTORY

    ....