Відмова: Я переважно пишу цей пост з синтаксичними міркуваннями та загальною поведінкою. Я не знайомий із аспектом пам’яті та процесора описаних методів, і я націлюю цю відповідь на тих, хто має досить малі набори даних, так що якість інтерполяції може бути головним аспектом, який слід враховувати. Я усвідомлюю, що при роботі з дуже великими наборами даних, ефективніші методи (а саме griddataі Rbf) можуть бути неможливими.
Я збираюся порівняти три види методів багатовимірної інтерполяції ( interp2d/ сплайни griddataта Rbf). Я підпорядкую їм два види завдань інтерполяції та два види базових функцій (пункти, з яких потрібно інтерполювати). Конкретні приклади демонструють двовимірну інтерполяцію, але життєздатні методи застосовні у довільних розмірах. Кожен метод забезпечує різні види інтерполяції; у всіх випадках я буду використовувати кубічну інтерполяцію (або щось близьке 1 ). Важливо зауважити, що щоразу, коли ви використовуєте інтерполяцію, ви вводите упередженість у порівнянні з вашими необробленими даними, а конкретні використовувані методи впливають на артефакти, якими ви в кінцевому підсумку. Завжди пам’ятайте про це та відповідально інтерполюйте.
Дві завдання інтерполяції будуть
- помірний вибір (вхідні дані є прямокутною сіткою, вихідні дані - щільнішою сіткою)
- інтерполяція розпорошених даних на звичайну сітку
Дві функції (над доменом [x,y] in [-1,1]x[-1,1]) будуть
- плавна і доброзичлива функція
cos(pi*x)*sin(pi*y):; діапазон в[-1, 1]
- зла (і, зокрема, неперервна) функція:
x*y/(x^2+y^2)зі значенням 0,5 біля початку; діапазон в[-0.5, 0.5]
Ось як вони виглядають:

Спочатку я продемонструю, як три методи поводяться під цими чотирма тестами, потім детально опишу синтаксис усіх трьох. Якщо ви знаєте, чого слід очікувати від методу, можливо, ви не хочете витрачати свій час на вивчення його синтаксису (дивлячись на вас, interp2d).
Дані тесту
Для наочності ось код, за допомогою якого я створив вхідні дані. Хоча в цьому конкретному випадку я, очевидно, знаю функцію, що лежить в основі даних, я буду використовувати це лише для отримання вводу для методів інтерполяції. Я використовую numpy для зручності (і в основному для генерації даних), але й науки однієї теж вистачить.
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]функцій плавного тестування:

Незважаючи на те, що це просте завдання, між результатами вже є тонкі відмінності. На перший погляд усі три виходи розумні. Зазначимо дві особливості, засновані на наших попередніх знаннях основної функції: середній випадок найбільше griddataспотворює дані. Зверніть увагу на y==-1межу ділянки (найближчий до xмітки): функція повинна бути суворо нульовою (оскільки y==-1це вузлова лінія для гладкої функції), але це не так griddata. Також зверніть увагу на x==-1межу ділянок (позаду, зліва): основна функція має локальний максимум (маючи на увазі нульовий градієнт поблизу кордону) при [-1, -0.5], але griddataвихід показує явно ненульовий градієнт у цій області. Ефект тонкий, але все-таки упередження. (ВірністьRbfще краще, якщо за замовчуванням вибір радіальних функцій, прозваний multiquadratic.)
Зла функція та перемогу
Трохи складніше завдання полягає в тому, щоб здійснити перемогу над нашою злою функцією:

Ясні відмінності починають проявлятися серед трьох методів. Дивлячись на поверхневі ділянки, є чіткі хибні екстремуми, що з'являються на виході з interp2d(зверніть увагу на два горбики з правого боку ділянки поверхні). Хоча griddataі, Rbfздається, дає подібні результати на перший погляд, останній, здається, дає глибший мінімум поблизу [0.4, -0.4], відсутній у основної функції.
Однак є один важливий аспект, в якому Rbfнабагато вищий: він поважає симетрію основної функції (що, звичайно, також стає можливим завдяки симетрії вибіркової сітки). Вихід з griddataпорушення порушує симетрію точок вибірки, що вже слабко видно в гладкому випадку.
Плавна функція та розсіяні дані
Найчастіше хочеться виконати інтерполяцію на розсіяних даних. З цієї причини я очікую, що ці тести будуть важливішими. Як показано вище, вибіркові бали були обрані псевдо-рівномірно в області, що цікавить. У реалістичних сценаріях у вас може виникнути додатковий шум при кожному вимірюванні, і ви повинні врахувати, чи є сенс інтерполювати ваші вихідні дані для початку.
Вихід для плавної функції:

Зараз вже триває шоу жахів. Я вирізав вихід interp2dміж ними [-1, 1]виключно для побудови графіків, щоб зберегти хоча б мінімальну кількість інформації. Зрозуміло, що хоча деякі основні форми присутні, є величезні галасливі регіони, де метод повністю руйнується. Другий випадок griddataвідтворює форму досить гарно, але відзначте білі області на межі контуру ділянки. Це пов’язано з тим, що griddataпрацює тільки всередині опуклого корпусу точок вхідних даних (іншими словами, він не виконує жодної екстраполяції ). Я зберігав значення NaN за замовчуванням для вихідних точок, що лежать поза опуклим корпусом. 2 Враховуючи ці особливості, Rbfздається, найкраще.
Зла функція і розпорошені дані
І момент, якого ми всі чекали:

Це не величезний сюрприз, який 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
Зауважте, що і вхідні, і вихідні точки в цьому випадку були 2d масивами, а вихід 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дані також повинні бути вирівняними. У нас є трохи більше свободи, коли йдеться про вихідні точки: їх чомусь можна також вказати як кордон багатовимірних масивів. Слід зазначити , що helpз griddataвводить в оману, оскільки це передбачає , що те ж саме справедливо і для вхідних точок ( по крайней мере , для версії 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. Використання найближчої сусідки та лінійної інтерполяції
NearestNDInterpolatorта LinearNDInterpolatorпід кришкою відповідно. 1d кубічна інтерполяція використовує сплайн, 2d кубічна інтерполяція використовує CloughTocher2DInterpolatorдля побудови безперервно диференційованого кусочно-кубічного інтерполятора.
- може порушити симетрію вхідних даних
scipy.interpolate.interp2d/scipy.interpolate.bisplrep
Єдина причина, яку я обговорюю interp2dта її родичів, - це те, що вона має оманливе ім’я, і люди, ймовірно, намагаються його використовувати. Попередження спойлера: не використовуйте його (станом на scipy версії 0.17.0). Це вже більш особливе, ніж попередні теми, оскільки воно спеціально використовується для двовимірної інтерполяції, але я підозрюю, що це, безумовно, найпоширеніший випадок багатоваріантної інтерполяції.
Що стосується синтаксису, interp2dвін схожий на Rbfте, що спочатку потрібно побудувати екземпляр інтерполяції, який можна викликати для надання фактичних інтерпольованих значень. Однак є привід: точки виведення повинні розташовуватися на прямокутній сітці, тому входи, що надходять у виклик інтерполятора, повинні бути 1d векторами, що охоплюють вихідну сітку, як би від 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
Однією з найпоширеніших помилок при використанні 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.py, bisplrepє деяка настройка і в кінцевому рахунку
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для вхідних точок, визначених у сітці, обмежено )
- виконує екстраполяцію
- створює інтерполятор як перший крок, тому оцінювати його в різних вихідних точках - менше додаткових зусиль
- може створювати вихід лише через прямокутну сітку, для розсіяного виходу вам доведеться викликати інтерполятор у циклі
- підтримує лінійну, кубічну та квінтичну інтерполяцію
- може порушити симетрію вхідних даних
1 Я абсолютно впевнений , що cubicі linearвид базисних функцій , Rbfнеточно відповідають до інших інтерполятора одного і того ж імені.
2 Ці NaN також є причиною того, що ділянка поверхні виглядає настільки дивним: matplotlib історично має труднощі з побудовою складних 3d-об'єктів з належною інформацією про глибину. Значення NaN у даних плутають рендерінг, тому частини поверхні, які повинні знаходитись іззаду, намічаються так, щоб вони були спереду. Це питання візуалізації, а не інтерполяції.