[python] scipy를 사용하여 2 차원 보간을 수행하려면 어떻게해야합니까?

이 Q & A는 scipy를 사용한 2 차원 (및 다차원) 보간에 관한 표준 (-ish)입니다. 다양한 다차원 보간 방법의 기본 구문에 관한 질문이 자주 있습니다. 이것도 곧바로 설정하고 싶습니다.

나는 흩어져 두 차원 데이터 포인트의 세트를 가지고 있고, 나는 바람직하게는 같은 것을 사용하여 좋은 표면에로 플롯 싶습니다 contourfplot_surfacematplotlib.pyplot. scipy를 사용하여 2 차원 또는 다차원 데이터를 메쉬에 어떻게 보간 할 수 있습니까?

scipy.interpolate하위 패키지를 찾았 지만 interp2d또는 bisplrep또는 griddata또는을 사용할 때 계속 오류가 발생합니다 rbf. 이 메소드의 적절한 구문은 무엇입니까?



답변

면책 조항 : 저는 주로 구문 적 고려 사항과 일반적인 동작을 염두에두고이 게시물을 작성하고 있습니다. 설명 된 방법의 메모리 및 CPU 측면에 익숙하지 않으며이 답변은 보간 품질이 고려할 주요 측면이 될 수 있도록 합리적으로 작은 데이터 집합을 가진 사람들을 대상으로합니다. 매우 큰 데이터 세트로 작업 할 때 더 나은 성능의 방법 (즉 griddataRbf)이 실행 가능하지 않을 수 있음을 알고 있습니다.

세 가지 종류의 다차원 보간 방법 ( interp2d/ splines griddataRbf) 을 비교해 보겠습니다 . 두 종류의 보간 작업과 두 종류의 기본 함수 (보간 할 지점)를 적용합니다. 구체적인 예는 2 차원 보간법을 보여 주지만 실행 가능한 방법은 임의의 차원에서 적용 할 수 있습니다. 각 방법은 다양한 종류의 보간을 제공합니다. 모든 경우에 3 차 보간 (또는 가까운 1 )을 사용합니다. 보간을 사용할 때마다 원시 데이터와 비교하여 편향이 발생하고 사용 된 특정 방법이 최종 결과물에 영향을 미친다는 점에 유의하는 것이 중요합니다. 항상이를 인식하고 책임감있게 보간하십시오.

두 가지 보간 작업은 다음과 같습니다.

  1. 업 샘플링 (입력 데이터는 직사각형 그리드에, 출력 데이터는 밀도가 높은 그리드에 있음)
  2. 분산 된 데이터를 일반 그리드에 보간

두 가지 기능 (도메인을 통해 [x,y] in [-1,1]x[-1,1])은

  1. 부드럽고 친근한 기능 : cos(pi*x)*sin(pi*y); 범위[-1, 1]
  2. 사악한 (특히 비 연속적인) 기능 : x*y/(x^2+y^2)원점 근처에서 값 0.5 범위[-0.5, 0.5]

그 모습은 다음과 같습니다.

그림 1 : 테스트 기능

먼저이 네 가지 테스트에서 세 가지 메서드가 어떻게 작동하는지 보여준 다음 세 가지 모두의 구문을 자세히 설명하겠습니다. 메소드에서 무엇을 기대해야하는지 안다면 구문을 배우는 데 시간을 낭비하고 싶지 않을 수 있습니다 ( interp2d).

테스트 데이터

명확성을 위해 입력 데이터를 생성하는 코드는 다음과 같습니다. 이 특정 경우에는 데이터의 기본 기능을 분명히 알고 있지만 보간 방법에 대한 입력을 생성하는 데만 이것을 사용합니다. 편의상 (그리고 대부분 데이터 생성을 위해) numpy를 사용하지만 scipy만으로도 충분합니다.

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)

부드러운 기능과 업 샘플링

가장 쉬운 작업부터 시작하겠습니다. 부드러운 테스트 기능을 위해 모양의 메시 [6,7]에서 하나의 메시로 업 샘플링하는 방법은 다음과 같습니다 [20,21].

fig2 : 부드러운 업 샘플링

이것은 간단한 작업이지만 이미 출력간에 미묘한 차이가 있습니다. 언뜻보기에 세 가지 출력 모두 합리적입니다. 기본 기능에 대한 사전 지식을 바탕으로 주목해야 할 두 가지 기능이 있습니다.griddata . 데이터를 가장 많이 왜곡하는 . y==-1플롯 의 경계 ( x라벨에 가장 가깝 음)에 유의하십시오 . 함수는 엄격하게 0이어야합니다 ( y==-1평활 함수에 대한 절 점선 이기 때문에 ). 그러나 griddata. 또한 참고 x==-1플롯 (뒤에서, 왼쪽)의 경계 : 기본 기능은 (경계부 근처 제로 기울기를 의미)는 로컬 최대 값을 가지고 [-1, -0.5], 아직 griddata명확하게 출력 프로그램이 영역의 비 – 제로 기울기. 그 효과는 미묘하지만 편견입니다. (충실도Rbf라고 불리는 방사형 함수의 기본 선택으로 훨씬 더 좋습니다 multiquadratic.)

사악한 기능과 업 샘플링

좀 더 어려운 작업은 우리의 사악한 기능에 대해 업 샘플링을 수행하는 것입니다.

fig3 : 사악한 업 샘플링

세 가지 방법간에 분명한 차이점이 나타나기 시작했습니다. 표면 플롯을 보면 출력에 명백한 허위 극단이 나타납니다 interp2d(플롯 된 표면의 오른쪽에있는 두 개의 혹에 주목). 동안griddataRbf첫눈에 유사한 결과를 생성하는 것, 후자는 근처에 깊은 최소를 생성하는 것 [0.4, -0.4]즉, 기본 기능에없는 것입니다.

그러나 한 가지 중요한 측면이 있습니다. Rbf 훨씬 우수한 이 있습니다. 기본 기능의 대칭을 존중합니다 (물론 샘플 메시의 대칭에 의해 가능함). 의 출력 griddata은 부드러운 케이스에서 이미 약하게 보이는 샘플 포인트의 대칭을 깨뜨립니다.

부드러운 기능과 분산 된 데이터

대부분의 경우 분산 된 데이터에 대해 보간을 수행하려고합니다. 이런 이유로 나는 이러한 테스트가 더 중요 할 것으로 기대합니다. 위와 같이 샘플 포인트는 관심 영역에서 의사 균일하게 선택되었습니다. 현실적인 시나리오에서는 측정 할 때마다 추가 노이즈가 발생할 수 있으며 시작하기 위해 원시 데이터를 보간하는 것이 적절한 지 여부를 고려해야합니다.

부드러운 기능을위한 출력 :

그림 4 : 부드러운 산란 보간

이제 벌써 약간의 공포 쇼가 진행되고 있습니다. 최소한의 정보를 보존하기 위해 플로팅 전용으로 출력 interp2d을 사이 에서 사이로 잘라 냈습니다 [-1, 1]. 일부 기본 모양이 있지만 방법이 완전히 분해되는 소음이 심한 영역이 있다는 것은 분명합니다. 두 번째 경우 griddata는 모양을 상당히 멋지게 재현하지만 등고선 플롯의 경계에있는 흰색 영역에 유의하십시오. 이는 griddata입력 데이터 포인트의 볼록 껍질 내에서만 작동 한다는 사실 때문입니다 (즉, 어떠한 외삽도 수행하지 않음 ). 볼록 껍질 외부에있는 출력 포인트에 대해 기본 NaN 값을 유지했습니다. 2 이러한 기능을 고려할 때 Rbf가장 잘 수행되는 것 같습니다.

사악한 기능과 분산 된 데이터

그리고 우리 모두가 기다려온 순간 :

fig5 : 사악한 산란 보간

interp2d포기 하는 것은 놀라운 일이 아닙니다 . 사실, 호출하는 동안 스플라인을 구성 할 수 없다고 불평 interp2d하는 친절한 사람들을 예상해야합니다 RuntimeWarning. 다른 두 가지 방법 Rbf은 결과가 외삽되는 영역의 경계 근처에서도 최상의 출력을 생성하는 것 같습니다.


그래서 세 가지 방법에 대해 선호하는 순서를 내림차순으로 몇 마디 말하겠습니다 (최악의 것은 누구에게도 읽을 가능성이 가장 적습니다).

scipy.interpolate.Rbf

Rbf클래스는 “방사형 기저 함수”를 의미합니다. 솔직히 말해서이 게시물을 조사하기 전에는이 접근 방식을 고려한 적이 없지만 앞으로이 방법을 사용할 것이라고 확신합니다.

스플라인 기반 메서드 (나중 참조)와 마찬가지로 사용법은 두 단계로 이루어집니다. 첫 번째 단계 Rbf는 입력 데이터를 기반으로 호출 가능한 클래스 인스턴스를 만든 다음 주어진 출력 메시에 대해이 개체를 호출하여 보간 된 결과를 얻습니다. 부드러운 업 샘플링 테스트의 예 :

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

참고 입력 및 출력 지점이 경우 2 차원 배열으며, 출력은 z_dense_smooth_rbf동일한 형상을 가지며, x_dense그리고 y_dense모든 노력없이. 또한 Rbf보간을 위해 임의의 치수를 지원합니다.

그래서, scipy.interpolate.Rbf

  • 미친 입력 데이터에 대해서도 잘 작동하는 출력을 생성합니다.
  • 더 높은 차원의 보간 지원
  • 입력 포인트의 볼록 껍질 밖에서 외삽합니다 (물론 외삽은 항상 도박이며 일반적으로 전혀 의존해서는 안됩니다).
  • 첫 번째 단계로 보간기를 생성하므로 다양한 출력 지점에서 평가하는 것이 추가 노력이 적습니다.
  • 임의의 모양의 출력 포인트를 가질 수 있습니다 (직사각형 메시로 제한되는 것과 반대, 나중에 참조)
  • 입력 데이터의 대칭성을 유지하는 경향이 있습니다.
  • 키워드 반경 여러 가지 기능을 지원한다 function: multiquadric, inverse, gaussian, linear, cubic, quintic, thin_plate및 사용자 정의 임의

scipy.interpolate.griddata

내가 가장 좋아 griddata하는는 임의의 차원에서 보간하는 일반적인 도구입니다. 노드 포인트의 볼록 껍질 외부에있는 포인트에 대해 사전 설정된 단일 값을 설정하는 것 이상으로 외삽을 수행하지는 않지만 외삽은 매우 변덕스럽고 위험한 것이므로 반드시 사기가 아닙니다. 사용 예 :

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

약간 복잡한 구문에 유의하십시오. 입력 점 모양의 배열로 지정 될 필요가 [N, D]있는 D크기. 이를 위해 먼저 2d 좌표 배열을 평탄화 (사용 ravel) 한 다음 배열을 연결하고 결과를 전치해야합니다. 이를 수행하는 방법에는 여러 가지가 있지만 모두 부피가 큰 것 같습니다. 입력 z데이터도 평면화되어야합니다. 출력 포인트에 관해서는 좀 더 자유 롭습니다. 어떤 이유로 이것들은 다차원 배열의 튜플로 지정 될 수도 있습니다. 참고 그 helpgriddata, 오해의 소지가있다이 같은이 마찬가지입니다 제안으로 입력 (적어도 버전 0.17.0을위한) 포인트 :

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.

간단히 말해서 scipy.interpolate.griddata

  • 미친 입력 데이터에 대해서도 잘 작동하는 출력을 생성합니다.
  • 더 높은 차원의 보간 지원
  • 외삽을 수행하지 않으면 입력 포인트의 볼록 껍질 외부의 출력에 대해 단일 값을 설정할 수 있습니다 (참조 fill_value ).
  • 단일 호출에서 보간 된 값을 계산하므로 여러 출력 포인트 세트를 처음부터 프로 빙하기 시작합니다.
  • 임의의 모양의 출력 포인트를 가질 수 있습니다.
  • 1d 및 2d에서 3 차, 임의 차원에서 가장 가까운 이웃 및 선형 보간을 지원합니다. 최근 접 이웃 및 선형 보간 사용 NearestNDInterpolator및 내부 LinearNDInterpolator에서 각각 사용. 1d 큐빅 보간은 스플라인을 사용하고 2d 큐빅 보간은CloughTocher2DInterpolator 지속적으로 미분 할 수있는 조각 별 큐브 보간기를 구성하는 됩니다.
  • 입력 데이터의 대칭을 위반할 수 있음

scipy.interpolate.interp2d/scipy.interpolate.bisplrep

내가 논의하는 유일한 이유는 interp2d 와 그 친척은 그것이기만적인 이름을 가지고 있고 사람들이 그것을 사용하려고 시도 할 가능성이 있기 때문입니다. 스포일러 경고 : 사용하지 마십시오 (scipy 버전 0.17.0 기준). 2 차원 보간에 특별히 사용된다는 점에서 이미 이전 주제보다 더 특별하지만, 이것이 다변량 보간에서 가장 일반적인 경우라고 생각합니다.

구문이 진행되는 한, interp2dRbf먼저 보간 인스턴스를 생성해야한다는 점 과 유사합니다 .이 인스턴스는 실제 보간 된 값을 제공하기 위해 호출 될 수 있습니다. 그러나 문제가 있습니다. 출력 포인트는 직사각형 메시에 있어야하므로 보간기에 대한 호출로 들어가는 입력은 numpy.meshgrid다음 과 같이 출력 그리드에 걸쳐있는 1d 벡터 여야합니다 .

# 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

사용시 가장 일반적인 실수 중 하나는 interp2d전체 2D 메시를 보간 호출에 넣는 것입니다. 이로 인해 폭발적인 메모리 소비가 발생하고 성급하게 MemoryError.

이제 가장 큰 문제 interp2d는 종종 작동하지 않는다는 것입니다. 이것을 이해하기 위해 우리는 내부를 살펴 봐야합니다. 이는 interp2d하위 수준 함수 bisplrep+ bisplev에 대한 래퍼이며, 이는 차례로 FITPACK 루틴 (Fortran으로 작성 됨)에 대한 래퍼입니다. 이전 예제에 해당하는 호출은 다음과 같습니다.

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

자, 여기에 대한 것입니다 interp2d: (scipy 버전 0.17.0에서)에 대한 멋진 댓글이interpolate/interpolate.py 있습니다 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)

실제로에서 interpolate/fitpack.pybisplrep있어 몇 가지 설정 및 궁극적으로

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)                 

그리고 그게 다야. 기본 루틴interp2d 은 실제로 보간을 수행하기위한 것이 아닙니다. 충분히 잘 작동하는 데이터에는 충분할 수 있지만 현실적인 상황에서는 다른 것을 사용하고 싶을 것입니다.

결론적으로 interpolate.interp2d

  • 잘 조정 된 데이터로도 인공물이 발생할 수 있습니다.
  • 특히 이변 량 문제를위한 것입니다 ( interpn그리드에 정의 된 입력 포인트에 대한 제한이 있지만 )
  • 외삽을 수행
  • 첫 번째 단계로 보간기를 생성하므로 다양한 출력 지점에서 평가하는 것이 추가 노력이 적습니다.
  • 직사각형 그리드를 통해서만 출력을 생성 할 수 있습니다. 분산 된 출력의 경우 루프에서 보간기를 호출해야합니다.
  • 선형, 3 차 및 5 차 보간 지원
  • 입력 데이터의 대칭을 위반할 수 있음

1cubiclinear기저 함수의 종류가 Rbf같은 이름의 다른 보간 기와 정확히 일치하지 않는다고 확신합니다 .
2 이러한 NaN은 표면 플롯이 너무 이상하게 보이는 이유이기도합니다. matplotlib는 역사적으로 적절한 깊이 정보로 복잡한 3D 객체를 그리는 데 어려움을 겪었습니다. 데이터의 NaN 값은 렌더러를 혼란스럽게하므로 뒷면에 있어야하는 표면 부분이 앞쪽에 있도록 플롯됩니다. 이것은 보간이 아니라 시각화 문제입니다.


답변