目录
例子 1 - 连续分段线性函数
import arviz as az
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import causalpy as cp
%load_ext autoreload
%autoreload 2
%config InlineBackend.figure_format = 'retina'
seed = 42
rng = np.random.default_rng(seed)
回归拐点设计应当通过分段连续函数来进行分析。具体来说:
- 我们想要一个函数能够捕捉拐点左边和右边的数据。
- 我们想要一个具有一个断点或拐点的分段函数。
- 该函数应在拐点处连续。
一个这样的函数的例子是一个分段连续多项式:
其中:
- 是未知参数,
- x 是连续变量,
- t 是一个指示变量,如果 x>=k 则取值为1,否则为 0
- k 是 x 在拐点处的值。
我们可以通过绘制一系列随机选择的 系数的函数图像来直观地展示这些函数的样子,但所有这些函数都具有相同的拐点在0.5。
def f(x, beta, kink):
return (
beta[0]
+ beta[1] * x
+ beta[2] * x**2
+ beta[3] * (x - kink) * (x >= kink)
+ beta[4] * (x - kink) ** 2 * (x >= kink)
)
def gradient_change(beta, kink, epsilon=0.01):
gradient_left = (f(kink, beta, kink) - f(kink - epsilon, beta, kink)) / epsilon
gradient_right = (f(kink + epsilon, beta, kink) - f(kink, beta, kink)) / epsilon
gradient_change = gradient_right - gradient_left
return gradient_change
x = np.linspace(-1, 1, 1000)
kink = 0.5
cols = 5
fig, ax = plt.subplots(2, cols, sharex=True, sharey=True, figsize=(15, 5))
for col in range(cols):
beta = rng.random(5)
ax[0, col].plot(x, f(x, beta, kink), lw=3)
ax[1, col].plot(x, np.gradient(f(x, beta, kink), x), lw=3)
ax[0, col].set(title=f"Random {col+1}")
ax[1, col].set(xlabel="x")
ax[0, 0].set(ylabel="$y = f(x)$")
ax[1, 0].set(ylabel=r"$\frac{dy}{dx}$");
回归拐点分析的思想是拟合一个合适的函数到数据上,并估计在拐点处函数梯度是否有变化。
下面我们将生成一些数据集并演示如何进行回归拐点分析。我们将使用一个函数来生成具有所需特性的模拟数据集。
def generate_data(beta, kink, sigma=0.05, N=50):
if beta is None:
beta = rng.random(5)
x = rng.uniform(-1, 1, N)
y = f(x, beta, kink) + rng.normal(0, sigma, N)
df = pd.DataFrame({"x": x, "y": y, "treated": x >= kink})
return df
例子 1 - 连续分段线性函数
在这个例子中,我们将坚持使用一个简单的连续分段函数。
kink = 0.5
# linear function with gradient change of 2 at kink point
beta = [0, -1, 0, 2, 0]
sigma = 0.05
df = generate_data(beta, kink, sigma=sigma)
fig, ax = plt.subplots()
ax.scatter(df["x"], df["y"], alpha=0.5)
ax.axvline(kink, color="red", linestyle="--")
ax.set(title=f"Change in gradient at kink point: {gradient_change(beta, kink):.2f}");
我们可以使用常规的 cp.pymc_models.LinearRegression
模型,并通过巧妙地通过 formula
输入指定设计矩阵来强制执行连续分段特性。
在这个例子中,将公式设置为 "y ~ 1 + x + I((x-0.5)*treated)"
(其中 0.5 是拐点)等同于以下模型:
拐点两侧的梯度变化是通过数值方法评估的。epsilon
参数决定了用于计算梯度变化的拐点两侧的距离。
result1 = cp.pymc_experiments.RegressionKink(
df,
formula=f"y ~ 1 + x + I((x-{kink})*treated)",
model=cp.pymc_models.LinearRegression(sample_kwargs={"random_seed": seed}),
kink_point=kink,
epsilon=0.1,
)
fig, ax = result1.plot()
如果你想绘制推断出的梯度变化的后验分布,你可以按照以下方式进行。
ax = az.plot_posterior(result1.gradient_change, ref_val=gradient_change(beta, kink))
ax.set(title="Gradient change");