使用线性向量自回归模型Linear VAR推断时序变量的因果关系

使用线性向量自回归模型Linear VAR推断时序变量的因果关系

最近接触到格兰杰因果关系(Granger Causality)这种研究时序变量之间的因果关系的统计工具,顺手找到了一段可以用的代码,即标题,特记录之。

稍微调研一下Granger Causality就知道,GC不是真正的因果关系,但是不妨碍各领域的研究人员使用它来探究变量之间的影响关系。

GC的定义大致是这样:假设有变量 X t X_t Xt Y t Y_t Yt,都是时序变量,若利用 X t X_t Xt的历史数据(当然也利用 Y t Y_t Yt的历史数据)去预测 Y t Y_t Yt的未来,比单独使用 Y t Y_t Yt的历史数据要效果好,就可以说 X t X_t Xt Y t Y_t YtGranger原因 X t X_t Xt granger causes Y t Y_t Yt

因此,假定我们已经收集了一些变量的序列数据,要想探索他们之间的Granger Causality,通常的做法是,构建一个VAR(Vector Auto-Regressor)模型,利用最小二乘法求出系数矩阵(一般不止一个),若所有矩阵的 ( i , j ) (i,j) (i,j)位置都是0,则说明变量 i i i j j j没有Granger因果关系,反之则有。

下图是wiki对Multivariate Granger Causality的描述,以及VAR的数学描述,比较简单不再赘述。
使用线性向量自回归模型Linear VAR推断时序变量的因果关系_第1张图片

直接看代码吧。需要用到Python的一个库,statsmodels,可能需要提前安装。

from statsmodels.tsa.api import VAR
import statsmodels.stats.multitest as multitest
import numpy as np

class LinVAR:
    def __init__(self, X: np.ndarray, K=1):
        """
        Linear VAR model.

        @param X: numpy array with data of shape T x p.
        @param K: order of the VAR model (maximum lag).
        """
        # X.shape: T x p
        super(LinVAR, self).__init__()

        self.model = VAR(X)
        self.p = X.shape[1]
        self.K = K

        # Fit the model
        self.model_results = self.model.fit(maxlags=self.K)

    def infer_causal_structure(self, kind="f", adjust=True, signed=True):
        """
        Infer GC based on the fitted VAR model.

        @param kind: type of the statistical test for GC (as implemented within statsmodels). Default: F-test. Option: 'wald'
        @param adjust: whether to adjust p-values? If True, p-values are adjusted using the Benjamini-Hochberg procedure
        for controlling the FDR.
        @param signed: whether to return coeffcient signs?
        @return: p x p array with p-values, p x p array with hypothesis test results, and, if signed == True,
        p x p array with coefficient signs.
        """
        pvals = np.zeros((self.p, self.p))
        reject = None
        for i in range(self.p):
            for j in range(self.p):
                pvals[i, j] = self.model_results.test_causality(caused=i, causing=j, kind=kind).pvalue
        reject = pvals <= 0.05
        if adjust:
            reject, pvals, alpha_Sidak, alpha_Bonf = multitest.multipletests(pvals.ravel(), method="fdr_bh")
            pvals = np.reshape(pvals, (self.p, self.p))
            reject = np.reshape(reject, (self.p, self.p))
        if signed:
            return pvals, reject, np.sign(self.model_results.params[1:, :].T * reject)
        else:
            return pvals, reject

if __name__ == '__main__':
    X = np.zeros((100,3), dtype=np.float32)
    X[:,0] = np.sin(np.arange(100))
    X[:,1] = np.sin(np.arange(100)-10)
    X[:,2] = np.random.rand(100)
    
    var = LinVAR(X)
    print(var.infer_causal_structure())

输出:

array([[0.        , 0.        , 0.80416488],
       [0.        , 0.        , 0.95490601],
       [0.93203971, 0.34845459, 0.30533194]]), 
array([[ True,  True, False],
       [ True,  True, False],
       [False, False, False]]), 
array([[ 1.,  1.,  0.],
       [-1., -1.,  0.],
       [ 0.,  0.,  0.]]))

很显然,对于这个小例子,变量1和变量2是有关系的,2是1的滞后,而变量3则是随机噪声。

你可能感兴趣的:(机器学习,回归,机器学习,python,因果推断)