散布図に従う近似線を描くために開発された局所回帰。 LOESS (locally estimated scatterplot smoothing) や LOWESS (locally weighted scatterplot smoothing) と呼ばれる。
Source
import numpy as np
np.random.seed(0)
x = np.linspace(0, 7, 100)
y = np.sin(x) + np.random.normal(0, 0.2, 100)
import statsmodels.api as sm
smoothed = sm.nonparametric.lowess(exog=x, endog=y, frac=0.2, it=3)
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.scatter(x, y)
ax.plot(smoothed[:, 0], smoothed[:, 1], c="k")
ax.set(xlabel="x", ylabel="y")
fig.show()
LOWESSのアルゴリズム¶
Cleveland (1979). に記されたアルゴリズムは以下の通り。
番目のサンプルの目的変数を特徴量とノンパラメトリックの平滑化関数で近似することを考える。
ここでは平均0で分散が一定の確率変数である。
ここで重み関数は以下の性質を満たすものとする
について
は について 非増加関数
について
は分子のの絶対値が分母のより大きければになるので重みが0になる。つまり、サンプルとして回帰に使用されなくなる。 なので重み関数は近傍の個のサンプルを取り出しつつ、個のサンプルにも距離に応じた重みをかける操作となる。
の例として tricube functionが考えられる
Source
def tricube(x: float) -> float:
if np.abs(x) >= 1:
return 0
return (1 - np.abs(x)**3 )**3
x = np.linspace(-2, 2, 100)
w = [tricube(x_i) for x_i in x]
# 別のやり方
# w = (1 - np.abs(x)**3)**3
# w[np.abs(x) >= 1] = 0
fig, ax = plt.subplots(figsize=[4,3])
ax.plot(x, w)
ax.set(title="tricube weight function", xlabel="x", ylabel="W(x)")
fig.show()
3. ロバスト性重みの計算¶
続いて、外れ値の影響を除外するための重みを計算する。 bisquare weight function を以下のように定義する
残差の絶対値の中央値をとする。ロバスト性重み(robustness weights)を
と定義する。もtricube functionと似た形状であり、残差の絶対値の中央値の6倍()以上の絶対値の残差を持つ外れ値は重みがゼロになり、推定に含まれなくなるので、推定からハズレ値の影響を除外できる。
Source
def bisquare(x: float) -> float:
if np.abs(x) >= 1:
return 0
return (1 - x**2 )**2
x = np.linspace(-2, 2, 100)
b = [bisquare(x_i) for x_i in x]
fig, ax = plt.subplots(figsize=[4,3])
ax.plot(x, b)
ax.set(title="bisquare weight function", xlabel="x", ylabel="B(x)")
fig.show()
4. で重み付け回帰を行う¶
また次多項式回帰を行い、新たな推定値を得る。このとき、重みはを使う。
5. 繰り返す¶
3.と4.のステップを回繰り返す。
実装¶
# サンプルデータ
import numpy as np
n = 100
np.random.seed(0)
x = np.linspace(0, 7, n)
y = np.sin(x) + np.random.normal(0, 0.2, n)import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.scatter(x, y)
ax.set(xlabel="x", ylabel="y")
fig.show()
frac = 0.66 # 使用するサンプルの割合
r = int(frac * n) # 使用する近傍のサンプル数
d = 3 # 多項式回帰の次数
t = 3 # iteration
def tricube(x: np.array) -> np.array:
w = (1 - np.abs(x)**3)**3
w[np.abs(x) >= 1] = 0
return w
def bisquare(x: np.array) -> np.array:
w = (1 - np.abs(x)**2)**2
w[np.abs(x) >= 1] = 0
return w
# d次多項式を作るための特徴量生成
X = np.vstack([x**j for j in range(d)]).T
n = X.shape[0]
delta = np.ones_like(x)
y_pred = np.zeros_like(x)
for _ in range(t):
for i in range(n):
# 重みの計算
dist = x - x[i]
idx = np.argsort(np.abs(dist))[:r]
h_i = np.abs(dist[idx]).max() # r番目に近いdiff
w = tricube(dist / h_i)
W = np.diag(delta * w)
# WLS
beta = np.linalg.inv(X.T @ W @ X) @ X.T @ W @ y
y_pred[i] = X[i,:] @ beta
e = y - y_pred
s = np.median(np.abs(e))
delta = bisquare(e / (6 * s))
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.scatter(x, y)
ax.plot(x, y_pred, c="k")
ax.set(xlabel="x", ylabel="y")
fig.show()
最近のLOWESSアルゴリズム¶
Wikipediaには、重み関数がtricubeではなくGaussianを使うものが紹介されている
Gaussian weight functionとは、2つのデータ点の特徴量ベクトル(は特徴量の次元数)について、
といったもの。
参考¶
Cleveland, W. S. (1979). Robust locally weighted regression and smoothing scatterplots. Journal of the American statistical association, 74(368), 829-836.