方差扩大因子(variance inflation factor)简称VIF,是表征自变量观察值之间复共线性程度的数值。线性回归分析中,回归系数βj的估计量的方差为σ2Cjj,其中Cjj=(1-Rj)-1,称Cjj为βj的方差扩大因子,这里Rj为xj对其余p-1个自变量的复相关系数的平方,显然Cjj≥1,它的大小可以反映出自变量的观察值之间是否存在复共线性以及其程度如何,Cjj越大,复共线性越严重。(以上引自百度百科),更多关于多重共线性的内容,可自行查询资料,资料很多。
本文重点说一下用Python调用variance_inflation_factor计算VIF函数遇到的坑。
下面的定义函数中直接调用的variance_inflation_factor函数计算的VIF,得到的结果是不对的。
def checkVIF(df):
from statsmodels.stats.outliers_influence import variance_inflation_factor
name = df.columns
x = np.matrix(df)
VIF_list = [variance_inflation_factor(x,i) for i in range(x.shape[1])]
VIF = pd.DataFrame({'feature':name,"VIF":VIF_list})
max_VIF = max(VIF_list)
print(max_VIF)
return VIF以上所用数据集都是经过预处理WOE转换过的
可以看到上边计算的VIF都异常的大(VIF大于10就认为存在严重多重共线性,严格一些大于5也认为存在多重共线性)。
下边我们从VIF的定义出发,再重新定义一个计算VIF的新函数。如下:
def vif(df):
from statsmodels.formula.api import ols
cols = list(df.columns)
cols_noti = cols
formula = col_i + '~' + '+'.join(cols_noti)
r2 = ols(formula, df).fit().rsquared
return 1. / (1. - r2)
for i in X_rep.columns:
print(i, '\t', vif(df=X_rep, col_i=i))
结果输出:以上所用数据均相同
以上输出值才是该数据集变量的VIF值。
那么为什么用variance_inflation_factor计算的不对呢?以下是variance_inflation_factor的源码:
from statsmodels.regression.linear_model import OLS
def variance_inflation_factor(exog, exog_idx):
'''variance inflation factor, VIF, for one exogenous variableThe variance inflation factor is a measure for the increase of thevariance of the parameter estimates if an additional variable, given byexog_idx is added to the linear regression. It is a measure formulticollinearity of the design matrix, exog.One recommendation is that if VIF is greater than 5, then the explanatoryvariable given by exog_idx is highly collinear with the other explanatoryvariables, and the parameter estimates will have large standard errorsbecause of this.Parameters----------exog : ndarray, (nobs, k_vars)design matrix with all explanatory variables, as for example used inregressionexog_idx : intindex of the exogenous variable in the columns of exogReturns-------vif : floatvariance inflation factorNotes-----This function does not save the auxiliary regression.See Also--------xxx : class for regression diagnostics TODO: doesn't exist yetReferences----------http://en.wikipedia.org/wiki/Variance_inflation_factor'''
k_vars = exog.shape[1]
x_i = exog[:, exog_idx]
mask = np.arange(k_vars) != exog_idx
x_noti = exog[:, mask]
r_squared_i = OLS(x_i, x_noti).fit().rsquared
vif = 1. / (1. - r_squared_i)
return vif
这是由于Python的OLS不同,在Python方差膨胀因子计算中使用的OLS默认情况下不会添加截距。
所以要做的是在数据框中再增加一列,并填充一列以代表一个常数(使用常数1)。这将是方程式的截距项。完成此操作后,计算的值就对了。如下:
def checkVIF_new(df):
from statsmodels.stats.outliers_influence import variance_inflation_factor
df['c'] = 1
name = df.columns
x = np.matrix(df)
VIF_list = [variance_inflation_factor(x,i) for i in range(x.shape[1])]
VIF = pd.DataFrame({'feature':name,"VIF":VIF_list})
VIF = VIF.drop('c',axis = 0)
max_VIF = max(VIF_list)
print(max_VIF)
return VIF
添加一列名为"c"的常数列,使填充值为1,结果输出如下:忽略常数c的值
这样结果就一样了。以上两种方式都可以计算正确的VIF值。