python 逐步回归
2019-09-30 本文已影响0人
这是沸羊羊的干爹
分析建模,日常问题整理(二十八)
2019.8.5~2019.9.14
- 1 逐步回归
在训练评分卡模型的时候要注意系数全为正且具可解释性。
算法:
每一步加入一个变量,是否保留该变量取决于筛选标准;
- 标准可是AIC,BIC,SSR,F显著性,t显著性等;
- 在评分卡场景中,如果F显著,所有参数显著,且参数为正,则保留变量;
下一轮迭代,重新从剩余变量中取出,参照以上规则。
为了在模型的精确度和过拟合方面的权衡(模型方差和偏差的权衡),设定一些信息准则,通过加入模型复杂度的惩罚项来避免过拟合问题。损失函数控制了模型精度,惩罚项限制了模型的复杂度。
AIC:赤池信息准则。
ln(L)是似然函数:已知观察结果,使得能出现该结果的参数的条件概率。等价于在某参数情况下x的条件概率,L(θ|x)=P(X=x|θ)。极大似然估计(已知抽样结果,寻找出能使抽样结果出现可能性最大的参数估计)
BIC:贝叶斯信息准则。
n>=8时,BIC的第一项大于aic的第一项,bic更倾向于选择简单的模型。
RSS/SSR:残差平方和
F:
(逻辑回归logit输出的是 Pseudo R-squ.虚拟判定系数)
借鉴的这篇文章啦(๑•ω•๑)
def stepwise(df, response, intercept=True, normalize=False, criterion='bic',
f_pvalue_enter=.05, p_value_enter=.05, direction='backward', show_step=True,
criterion_enter=None, criterion_remove=None,max_iter=200, **kw):
'''
逐步回归
参数
----
df : dataframe
分析用数据框,response为第一列。
response : str
回归分析相应变量。
intercept : bool, 默认是True
模型是否有截距项。
criterion : str, 默认是'bic'
逐步回归优化规则。
f_pvalue_enter : float, 默认是.05
当选择criterion=’ssr‘时,模型加入或移除变量的f_pvalue阈值。
p_value_enter : float, 默认是.05
当选择derection=’both‘时,移除变量的pvalue阈值。
direction : str, 默认是'backward'
逐步回归方向。
show_step : bool, 默认是True
是否显示逐步回归过程。
criterion_enter : float, 默认是None
当选择derection=’both‘或'forward'时,模型加入变量的相应的criterion阈值。
criterion_remove : float, 默认是None
当选择derection='backward'时,模型移除变量的相应的criterion阈值。
max_iter : int, 默认是200
模型最大迭代次数。
'''
criterion_list = ['bic', 'aic', 'ssr', 'rsquared', 'rsquared_adj']
if criterion not in criterion_list:
raise IOError('请输入正确的criterion, 必须是以下内容之一:', '\n', criterion_list)
direction_list = ['backward', 'forward', 'both']
if direction not in direction_list:
raise IOError('请输入正确的direction, 必须是以下内容之一:', '\n', direction_list)
# 默认p_enter参数
p_enter = {'bic':0.0, 'aic':0.0, 'ssr':0.05, 'rsquared':0.05, 'rsquared_adj':-0.05}
if criterion_enter: # 如果函数中对p_remove相应key传参,则变更该参数
p_enter[criterion] = criterion_enter
# 默认p_remove参数
p_remove = {'bic':0.01, 'aic':0.01, 'ssr':0.1, 'rsquared':0.05, 'rsquared_adj':-0.05}
if criterion_remove: # 如果函数中对p_remove相应key传参,则变更该参数
p_remove[criterion] = criterion_remove
if normalize: # 如果需要标准化数据
intercept = False # 截距强制设置为0
df_std = StandardScaler().fit_transform(df)
df = pd.DataFrame(df_std, columns=df.columns, index=df.index)
''' forward '''
if direction == 'forward':
remaining = list(df.columns) # 自变量集合
remaining.remove(response)
selected = [] # 初始化选入模型的变量列表
# 初始化当前评分,最优新评分
if intercept: # 是否有截距
formula = "{} ~ {} + 1".format(response, remaining[0])
else:
formula = "{} ~ {} - 1".format(response, remaining[0])
result = smf.ols(formula, df).fit() # 最小二乘法回归模型拟合
current_score = eval('result.' + criterion)
best_new_score = eval('result.' + criterion)
if show_step:
print('\nstepwise starting:\n')
iter_times = 0
# 当变量未剔除完,并且当前评分更新时进行循环
while remaining and (current_score == best_new_score) and (iter_times<max_iter):
scores_with_candidates = [] # 初始化变量以及其评分列表
for candidate in remaining: # 在未剔除的变量中每次选择一个变量进入模型,如此循环
if intercept: # 是否有截距
formula = "{} ~ {} + 1".format(response, ' + '.join(selected + [candidate]))
else:
formula = "{} ~ {} - 1".format(response, ' + '.join(selected + [candidate]))
result = smf.ols(formula, df).fit() # 最小二乘法回归模型拟合
fvalue = result.fvalue
f_pvalue = result.f_pvalue
params = result.params
t_pvalue = result.pvalues
score = eval('result.' + criterion)
scores_with_candidates.append((score, candidate, fvalue, f_pvalue,
len([x for x in result.params[1:] if x<0]),
len([x for x in result.pvalues[1:] if x>0.05]))) # 记录此次循环的变量、评分列表
if criterion == 'ssr': # 这几个指标取最小值进行优化
scores_with_candidates.sort(reverse=True) # 对评分列表进行降序排序
best_new_score, best_candidate, best_new_fvalue, best_new_f_pvalue,len_paras_neg,len_tvaluenot = scores_with_candidates.pop()
# 提取最小分数及其对应变量
if ((current_score - best_new_score) > p_enter[criterion]) and (best_new_f_pvalue < f_pvalue_enter) and len_paras_neg==0 and len_tvaluenot==0:
# 如果当前评分大于最新评分
remaining.remove(best_candidate) # 从剩余未评分变量中剔除最新最优分对应的变量
selected.append(best_candidate) # 将最新最优分对应的变量放入已选变量列表
current_score = best_new_score # 更新当前评分
if show_step: # 是否显示逐步回归过程
print('Adding %s, SSR = %.3f, Fstat = %.3f, FpValue = %.3e' %
(best_candidate, best_new_score, best_new_fvalue, best_new_f_pvalue))
elif (current_score - best_new_score) >= 0 and (best_new_f_pvalue < f_pvalue_enter) and iter_times == 0 and len_paras_neg==0 and len_tvaluenot==0: # 当评分差大于等于0,且为第一次迭代
remaining.remove(best_candidate)
selected.append(best_candidate)
current_score = best_new_score
if show_step: # 是否显示逐步回归过程
print('Adding %s, %s = %.3f' % (best_candidate, criterion, best_new_score))
elif (best_new_f_pvalue < f_pvalue_enter) and iter_times == 0: # 当评分差小于p_enter,且为第一次迭代
selected.append(remaining[0])
remaining.remove(remaining[0])
if show_step: # 是否显示逐步回归过程
print('Adding %s, %s = %.3f' % (remaining[0], criterion, best_new_score))
elif criterion in ['bic', 'aic']: # 这几个指标取最小值进行优化
scores_with_candidates.sort(reverse=True) # 对评分列表进行降序排序
best_new_score, best_candidate, best_new_fvalue, best_new_f_pvalue,len_paras_neg,len_tvaluenot = scores_with_candidates.pop() # 提取最小分数及其对应变量
if (current_score - best_new_score) > p_enter[criterion] and len_paras_neg==0 and len_tvaluenot==0: # 如果当前评分大于最新评分
remaining.remove(best_candidate) # 从剩余未评分变量中剔除最新最优分对应的变量
selected.append(best_candidate) # 将最新最优分对应的变量放入已选变量列表
current_score = best_new_score # 更新当前评分
#print(iter_times)
if show_step: # 是否显示逐步回归过程
print('Adding %s, %s = %.3f' % (best_candidate, criterion, best_new_score))
elif (current_score - best_new_score) >= 0 and iter_times == 0 and len_paras_neg==0 and len_tvaluenot==0: # 当评分差大于等于0,且为第一次迭代
remaining.remove(best_candidate)
selected.append(best_candidate)
current_score = best_new_score
if show_step: # 是否显示逐步回归过程
print('Adding %s, %s = %.3f' % (best_candidate, criterion, best_new_score))
elif iter_times == 0: # 当评分差小于p_enter(这里是0),且为第一次迭代
selected.append(remaining[0])
remaining.remove(remaining[0])
if show_step: # 是否显示逐步回归过程
print('Adding %s, %s = %.3f' % (remaining[0], criterion, best_new_score))
else:
scores_with_candidates.sort()
best_new_score, best_candidate, best_new_fvalue, best_new_f_pvalue,len_paras_neg,len_tvaluenot = scores_with_candidates.pop()
if (best_new_score - current_score) > p_enter[criterion] and len_paras_neg==0 and len_tvaluenot==0:
remaining.remove(best_candidate)
selected.append(best_candidate)
current_score = best_new_score
print(iter_times, flush=True)
if show_step: # 是否显示逐步回归过程
print('Adding %s, %s = %.3f' % (best_candidate, criterion, best_new_score))
elif (best_new_score - current_score) >= 0 and iter_times == 0 and len_paras_neg==0 and len_tvaluenot==0: # 当评分差大于等于0,且为第一次迭代
remaining.remove(best_candidate)
selected.append(best_candidate)
current_score = best_new_score
if show_step: # 是否显示逐步回归过程
print('Adding %s, %s = %.3f' % (best_candidate, criterion, best_new_score))
elif iter_times == 0: # 当评分差小于p_enter,且为第一次迭代
selected.append(remaining[0])
remaining.remove(remaining[0])
if show_step: # 是否显示逐步回归过程
print('Adding %s, %s = %.3ssrf' % (remaining[0], criterion, best_new_score))
iter_times += 1
if intercept: # 是否有截距
formula = "{} ~ {} + 1".format(response, ' + '.join(selected))
else:
formula = "{} ~ {} - 1".format(response, ' + '.join(selected))
print('\n', formula)
print(df.info())
stepwise_model = smf.ols(formula, df).fit() # 最优模型拟合
if show_step: # 是否显示逐步回归过程
print('\nLinear regression model:', '\n ', stepwise_model.model.formula)
# print('\n', stepwise_model.summary())
return stepwise_model
from statsmodels.formula import api as smf
stepwise_ = stepwise(data.iloc[:,1:1000], 'y', intercept=True, normalize=False, criterion='bic',
f_pvalue_enter=.05, p_value_enter=.05, direction='forward', show_step=True,
criterion_enter=None, criterion_remove=None,max_iter=200)