scipy.interpolate가 입력 범위를 초과하는 외삽 결과를 제공하도록 만드는 방법은 무엇입니까?


82

scipy에서 제공하는 보간기를 사용하기 위해 수학자 동료가 개발 한 수동 보간기를 사용하는 프로그램을 이식하려고합니다. scipy 보간기를 사용하거나 래핑하여 가능한 한 이전 보간기에 가깝게 작동하도록하고 싶습니다.

두 기능의 주요 차이점은 원래 보간 기에서 입력 값이 입력 범위보다 크거나 작 으면 원래 보간 기가 결과를 추정한다는 것입니다. scipy 보간기로 이것을 시도하면 ValueError. 이 프로그램을 예로 고려하십시오.

import numpy as np
from scipy import interpolate

x = np.arange(0,10)
y = np.exp(-x/3.0)
f = interpolate.interp1d(x, y)

print f(9)
print f(11) # Causes ValueError, because it's greater than max(x)

충돌하는 대신 최종 라인이 단순히 선형 외삽을 수행하여 첫 번째와 마지막 두 점으로 정의 된 기울기를 무한대로 계속하도록 만드는 합리적인 방법이 있습니까?

실제 소프트웨어에서는 실제로 exp 함수를 사용하지 않습니다. 여기에서는 설명 용으로 만 사용합니다!


2
scipy.interpolate.UnivariateSpline문제없이 외삽하는 것 같습니다.
heltonbiker

답변:


37

1. 상수 외삽

interpscipy의 함수를 사용할 수 있으며 왼쪽 및 오른쪽 값을 범위를 초과하는 상수로 외삽합니다.

>>> from scipy import interp, arange, exp
>>> x = arange(0,10)
>>> y = exp(-x/3.0)
>>> interp([9,10], x, y)
array([ 0.04978707,  0.04978707])

2. 선형 (또는 기타 사용자 정의) 외삽

선형 외삽을 처리하는 보간 함수 주위에 래퍼를 작성할 수 있습니다. 예를 들면 :

from scipy.interpolate import interp1d
from scipy import arange, array, exp

def extrap1d(interpolator):
    xs = interpolator.x
    ys = interpolator.y

    def pointwise(x):
        if x < xs[0]:
            return ys[0]+(x-xs[0])*(ys[1]-ys[0])/(xs[1]-xs[0])
        elif x > xs[-1]:
            return ys[-1]+(x-xs[-1])*(ys[-1]-ys[-2])/(xs[-1]-xs[-2])
        else:
            return interpolator(x)

    def ufunclike(xs):
        return array(list(map(pointwise, array(xs))))

    return ufunclike

extrap1d보간 함수를 받고 외삽 할 수있는 함수를 반환합니다. 다음과 같이 사용할 수 있습니다.

x = arange(0,10)
y = exp(-x/3.0)
f_i = interp1d(x, y)
f_x = extrap1d(f_i)

print f_x([9,10])

산출:

[ 0.04978707  0.03009069]

1
Python 3.6에서는 반복자를 해결하기 위해 listreturn : return array(list(map(pointwise, array(xs))))을 추가 해야했습니다 .
user21387

이 솔루션은 fill_value = "extrapolate"옵션보다 더 유연합니다. 내 필요에 따라 'pointwise'내부 기능을 조정할 수 있었고 위의 주석을 두 번째로하고 필요할 때 목록을 삽입했습니다. 그렇긴하지만 때로는 발전기를 갖고 싶을 수도 있습니다.
Wilmer E. Henao

1
기반의 첫 번째 솔루션 scipy.interp은 더 이상 사용되지 않으며 SciPy 2.0.0에서 사라질 것이므로 더 이상 권장되지 않습니다. numpy.interp대신 사용 을 권장 하지만 질문에서 언급했듯이 여기에서는 작동하지 않습니다
Yosko

86

InterpolatedUnivariateSpline을 살펴볼 수 있습니다.

다음은 그것을 사용하는 예입니다.

import matplotlib.pyplot as plt
import numpy as np
from scipy.interpolate import InterpolatedUnivariateSpline

# given values
xi = np.array([0.2, 0.5, 0.7, 0.9])
yi = np.array([0.3, -0.1, 0.2, 0.1])
# positions to inter/extrapolate
x = np.linspace(0, 1, 50)
# spline order: 1 linear, 2 quadratic, 3 cubic ... 
order = 1
# do inter/extrapolation
s = InterpolatedUnivariateSpline(xi, yi, k=order)
y = s(x)

# example showing the interpolation for linear, quadratic and cubic interpolation
plt.figure()
plt.plot(xi, yi)
for order in range(1, 4):
    s = InterpolatedUnivariateSpline(xi, yi, k=order)
    y = s(x)
    plt.plot(x, y)
plt.show()

2
이것이 최고의 답변입니다. 그게 내가 한 일입니다. I used k=1 (order)그래서 그것은 선형 보간되고,I used bbox=[xmin-w, xmax+w] where w is my tolerance
eusoubrasileiro

76

SciPy 버전 0.17.0부터 외삽을 허용 하는 scipy.interpolate.interp1d에 대한 새로운 옵션 이 있습니다. 호출에서 fill_value = 'extrapolate'를 설정하기 만하면됩니다. 이 방법으로 코드를 수정하면 다음이 제공됩니다.

import numpy as np
from scipy import interpolate

x = np.arange(0,10)
y = np.exp(-x/3.0)
f = interpolate.interp1d(x, y, fill_value='extrapolate')

print f(9)
print f(11)

출력은 다음과 같습니다.

0.0497870683679
0.010394302658

외삽 종류가 보간 종류와 비슷합니까? 예를 들어, 가장 가까운 점 외삽으로 선형 보간을 할 수 있습니까?
a.sam

kind = 'cubic'이면 fill_value = 'extrapolate'가 작동하지 않습니다.
vlmercado

@ a.sam : 무슨 뜻인지 잘 모르겠습니다 ... 아마도 fill_value = 'interpolation'과 함께 kind = 'linear'를 사용하면 선형 보간을 얻을 수 있고 fill_value = 'extrapolation'과 함께 사용하면 그런 다음 선형 외삽을 얻습니다.
Moot

@vlmercado : 작동하지 않는 방식을 설명해 주시겠습니까? 나는 kind = 'cubic'을 추가하여 위의 예제를 실행 해 보았고 잘 작동합니다.
Moot

@Moot, scipy 0.18.1을 사용하면 다음과 같은 메시지가 나타납니다. ValueError : Extrapolation does not work with kind = spline
vlmercado

8

scipy.interpolate.splrep은 어떻습니까 (차수가 1이고 평활화 없음) :

>> tck = scipy.interpolate.splrep([1, 2, 3, 4, 5], [1, 4, 9, 16, 25], k=1, s=0)
>> scipy.interpolate.splev(6, tck)
34.0

34 = 25 + (25-16)이므로 원하는 것을 수행하는 것 같습니다.


7

다음은 numpy 패키지 만 사용하는 대체 방법입니다. numpy의 배열 함수를 활용하므로 큰 배열을 보간 / 외삽 할 때 더 빠를 수 있습니다.

import numpy as np

def extrap(x, xp, yp):
    """np.interp function with linear extrapolation"""
    y = np.interp(x, xp, yp)
    y = np.where(x<xp[0], yp[0]+(x-xp[0])*(yp[0]-yp[1])/(xp[0]-xp[1]), y)
    y = np.where(x>xp[-1], yp[-1]+(x-xp[-1])*(yp[-1]-yp[-2])/(xp[-1]-xp[-2]), y)
    return y

x = np.arange(0,10)
y = np.exp(-x/3.0)
xtest = np.array((8.5,9.5))

print np.exp(-xtest/3.0)
print np.interp(xtest, x, y)
print extrap(xtest, x, y)

편집 : Mark Mikofski가 제안한 "extrap"기능 수정 :

def extrap(x, xp, yp):
    """np.interp function with linear extrapolation"""
    y = np.interp(x, xp, yp)
    y[x < xp[0]] = yp[0] + (x[x<xp[0]]-xp[0]) * (yp[0]-yp[1]) / (xp[0]-xp[1])
    y[x > xp[-1]]= yp[-1] + (x[x>xp[-1]]-xp[-1])*(yp[-1]-yp[-2])/(xp[-1]-xp[-2])
    return y

2
실제 예를 들어 한,하지만 당신은 또한 사용할 수있는 부울 인덱싱을 하고 여기에 y[x < xp[0]] = fp[0] + (x[x < xp[0]] - xp[0]) / (xp[1] - xp[0]) * (fp[1] - fp[0])y[x > xp[-1]] = fp[-1] + (x[x > xp[-1]] - xp[-1]) / (xp[-2] - xp[-1]) * (fp[-2] - fp[-1])대신 np.where의 이후 False옵션 y변경되지 않습니다.
마크 Mikofski

6

그것은 사용하기 빨라질 수 부울 인덱싱대용량 데이터 부울 인덱싱 쉽고 빠르게 비교를 허용하는 반면 모든 점은, 간격 외측에있는 경우, 알고리즘 검사 이후.

예를 들면 :

# Necessary modules
import numpy as np
from scipy.interpolate import interp1d

# Original data
x = np.arange(0,10)
y = np.exp(-x/3.0)

# Interpolator class
f = interp1d(x, y)

# Output range (quite large)
xo = np.arange(0, 10, 0.001)

# Boolean indexing approach

# Generate an empty output array for "y" values
yo = np.empty_like(xo)

# Values lower than the minimum "x" are extrapolated at the same time
low = xo < f.x[0]
yo[low] =  f.y[0] + (xo[low]-f.x[0])*(f.y[1]-f.y[0])/(f.x[1]-f.x[0])

# Values higher than the maximum "x" are extrapolated at same time
high = xo > f.x[-1]
yo[high] = f.y[-1] + (xo[high]-f.x[-1])*(f.y[-1]-f.y[-2])/(f.x[-1]-f.x[-2])

# Values inside the interpolation range are interpolated directly
inside = np.logical_and(xo >= f.x[0], xo <= f.x[-1])
yo[inside] = f(xo[inside])

제 경우에는 300000 포인트의 데이터 세트를 사용하면 25.8 초에서 0.094 초로 속도가 빨라지고 250 배 이상 빠릅니다 .


이것은 좋지만 x0이 float이거나 y [0]이 np.nan이거나 y [-1]이 np.nan이면 작동하지 않습니다.
2013

2

초기 배열에 포인트를 추가하여 수행했습니다. 이런 식으로 나는 스스로 만든 함수를 정의하는 것을 피하고 선형 외삽 (아래 예제에서 : 오른쪽 외삽)은 괜찮아 보입니다.

import numpy as np  
from scipy import interp as itp  

xnew = np.linspace(0,1,51)  
x1=xold[-2]  
x2=xold[-1]  
y1=yold[-2]  
y2=yold[-1]  
right_val=y1+(xnew[-1]-x1)*(y2-y1)/(x2-x1)  
x=np.append(xold,xnew[-1])  
y=np.append(yold,right_val)  
f = itp(xnew,x,y)  

1

내가 아는 한 Scipy에서 이것을 쉽게 할 수 없다는 것이 두렵습니다. 알고 계시 겠지만, 경계 오류를 끄고 범위를 벗어난 모든 함수 값을 상수로 채울 수 있지만 실제로는 도움이되지 않습니다. 더 많은 아이디어를 얻으려면 메일 링리스트 에서이 질문 을 참조하십시오 . 아마도 어떤 종류의 조각 기능을 사용할 수 있지만 그것은 큰 고통처럼 보입니다.


이것이 제가 최소한 scipy 0.7을 사용하여 내린 결론입니다. 그러나 21 개월 전에 작성된이 튜토리얼은 interp1d 함수가 "linear"로 설정할 수있는 높고 낮은 속성을 가지고 있음을 시사합니다. 튜토리얼은 어떤 버전의 scipy this인지 명확하지 않습니다. 적용 대상 : projects.scipy.org/scipy/browser/branches/Interpolate1D/docs/…
Salim Fadhley 2010

아직 메인 버전에 동화되지 않은 브랜치의 일부인 것 같으므로 여전히 문제가있을 수 있습니다. 현재 코드는 projects.scipy.org/scipy/browser/branches/interpolate/…에 있지만 페이지 하단으로 스크롤하여 클릭하여 일반 텍스트로 다운로드 할 수도 있습니다. 아직 직접 해본 적은 없지만 유망 해 보인다고 생각합니다.
Justin Peel

1

아래 코드는 간단한 외삽 모듈을 제공합니다. k 는 데이터 세트 x를 기반으로 데이터 세트 y 를 외삽해야하는 값입니다. numpy모듈이 필요합니다.

 def extrapol(k,x,y):
        xm=np.mean(x);
        ym=np.mean(y);
        sumnr=0;
        sumdr=0;
        length=len(x);
        for i in range(0,length):
            sumnr=sumnr+((x[i]-xm)*(y[i]-ym));
            sumdr=sumdr+((x[i]-xm)*(x[i]-xm));

        m=sumnr/sumdr;
        c=ym-(m*xm);
        return((m*k)+c)

0

표준 보간 + 선형 외삽 :

    def interpola(v, x, y):
        if v <= x[0]:
            return y[0]+(y[1]-y[0])/(x[1]-x[0])*(v-x[0])
        elif v >= x[-1]:
            return y[-2]+(y[-1]-y[-2])/(x[-1]-x[-2])*(v-x[-2])
        else:
            f = interp1d(x, y, kind='cubic') 
            return f(v)

1
안녕 페데리코! 왜 비추천을 받았는지 궁금하다면 질문에 답할 때 실제로 문제를 해결하는 방법을 설명해야합니다. 이 답변은 코드 덤프 일 뿐이며 그 이유 및 / 또는 유용성을 설명하는 문장이 최소한 몇 개 있어야합니다. 감사!
Félix Gagnon-Grenier
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.