製作中のプログラムに補間が必要だったのですが、調べてみると様々な種類の補間法があり、結局どの補間を使えばいいのか迷ったので備忘録として記事にしました。
私の場合、結果として必要なのは補間ではなくカーブフィッティングだったことがわかりました。
Pythonで補間を行うためのライブラリ
Pythonで補間を使うには、科学技術計算を行うためのライブラリであるSciPyを使うのが一般的かと思います。MatplotlibやPandasでも補間を使うことが出来るようですが、今回、実例として示すのはscipy.interpolateを使ったものです。
用語
カーブフィッティング(曲線あてはめ)
補間はデータのすべての点を通りますが、カーブフィッティングはデータに最もよく当てはまる曲線を求めるため、必ずしもすべての点は通りません。
ルンゲ現象
ラグランジュ補間など、高次の多項式で多項式補間する際に発生する現象で、補間多項式の次数が高くなるほど、補間点間の誤差は大きくなります。
画像のオレンジが元の関数、青い点が補間前データ、緑がラグランジュ補間後です。
オーバーシュート/アンダーシュート
データの傾きが急激に変化した際に、上もしくは下に振動してしまうことです。
補間法の種類
それぞれの補間に関するコードを以下のコード内に入力してください。x, y, xnewはお好きなものを使用してください。
import matplotlib.pyplot as plt
import numpy as np
from scipy import interpolate
# 画像1枚目
x = np.linspace(-3, 3, 9)
y = 1 / (x**2 + 1)
xnew = np.linspace(-3, 3, 99)
# 画像2枚目
x = np.linspace(-2, 2, 7)
y = x**3 - 3*x
xnew = np.linspace(-2, 2, 99)
# 画像3枚目 適当なデータ
x = [22,32,64,77,105,139,194,246,253,263,282,299,319,360,390,397,399,442,458]
y = [309,267,175,141,124,124,141,174,232,301,345,355,347,322,282,234,163,112,99]
xnew = np.linspace(min(x), max(x), 99)
##### ここに f = interpolate.{関数}を入力 #####
# 例: f = interpolate.interp1d(x, y, kind='cubic')
plt.plot(x, y, 'o', xnew, f(xnew), '-')
plt.show()
線形補間(1次補間)
f = interpolate.interp1d(x, y, kind='linear')
他の補間法に比べ精度は悪いですが計算量が少ないです。
主に直線であることが前提となっているもの、例えばゲーム制作で、キャラクターを地点Aから地点Bへ滑らかに移動させたい時などによく使われます。
スプライン補間系
スプライン補間
# 今回は3次スプライン補間
f = interpolate.interp1d(x, y, kind='cubic')
データの区分ごとに区切ってそれぞれ補間する方式で非常によく使われます。
平滑化スプライン
f = interpolate.UnivariateSpline(x, y, k=4)
補間ではなくカーブフィッティングですが、比較用に記載しました。
秋間補間
f = interpolate.Akima1DInterpolator(x, y)
スプライン補間と線形補間の中間のような補間法で、データ間の補間にエルミート多項式を介している 修正された秋間補間(Modified Akima interpolation)は、特にオーバーシュート/アンダーシュートが発生しにくいです。
最近傍補間
f = interpolate.interp1d(x, y, kind="nearest")
最近近傍というと難しそうに見えますが、英語だと Nearest-neighbor つまり、最も近い位置にある元のデータの値を参照するだけなので、仕組みとしてはとても単純です。
アルゴリズムの実装が非常に簡単で計算量も少ないので、リアルタイム3Dレンダリングにおいて、テクスチャ表面の色の値を決定するのによく使われます。
ラグランジュ補間系
ラグランジュ補間
f = interpolate.lagrange(x, y)
データの点が等間隔に配置されている場合や補間の点数が多い場合、ルンゲ現象が発生しやすく、この補間を使うくらいならスプライン補間を使った方がいいと思います。
エルミート補間
# 3次エルミート補間
f = interpolate.PchipInterpolator(x, y)
ラグランジュ補間を一般化した多項式補間です。
Barycentric補間(重心補間)
f = interpolate.BarycentricInterpolator(x, y)
ラグランジュ補間の一種で、データの点が滑らかであれば非常に高速です。
Krogh補間
f = interpolate.KroghInterpolator(x, y)
この補間に関する情報がほぼありませんでしたが、おそらくニュートン補間(ラグランジュ補間)に基づくアルゴリズムだと思われます。
結論
補間する方法を色々試してみましたが、結局のところデータによって合う合わないあるので1つには絞れないですが、最初に試すものとしては、2点間の補間であれば線形補間、ある程度連続した3点以上のデータの補間ではスプライン補間か秋間補間をとりあえず選択することになるのではないかと思います。