在 DAG 因果推理框架中调整代理变量的理由是什么?

机器算法验证 因果关系 达格
2022-03-25 15:05:57

考虑以下假设。我是 Acme Inc. 的一名员工,我想量化加入 Customer Loyalty Club (CLC) 对支出的影响(以美元为单位)。

当然,只有已经对 Acme 特别感兴趣/热情的客户才会加入 CLC,因此我们无法通过加入 CLC 的人和未加入 CLC 的人之间的平均花费的简单差异来量化治疗效果。

现在,对 Acme 的兴趣是不可观察的。(无论如何,让我们假设。)然后,我们可能会搜索一个我们可以调整的代理变量。上一年的支出似乎是有道理的。对 Acme 产品感兴趣的人可能比不感兴趣的客户在上一年花更多的钱。事实上,客户对 Acme 的兴趣似乎对客户在任何给定年份在 Acme 的消费金额都有因果影响。

这是我的问题。假设如下所示的 DAG,我看不出如何证明调整前一年的支出是合理的。我错过了什么吗?

编辑:为了澄清,后门标准似乎没有帮助,因为 Spend_in_Prev_Year 上的条件不会阻止任何后门路径。是否有其他理由可以对 Spend_in_Prev_Year 进行调节(该理由可能是重新绘制此 DAG 的合理方式)?

在此处输入图像描述

2个回答

由于是不可观察的,因此onInterest_in_Acme的平均因果效应是不可识别的。但是,该规则有一个重要的例外,即 if完全相关(如果这两个变量完全相关(即包含相同的信息),则可以对其进行调整并用于识别平均因果效应。Loyalty Club MembershipSpendInterest_in_Acmer=1.0r=0.0Spend_in_Prev_YearSpend_in_Prev_Year

Interest_in_Acme在与有点相关的更有可能的情况下Spend_in_Prev_Year,可以获得对平均因果效应的有点偏颇的估计。两者相关的越多,调整后的估计偏差就越小Spend_in_Prev_Year

一个简单的模拟研究

为了演示这个概念,下面是一个简单的模拟研究(Python 3.5+ 代码)。 be be be 是治疗计划下的潜力是观察到的支出。为简单起见,我的模拟使用二进制变量。为了减少样本量的可变性,我设置了对于平均因果效应的估计,我使用了标准化的平均差(即 g-formula、do-calculus 等)LInterest_in_AcmeLSpend_in_Prev_YearALoyalty Club MembershipY(a)SpendaYn=1,000,000

import numpy as np
import pandas as pd

# Simulation parameters
n = 1000000
correlation = 1.0
np.random.seed(20191223)

# Simulating data set
df = pd.DataFrame()
df['L'] = np.random.binomial(n=1, p=0.25, size=n)
df['L*'] = np.random.binomial(n=1, p=correlation*df['L'] + (1-correlation)*(1-df['L']), size=n)
df['A'] = np.random.binomial(1, p=(0.25 + 0.5*df['L']), size=n)
df['Ya0'] = np.random.binomial(1, p=(0.75 - 0.5*df['L']), size=n)
df['Ya1'] = np.random.binomial(1, p=(0.75 - 0.5*df['L'] - 0.1*1 -0.1*1*df['L']), size=n)
df['Y'] = (1-df['A'])*df['Ya0'] + df['A']*df['Ya1']

# True causal effect
print("True Causal Effect:", np.mean(df['Ya1'] - df['Ya0']))

# Standardized Mean Estimator
l1 = np.mean(df['L*'])
l0 = 1 - l1
r1_l0 = np.mean(df.loc[(df['A']==1) & (df['L*']==0)]['Y'])
r1_l1 = np.mean(df.loc[(df['A']==1) & (df['L*']==1)]['Y'])
r0_l0 = np.mean(df.loc[(df['A']==0) & (df['L*']==0)]['Y'])
r0_l1 = np.mean(df.loc[(df['A']==0) & (df['L*']==1)]['Y'])
rd_stdmean = (r1_l0*l0 + r1_l1*l1) - (r0_l0*l0 + r0_l1*l1)
print('Standardized Mean Risk Difference:', rd_stdmean)

下面是一些各种相关性的结果(您也可以运行此代码并更改correlation参数以查看各种更改的结果。注意是没有相关性r=0.50

真实平均因果效应:-0.124

r=1.0:-0.123

r=0.99:-0.136

r=0.50:-0.347

r=0.05:-0.180

概括

作为一个理由,您可能相信Interest_in_Acme并且Spend_in_Prev_Year高度相关,这意味着您可能接近真正的平均因果效应。虽然您无法完全确定,但您可能认为这两个变量高度相关,因此您的估计接近事实。最后一点,对于连续变量,这个问题变得更加复杂,因为变量的函数形式可能不同。

此处无法进行精确的点识别,但调整 forSpend_in_Prev_Year 确实部分阻塞了后门路径,因此这就是它的基本原理。作为一般建议,您应该在没有真正混杂因素的情况下调整代理(也有例外,例如,代理可能会打开其他后门路径,但在您的示例中并非如此)。

现在我要补充一点,既然你知道你没有完全阻止后门路径,你应该进行敏感性分析——我们从构造上知道你的估计是有偏差的,所以我们想判断它可能有多大的偏差。

例如,如果您使用的是线性模型,您可以通过简单地比较真实变量可以解释您的治疗和结果的变异程度与您拥有的代理变量相比,执行相当普遍但简单的敏感性分析测量(参见Cinelli 和 Hazlett 2020 -非门控版本)。如果您认为代理做得很好,并且真实变量不能比代理强多少,那么您的估计很可能没有太大偏差。

我将在这里展示一个使用包 sensemakr 在 R 中的示例。假设您测量了混杂而不是,并且您获得了以下估计值,XX

set.seed(10)
n <- 1e4
x <- rnorm(n)
xs <- x + rnorm(n)
d <- rbinom(n, 1, plogis(x))
y <- d + x + rnorm(n)

model <- lm(y ~ d + xs)
model
#> 
#> Call:
#> lm(formula = y ~ d + xs)
#> 
#> Coefficients:
#> (Intercept)            d           xs  
#>     -0.2411       1.4882       0.4537

的整个估计是否可能是由于偏差,因为您没有控制“真实”1.48X

需要有多强的关联还是与结果的关联,以完全解释观察到的关联(与代理相比,并且高于代理已经解释过了)。正如您在示例中所见,真实变量的强度需要是代理变量的 3 倍,才能完全解释您的估计。如果您认为这不太可能,并且您认为真正的变量可以(另外)解释的只是代理已经解释的内容的两倍或两倍,那么您可以声称真正的效果不小于 0.54(在我们的例子中)我们知道它是 1)。XDY

library(sensemakr)
#> See details in:
#> Carlos Cinelli and Chad Hazlett (2020). Making Sense of Sensitivity: Extending Omitted Variable Bias. Journal of the Royal Statistical Society Series B.
sense <- sensemakr(model = model, treatment = "d", 
                   benchmark_covariates = "xs", 
                   kd = 1:3)
plot(sense)

在此处输入图像描述