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