Проблема с 2D-интерполяцией в непрямоугольной сетке SciPy

Я пытался использовать scipy.interpolate.bisplrep () и scipy.interpolate.interp2d (), чтобы найти интерполяции для данных на моем (218x135) ) 2D сферически-полярная сетка. К ним я передаю 2D-массивы, X и Y, декартовых позиций моих узлов сетки. Я продолжаю получать ошибки, подобные следующим (для линейного взаимодействия с interp2d):

"Предупреждение: больше нельзя добавлять узлы, потому что дополнительный узел будет совпадать со старым. Вероятно, причина: слишком маленький или слишком большой вес к неточной точке данных. (FP> s) kx, ky = 1,1 nx, ny = 4,5 м = 29430 фп = 1390609718.902140 s = 0,000000 "

Я получаю аналогичный результат для двумерных сплайнов со значением по умолчанию параметра сглаживания s и т. д. Мои данные являются гладкими Я приложил свой код ниже на случай, если я что-то делаю явно неправильно.

Есть идеи? Спасибо! Кайл

class Field(object):
  Nr = 0
  Ntheta = 0
  grid = np.array([])

  def __init__(self, Nr, Ntheta, f):
    self.Nr = Nr
    self.Ntheta = Ntheta
    self.grid = np.empty([Nr, Ntheta])
    for i in range(Nr):
      for j in range(Ntheta):
        self.grid[i,j] = f[i*Ntheta + j]


def calculate_lines(filename):
  ri,ti,r,t,Br,Bt,Bphi,Bmag = np.loadtxt(filename, skiprows=3,\
    usecols=(1,2,3,4,5,6,7,9), unpack=True)
  Nr = int(max(ri)) + 1
  Ntheta = int(max(ti)) + 1

  ### Initialise coordinate grids ###
  X = np.empty([Nr, Ntheta])
  Y = np.empty([Nr, Ntheta])
  for i in range(Nr):
    for j in range(Ntheta):
      indx = i*Ntheta + j
      X[i,j] = r[indx]*sin(t[indx])
      Y[i,j] = r[indx]*cos(t[indx])

  ### Initialise field objects ###
  Bradial = Field(Nr=Nr, Ntheta=Ntheta, f=Br)

  ### Interpolate the fields ###
  intp_Br = interpolate.interp2d(X, Y, Bradial.grid, kind='linear')

  #rbf_0 = interpolate.Rbf(X,Y, Bradial.grid, epsilon=2)

  return
7
задан Kyle 20 August 2010 в 14:38
поделиться

1 ответ

Добавлено 27 августа: Кайл следил за этим на тема пользователя scipy.

30Aug: @Kyle, похоже, есть путаница между Cartesion X,Y и полярным Xnew,Ynew. См. «полярный» в слишком длинных примечаниях ниже.

alt text

# griddata vs SmoothBivariateSpline
# http://stackoverflow.com/questions/3526514/
#   problem-with-2d-interpolation-in-scipy-non-rectangular-grid

# http://www.scipy.org/Cookbook/Matplotlib/Gridding_irregularly_spaced_data
# http://en.wikipedia.org/wiki/Natural_neighbor
# http://docs.scipy.org/doc/scipy/reference/tutorial/interpolate.html

from __future__ import division
import sys
import numpy as np
from scipy.interpolate import SmoothBivariateSpline  # $scipy/interpolate/fitpack2.py
from matplotlib.mlab import griddata

__date__ = "2010-10-08 Oct"  # plot diffs, ypow
    # "2010-09-13 Sep"  # smooth relative

def avminmax( X ):
    absx = np.abs( X[ - np.isnan(X) ])
    av = np.mean(absx)
    m, M = np.nanmin(X), np.nanmax(X)
    histo = np.histogram( X, bins=5, range=(m,M) ) [0]
    return "av %.2g  min %.2g  max %.2g  histo %s" % (av, m, M, histo)

def cosr( x, y ):
    return 10 * np.cos( np.hypot(x,y) / np.sqrt(2) * 2*np.pi * cycle )

def cosx( x, y ):
    return 10 * np.cos( x * 2*np.pi * cycle )

def dipole( x, y ):
    r = .1 + np.hypot( x, y )
    t = np.arctan2( y, x )
    return np.cos(t) / r**3

#...............................................................................
testfunc = cosx
Nx = Ny = 20  # interpolate random Nx x Ny points -> Newx x Newy grid
Newx = Newy = 100
cycle = 3
noise = 0
ypow = 2  # denser => smaller error
imclip = (-5., 5.)  # plot trierr, splineerr to same scale
kx = ky = 3
smooth = .01  # Spline s = smooth * z2sum, see note
    # s is a target for sum (Z() - spline())**2  ~ Ndata and Z**2;
    # smooth is relative, s absolute
    # s too small => interpolate/fitpack2.py:580: UserWarning: ier=988, junk out
    # grr error message once only per ipython session
seed = 1
plot = 0

exec "\n".join( sys.argv[1:] )  # run this.py N= ...
np.random.seed(seed)
np.set_printoptions( 1, threshold=100, suppress=True )  # .1f

print 80 * "-"
print "%s  Nx %d Ny %d -> Newx %d Newy %d  cycle %.2g noise %.2g  kx %d ky %d smooth %s" % (
    testfunc.__name__, Nx, Ny, Newx, Newy, cycle, noise, kx, ky, smooth)

#...............................................................................

    # interpolate X Y Z to xnew x ynew --
X, Y = np.random.uniform( size=(Nx*Ny, 2) ) .T
Y **= ypow
    # 1d xlin ylin -> 2d X Y Z, Ny x Nx --
    # xlin = np.linspace( 0, 1, Nx )
    # ylin = np.linspace( 0, 1, Ny )
    # X, Y = np.meshgrid( xlin, ylin )
Z = testfunc( X, Y )  # Ny x Nx
if noise:
    Z += np.random.normal( 0, noise, Z.shape )
# print "Z:\n", Z
z2sum = np.sum( Z**2 )

xnew = np.linspace( 0, 1, Newx )
ynew = np.linspace( 0, 1, Newy )
Zexact = testfunc( *np.meshgrid( xnew, ynew ))
if imclip is None:
    imclip = np.min(Zexact), np.max(Zexact)
xflat, yflat, zflat = X.flatten(), Y.flatten(), Z.flatten()

#...............................................................................
print "SmoothBivariateSpline:"
fit = SmoothBivariateSpline( xflat, yflat, zflat, kx=kx, ky=ky, s = smooth * z2sum )
Zspline = fit( xnew, ynew ) .T  # .T ??

splineerr = Zspline - Zexact
print "Zspline - Z:", avminmax(splineerr)
print "Zspline:    ", avminmax(Zspline)
print "Z:          ", avminmax(Zexact)
res = fit.get_residual()
print "residual %.0f  res/z2sum %.2g" % (res, res / z2sum)
# print "knots:", fit.get_knots()
# print "Zspline:", Zspline.shape, "\n", Zspline
print ""

#...............................................................................
print "griddata:"
Ztri = griddata( xflat, yflat, zflat, xnew, ynew )
        # 1d x y z -> 2d Ztri on meshgrid(xnew,ynew)

nmask = np.ma.count_masked(Ztri)
if nmask > 0:
    print "info: griddata: %d of %d points are masked, not interpolated" % (
        nmask, Ztri.size)
    Ztri = Ztri.data  # Nans outside convex hull
trierr = Ztri - Zexact
print "Ztri - Z:", avminmax(trierr)
print "Ztri:    ", avminmax(Ztri)
print "Z:       ", avminmax(Zexact)
print ""

#...............................................................................
if plot:
    import pylab as pl
    nplot = 2
    fig = pl.figure( figsize=(10, 10/nplot + .5) )
    pl.suptitle( "Interpolation error: griddata - %s, BivariateSpline - %s" % (
        testfunc.__name__, testfunc.__name__ ), fontsize=11 )

    def subplot( z, jplot, label ):
        ax = pl.subplot( 1, nplot, jplot )
        im = pl.imshow(
            np.clip( z, *imclip ),  # plot to same scale
            cmap=pl.cm.RdYlBu,
            interpolation="nearest" )
                # nearest: squares, else imshow interpolates too
                # todo: centre the pixels
        ny, nx = z.shape
        pl.scatter( X*nx, Y*ny, edgecolor="y", s=1 )  # for random XY
        pl.xlabel(label)
        return [ax, im]

    subplot( trierr, 1,
        "griddata, Delaunay triangulation + Natural neighbor: max %.2g" %
        np.nanmax(np.abs(trierr)) )

    ax, im = subplot( splineerr, 2,
        "SmoothBivariateSpline kx %d ky %d smooth %.3g: max %.2g" % (
        kx, ky, smooth, np.nanmax(np.abs(splineerr)) ))

    pl.subplots_adjust( .02, .01, .92, .98, .05, .05 )  # l b r t
    cax = pl.axes([.95, .05, .02, .9])  # l b w h
    pl.colorbar( im, cax=cax )  # -1.5 .. 9 ??
    if plot >= 2:
        pl.savefig( "tmp.png" )
    pl.show() 

Примечания по двумерной интерполяции, BivariateSpline и griddata.

scipy.interpolate.*BivariateSpline и matplotlib.mlab.griddata оба принимают массивы 1d в качестве аргументов:

Znew = griddata( X,Y,Z, Xnew,Ynew )
    # 1d X Y Z Xnew Ynew -> interpolated 2d Znew on meshgrid(Xnew,Ynew)
assert X.ndim == Y.ndim == Z.ndim == 1  and  len(X) == len(Y) == len(Z)

Входные данные X,Y,Z описывают поверхность или облако точек в трехмерном пространстве: X,Y (или широта, долгота или ...) точки на плоскости, и Z поверхность или местность над ней. X,Y могут заполнять большую часть прямоугольника [Xmin .. Xmax] x [Ymin .. Ymax], или может быть просто волнистой S или Y внутри него. Поверхность Z может быть гладкой или гладкой + немного шума, или вовсе не гладкие, шероховатые вулканические горы.

Xnew и Ynew обычно равны 1d, описывая прямоугольную сетку. |Xnew| х |Yновый| точки, где вы хотите интерполировать или оценить Z.
Znew = griddata(...) возвращает двумерный массив по этой сетке, np.meshgrid(Xnew,Ynew):

Znew[Xnew0,Ynew0], Znew[Xnew1,Ynew0], Znew[Xnew2,Ynew0] ...
Znew[Xnew0,Ynew1] ...
Znew[Xnew0,Ynew2] ...
...

Xnew,Ynew указывает далеко от любой из входных X,Y проблем с заклинаниями. griddata проверяет следующее:

Замаскированный массив возвращается, если какие-либо точки сетки находятся за пределами выпуклости. корпус определяется входными данными (экстраполяция не производится).

(«Выпуклая оболочка» — это площадь внутри воображаемой круглая резинка, натянутая вокруг всех точек X, Y.)

griddata сначала строит триангуляцию Делоне ввода X, Y, затем выполнение Естественный сосед интерполяция. Это надежно и довольно быстро.

BivariateSpline, тем не менее, может экстраполировать, генерирование диких колебаний без предупреждения. Кроме того, все процедуры *Spline в Fitpack очень чувствительны к параметру сглаживания S. В книге Диркса (books.google isbn 019853440X стр. 89) говорится:
если S слишком мало, аппроксимация сплайна будет слишком волнистой. и улавливает слишком много шума (переобучение);
если S слишком велико, сплайн будет слишком гладким и сигнал будет потерян (недостаточное соответствие).

Интерполяция разбросанных данных затруднена, сглаживание не просто, и то, и другое вместе очень сложно. Что должен делать интерполятор с большими дырами в XY или с очень зашумленным Z? («Если вы хотите его продать, вам придется его описать».)

Еще примечания, напечатанные мелким шрифтом:

1d против 2d: некоторые интерполяторы принимают X, Y, Z либо 1d, либо 2d. . Другие принимают только 1d, поэтому сглаживают перед интерполяцией:

Xmesh, Ymesh = np.meshgrid( np.linspace(0,1,Nx), np.linspace(0,1,Ny) )
Z = f( Xmesh, Ymesh )  # Nx x Ny
Znew = griddata( Xmesh.flatten(), Ymesh.flatten(), Z.flatten(), Xnew, Ynew )

На маскированных массивах: matplotlib обрабатывает их просто отлично, построение только немаскированных / не-NaN точек. Но я бы не стал держать пари, что функции bozo numpy/scipy вообще будут работать. Проверьте наличие интерполяции за пределами выпуклой оболочки X,Y следующим образом:

Znew = griddata(...)
nmask = np.ma.count_masked(Znew)
if nmask > 0:
    print "info: griddata: %d of %d points are masked, not interpolated" % (
        nmask, Znew.size)
    # Znew = Znew.data  # array with NaNs

В полярных координатах: X,Y и Xnew,Ynew должны быть в одном месте, оба Cartesion, или оба в [rmin .. rmax] x [tmin .. tmax].
Чтобы построить точки (r, theta, z) в 3d:

from mpl_toolkits.mplot3d import Axes3D
Znew = griddata( R,T,Z, Rnew,Tnew )
ax = Axes3D(fig)
ax.plot_surface( Rnew * np.cos(Tnew), Rnew * np.sin(Tnew), Znew )

См. также (не пробовал):

ax = subplot(1,1,1, projection="polar", aspect=1.)
ax.pcolormesh(theta, r, Z)


Два совета для осторожного программиста:

проверить выбросы или смешное масштабирование:

def minavmax( X ):
    m = np.nanmin(X)
    M = np.nanmax(X)
    av = np.mean( X[ - np.isnan(X) ])  # masked ?
    histo = np.histogram( X, bins=5, range=(m,M) ) [0]
    return "min %.2g  av %.2g  max %.2g  histo %s" % (m, av, M, histo)

for nm, x in zip( "X Y Z  Xnew Ynew Znew".split(),
                (X,Y,Z, Xnew,Ynew,Znew) ):
    print nm, minavmax(x)

проверить интерполяцию с простыми данными:

interpolate( X,Y,Z, X,Y )  -- interpolate at the same points
interpolate( X,Y, np.ones(len(X)), Xnew,Ynew )  -- constant 1 ?
15
ответ дан 6 December 2019 в 14:00
поделиться
Другие вопросы по тегам:

Похожие вопросы: