Scipy kullanarak iki boyutlu enterpolasyonu nasıl yapabilirim?


105

Bu Soru-Cevap, scipy kullanarak iki boyutlu (ve çok boyutlu) enterpolasyonla ilgili bir kanonik (-ish) olarak tasarlanmıştır. Çeşitli çok boyutlu enterpolasyon yöntemlerinin temel sözdizimi ile ilgili sık sık sorular vardır, bunları da düzene sokmayı umuyorum.

Ben dağınık iki boyutlu veri noktalarının kümesi var ve tercihen gibi bir şey kullanarak, güzel bir yüzey olarak bunları çizmek istiyorum contourfya plot_surfaceiçinde matplotlib.pyplot. Scipy kullanarak iki boyutlu veya çok boyutlu verilerimi bir ağa nasıl enterpolasyon yapabilirim?

scipy.interpolateAlt paketi buldum , ancak interp2dveya bisplrepveya griddataveya kullanırken hatalar almaya devam ediyorum rbf. Bu yöntemlerin doğru sözdizimi nedir?

Yanıtlar:


164

Feragatname: Bu yazıyı çoğunlukla sözdizimsel düşünceler ve genel davranışları göz önünde bulundurarak yazıyorum. Tarif edilen yöntemlerin bellek ve CPU yönüne aşina değilim ve bu cevabı, enterpolasyon kalitesinin dikkate alınması gereken ana husus olabileceği şekilde, oldukça küçük veri kümelerine sahip olanları hedefliyorum. Çok büyük veri kümeleriyle çalışırken, daha iyi performans gösteren yöntemlerin (yani griddatave Rbf) mümkün olmayabileceğinin farkındayım.

Üç çeşit çok boyutlu enterpolasyon yöntemini karşılaştıracağım ( interp2d/ splines griddatave Rbf). Bunları iki tür enterpolasyon görevine ve iki tür temel işleve tabi tutacağım (enterpolasyon yapılacak noktalar). Spesifik örnekler, iki boyutlu enterpolasyonu gösterecektir, ancak uygulanabilir yöntemler, keyfi boyutlarda uygulanabilir. Her yöntem çeşitli türde enterpolasyon sağlar; her durumda kübik enterpolasyon (veya 1'e yakın bir şey ) kullanacağım . Enterpolasyonu her kullandığınızda, ham verilerinize kıyasla önyargı oluşturduğunuzu ve kullanılan belirli yöntemlerin, sonuçta elde edeceğiniz yapıları etkilediğini unutmamak önemlidir. Her zaman bunun farkında olun ve sorumlu bir şekilde yorumlayın.

İki enterpolasyon görevi,

  1. yukarı örnekleme (giriş verileri dikdörtgen bir ızgarada, çıktı verileri daha yoğun bir ızgarada)
  2. Dağınık verilerin normal bir ızgaraya enterpolasyonu

İki işlev (etki alanı üzerinden [x,y] in [-1,1]x[-1,1])

  1. pürüzsüz ve dostça bir işlev cos(pi*x)*sin(pi*y):; aralığı[-1, 1]
  2. kötü (ve özellikle sürekli olmayan) işlev: x*y/(x^2+y^2)başlangıç ​​noktasına yakın 0,5 değerinde; aralığı[-0.5, 0.5]

İşte nasıl göründükleri:

fig1: test fonksiyonları

Önce üç yöntemin bu dört test altında nasıl davrandığını göstereceğim, ardından üçünün de sözdizimini detaylandıracağım. Bir yöntemden ne beklemeniz gerektiğini biliyorsanız, sözdizimini öğrenerek (size bakarak interp2d) zamanınızı boşa harcamak istemeyebilirsiniz .

Test verisi

Açıklık adına, giriş verilerini oluşturduğum kod burada. Bu özel durumda, verinin altında yatan işlevin açıkça farkındayken, bunu yalnızca enterpolasyon yöntemleri için girdi oluşturmak için kullanacağım. Kolaylık sağlamak için (ve çoğunlukla veri oluşturmak için) numpy kullanıyorum, ancak tek başına scipy de yeterli olacaktır.

import numpy as np
import scipy.interpolate as interp

# auxiliary function for mesh generation
def gimme_mesh(n):
    minval = -1
    maxval =  1
    # produce an asymmetric shape in order to catch issues with transpositions
    return np.meshgrid(np.linspace(minval,maxval,n), np.linspace(minval,maxval,n+1))

# set up underlying test functions, vectorized
def fun_smooth(x, y):
    return np.cos(np.pi*x)*np.sin(np.pi*y)

def fun_evil(x, y):
    # watch out for singular origin; function has no unique limit there
    return np.where(x**2+y**2>1e-10, x*y/(x**2+y**2), 0.5)

# sparse input mesh, 6x7 in shape
N_sparse = 6
x_sparse,y_sparse = gimme_mesh(N_sparse)
z_sparse_smooth = fun_smooth(x_sparse, y_sparse)
z_sparse_evil = fun_evil(x_sparse, y_sparse)

# scattered input points, 10^2 altogether (shape (100,))
N_scattered = 10
x_scattered,y_scattered = np.random.rand(2,N_scattered**2)*2 - 1
z_scattered_smooth = fun_smooth(x_scattered, y_scattered)
z_scattered_evil = fun_evil(x_scattered, y_scattered)

# dense output mesh, 20x21 in shape
N_dense = 20
x_dense,y_dense = gimme_mesh(N_dense)

Düzgün işlev ve örnekleme

En kolay görevle başlayalım. Sorunsuz test işlevi için [6,7]bir şekil ağından birine yukarı örnekleme nasıl yapılır [20,21]:

fig2: düzgün örnekleme

Bu basit bir görev olsa da, çıktılar arasında halihazırda ince farklılıklar vardır. İlk bakışta üç çıktının tümü makul. Altta yatan işleve ilişkin önceki bilgilerimize dayanarak dikkat edilmesi gereken iki özellik vardır: orta durum griddata, verileri en çok bozar. Grafiğin y==-1sınırına dikkat edin ( xetikete en yakın ): fonksiyon kesinlikle sıfır olmalıdır (çünkü y==-1düzgün fonksiyon için düğüm çizgisidir), ancak durum böyle değildir griddata. Ayrıca x==-1grafiklerin sınırına da dikkat edin (arkada, solda): temeldeki fonksiyonun yerel maksimum değeri (sınırın yakınında sıfır gradyan olduğu anlamına gelir) [-1, -0.5], ancak griddataçıktı bu bölgede açıkça sıfır olmayan gradyan gösterir. Etki inceliklidir, ancak bir önyargıdır. (SadakatRbfdublajlı varsayılan radyal işlev seçimi ile daha da iyidir multiquadratic.)

Kötü işlev ve örnekleme

Kötü işlevimiz üzerinde örnekleme yapmak biraz daha zor:

fig3: kötü örnekleme

Üç yöntem arasında açık farklar ortaya çıkmaya başlıyor. Yüzey grafiklerine bakıldığında, çıkışta görünen açık sahte ekstremler var interp2d(çizilen yüzeyin sağ tarafındaki iki tümseklere dikkat edin). İken griddatave Rbfilk bakışta benzer bir sonuç ortaya, ikincisi yakın daha derin asgari üretim görünüyor [0.4, -0.4]bu temel işlevinden yoktur.

Bununla birlikte, Rbfçok daha üstün olan çok önemli bir yön vardır : temelde yatan fonksiyonun simetrisine saygı duyar (bu, elbette numune ağının simetrisiyle de mümkün kılınmıştır). Çıktı griddata, örnek noktalarının simetrisini bozar ve bu, düzgün durumda zaten zayıf bir şekilde görülebilir.

Düzgün işlev ve dağınık veriler

Çoğu zaman, dağınık veriler üzerinde enterpolasyon yapmak istenir. Bu nedenle bu testlerin daha önemli olmasını bekliyorum. Yukarıda gösterildiği gibi, numune noktaları ilgilenilen alanda sözde tekbiçimli seçilmiştir. Gerçekçi senaryolarda, her ölçümde ek gürültü olabilir ve başlangıçta ham verilerinizi enterpolasyona tabi tutmanın mantıklı olup olmadığını düşünmelisiniz.

Düzgün işlev için çıktı:

fig4: düzgün dağınık enterpolasyon

Şimdi zaten devam eden bir korku gösterisi var. En azından minimum miktarda bilgiyi korumak için çıktıyı yalnızca çizim interp2diçin arasına kırptım [-1, 1]. Açıktır ki, altta yatan şeklin bir kısmı mevcutken, yöntemin tamamen bozulduğu çok büyük gürültülü bölgeler vardır. İkinci durum griddataşekli oldukça güzel bir şekilde yeniden üretir, ancak kontur grafiğinin sınırındaki beyaz bölgelere dikkat edin. Bunun nedeni, griddatayalnızca giriş veri noktalarının dışbükey gövdesi içinde çalışmasıdır (başka bir deyişle, herhangi bir ekstrapolasyon gerçekleştirmez ). Dışbükey gövdenin dışında yatan çıkış noktaları için varsayılan NaN değerini tuttum. 2 Bu özellikler göz önüne alındığında, Rbfen iyi performansı gösteriyor gibi görünüyor.

Kötü işlev ve dağınık veriler

Ve hepimizin beklediği an:

fig5: kötü dağınık enterpolasyon

Vazgeçen büyük bir sürpriz değil interp2d. Aslında, interp2dsizi aradığınızda RuntimeWarning, spline'ın inşa edilmesinin imkansızlığı konusunda bazı dostların şikayet etmesini beklemelisiniz . Diğer iki yönteme gelince Rbf, sonucun tahmin edildiği alan sınırlarının yakınında bile en iyi çıktıyı üretiyor gibi görünüyor.


Öyleyse, tercih sırasını azaltarak üç yöntem hakkında birkaç söz söyleyeyim (böylece en kötüsü, herkes tarafından okunma olasılığı en düşük olanıdır).

scipy.interpolate.Rbf

RbfSınıf "radyal tabanlı fonksiyonlar" anlamına gelir. Dürüst olmak gerekirse, bu yazı için araştırmaya başlayana kadar bu yaklaşımı hiç düşünmedim, ancak gelecekte bunları kullanacağımdan oldukça eminim.

Tıpkı spline tabanlı yöntemlerde olduğu gibi (ileriye bakın), kullanım iki adımda gelir: Birincisi Rbf, giriş verilerine dayalı olarak çağrılabilir bir sınıf örneği oluşturur ve daha sonra, enterpolasyonlu sonucu elde etmek için belirli bir çıktı ağı için bu nesneyi çağırır. Sorunsuz yukarı örnekleme testinden örnek:

import scipy.interpolate as interp
zfun_smooth_rbf = interp.Rbf(x_sparse, y_sparse, z_sparse_smooth, function='cubic', smooth=0)  # default smooth=0 for interpolation
z_dense_smooth_rbf = zfun_smooth_rbf(x_dense, y_dense)  # not really a function, but a callable class instance

Bu durumda hem giriş hem de çıkış noktalarının 2d diziler olduğunu ve çıktının herhangi bir çaba ile ve çaba olmadan z_dense_smooth_rbfaynı şekle sahip olduğunu unutmayın . Ayrıca enterpolasyon için rastgele boyutları desteklediğini unutmayın .x_densey_denseRbf

Yani, scipy.interpolate.Rbf

  • çılgın girdi verileri için bile iyi davranan çıktı üretir
  • daha yüksek boyutlarda enterpolasyonu destekler
  • Giriş noktalarının dışbükey gövdesi dışında tahminler yapar (elbette ekstrapolasyon her zaman bir kumardır ve genellikle buna hiç güvenmemelisiniz)
  • ilk adım olarak bir enterpolatör oluşturur, bu nedenle onu çeşitli çıktı noktalarında değerlendirmek daha az ek çaba gerektirir
  • rastgele şekle sahip çıktı noktalarına sahip olabilir (dikdörtgen ağlarla sınırlandırılmanın aksine, daha sonra bakın)
  • giriş verilerinin simetrisini korumaya eğilimli
  • Anahtar kelime için radyal fonksiyonların birden fazla türde destekler function: multiquadric, inverse, gaussian, linear, cubic, quintic, thin_plateve kullanıcı tanımlı keyfi

scipy.interpolate.griddata

Eski favorim, griddatakeyfi boyutlarda enterpolasyon için genel bir iş gücü. Düğüm noktalarının dışbükey gövdesi dışındaki noktalar için tek bir ön ayar değeri belirlemenin ötesinde bir ekstrapolasyon gerçekleştirmez, ancak ekstrapolasyon çok kararsız ve tehlikeli bir şey olduğundan, bu mutlaka bir con değildir. Kullanım örneği:

z_dense_smooth_griddata = interp.griddata(np.array([x_sparse.ravel(),y_sparse.ravel()]).T,
                                          z_sparse_smooth.ravel(),
                                          (x_dense,y_dense), method='cubic')   # default method is linear

Biraz kludgy sözdizimine dikkat edin. Giriş noktaları [N, D], Dboyutlarda bir şekil dizisinde belirtilmelidir . Bunun için önce 2d koordinat dizilerimizi (kullanarak ravel) düzleştirmeli , ardından dizileri birleştirmeli ve sonucu transpoze etmeliyiz . Bunu yapmanın birden çok yolu var, ancak hepsi hantal görünüyor. Giriş zverilerinin de düzleştirilmesi gerekir. Çıktı noktaları söz konusu olduğunda biraz daha özgürlüğümüz var: bazı nedenlerden dolayı bunlar çok boyutlu dizilerin bir demeti olarak da belirtilebilir. Not bu helpbölgesinin griddata, yanıltıcı aynı için de geçerli olduğu anlaşılacağı gibi giriş (en azından sürüm 0.17.0 için) noktaları:

griddata(points, values, xi, method='linear', fill_value=nan, rescale=False)
    Interpolate unstructured D-dimensional data.

    Parameters
    ----------
    points : ndarray of floats, shape (n, D)
        Data point coordinates. Can either be an array of
        shape (n, D), or a tuple of `ndim` arrays.
    values : ndarray of float or complex, shape (n,)
        Data values.
    xi : ndarray of float, shape (M, D)
        Points at which to interpolate data.

Kısaca, scipy.interpolate.griddata

  • çılgın girdi verileri için bile iyi davranan çıktı üretir
  • daha yüksek boyutlarda enterpolasyonu destekler
  • ekstrapolasyon yapmazsa, giriş noktalarının dışbükey gövdesi dışındaki çıktı için tek bir değer ayarlanabilir (bkz. fill_value)
  • tek bir çağrıda enterpolasyonlu değerleri hesaplar, böylece birden fazla çıktı noktası kümesinin araştırılması sıfırdan başlar
  • keyfi şekle sahip çıktı noktaları olabilir
  • 1d ve 2d'de kübik, rastgele boyutlarda en yakın komşu ve doğrusal enterpolasyonu destekler. En yakın komşu ve lineer enterpolasyon kullanımı NearestNDInterpolatorve LinearNDInterpolatorsırasıyla başlık altında. 1d kübik enterpolasyon bir spline kullanır, 2d kübik enterpolasyon CloughTocher2DInterpolatorsürekli türevlenebilir parça parça kübik enterpolatör oluşturmak için kullanır .
  • giriş verilerinin simetrisini ihlal edebilir

scipy.interpolate.interp2d/scipy.interpolate.bisplrep

Tartışmamın tek nedeni interp2dve akrabalarını aldatıcı bir isme sahip olması ve insanların onu kullanmaya çalışması muhtemeldir. Spoiler uyarısı: kullanmayın (scipy sürüm 0.17.0'dan itibaren). Özellikle iki boyutlu enterpolasyon için kullanıldığı için önceki konulardan daha özeldir, ancak bunun çok değişkenli enterpolasyon için en yaygın durum olduğundan şüpheleniyorum.

Sözdizimine gelince, ilk olarak gerçek enterpolasyonlu değerleri sağlamak için çağrılabilen bir enterpolasyon örneği oluşturmaya ihtiyaç duyması interp2dile benzerdir Rbf. Bununla birlikte, bir yakalama var: çıktı noktaları dikdörtgen bir ağ üzerinde konumlandırılmalıdır, bu nedenle, enterpolatöre yapılan çağrıya giden girdiler, aşağıdaki gibi çıktı ızgarasını kapsayan 1d vektörler olmalıdır numpy.meshgrid:

# reminder: x_sparse and y_sparse are of shape [6, 7] from numpy.meshgrid
zfun_smooth_interp2d = interp.interp2d(x_sparse, y_sparse, z_sparse_smooth, kind='cubic')   # default kind is 'linear'
# reminder: x_dense and y_dense are of shape [20, 21] from numpy.meshgrid
xvec = x_dense[0,:] # 1d array of unique x values, 20 elements
yvec = y_dense[:,0] # 1d array of unique y values, 21 elements
z_dense_smooth_interp2d = zfun_smooth_interp2d(xvec,yvec)   # output is [20, 21]-shaped array

Kullanırken en yaygın hatalardan biri, interp2dtam 2d ağlarınızı enterpolasyon çağrısına koymaktır, bu da aşırı bellek tüketimine ve umarım aceleye yol açar MemoryError.

Şimdi, en büyük sorun, interp2dçoğu zaman işe yaramamasıdır. Bunu anlamak için kaputun altına bakmamız gerekiyor. Bunun , FITPACK rutinleri (Fortran'da yazılmıştır) için sarmalayıcı olan interp2dalt düzey işlevler bisplrep+ için bir sarmalayıcı olduğu ortaya çıktı bisplev. Önceki örneğe eşdeğer çağrı şöyle olacaktır:

kind = 'cubic'
if kind=='linear':
    kx=ky=1
elif kind=='cubic':
    kx=ky=3
elif kind=='quintic':
    kx=ky=5
# bisplrep constructs a spline representation, bisplev evaluates the spline at given points
bisp_smooth = interp.bisplrep(x_sparse.ravel(),y_sparse.ravel(),z_sparse_smooth.ravel(),kx=kx,ky=ky,s=0)
z_dense_smooth_bisplrep = interp.bisplev(xvec,yvec,bisp_smooth).T  # note the transpose

Şimdi, burada bir şey hakkında interp2d(scipy sürümü 0.17.0 olarak) güzel vardır: yorum yapıninterpolate/interpolate.py için interp2d:

if not rectangular_grid:
    # TODO: surfit is really not meant for interpolation!
    self.tck = fitpack.bisplrep(x, y, z, kx=kx, ky=ky, s=0.0)

ve gerçekten de içinde interpolate/fitpack.py, içinde bisplrepvar bazı kurulum ve sonuçta

tx, ty, c, o = _fitpack._surfit(x, y, z, w, xb, xe, yb, ye, kx, ky,
                                task, s, eps, tx, ty, nxest, nyest,
                                wrk, lwrk1, lwrk2)                 

Ve bu kadar. Altta yatan rutinler interp2dgerçekten enterpolasyon gerçekleştirmek için tasarlanmamıştır. Yeterince iyi işlenmiş veriler için yeterli olabilirler, ancak gerçekçi koşullar altında muhtemelen başka bir şey kullanmak isteyeceksiniz.

Sadece sonuçlandırmak için, interpolate.interp2d

  • iyi ayarlanmış verilerle bile yapaylıklara yol açabilir
  • özellikle iki değişkenli problemler içindir ( interpnbir ızgarada tanımlanan giriş noktaları için sınırlı olmasına rağmen )
  • ekstrapolasyon yapar
  • ilk adım olarak bir enterpolatör oluşturur, bu nedenle onu çeşitli çıktı noktalarında değerlendirmek daha az ek çaba gerektirir
  • yalnızca dikdörtgen bir ızgara üzerinde çıktı üretebilir, dağınık çıktı için bir döngüde interpolatörü çağırmanız gerekir
  • doğrusal, kübik ve beşli enterpolasyonu destekler
  • giriş verilerinin simetrisini ihlal edebilir

1 ' in temel işlevlerinin cubicve lineartürünün Rbfaynı adı taşıyan diğer aradeğerlere tam olarak karşılık gelmediğinden oldukça eminim .
2 Bu NaN'ler aynı zamanda yüzey grafiğinin bu kadar tuhaf görünmesinin nedenidir: matplotlib tarihsel olarak karmaşık 3 boyutlu nesneleri doğru derinlik bilgisiyle çizmekte zorluklar yaşamaktadır. Verilerdeki NaN değerleri oluşturucuyu karıştırır, bu nedenle yüzeyin arka tarafta olması gereken kısımları önde olacak şekilde çizilir. Bu, enterpolasyon değil, görselleştirme ile ilgili bir sorundur.


2
Rbf, veri noktalarının sayısına ve boyutların sayısına bağlı olarak griddata'dan daha fazla bellek tüketebilir. Ayrıca griddata, 2 adımda Rbf gibi kullanılabilen temel nesne LinearNDInterpolator'a sahiptir.
denfromufa

1
Griddata'nın kübik enterpolasyonu 2 (?) Boyutla sınırlıdır. Daha yüksek boyutlar için, chebfun'a dayalı smolyak seyrek ızgaralar dikkate değerdir.
denfromufa

1
yorumlarımı, tüm dağınık enterpolasyon seçeneklerini araştırdığım bu bağlantıyla sonlandırmama izin verin: scicomp.stackexchange.com/questions/19137/…
denfromufa

4
griddata doğrusal enterpolasyonu yereldir, griddata kübik enterpolasyonu globaldir. Ekstrapolasyon desteklenmiyor, çünkü sürekliliği / farklılaştırılabilirliği nasıl koruyacağımı anlamaya zamanım olmadı. Rbf, küçük veri kümeleri için iyidir, ancak n veri noktasının enterpolasyonunu yapmak için nxn matrisini tersine çevirmesi gerekir, bu da n> 5000'den sonra imkansız hale gelir. Rbf ayrıca verilerin dağıtımına duyarlı olabilir ve parametrelerini elle ayarlamanız gerekebilir. Büyük veri kümeleri için Rbf yapmak mümkündür, ancak bu scipy'de uygulanmaz.
pv.

1
işte büyük veri kümeleri için rbf: github.com/scipy/scipy/issues/5180
denfromufa
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.