Scipy.interpolate'in giriş aralığının ötesinde tahmini bir sonuç vermesini nasıl sağlayabilirim?


82

Scipy tarafından sağlanan interpolatörleri kullanmak için elle çevrilmiş bir interpolatör (matematikçi bir meslektaş tarafından geliştirilmiş) kullanan bir programı taşımaya çalışıyorum. Eski enterpolatöre mümkün olduğu kadar yakın davranışa sahip olması için scipy interpolatörünü kullanmak veya sarmak istiyorum.

İki fonksiyon arasındaki temel fark, orijinal enterpolatörümüzde - eğer giriş değeri giriş aralığının üstünde veya altında ise, orijinal enterpolatörümüz sonucu tahmin eder. Bunu scipy interpolator ile denerseniz, bir ValueError. Bu programı bir örnek olarak düşünün:

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)

Bunu yapmanın mantıklı bir yolu var mı, böylece son çizginin çarpmak yerine, ilk ve son iki nokta tarafından tanımlanan gradyanları sonsuza kadar devam ettirerek basitçe doğrusal bir ekstrapolasyon yapacak.

Not, gerçek yazılımda aslında exp işlevini kullanmıyorum - bu sadece gösterim amaçlı burada!


2
scipy.interpolate.UnivariateSplinesorunsuz bir sonuca varıyor gibi görünüyor.
heltonbiker

Yanıtlar:


38

1. Sabit ekstrapolasyon

interpScipy'den işlevi kullanabilirsiniz , sol ve sağ değerleri aralığın ötesinde sabit olarak tahmin eder:

>>> 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. Doğrusal (veya diğer özel) ekstrapolasyon

Doğrusal ekstrapolasyonla ilgilenen bir enterpolasyon işlevinin etrafına bir sarmalayıcı yazabilirsiniz. Örneğin:

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

extrap1dbir enterpolasyon işlevi alır ve aynı zamanda dış değer hesaplayabilen bir işlev döndürür. Ve bunu şu şekilde kullanabilirsiniz:

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

print f_x([9,10])

Çıktı:

[ 0.04978707  0.03009069]

1
Python 3.6'da, iteratörü çözmek için listdönüşe eklemeliydim return array(list(map(pointwise, array(xs)))).
çoğunluklaWright

Bu çözüm, fill_value = "extrapolate" seçeneğinden daha esnektir. İç işlevi 'noktasal' ihtiyaçlarıma göre uyarlayabildim, yukarıdaki yorumu ikinci olarak aldım ve gerektiğinde listeyi ekledim. Bunu söyledikten sonra, bazen sadece bir jeneratöre sahip olmak isteyebilirsiniz.
Wilmer E. Henao

1
Temel alınan ilk çözümün scipy.interpartık önerilmediğini ve SciPy 2.0.0'da ortadan kalkacağını unutmayın. numpy.interpBunun yerine kullanılmasını tavsiye ediyorlar, ancak soruda belirtildiği gibi burada işe yaramayacak
Yosko

86

InterpolatedUnivariateSpline'a bir göz atabilirsiniz.

İşte onu kullanan bir örnek:

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
bu en iyi cevap. Ben de öyle yaptım. I used k=1 (order), dolayısıyla doğrusal bir enterpolasyona dönüşür veI used bbox=[xmin-w, xmax+w] where w is my tolerance
eusoubrasileiro

78

SciPy 0.17.0 sürümünden itibaren, scipy.interpolate.interp1d için ekstrapolasyona izin veren yeni bir seçenek bulunmaktadır . Çağrıda fill_value = 'extrapolate' ayarlamanız yeterlidir. Kodunuzu bu şekilde değiştirmek şunları sağlar:

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)

ve çıktı:

0.0497870683679
0.010394302658

Ekstrapolasyon türü, enterpolasyon türüne benzer mi? Örneğin, en yakın nokta ekstrapolasyonu ile doğrusal enterpolasyona sahip olabilir miyiz?
a.sam

Tür = 'kübik' ise, fill_value = 'extrapolate' çalışmaz.
vlmercado

@ a.sam: Ne demek istediğinizden emin değilim ... Muhtemelen, fill_value = 'interpolation' ile kind = 'linear' kullanırsanız, doğrusal bir interpolasyon elde edersiniz ve fill_value = 'extrapolation' ile kullanırsanız o zaman doğrusal bir tahmin elde edersiniz, değil mi?
Moot

@vlmercado: Nasıl çalışmadığını açıklayabilir misin? Yukarıdaki örneği tür = 'kübik' ekleyerek çalıştırmayı denedim ve benim için iyi çalışıyor.
Moot

@Moot, scipy 0.18.1 kullanarak aşağıdakileri alıyorum: ValueError: Dış değerleme kind = spline
vlmercado

8

Peki scipy.interpolate.splrep (1. derece ile ve yumuşatma olmadan):

>> 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) olduğundan, istediğinizi yapıyor gibi görünüyor.


7

İşte sadece numpy paketini kullanan alternatif bir yöntem. Numpy'nin dizi işlevlerinden yararlanır, bu nedenle büyük dizileri enterpolasyon / ekstrapolasyon sırasında daha hızlı olabilir:

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)

Düzenleme: Mikofski'nin "ekstrap" işlevinin önerdiği değişikliği işaretleyin:

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
Gerçek bir örnek için +1, ancak boole indekslemeyi de kullanabilirsiniz ve burada y[x < xp[0]] = fp[0] + (x[x < xp[0]] - xp[0]) / (xp[1] - xp[0]) * (fp[1] - fp[0]) ve y[x > xp[-1]] = fp[-1] + (x[x > xp[-1]] - xp[-1]) / (xp[-2] - xp[-1]) * (fp[-2] - fp[-1])yerine np.where, Falseseçenek ydeğişmediğinden.
Mark Mikofski

6

Kullanımı daha hızlı olabilir boolean indeksleme ile büyük veri kümelerinde boolean indeksleme daha kolay ve daha hızlı bir karşılaştırma verir, oysa her noktasının, aralık dışında ise algoritma kontrolleri beri.

Örneğin:

# 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])

Benim durumumda, 300000 noktadan oluşan bir veri setiyle bu, 25,8 saniyeden 0,094 saniyeye kadar bir hız anlamına gelir, bu 250 kattan daha hızlıdır .


Bu güzel, ancak x0 bir kayan nokta ise, y [0] np.nan ise veya y [-1] np.nan ise çalışmaz.
Stretch

2

İlk dizilerime bir nokta ekleyerek yaptım. Bu şekilde, kendi kendine yapılan fonksiyonları tanımlamaktan kaçınıyorum ve doğrusal ekstrapolasyon (aşağıdaki örnekte: doğru ekstrapolasyon) iyi görünüyor.

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

Korkarım bunu Scipy'de yapmanın kolay olmadığını biliyorum. Bildiğinizden oldukça emin olduğum gibi, sınır hatalarını kapatabilir ve aralığın ötesindeki tüm fonksiyon değerlerini bir sabit ile doldurabilirsiniz, ancak bu gerçekten yardımcı olmuyor. Daha fazla fikir için posta listesindeki bu soruya bakın . Belki bir tür parçalı işlev kullanabilirsiniz, ancak bu büyük bir acı gibi görünüyor.


En azından scipy 0.7 ile vardığım sonuç buydu, ancak 21 ay önce yazılan bu öğretici, interp1d işlevinin "doğrusal" olarak ayarlanabilen yüksek ve düşük bir özniteliğe sahip olduğunu öne sürüyor, öğretici scipy'nin hangi sürümünün bu için geçerlidir: projects.scipy.org/scipy/browser/branches/Interpolate1D/docs/…
Salim Fadhley

Görünüşe göre bu, henüz ana versiyonda asimile edilmemiş bir dalın parçası, bu yüzden hala bazı problemler olabilir. Bunun için geçerli kod, projects.scipy.org/scipy/browser/branches/interpolate/… adresindedir , ancak sayfanın en altına kaydırıp düz metin olarak indirmek için tıklayabilirsiniz. Henüz denemediğim halde bunun umut verici göründüğünü düşünüyorum.
Justin Peel

1

Aşağıdaki kod size basit ekstrapolasyon modülünü verir. k , veri kümesi y'nin , x veri kümesine dayalı olarak ekstrapole edilmesi gereken değerdir . numpyModül gereklidir.

 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

Standart enterpolate + doğrusal ekstrapolat:

    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
Hey Federico! Neden reddedildiğinizi merak ediyorsanız, lütfen soruları yanıtlarken sorunu nasıl çözdüğünü gerçekten açıklamanız gerektiğini unutmayın. Bu cevap, olduğu gibi, yalnızca bir kod dökümüdür ve neden ve / veya nasıl yararlı olduğunu açıklayan en az birkaç cümle içermelidir. Teşekkürler!
Félix Gagnon-Grenier

0

Yorum yapacak kadar itibarım yok, ancak birisi scipy ile doğrusal bir 2d-enterpolasyon için bir ekstrapolasyon sarmalayıcısı arıyorsa, burada verilen cevabı 1d enterpolasyon için uyarladım.

def extrap2d(interpolator):
xs = interpolator.x
ys = interpolator.y
zs = interpolator.z
zs = np.reshape(zs, (-1, len(xs)))
def pointwise(x, y):
    if x < xs[0] or y < ys[0]:
        x1_index = np.argmin(np.abs(xs - x))
        x2_index = x1_index + 1
        y1_index = np.argmin(np.abs(ys - y))
        y2_index = y1_index + 1
        x1 = xs[x1_index]
        x2 = xs[x2_index]
        y1 = ys[y1_index]
        y2 = ys[y2_index]
        z11 = zs[x1_index, y1_index]
        z12 = zs[x1_index, y2_index]
        z21 = zs[x2_index, y1_index]
        z22 = zs[x2_index, y2_index]

        return (z11 * (x2 - x) * (y2 - y) +
        z21 * (x - x1) * (y2 - y) +
        z12 * (x2 - x) * (y - y1) +
        z22 * (x - x1) * (y - y1)
       ) / ((x2 - x1) * (y2 - y1) + 0.0)


    elif x > xs[-1] or y > ys[-1]:
        x1_index = np.argmin(np.abs(xs - x))
        x2_index = x1_index - 1
        y1_index = np.argmin(np.abs(ys - y))
        y2_index = y1_index - 1
        x1 = xs[x1_index]
        x2 = xs[x2_index]
        y1 = ys[y1_index]
        y2 = ys[y2_index]
        z11 = zs[x1_index, y1_index]
        z12 = zs[x1_index, y2_index]
        z21 = zs[x2_index, y1_index]
        z22 = zs[x2_index, y2_index]#

        return (z11 * (x2 - x) * (y2 - y) +
        z21 * (x - x1) * (y2 - y) +
        z12 * (x2 - x) * (y - y1) +
        z22 * (x - x1) * (y - y1)
        ) / ((x2 - x1) * (y2 - y1) + 0.0)
    else:
        return interpolator(x, y)
def ufunclike(xs, ys):
    if  isinstance(xs, int) or isinstance(ys, int) or isinstance(xs, np.int32) or isinstance(ys, np.int32):
        res_array = pointwise(xs, ys)
    else:
        res_array = np.zeros((len(xs), len(ys)))
        for x_c in range(len(xs)):
            res_array[x_c, :] = np.array([pointwise(xs[x_c], ys[y_c]) for y_c in range(len(ys))]).T

    return res_array
return ufunclike

Çok yorum yapmadım ve kodun çok temiz olmadığının farkındayım. Herhangi bir hata görürse lütfen bana bildirin. Mevcut kullanım durumumda sorunsuz çalışıyor :)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.