LOWESS#
散布図に従う近似線を描くために開発された局所回帰。 LOESS (locally estimated scatterplot smoothing) や LOWESS (locally weighted scatterplot smoothing) と呼ばれる。
Show code cell 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). に記されたアルゴリズムは以下の通り。
ここで
1. 重みの計算と 個の最近傍サンプルの取得#
重み関数
を計算する
ここで重み関数
について は について 非増加関数 について
Show code cell 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()

2. 多項式回帰のフィッティング#
非線形回帰として
3. ロバスト性重み の計算#
続いて、外れ値の影響を除外するための重みを計算する。
bisquare weight function
残差
と定義する。
Show code cell 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.