├── README.md ├── example.py ├── requirements.txt ├── riskscore ├── EDA.py ├── ETL.py ├── FeatureEngineering.py ├── Modeling.py ├── Preprocessing.py ├── RiskStragety.py ├── __init__.py ├── data │ ├── __init__.py │ ├── german.txt │ ├── germancredit.csv │ └── germancredit.py └── utils │ ├── InputCondition.py │ ├── PltFunction.py │ ├── __init__.py │ ├── btl │ ├── __init__.py │ ├── merge.py │ └── monotous.py │ └── tools.py └── setup.py /README.md: -------------------------------------------------------------------------------- 1 | # 已停止更新, 请移步risk-model 2 | # *riskscore* 3 | ### 风险评分模型 4 | ## *Installation* 5 | ### pip install riskscore 6 | *** 7 | ## *主要功能* 8 | - 数据导入 DbRead(`mysql中导入`,`mongodb中导入`) 9 | - 数据探索分析 (`数据概述BasicStat`,`单变量分布Plot`,`缺失可视化Lot.plot_miss`) 10 | - 数据清洗 Preprocess(`变量类型划分--异常值检测--异常值处理--重复行(列)处理--众数处理--缺失处理`) 11 | - 数据采样 DataSample(`随机采样, 随机平衡采样, Borderline-SMOTE过采样, Easy Ensemble 欠采样`) 12 | - 特征离散化,特征工程 FeatureBin(`无序类别变量分箱组合, 有序数值变量分箱组合,支持单调性检验, 自定义分箱`) 13 | - WOE转换(`woe_trans`) 14 | - 特征选择 SelectFeature(`基于IV信息值, 基于重要度, 基于共线性, 15 | 基于VIF方差膨胀因子, 基于逐步回归, 基于L1正则化`) 16 | - 参数调优 SearchParam(`网格搜索,贝叶斯调优`) 17 | - 模型训练 TrainLr(`普通回归,交叉验证回归`) 18 | - 评分卡模型 ScoreCard(`标准评分卡转换,模型预测[概率预测,评分预测]`) 19 | - 风险决策 (`风险策略,稳定性评估PSI,评分决策表,KS`) 20 | *** 21 | # *Example* 22 | *使用示例: 0.数据探索分析, 1.数据清洗, 2.特征分箱, 3.特征选择, 4.模型训练, 5.评分卡构建, 6.模型评估, 7.风险决策* 23 | 24 | ```python 25 | from sklearn.externals import joblib 26 | import riskscore as rs 27 | 28 | file = './result/' 29 | # 导入数据 30 | germancredit = rs.Germancredit() 31 | german = germancredit.get_data() 32 | print("数据描述:", germancredit.get_describe()) 33 | print("数据样例:", german.head()) 34 | 35 | # 预处理变量 36 | ######################################## 37 | # 0.数据探索分析 38 | ####################################### 39 | bs = rs.BasicStat(df=german) 40 | # 字段基本统计 41 | for col in german.columns: 42 | describe = bs.feature_describe(x=col) 43 | print(describe) 44 | # 数据分布报告 45 | bs.df_report(filename=file+'germancredit.html') 46 | # 分布图 47 | plot = rs.Plot(df=german) 48 | # 缺失可视化 49 | plot.plot_miss(filename=file+'miss_draw.png',asc=1,figsize=(15,8)) 50 | # 单变量探索(示例) 51 | plot.draw_histogram(var='credit_amount',num_bins=20,filename=file+'credit_amount.png') 52 | 53 | ######################################## 54 | # 1.数据清洗 55 | ####################################### 56 | pr = rs.Preprocess() 57 | # 变量类型划分(实际业务中不能使用此方法) 58 | # num_col,cat_col = pr.split_col(df=german,no_split=['creditability'],min_size=4) 59 | # print("number:",num_col,"\n","category:",cat_col) 60 | # 数值化 61 | for col in germancredit.sub_col: 62 | german[col] = german[col].apply(lambda x:int(str(x)[1:])) 63 | # 替换 64 | german = pr.replace_str(df=german,replace_dict=germancredit.rep_dict) 65 | # 连续变量中特殊字符检测 66 | str_set = pr.find_str(df=german,num_col=germancredit.num_col) 67 | print("特殊字符:",str_set) 68 | # 异常字符检测 69 | special_set = pr.special_char(df=german,feature_col=germancredit.all_feature) 70 | print("异常字符:",special_set) 71 | # 异常值检测及处理 72 | german = pr.outlier(df=german,col="age_in_years",low_percent=0.05,up_percent=0.97,cat_percent=0.001,special_value=None) 73 | # 删除重复行 74 | german = pr.drop_dupl(df=german,axis=0) 75 | # 缺失和众数删除 76 | delete_col = pr.drop_nan_mode(df=german,nan_percent=0.9,mode_percent=0.95,col_list=germancredit.all_feature,drop=False) 77 | print("删除变量:",delete_col) 78 | all_feature = [i for i in germancredit.all_feature if i not in delete_col] 79 | num_col = [i for i in germancredit.num_col if i not in delete_col] 80 | cat_col = [i for i in germancredit.cat_col if i not in delete_col] 81 | int_col = [i for i in germancredit.int_col if i not in delete_col] 82 | german = german[all_feature+['target']] 83 | # 缺失填充 84 | # 类别 85 | german = pr.fill_nan(df=german,value='-9999',method='mode',col_list=cat_col,min_miss_rate=0.05) 86 | # 数值 87 | german = pr.fill_nan(df=german,value=-9999,method='mode',col_list=num_col,min_miss_rate=0.05) 88 | # 保存清洗后的数据 89 | german['target'] = german['target'].apply(lambda x: 1 if x ==2 else 0) 90 | 91 | # 过采样,注意: 随机采样,平衡采样方法无需数值化, 过采样和下采样方法必须数值化 92 | df,factorize_dict = pr.factor_map(df=german,col_list=cat_col) 93 | ds = rs.DataSample(df=df,target='target') 94 | df_res = ds.over_sample(method='BorderLine',sampling_strategy="minority") 95 | # 变量值映射,对已数值化变量映射为字符型 96 | # 类别变量和有序离散变量整数化 97 | for col in int_col: 98 | df_res[col] = df_res[col].apply(lambda x:int(x)) 99 | df[col] = df[col].apply(lambda x:int(x)) 100 | 101 | replace_dict={k:{i:j for j,i in v.items()} for k,v in factorize_dict.items()} 102 | # print(replace_dict) 103 | df_res = pr.replace_str(df=df_res,replace_dict=replace_dict) 104 | df = pr.replace_str(df=df,replace_dict=replace_dict) 105 | ############################################# 106 | # 2.特征工程 107 | ############################################# 108 | fe = rs.FeatureBin(df=df_res,target="target",special_values=['-9999',-9999], 109 | min_per_fine_bin=0.01,stop_limit=0.01,min_per_coarse_bin=0.05,max_num_bin=8,method="tree") 110 | # 类别变量分箱 111 | cat_bin_dict,cat_var_iv = fe.category_bin(bin_feature=cat_col,max_num_bin=5) 112 | # 数值变量分箱 113 | num_bin_dict,num_var_iv = fe.number_bin(bin_feature=num_col,max_num_bin=5,no_mono_feature=None) 114 | # 自定义分箱 115 | # self_bin_dict,self_var_iv = fe.self_bin(var='',special_values=[],breaks_list={}) 116 | # 合并分箱结果 117 | bin_dict = {**cat_bin_dict,**num_bin_dict} 118 | var_iv = {**cat_var_iv,**num_var_iv} 119 | # 分箱可视化 120 | bplt = rs.WoeBinPlot(bin_dict=bin_dict) 121 | _ = bplt.woe_plot(features=all_feature,show_iv=True,save=True,path=file) 122 | # woe 转换 123 | df_woe,woe_feature = rs.woe_trans(df=df_res,bin_dict=bin_dict,trans_feature=all_feature,target="target") 124 | 125 | ############################################### 126 | # 3.特征选择 127 | ############################################## 128 | sf = rs.SelectFeature(df_woe=df_woe) 129 | # 基于特征IV 130 | high_iv = sf.baseOn_iv(var_iv=var_iv,threshold=0.02,is_save=False,path=file) 131 | high_iv = {k+"_woe":v for k,v in high_iv.items()} 132 | print("high iv feature:",high_iv) 133 | # 基于特征重要度 134 | high_importance = sf.baseOn_importance(features=woe_feature,target="target",n_estimators=100,is_save=False,path=file) 135 | print("high importance feature:",high_importance) 136 | # 基于共线性检验 137 | feature_vif1 = sf.baseOn_collinear(features=high_iv,cor_threshold=0.8,is_save=False,path=file) 138 | print("两两共线性检验:",feature_vif1) 139 | feature_vif2 = sf.baseOn_vif(features=high_iv,max_vif=10) 140 | print("VIF共线性检验:",feature_vif2) 141 | # 基于逐步回归 142 | step_feature = sf.baseOn_steplr(features=feature_vif2,target='target',C=1,class_weight='balanced',norm="AIC") 143 | # 基于l1正则化 144 | select_feature = sf.baseOn_l1(features=list(step_feature.keys()),target='target',C=0.1,class_weight="balanced",drop_plus=False) 145 | print("基于L1正在:",select_feature) 146 | 147 | ############################################## 148 | # 4.模型训练 149 | ############################################# 150 | select_feature = list(select_feature.keys()) 151 | # 超参优化 152 | ps = rs.SearchParam(X=df_woe[select_feature].values,y=df_woe['target'].values) 153 | grid_param = ps.grid_search(param_grid={"penalty":["l1","l2"],"C":[0.01,0.05,0.1,0.5,1]},cv=5,class_weight='balanced',scoring='roc_auc') 154 | print("grid best param:",grid_param) 155 | bayes_param = ps.bayes_search(param_grid={"penalty":["l1","l2"],"C":(0.001,1)},cv=5,n_iter=10,class_weight='balanced',scoring='roc_auc') 156 | print("bayes best param:",bayes_param) 157 | # 模型实例化 158 | tlr = rs.TrainLr(df_woe=df_woe,features=select_feature,target='target',penalty='l2',class_weight='balanced') 159 | lr = tlr.lr(C=0.37,filename=file) 160 | lr_cv = tlr.lr_cv(Cs=[0.01,0.05,0.1,0.5,1],cv=5,scoring='roc_auc',filename=file) 161 | 162 | ################################ 163 | # 5.评分卡构建 164 | ################################ 165 | model_feature = [i.replace("_woe","") for i in select_feature] 166 | sc = rs.ScoreCard(lr=lr_cv,bin_dict=bin_dict,model_feature=model_feature,score0=600,pdo=50) 167 | # 输出标准评分卡 168 | df_card = sc.score_card(return_df=True) 169 | df_card.to_excel(file+"score_card.xlsx") 170 | 171 | dict_card = sc.score_card(return_df=False) 172 | joblib.dump(dict_card,file+"score_card.pkl") 173 | df_res['score'] = sc.score_ply(df=df_res) 174 | 175 | ############################### 176 | # 6.模型评估,真实样本评估 177 | ############################## 178 | german_woe,_ = rs.woe_trans(df=df,bin_dict=bin_dict,trans_feature=all_feature,target="target") 179 | # print(german_woe.isnull().sum()) 180 | y_prob = lr_cv.predict_proba(german_woe[select_feature].values)[:,1] 181 | y_true = german_woe['target'].values 182 | # 模型指标 183 | model_norm = rs.model_norm(y_true=y_true,y_prob=y_prob) 184 | print("模型测试结果: ",model_norm) 185 | # 作图 186 | mplt = rs.PlotModel(y_true=y_true,y_prob=y_prob) 187 | mplt.plot_roc_curve(filename=file) 188 | mplt.plot_ks_curve(filename=file) 189 | mplt.plot_confusion_matrix(labels=[0,1],filename=file) 190 | df['score'] = sc.score_ply(df=df,only_total_score=True) 191 | ############################### 192 | # 7.风险决策,score cut_off 193 | ############################## 194 | cut_off_score = rs.stragety_score(score_df=df,step=25,score="score",label='target', 195 | amount=5000,tenor=6,IRR=0.3,capital_cost=0.08, 196 | guest_cost=100,data_cost=30,bad_loss=0.6) 197 | cut_off_score.to_excel(file+"cut_off_score.xlsx") 198 | ``` 199 | 200 | -------------------------------------------------------------------------------- /example.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | 评分卡示例: 4 | 0.数据探索分析,1.数据清洗,2.特征分箱,3.特征选择,4.模型训练,5.评分卡构建,6.模型评估,7.风险决策 5 | """ 6 | from sklearn.externals import joblib 7 | import riskscore as rs 8 | 9 | file = './result/' 10 | # 导入数据 11 | germancredit = rs.Germancredit() 12 | german = germancredit.get_data() 13 | print("数据描述:", germancredit.get_describe()) 14 | print("数据样例:", german.head()) 15 | 16 | # 预处理变量 17 | ######################################## 18 | # 0.数据探索分析 19 | ####################################### 20 | bs = rs.BasicStat(df=german) 21 | # 字段基本统计 22 | for col in german.columns: 23 | describe = bs.feature_describe(x=col) 24 | print(describe) 25 | # 数据分布报告 26 | bs.df_report(filename=file+'germancredit.html') 27 | # 分布图 28 | plot = rs.Plot(df=german) 29 | # 缺失可视化 30 | plot.plot_miss(filename=file+'miss_draw.png',asc=1,figsize=(15,8)) 31 | # 单变量探索(示例) 32 | plot.draw_histogram(var='credit_amount',num_bins=20,filename=file+'credit_amount.png') 33 | 34 | ######################################## 35 | # 1.数据清洗 36 | ####################################### 37 | pr = rs.Preprocess() 38 | # 变量类型划分(实际业务中不能使用此方法) 39 | # num_col,cat_col = pr.split_col(df=german,no_split=['creditability'],min_size=4) 40 | # print("number:",num_col,"\n","category:",cat_col) 41 | # 数值化 42 | for col in germancredit.sub_col: 43 | german[col] = german[col].apply(lambda x:int(str(x)[1:])) 44 | # 替换 45 | german = pr.replace_str(df=german,replace_dict=germancredit.rep_dict) 46 | # 连续变量中特殊字符检测 47 | str_set = pr.find_str(df=german,num_col=germancredit.num_col) 48 | print("特殊字符:",str_set) 49 | # 异常字符检测 50 | special_set = pr.special_char(df=german,feature_col=germancredit.all_feature) 51 | print("异常字符:",special_set) 52 | # 异常值检测及处理 53 | german = pr.outlier(df=german,col="age_in_years",low_percent=0.05,up_percent=0.97,cat_percent=0.001,special_value=None) 54 | # 删除重复行 55 | german = pr.drop_dupl(df=german,axis=0) 56 | # 缺失和众数删除 57 | delete_col = pr.drop_nan_mode(df=german,nan_percent=0.9,mode_percent=0.95,col_list=germancredit.all_feature,drop=False) 58 | print("删除变量:",delete_col) 59 | all_feature = [i for i in germancredit.all_feature if i not in delete_col] 60 | num_col = [i for i in germancredit.num_col if i not in delete_col] 61 | cat_col = [i for i in germancredit.cat_col if i not in delete_col] 62 | int_col = [i for i in germancredit.int_col if i not in delete_col] 63 | german = german[all_feature+['target']] 64 | # 缺失填充 65 | # 类别 66 | german = pr.fill_nan(df=german,value='-9999',method='mode',col_list=cat_col,min_miss_rate=0.05) 67 | # 数值 68 | german = pr.fill_nan(df=german,value=-9999,method='mode',col_list=num_col,min_miss_rate=0.05) 69 | # 保存清洗后的数据 70 | german['target'] = german['target'].apply(lambda x: 1 if x ==2 else 0) 71 | 72 | # 过采样,注意: 随机采样,平衡采样方法无需数值化, 过采样和下采样方法必须数值化 73 | df,factorize_dict = pr.factor_map(df=german,col_list=cat_col) 74 | ds = rs.DataSample(df=df,target='target') 75 | df_res = ds.over_sample(method='BorderLine',sampling_strategy="minority") 76 | # 变量值映射,对已数值化变量映射为字符型 77 | # 类别变量和有序离散变量整数化 78 | for col in int_col: 79 | df_res[col] = df_res[col].apply(lambda x:int(x)) 80 | df[col] = df[col].apply(lambda x:int(x)) 81 | 82 | replace_dict={k:{i:j for j,i in v.items()} for k,v in factorize_dict.items()} 83 | # print(replace_dict) 84 | df_res = pr.replace_str(df=df_res,replace_dict=replace_dict) 85 | df = pr.replace_str(df=df,replace_dict=replace_dict) 86 | ############################################# 87 | # 2.特征工程 88 | ############################################# 89 | fe = rs.FeatureBin(df=df_res,target="target",special_values=['-9999',-9999], 90 | min_per_fine_bin=0.01,stop_limit=0.01,min_per_coarse_bin=0.05,max_num_bin=8,method="tree") 91 | # 类别变量分箱 92 | cat_bin_dict,cat_var_iv = fe.category_bin(bin_feature=cat_col,max_num_bin=5) 93 | # 数值变量分箱 94 | num_bin_dict,num_var_iv = fe.number_bin(bin_feature=num_col,max_num_bin=5,no_mono_feature=None) 95 | # 自定义分箱 96 | # self_bin_dict,self_var_iv = fe.self_bin(var='',special_values=[],breaks_list={}) 97 | # 合并分箱结果 98 | bin_dict = {**cat_bin_dict,**num_bin_dict} 99 | var_iv = {**cat_var_iv,**num_var_iv} 100 | # 分箱可视化 101 | bplt = rs.WoeBinPlot(bin_dict=bin_dict) 102 | _ = bplt.woe_plot(features=all_feature,show_iv=True,save=True,path=file) 103 | # woe 转换 104 | df_woe,woe_feature = rs.woe_trans(df=df_res,bin_dict=bin_dict,trans_feature=all_feature,target="target") 105 | 106 | ############################################### 107 | # 3.特征选择 108 | ############################################## 109 | sf = rs.SelectFeature(df_woe=df_woe) 110 | # 基于特征IV 111 | high_iv = sf.baseOn_iv(var_iv=var_iv,threshold=0.02,is_save=False,path=file) 112 | high_iv = {k+"_woe":v for k,v in high_iv.items()} 113 | print("high iv feature:",high_iv) 114 | # 基于特征重要度 115 | high_importance = sf.baseOn_importance(features=woe_feature,target="target",n_estimators=100,is_save=False,path=file) 116 | print("high importance feature:",high_importance) 117 | # 基于共线性检验 118 | feature_vif1 = sf.baseOn_collinear(features=high_iv,cor_threshold=0.8,is_save=False,path=file) 119 | print("两两共线性检验:",feature_vif1) 120 | feature_vif2 = sf.baseOn_vif(features=high_iv,max_vif=10) 121 | print("VIF共线性检验:",feature_vif2) 122 | # 基于逐步回归 123 | step_feature = sf.baseOn_steplr(features=feature_vif2,target='target',C=1,class_weight='balanced',norm="AIC") 124 | # 基于l1正则化 125 | select_feature = sf.baseOn_l1(features=list(step_feature.keys()),target='target',C=0.1,class_weight="balanced",drop_plus=False) 126 | print("基于L1正在:",select_feature) 127 | 128 | ############################################## 129 | # 4.模型训练 130 | ############################################# 131 | select_feature = list(select_feature.keys()) 132 | # 超参优化 133 | ps = rs.SearchParam(X=df_woe[select_feature].values,y=df_woe['target'].values) 134 | grid_param = ps.grid_search(param_grid={"penalty":["l1","l2"],"C":[0.01,0.05,0.1,0.5,1]},cv=5,class_weight='balanced',scoring='roc_auc') 135 | print("grid best param:",grid_param) 136 | bayes_param = ps.bayes_search(param_grid={"penalty":["l1","l2"],"C":(0.001,1)},cv=5,n_iter=10,class_weight='balanced',scoring='roc_auc') 137 | print("bayes best param:",bayes_param) 138 | # 模型实例化 139 | tlr = rs.TrainLr(df_woe=df_woe,features=select_feature,target='target',penalty='l2',class_weight='balanced') 140 | lr = tlr.lr(C=0.37,filename=file) 141 | lr_cv = tlr.lr_cv(Cs=[0.01,0.05,0.1,0.5,1],cv=5,scoring='roc_auc',filename=file) 142 | 143 | ################################ 144 | # 5.评分卡构建 145 | ################################ 146 | model_feature = [i.replace("_woe","") for i in select_feature] 147 | sc = rs.ScoreCard(lr=lr_cv,bin_dict=bin_dict,model_feature=model_feature,score0=600,pdo=50) 148 | # 输出标准评分卡 149 | df_card = sc.score_card(return_df=True) 150 | df_card.to_excel(file+"score_card.xlsx") 151 | 152 | dict_card = sc.score_card(return_df=False) 153 | joblib.dump(dict_card,file+"score_card.pkl") 154 | df_res['score'] = sc.score_ply(df=df_res) 155 | 156 | ############################### 157 | # 6.模型评估,真实样本评估 158 | ############################## 159 | german_woe,_ = rs.woe_trans(df=df,bin_dict=bin_dict,trans_feature=all_feature,target="target") 160 | # print(german_woe.isnull().sum()) 161 | y_prob = lr_cv.predict_proba(german_woe[select_feature].values)[:,1] 162 | y_true = german_woe['target'].values 163 | # 模型指标 164 | model_norm = rs.model_norm(y_true=y_true,y_prob=y_prob) 165 | print("模型测试结果: ",model_norm) 166 | # 作图 167 | mplt = rs.PlotModel(y_true=y_true,y_prob=y_prob) 168 | mplt.plot_roc_curve(filename=file) 169 | mplt.plot_ks_curve(filename=file) 170 | mplt.plot_confusion_matrix(labels=[0,1],filename=file) 171 | df['score'] = sc.score_ply(df=df,only_total_score=True) 172 | ############################### 173 | # 7.风险决策, score cut_off 174 | ############################## 175 | cut_off_score = rs.stragety_score(score_df=df,step=25,score="score",label='target', 176 | amount=5000,tenor=6,IRR=0.3,capital_cost=0.08, 177 | guest_cost=100,data_cost=30,bad_loss=0.6) 178 | cut_off_score.to_excel(file+"cut_off_score.xlsx") -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pymongo==3.7.2 2 | PyMySQL==0.9.3 3 | missingno==0.4.1 4 | pandas-profiling==1.4.1 5 | numpy==1.15.0 6 | matplotlib==3.0.2 7 | pandas==0.23.4 8 | scikit-learn==0.21.3 9 | imbalanced-learn==0.5.0 10 | statsmodels==0.9.0 11 | scorecardpy==0.1.7 12 | seaborn==0.9.0 13 | bayesian-optimization==1.0.1 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /riskscore/EDA.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | 数据探索分析 4 | """ 5 | import warnings 6 | warnings.filterwarnings('ignore') 7 | import pandas as pd 8 | import missingno as msno 9 | import pandas_profiling as pandas_pf 10 | import numpy as np 11 | import matplotlib.pyplot as plt 12 | plt.rcParams['backend'] = 'Agg' 13 | import matplotlib.mlab as mlab 14 | from .utils.InputCondition import check_df,check_str,check_str_list 15 | 16 | class BasicStat(object): 17 | """数据概述""" 18 | def __init__(self,df:pd.DataFrame): 19 | """ 20 | 基本指标统计类 21 | :param df:dfframe 数据集 22 | """ 23 | df = check_df(df) 24 | self._df = df 25 | self.dtypes = df.dtypes.to_dict() 26 | 27 | def feature_describe(self,x=None,percentiles=None,include=None): 28 | """ 29 | 字段基本信息 30 | :param x: str or list 统计变量, 默认None 31 | :param percentiles: list-like of float 默认 None,[0.25,0.5,0.75] 32 | :param include: 'all', list-like of dtypes or None (default) 33 | :return:feature_describe 字段基本信息 34 | """ 35 | # x = check_str_list(x) 36 | if x: 37 | feature_describe = self._df[x].describe(percentiles=percentiles,include=include).T 38 | else: 39 | feature_describe = self._df.describe(percentiles=percentiles,include=include).T 40 | return feature_describe 41 | 42 | 43 | def df_report(self,filename): 44 | """ 45 | 数据报告 46 | :param filename: str 保存文件名 47 | :return:数据报告 html格式 48 | """ 49 | filename = check_str(filename) 50 | profile = pandas_pf.ProfileReport(self._df) 51 | profile.to_file(outputfile=filename) 52 | print("report done") 53 | 54 | class Plot(object): 55 | """ 56 | 作图 57 | """ 58 | def __init__(self,df:pd.DataFrame): 59 | """公共属性""" 60 | df = check_df(df) 61 | self._df = df 62 | 63 | def draw_pie(self,var:str,filename:str): 64 | """ 65 | 字符型变量饼图 66 | ------------------------------------- 67 | Params 68 | s: pandas Series 69 | lalels:labels of each unique value in s 70 | dropna:bool obj 71 | filename: 保存图路径及文件名 72 | ------------------------------------- 73 | Return 74 | show the plt object 75 | """ 76 | var = check_str(var) 77 | filename = check_str(filename) 78 | counts = self._df[var].value_counts(dropna=True) 79 | labels = counts.index 80 | fig, ax = plt.subplots(figsize=(8, 8)) 81 | ax.pie(counts, labels=labels, autopct='%1.2f%%', shadow=True, startangle=90) 82 | ax.axis('equal') 83 | ax.set_title(r'pie of {}'.format(var)) 84 | plt.savefig(filename) 85 | plt.close() 86 | 87 | def draw_bar(self,var:str, filename:str, pct=False, horizontal=False): 88 | """ 89 | 字符型变量条形图 90 | ------------------------------------------- 91 | Params 92 | s: pandas Series 93 | x_ticks: list, ticks in X axis 94 | pct: bool, True means trans df to odds 95 | dropna: bool obj,True means drop nan 96 | horizontal: bool, True means draw horizontal plot 97 | ------------------------------------------- 98 | Return 99 | show the plt object 100 | """ 101 | var = check_str(var) 102 | filename = check_str(filename) 103 | 104 | counts = self._df[var].value_counts(dropna=False) 105 | if pct: 106 | counts = counts / self._df[var].shape[0] 107 | ind = np.arange(counts.shape[0]) 108 | plt.figure(figsize=(8, 6)) 109 | 110 | if not horizontal: 111 | plt.bar(ind, counts) 112 | plt.ylabel('frequecy') 113 | plt.xticks(ind, tuple(counts.index)) 114 | else: 115 | plt.barh(ind, counts) 116 | plt.xlabel('frequecy') 117 | plt.yticks(ind, tuple(counts.index)) 118 | plt.title('Bar plot for {}'.format(var)) 119 | plt.savefig(filename) 120 | plt.close() 121 | 122 | def draw_histogram(self,var:str,filename:str,num_bins:int=20): 123 | """ 124 | 连续变量分布图 125 | --------------------------------------------- 126 | Params 127 | s: pandas series 128 | num_bins: number of bins 129 | filename png name 130 | --------------------------------------------- 131 | Return 132 | show the plt object 133 | """ 134 | var = check_str(var) 135 | filename = check_str(filename) 136 | 137 | fig, ax = plt.subplots(figsize=(14, 7)) 138 | mu = self._df[var].mean() 139 | sigma = self._df[var].std() 140 | 141 | n, bins, patches = ax.hist(self._df[var], num_bins, normed=1, rwidth=0.95, facecolor="blue") 142 | 143 | y = mlab.normpdf(bins, mu, sigma) 144 | ax.plot(bins, y, 'r--') 145 | ax.set_xlabel(var) 146 | ax.set_ylabel('Probability density') 147 | ax.set_title(r'Histogram of %s: $\mu=%.2f$, $\sigma=%.2f$' % (var, mu, sigma)) 148 | plt.savefig(filename) 149 | plt.close() 150 | 151 | def plot_miss(self,filename:str,asc=0,figsize=(10,6)): 152 | """ 153 | 缺失可视化 154 | :param df:df 155 | :param filename:str 路径及文件名 156 | :param asc: int 统计方法,Matrix(asc=0),BarChart(asc=1),Heatmap(asc=2) 157 | :param figsize tupe 图片大小 158 | :return:保存结果 159 | """ 160 | filename = check_str(filename) 161 | if asc == 0: 162 | msno.matrix(df=self._df) 163 | elif asc == 1: 164 | msno.bar(df=self._df, figsize=figsize) 165 | else: 166 | msno.heatmap(df=self._df, figsize=figsize) 167 | plt.savefig(filename) 168 | 169 | def plot_scatter(self,var1:str,var2:str,filename:str,x1label=None,x2label=None): 170 | """ 171 | 散点图 172 | :param df:dataframe 173 | :param filename str 路径及文件名 174 | :param x1: str x1变量 175 | :param x2: str x2 变量 176 | :param x1label: x1 str 标签 177 | :param x2label: x2 str 标签 178 | :return:保存图 179 | """ 180 | var1 = check_str(var1) 181 | var2 = check_str(var2) 182 | filename = check_str(filename) 183 | 184 | plt.scatter(df=self._df,x=var1,y=var2) 185 | plt.xlabel(x1label) 186 | plt.ylabel(x2label) 187 | plt.savefig(filename) 188 | plt.close() 189 | 190 | def mult_boxplot(self,variable:str,category:str,filename:str,xlabel=None, ylabel=None, title=None): 191 | """ 192 | 箱线图,探索数据分布 193 | :param df: df 194 | :param variable: str 统计变量 195 | :param category: str 分组值 196 | :param filename: str 路径及文件名 197 | :param xlabel: str 198 | :param ylabel: str 199 | :param title: str 200 | :return: 保存图 201 | """ 202 | variable = check_str(variable) 203 | category = check_str(category) 204 | filename = check_str(filename) 205 | 206 | self._df[[variable, category]].boxplot(by=category) #作图 207 | 208 | if xlabel: 209 | plt.xlabel(xlabel) 210 | if ylabel: 211 | plt.ylabel(ylabel) 212 | if title: 213 | plt.title(title) 214 | 215 | plt.savefig(filename) 216 | plt.close() -------------------------------------------------------------------------------- /riskscore/ETL.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | import origin data 4 | from database,os. 5 | database: mysql, mongodb 6 | """ 7 | 8 | from pymongo import MongoClient 9 | import pymysql 10 | 11 | class DbRead(object): 12 | """从数据库中读取""" 13 | def __init__(self,host,port,user,password,database): 14 | """ 15 | 数据库连接信息 16 | :param host:str,IP 17 | :param port: int 端口 18 | :param user: str 用户名 19 | :param password: str 密码 20 | :param database: str or object 数据库名 21 | """ 22 | self._host = host 23 | self._port = port 24 | self._user = user 25 | self._password = password 26 | self._database = database 27 | 28 | def read_mysql(self,sql): 29 | """ 30 | 从mysql中获取数据 31 | :param sql: str, 执行的sql语句 32 | :return: data,iterable 数据生成器 33 | """ 34 | dbConfig = { 35 | "host" : self._host, 36 | "port" : self._port, 37 | "user" : self._user, 38 | "password" : self._password, 39 | "db" : self._database, 40 | "cursorclass" : pymysql.cursors.DictCursor, 41 | "charset" : "utf8" 42 | } 43 | 44 | dbMysql = pymysql.connect(**dbConfig) 45 | cursor = dbMysql.cursor() 46 | cursor.execute(sql) 47 | data = cursor.fetchall() 48 | dbMysql.close() 49 | return data 50 | 51 | def read_mongodb(self,collection, findCondition): 52 | """ 53 | 从mongoDB中获取数据 54 | :param collection: object,表名 55 | :param findCondition: dict 查询条件 56 | :return: data iterable 数据生成器 57 | """ 58 | conn = MongoClient(host=self._host,port=self._port) 59 | dbMongo = conn.get_database(name=self._database) 60 | dbMongo.authenticate(self._user,self._password) 61 | col = dbMongo.get_collection(collection) 62 | data = col.find(findCondition) 63 | conn.close() 64 | return data 65 | 66 | -------------------------------------------------------------------------------- /riskscore/FeatureEngineering.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | 特征工程 4 | 1. 无序类别变量分箱组合, 有序数值变量分箱组合, 自定义分箱,WOE转换 5 | 2. 基于IV信息值特征选择, 基于重要度特征选择,基于共线性特征选择, 6 | 基于VIF方差膨胀因子特征选择,基于逐步回归特征选择,基于L1正则化特征选择 7 | """ 8 | import copy 9 | import warnings 10 | import time 11 | import numpy as np 12 | import pandas as pd 13 | import matplotlib.pyplot as plt 14 | plt.rcParams['backend'] = 'Agg' 15 | from sklearn.linear_model import LogisticRegression 16 | from sklearn.ensemble import RandomForestClassifier 17 | from statsmodels.stats.outliers_influence import variance_inflation_factor # 共线性检验 18 | from scorecardpy import woebin,woebin_ply,woebin_plot 19 | # 自定义 20 | from .utils.btl.merge import monotonous_bin 21 | from .utils.PltFunction import PlotFeatureEn 22 | from .utils.tools import calculate_AIC,model_norm 23 | from .utils import InputCondition as IC 24 | 25 | class FeatureBin(object): 26 | """ 27 | 变量分箱, 即特征离散化 28 | """ 29 | def __init__(self,df,target="target",special_values=None,breaks_list=None,min_per_fine_bin=0.01, 30 | stop_limit=0.02, min_per_coarse_bin=0.05, max_num_bin=5, method="tree"): 31 | """ 32 | 分箱类属性 33 | :param df: df 34 | :param target: str 标签名 35 | :param special_values: list, dict 特殊值, 为list是作用于所有变量, 也可以每个变量指定, 36 | 例如 {"A":[2600, 9960, "6850%,%missing"],"B":["education", "others%,%missing"]} 37 | :param breaks_list: list of dict 指定断点,变量指定,例如 {"A":[2600, 9960, "6850%,%missing"],"B":["education", "others%,%missing"]} 38 | :param min_per_fine_bin: float 初始分箱数,接受范围 0.01-0.2, 默认 0.01,即可初始分箱数为100 39 | :param stop_limit: float 最小IV值或卡方值,可接受范围 0-0.5, 默认 0.02. 40 | :param min_per_coarse_bin: float 最终每箱占比,接受范围 0.01-0.2, 默认 0.05, 即最小箱占比5%. 41 | :param max_num_bin: 最大分箱数 默认5 42 | :param method: str 方法,默认 tree 可选 chimerge 卡方分箱 43 | """ 44 | self._df = IC.check_df(df) 45 | self._target = IC.check_str(target) 46 | if special_values is not None: 47 | self._special_values = IC.check_special_values(special_values) 48 | else: 49 | self._special_values = special_values 50 | if breaks_list is not None: 51 | self._breaks_list = IC.check_list_of_dict(breaks_list) 52 | else: 53 | self._breaks_list = breaks_list 54 | self._min_per_fine_bin = IC.check_float(min_per_fine_bin) 55 | self._stop_limit = IC.check_float(stop_limit) 56 | self._min_per_coarse_bin = IC.check_float(min_per_coarse_bin) 57 | self._max_num_bin = IC.check_int(max_num_bin) 58 | self._method = IC.check_str(method) 59 | 60 | def category_bin(self,bin_feature,max_num_bin=None): 61 | """ 62 | 无序类别变量分箱组合 63 | :param max_num_bin:int 最大分箱数 64 | :param bin_feature: list, 参与分箱的变量 65 | :return: bin_dict:dict, var_iv:dict 66 | """ 67 | bin_feature = IC.check_list(bin_feature) 68 | t0 = time.process_time() 69 | bin_dict,var_iv = dict(),dict() 70 | df = self._df 71 | df[bin_feature] = df[bin_feature].astype("str") # 防止误将无需类别变量当做连续变量处理 72 | 73 | if max_num_bin == None: 74 | max_num_bin = self._max_num_bin 75 | else: 76 | max_num_bin = IC.check_int(max_num_bin) 77 | 78 | # 开始分箱 79 | for col in bin_feature: 80 | bin_dict[col] = woebin(dt=df[[col,self._target]],y=self._target,x=col,breaks_list=self._breaks_list,special_values=self._special_values, 81 | min_perc_fine_bin=self._min_per_fine_bin,min_perc_coarse_bin=self._min_per_coarse_bin,stop_limit=self._stop_limit, 82 | max_num_bin=max_num_bin,method=self._method)[col] 83 | var_iv[col] = bin_dict[col]["total_iv"].unique()[0] 84 | 85 | print("处理完{}个无序类别变量,耗时:{}秒".format(len(bin_feature),(time.process_time()-t0)*100/60)) 86 | 87 | return bin_dict,var_iv 88 | 89 | def number_bin(self,bin_feature,max_num_bin=None,no_mono_feature=None): 90 | """ 91 | 有序数值变量分箱组合 92 | :param bin_feature:list, 参与分箱的变量 93 | :param max_num_bin: int 最大分箱数 94 | :param no_mono_feature list 不参与单调检验的变量 95 | :return: bin_dict:dict, var_iv:dict 96 | """ 97 | t0 = time.process_time() 98 | bin_dict,var_iv = {},{} 99 | df = copy.deepcopy(self._df) 100 | bin_feature = IC.check_list(bin_feature) 101 | df[bin_feature] = df[bin_feature].astype("float") #防止误将分类变量当做连续变量处理,若不是, 调用该函数将报错 102 | 103 | if max_num_bin == None: 104 | max_num_bin = self._max_num_bin 105 | else: 106 | max_num_bin = IC.check_int(max_num_bin) 107 | if no_mono_feature == None: 108 | no_mono_feature = [] 109 | else: 110 | no_mono_feature = IC.check_list(no_mono_feature) 111 | 112 | # 开始分箱 113 | for col in bin_feature: 114 | if isinstance(self._special_values,dict): 115 | special_values = self._special_values[col] 116 | else: 117 | special_values = self._special_values 118 | # 对于唯一值的变量进行跳过 119 | unique_values = [v for v in df[col].unique() if v not in special_values] 120 | if len(unique_values) ==1: 121 | warnings.warn("There are {} columns have only one unique values,{}, which are skipping from this bing.".format(col,unique_values)) 122 | continue 123 | 124 | if col not in no_mono_feature: 125 | cutOffPoints = woebin(dt=df[[col,self._target]],y=self._target,x=col,breaks_list=self._breaks_list,special_values=special_values, 126 | min_perc_fine_bin=self._min_per_fine_bin,min_perc_coarse_bin=self._min_per_coarse_bin,stop_limit=self._stop_limit, 127 | max_num_bin=max_num_bin,method=self._method)[col]["breaks"].tolist() 128 | 129 | cutOffPoints = [float(i) for i in set(cutOffPoints) if str(i) not in ['inf','-inf']] # 切分点 130 | cutOffPoints = sorted([i for i in cutOffPoints if i not in special_values]) 131 | 132 | if not cutOffPoints: # 切分点为空 133 | warnings.warn("There are zero cutOffPoint of {} columns from this bing, which select all unique values insert cutOffPoint".format(col)) 134 | cutOffPoints = sorted([i for i in df[col].unique() if i not in special_values]) 135 | 136 | # 单调检验合并方案结果 137 | # mono_cutOffPoints:dict 138 | mono_cutOffPoints = monotonous_bin(df=df[[col,self._target]],col=col,cutOffPoints=cutOffPoints, 139 | target=self._target,special_values=special_values) 140 | else: 141 | mono_cutOffPoints = {} 142 | 143 | # 最终方案 144 | bin_dict[col] = woebin(dt=df[[col,self._target]],y=self._target,x=col,breaks_list=mono_cutOffPoints,special_values=special_values, 145 | min_perc_fine_bin=self._min_per_fine_bin,min_perc_coarse_bin=self._min_per_coarse_bin,stop_limit=self._stop_limit, 146 | max_num_bin=max_num_bin,method=self._method)[col] 147 | # 保存IV 148 | var_iv[col] = bin_dict[col]["total_iv"].unique()[0] 149 | 150 | print("处理完{}个有序数值变量,耗时:{}秒".format(len(bin_feature), (time.process_time() - t0) * 100 / 60)) 151 | return bin_dict, var_iv 152 | 153 | def self_bin(self,var,special_values=None,breaks_list=None): 154 | """ 155 | 指定断点 156 | :param var: str, 自定义分箱单变量 157 | :param special_values: list of dict 158 | :param breaks_list: list of dict 159 | :return: bin_dict:dict, var_iv:dict 160 | 161 | breaks_list = { 162 | 'age.in.years': [26, 35, 37, "Inf%,%missing"], 163 | 'housing': ["own", "for free%,%rent"] 164 | } 165 | special_values = { 166 | 'credit.amount': [2600, 9960, "6850%,%missing"], 167 | 'purpose': ["education", "others%,%missing"] 168 | } 169 | """ 170 | var = IC.check_str(var) 171 | bin_dict, var_iv = dict(),dict() 172 | if special_values == None: 173 | special_values = self._special_values 174 | else: 175 | special_values = IC.check_special_values(special_values) 176 | if breaks_list == None: 177 | breaks_list = self._breaks_list 178 | else: 179 | breaks_list = IC.check_list_of_dict(breaks_list) 180 | 181 | bin_dict[var] = woebin(dt=self._df[[var,self._target]], y=self._target, x=var, 182 | breaks_list=breaks_list, special_values=special_values)[var] 183 | # 保存IV 184 | var_iv[var] = bin_dict[var]["total_iv"].unique()[0] 185 | 186 | return bin_dict,var_iv 187 | 188 | def woe_trans(df,bin_dict,trans_feature,target="target"): 189 | """ 190 | woe 转换 191 | :param df: origin data 包含 目标变量 192 | :param bin_dict: dict 193 | :param trans_feature:list 转换变量 194 | :param target: str 目标变量 195 | :return: df_woe,new_feature 196 | """ 197 | df = IC.check_df(df) 198 | bin_dict = IC.check_dict_of_df(bin_dict) 199 | trans_feature = IC.check_list(trans_feature) 200 | if not set(trans_feature).issubset(set(list(bin_dict.keys()))): 201 | print("bin_dict.key",bin_dict.keys(),'\n') 202 | print("trans_feature:",trans_feature) 203 | warnings.warn("trans_feature not in bin_dict.keys, Please double check feature set") 204 | raise SystemExit(0) 205 | 206 | dt = df[trans_feature+[target]] 207 | df_woe = woebin_ply(dt=dt,bins=bin_dict) 208 | new_feature = [i+"_woe" for i in trans_feature] 209 | 210 | if not set(new_feature).issubset(set(df_woe.columns.difference([target]).tolist())): 211 | warnings.warn("new feature not in df_woe.columns, Please double check feature set") 212 | raise SystemExit(0) 213 | 214 | return df_woe,new_feature 215 | 216 | class WoeBinPlot(object): 217 | """ 218 | 分箱作图 219 | """ 220 | def __init__(self,bin_dict): 221 | """ 222 | :param bin_dict:DataFrame of bin_dict, 分箱数据字典 223 | """ 224 | bin_dict = IC.check_dict_of_df(bin_dict) 225 | self._bins = bin_dict 226 | 227 | def woe_plot(self,features:list=None,show_iv:bool=False,save:bool = False,path:str='./'): 228 | """ 229 | woe全部变量作图 230 | :param var: str, 作图变量 231 | :param title: str 图标题 232 | :param show_iv: bool, 是否展示iv 233 | :param save: bool 是否保存 234 | :return: dict a dict of matplotlib figure objests 235 | ---------- 236 | Examples 237 | ---------- 238 | plotlist = woe_plot(bins) 239 | 240 | # save binning plot 241 | for key,i in plotlist.items(): 242 | plt.show(i) 243 | plt.savefig(str(key)+'.png') 244 | """ 245 | features = IC.check_list(features) 246 | if not set(features).issubset(set(list(self._bins.keys()))): 247 | print("bin_dict.key", self._bins.keys(), '\n') 248 | print("features:", features) 249 | warnings.warn("features not in bin_dict.keys, Please double check feature set") 250 | raise SystemExit(0) 251 | 252 | plotList = woebin_plot(bins=self._bins,x=features,show_iv=show_iv) 253 | if save: 254 | # save binning plot 255 | for key,i in plotList.items(): 256 | plt.savefig(path+str(key)+'_bin.png') 257 | plt.close() 258 | return plotList 259 | 260 | PltF = PlotFeatureEn() 261 | class SelectFeature(object): 262 | """特征选择""" 263 | 264 | def __init__(self,df_woe): 265 | df_woe = IC.check_df(df_woe) 266 | self._df_woe = df_woe 267 | 268 | def baseOn_iv(self,var_iv:dict,threshold:float=0.02,xlabel=None,figsize:tuple=(15,7), is_save:bool=False,path="./"): 269 | """ 270 | 选择IV高于阈值的变量, 一般说来,信息值0.02以下表示与目标变量相关性非常弱。 271 | 0.02-0.1很弱;0.1-0.3一般;0.3-0.5强;0.5-1很强,1以上异常,单独关注 272 | :param var_iv: dict 特征信息值字典 273 | :param threshold: float,iv阈值 274 | :param path:文件存储地址 275 | :return:dict 276 | """ 277 | var_iv = IC.check_dict_of_float(var_iv) 278 | high_IV = {k: v for k, v in var_iv.items() if v >= threshold} # 根据IV选择变量 279 | high_IV = {k:v for (k,v) in sorted(high_IV.items(),key=lambda x:x[1],reverse=True)} # 排序 280 | high_IV_df = pd.Series(high_IV) 281 | if is_save: #保存IV图片及IV表 282 | PltF.draw_IV(IV_dict=high_IV, path=path, xlabel=xlabel, figsize=figsize, is_save=is_save) 283 | high_IV_df.to_excel(path+"high_iv_df.xlsx",index=False) 284 | return high_IV 285 | 286 | def baseOn_importance(self,features:list,target:str='target',n_estimators:int = 100,is_save:bool=False, 287 | figsize: tuple = (15, 7),path="./"): 288 | """ 289 | 基于随机森林特征权重 290 | -------------------- 291 | parameter: 292 | features: list 评估变量集 293 | n_estimators: 随机森林树量 294 | target: str 295 | is_save:bool 作图 296 | figsize: tuple 图片大小 297 | path: str 地址 298 | return: 299 | feature_importance: dict and importance draw 300 | """ 301 | X = self._df_woe[features] 302 | y= self._df_woe[target] 303 | features = IC.check_list(features) 304 | 305 | rf = RandomForestClassifier(n_estimators=n_estimators, random_state=42) # 构建分类随机森林分类器 306 | rf.fit(X.values, y.values) # 对自变量和因变量进行拟合 307 | 308 | # feature importances dict 309 | importance = rf.feature_importances_ 310 | feature_importance = dict() 311 | for k, v in zip(features, importance): 312 | feature_importance[k] = v 313 | # 可视化 314 | if is_save: 315 | PltF.draw_importance(importance=importance,features=features,figsize=figsize,path=path) 316 | feature_importance = {k:v for k,v in sorted(feature_importance.items(),key=lambda x:x[1],reverse=True)} 317 | return feature_importance 318 | 319 | 320 | def baseOn_collinear(self,features:dict,cor_threshold:float=0.7, 321 | is_save:bool=False,figsize:tuple=(12,12),path:str ='./'): 322 | 323 | # 基于两两相关性共线性检验 324 | # 1,将候选变量按照IV进行降序排列 325 | # 2,计算第i和第i+1的变量的线性相关系数 326 | # 3,对于系数超过阈值的两个变量,剔除IV较低的一个 327 | features = IC.check_dict_of_float(features) 328 | 329 | deleted_feature = [] # 删除的变量 330 | for col1 in features.keys(): 331 | if col1 in deleted_feature: 332 | continue 333 | for col2 in features.keys(): 334 | if col1 == col2 or col2 in deleted_feature: 335 | continue 336 | cor_v = np.corrcoef(self._df_woe[col1], self._df_woe[col2])[0, 1] 337 | if abs(cor_v) >= cor_threshold: 338 | if features[col1] >= features[col2]: 339 | deleted_feature.append(col2) 340 | print("相关性检验,删除变量: ",col2) 341 | else: 342 | deleted_feature.append(col1) 343 | print("相关性检验,删除变量: ", col1) 344 | 345 | last_feature = [i for i in features.keys() if i not in deleted_feature] 346 | # 多变量分析:VIF 347 | X = np.matrix(self._df_woe[last_feature]) 348 | VIF_list = [variance_inflation_factor(X, i) for i in range(X.shape[1])] 349 | max_VIF = max(VIF_list) 350 | print("最大方差膨胀因子:{}".format(max_VIF)) 351 | feature_VIF = {k:v for k,v in zip(last_feature,VIF_list)} 352 | #相关性可视化 353 | if is_save: 354 | PltF.draw_corr(df=self._df_woe,figsize=figsize,path=path) 355 | return feature_VIF 356 | 357 | def baseOn_vif(self,features:dict,max_vif:int=10): 358 | """ 359 | 基于方差膨胀因子共线性检验 360 | --------------------------------------- 361 | parameter: 362 | df: df 363 | features:dict 364 | max_ivf : float or int, vif threshold 365 | return: 366 | keep_feature_vif:dict 367 | 368 | """ 369 | features = IC.check_dict_of_float(features) 370 | data = self._df_woe 371 | features_sort = list({k:v for k,v in sorted(features.items(), key=lambda x: x[1], reverse=True)}.keys()) # 排序 372 | select_feature = [features_sort[0]] 373 | for col,i in zip(features_sort[1:],range(1,len(features_sort))): 374 | temp_feature = select_feature + [features_sort[i]] 375 | X = np.matrix(data[temp_feature]) 376 | vif_list = [variance_inflation_factor(X, i) for i in range(X.shape[1])] 377 | if max(vif_list) < max_vif: 378 | select_feature.append(col) 379 | else: 380 | print("共线性VIF: {},删除变量: {}".format(max(vif_list),col)) 381 | 382 | vif_last = [variance_inflation_factor(data[select_feature].values, i) for i in range(len(select_feature))] 383 | feature_VIF = {k:v for k,v in zip(select_feature,vif_last)} 384 | return feature_VIF 385 | 386 | def baseOn_steplr(self,features:dict,target:str="target",C:float=1,class_weight="balanced",norm:str="AIC"): 387 | """ 388 | 基于逐步回归特征选择,评估准则: 赤池信息准则AIC,区分度KS,受试者特征AUC 389 | :param features: dict 特征重要度或信息值 390 | :param target: str 标签 391 | :param C: float 正则值 392 | :param norm option, AIC或者KS 393 | :param class_weight: 类别权重 394 | :return: select_feature 395 | """ 396 | features = IC.check_dict_of_float(features) 397 | data = self._df_woe 398 | features_sort = list({k: v for (k, v) in sorted(features.items(), key=lambda x: x[1], reverse=True)}.keys()) # 排序 399 | X,y = data[features_sort],data[target].values 400 | lr = LogisticRegression(C=C, penalty='l1', class_weight=class_weight) 401 | 402 | # 当前AIC和AUC,KS 403 | lr.fit(X.values,y) 404 | y_prob = lr.predict_proba(X.values)[:,1] 405 | AIC = calculate_AIC(X=X,y_true=y,y_prob=y_prob) 406 | KS_AUC = model_norm(y_true=y,y_prob=y_prob) 407 | print("current AIC is {},KS is {}, AUC is {}".format(AIC,KS_AUC["KS"],KS_AUC['AUC'])) 408 | 409 | # 向前逐步回归 410 | if norm not in ["AIC","KS","AUC"]: 411 | best_norm = -9999 # 默认AIC评估指标 412 | warnings.warn("Incorrect inputs,must be 'AIC','KS','AUC' of one,default AIC") 413 | else: 414 | best_norm = -9999 415 | 416 | forward_feature = list() 417 | for col in features_sort: 418 | X_new = X[forward_feature+[col]].values 419 | lr.fit(X_new,y) 420 | y_prob = lr.predict_proba(X_new)[:,1] 421 | if norm == "AIC" or norm not in ["AIC","KS","AUC"]: 422 | norm_new = - calculate_AIC(X=X_new,y_true=y,y_prob=y_prob) 423 | elif norm == "KS": 424 | norm_new = model_norm(y_true=y,y_prob=y_prob)["KS"] 425 | else: 426 | norm_new = model_norm(y_true=y,y_prob=y_prob)["AUC"] 427 | # 最优评估指标 428 | if norm_new > best_norm: 429 | best_norm = norm_new 430 | forward_feature.append(col) 431 | else: 432 | continue 433 | print("forward step best {} is {}".format(norm,best_norm)) 434 | 435 | # 向后逐步回归 436 | deleted_feature = [] 437 | for col in features_sort[::-1]: 438 | select_feature = [i for i in features_sort if i not in deleted_feature] 439 | X_new = X[select_feature].values 440 | lr.fit(X_new,y) 441 | y_prob = lr.predict_proba(X_new)[:,1] 442 | if norm == "AIC" or norm not in ["AIC","KS","AUC"]: 443 | norm_new = - calculate_AIC(X=X_new,y_true=y,y_prob=y_prob) 444 | cur_norm = AIC 445 | elif norm == "KS": 446 | norm_new = model_norm(y_true=y,y_prob=y_prob)["KS"] 447 | cur_norm = KS_AUC["KS"] 448 | else: 449 | norm_new = model_norm(y_true=y,y_prob=y_prob)["AUC"] 450 | cur_norm = KS_AUC["AUC"] 451 | # 最优评估指标 452 | if norm_new >= cur_norm: 453 | best_norm = norm_new 454 | deleted_feature.append(col) 455 | else: 456 | continue 457 | print("backward step best {} is {}".format(norm, best_norm)) 458 | 459 | print("forward step deleted cols are {}".format([i for i in features_sort if i not in forward_feature])) 460 | print("backward step deleted cols are {}".format(deleted_feature)) 461 | 462 | last_feature = {k:v for k,v in features.items() if k in forward_feature and k not in deleted_feature} 463 | return last_feature 464 | 465 | 466 | def baseOn_l1(self,features:list,target:str="target",C=1,class_weight="balanced",drop_plus:bool=False): 467 | """ 468 | 基于L1正则选择特征 469 | :param features: list 模型特征 470 | :param target: str 标签 471 | :param C: float 正则力度 >0 472 | :param class_weight: str, dict 标签权重, 例如,{1:0.8,0:0.2} 473 | :param drop_plus bool 是否删除正数 474 | :return:feature_coe 变量系数 475 | """ 476 | features = IC.check_list(features) 477 | X = self._df_woe[features] 478 | y = self._df_woe[target] 479 | 480 | lr = LogisticRegression(C=C, penalty='l1', class_weight=class_weight) 481 | lr.fit(X,y) 482 | # 模型系数 483 | paramsEst = pd.Series(lr.coef_.tolist()[0], index=features) 484 | feature_coe = paramsEst.to_dict() 485 | # 变量选择 486 | if drop_plus: 487 | select_feature = {k:v for k,v in feature_coe.items() if v < 0} 488 | else: 489 | select_feature = {k:v for k,v in feature_coe.items() if v > 0} 490 | return select_feature -------------------------------------------------------------------------------- /riskscore/Modeling.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | 超参调优,模型训练,模型评估,标准评分卡转换,模型预测(概率预测,评分预测) 4 | """ 5 | import re 6 | import numpy as np 7 | import pandas as pd 8 | from sklearn.linear_model import LogisticRegression,LogisticRegressionCV 9 | from sklearn.model_selection import GridSearchCV 10 | import matplotlib.pyplot as plt 11 | plt.rcParams['backend'] = 'Agg' 12 | from sklearn.model_selection import cross_val_score # 交叉验证 13 | from bayes_opt import BayesianOptimization # 贝叶斯调参 14 | from .utils.tools import model_norm,Prob2Score 15 | from .utils.PltFunction import PlotModel 16 | from scorecardpy import scorecard_ply 17 | from .utils import InputCondition as IC 18 | 19 | class SearchParam(object): 20 | """ 21 | 超参C值调优 22 | 方法:网格搜索,贝叶斯调参 23 | """ 24 | def __init__(self,X,y): 25 | """ 26 | :param X: like-array 27 | :param y: like-array 28 | """ 29 | X = IC.check_array(X) 30 | y = IC.check_array(y) 31 | self.X = X 32 | self.y = y 33 | 34 | def grid_search(self,param_grid:dict,cv:int=5,class_weight="balanced",scoring:str="roc_auc"): 35 | """ 36 | 网格搜索 37 | :param param_grid:dict, 检索参数 38 | :param cv:int 交叉验证 39 | :param class_weight: 类别权重 40 | :param scoring:str, 评估指标 41 | :return:float best_param 42 | """ 43 | param_grid = IC.check_list_of_dict(param_grid) 44 | cv = IC.check_int(cv) 45 | 46 | lr = LogisticRegression(class_weight=class_weight) 47 | gr = GridSearchCV(estimator=lr,param_grid=param_grid,scoring=scoring,cv=cv) 48 | gr.fit(self.X,self.y) 49 | best_param = gr.best_params_ 50 | print("grid search best score is ",gr.best_score_) 51 | return best_param 52 | 53 | def bayes_search(self,param_grid:dict,cv:int=5,n_iter:int=20, 54 | class_weight="balanced", scoring="roc_auc"): 55 | """ 56 | 基于贝叶斯调参 57 | :param param_grid:dict 58 | :param cv: int 59 | :param n_iter:int 60 | :return:bast_param 61 | """ 62 | param_grid = IC.check_list_of_dict(param_grid) 63 | C = param_grid["C"] 64 | L = param_grid["penalty"] 65 | X,y = self.X,self.y 66 | best_param = {} 67 | for l in L: 68 | def target(C): 69 | """目标函数""" 70 | lr = LogisticRegression(penalty=l,C=C,class_weight=class_weight,random_state=42) 71 | cls = cross_val_score(estimator=lr,X=X,y=y,scoring=scoring,cv=cv).mean() 72 | return cls 73 | 74 | bs = BayesianOptimization(f=target,pbounds={"C":C},random_state=42,verbose=2) 75 | bs.maximize(n_iter=n_iter) 76 | best_param[l] = bs.max 77 | return best_param 78 | 79 | class TrainLr(object): 80 | """模型实例化""" 81 | def __init__(self,df_woe,features:list,target="target",penalty="l1",class_weight="balanced"): 82 | """ 83 | :param features:list 84 | :param X:like-array 85 | :param y: like-array 86 | :param penalty: str, l1, l2 87 | :param class_weight: dict 88 | """ 89 | df_woe = IC.check_df(df_woe) 90 | features = IC.check_list(features) 91 | self.features = features 92 | self.X = df_woe[features].values 93 | self.y = df_woe[target].values 94 | self.penalty = penalty 95 | self.class_weight = class_weight 96 | if penalty == "l1": 97 | self.solver = "liblinear" 98 | else: 99 | self.solver = "lbfgs" 100 | 101 | def lr(self,C,filename='./'): 102 | """ 103 | :param C: float 正则参数 104 | :return: object lr_model 105 | """ 106 | C = IC.check_float(C) 107 | lr_model = LogisticRegression(penalty=self.penalty,C=C,solver=self.solver,class_weight=self.class_weight) 108 | lr_model.fit(X=self.X,y=self.y) 109 | # 评估 110 | y_prob = lr_model.predict_proba(self.X)[:,1] 111 | norm = model_norm(y_true=self.y, y_prob=y_prob) 112 | print("training model result:",norm) 113 | # 作图 114 | pltm = PlotModel(y_true=self.y,y_prob=y_prob) 115 | pltm.plot_roc_curve(filename=filename) 116 | pltm.plot_ks_curve(filename=filename) 117 | # 模型系数 118 | paramsEst = pd.Series(lr_model.coef_.tolist()[0], index=self.features) 119 | paramsEst["intercept"] = lr_model.intercept_.tolist()[0] 120 | print("model params:", paramsEst) 121 | return lr_model 122 | 123 | def lr_cv(self,Cs=1,cv=5,scoring="roc_auc",filename='./',solver='liblinear'): 124 | """ 125 | 交叉验证训练 126 | :param Cs: float, list 127 | :param cv: int 128 | :param scoring: str 评估指标 129 | :return: object 130 | """ 131 | Cs = IC.check_float_list(Cs) 132 | cv = IC.check_int(cv) 133 | 134 | if self.penalty == 'l1': 135 | solver = 'liblinear' 136 | lr_model = LogisticRegressionCV(Cs=Cs,cv=cv,penalty=self.penalty,scoring=scoring,solver=solver) 137 | lr_model.fit(self.X,self.y) 138 | # 评估 139 | y_prob = lr_model.predict_proba(self.X)[:,1] 140 | norm = model_norm(y_true=self.y,y_prob=y_prob) 141 | print("training model result:",norm) 142 | # 作图 143 | pltm = PlotModel(y_true=self.y,y_prob=y_prob) 144 | pltm.plot_roc_curve(filename=filename) 145 | pltm.plot_ks_curve(filename=filename) 146 | # 模型系数 147 | paramsEst = pd.Series(lr_model.coef_.tolist()[0], index=self.features) 148 | paramsEst["intercept"] = lr_model.intercept_.tolist()[0] 149 | print("model params:", paramsEst) 150 | return lr_model 151 | 152 | class ScoreCard(object): 153 | """ 154 | 评分卡模型 155 | """ 156 | def __init__(self,lr,bin_dict,model_feature,score0=600,pdo=50): 157 | """ 158 | :param lr:object, 经过实例化后的lr 159 | :param bin_dict:dict of df 分箱字典 160 | :param model_feature: list 模型变量 161 | :param score0:int 初始分 162 | :param pdo: 倍率 163 | """ 164 | self.model = lr 165 | self.bins = IC.check_dict_of_df(bin_dict) 166 | self.model_feature = IC.check_list(model_feature) 167 | self.score0 = IC.check_int(score0) 168 | self.pdo= IC.check_int(pdo) 169 | 170 | def score_card(self,return_df:bool=True): 171 | ''' 172 | Creating a Scorecard 173 | ------ 174 | `scorecard` creates a scorecard based on the results from `bins` 175 | and LogisticRegression of sklearn.linear_model 176 | 177 | Returns 178 | ------ 179 | DataFrame 180 | scorecard df 181 | ''' 182 | # coefficients 183 | A = self.score0 184 | B = self.pdo / np.log(2) 185 | # bins # if (is.list(bins)) rbindlist(bins) 186 | if isinstance(self.bins, dict): 187 | bins_df = pd.concat(self.bins, ignore_index=True) 188 | else: 189 | bins_df = None 190 | 191 | xs = [re.sub('_woe$', '', i) for i in self.model_feature] 192 | # coefficients 193 | coef_df = pd.Series(self.model.coef_[0], index=np.array(xs)).loc[lambda x: x != 0] # .reset_index(drop=True) 194 | 195 | # scorecard 196 | basepoints = A - B * self.model.intercept_[0] 197 | card = {} 198 | card['baseScore'] = pd.DataFrame({'variable': "basepoints", 'bin': "基础分", 'points': round(basepoints, 2)}, 199 | index=np.arange(1)) 200 | for i in coef_df.index: 201 | card[i] = bins_df.loc[bins_df['variable'] == i, ['variable', 'bin', 'woe']] \ 202 | .assign(points=lambda x: round(-B * x['woe'] * coef_df[i], 2))[["variable", "bin", "points"]] 203 | 204 | # 转换为df 205 | df = pd.DataFrame() 206 | for col in card.keys(): 207 | col_df = card[col] 208 | df = pd.concat([df,col_df]) 209 | df.set_index(["variable"], inplace=True) 210 | 211 | if return_df: 212 | return df 213 | else: 214 | return card 215 | 216 | def pred_score(self,df_woe,only_total_score=True): 217 | df_woe = IC.check_df(df_woe) 218 | df_score = df_woe 219 | y_prob = self.model.predict_proba(df_woe[self.model_feature].values)[:,1] 220 | score = Prob2Score(prob=y_prob,basePoint=self.score0,PDO=self.pdo) 221 | df_score["score"] = score 222 | if only_total_score: 223 | return df_score["score"] 224 | else: 225 | return df_score 226 | 227 | def score_ply(self,df,only_total_score=True, print_step=0): 228 | df_score = scorecard_ply(dt=df,card=self.score_card(return_df=False),only_total_score=only_total_score,print_step=print_step) 229 | return df_score -------------------------------------------------------------------------------- /riskscore/Preprocessing.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | 数据清洗 4 | 1. 变量类型划分--异常值检测--异常值处理--重复行(列)处理--众数处理--缺失处理 5 | 2. 样本采样: 随机采样, 随机平衡采样, Borderline-SMOTE过采样, Easy Ensemble 欠采样. 6 | """ 7 | import warnings 8 | import pandas as pd 9 | import numpy as np 10 | import numbers 11 | from sklearn.utils import shuffle 12 | from .utils.tools import str2num 13 | import re 14 | from imblearn.over_sampling import BorderlineSMOTE,ADASYN,KMeansSMOTE,RandomOverSampler,SVMSMOTE 15 | from imblearn.ensemble import BalanceCascade,EasyEnsemble 16 | from collections import Counter 17 | 18 | class Preprocess(object): 19 | """ 20 | 数据预处理 21 | """ 22 | 23 | def split_col(self,df,no_split,min_size=5): 24 | """ 25 | 变量类型划分, 无序类别变量, 有序类别变量,有序离散数值变量,连续数值变量. 26 | :param no_split: list 不参与分类变量 27 | :param min_size: int 连续变量最小类别数,默认5 28 | :return: num_col and cat_col list 分类结果 29 | """ 30 | num_col,cat_col = [],[] 31 | for i in [i for i in df.columns.tolist() if i not in no_split]: 32 | unique_list = df[i].unique().tolist() 33 | if len(unique_list) > min_size and isinstance(str2num(unique_list[0]), numbers.Real) and unique_list[0] is not np.nan: 34 | num_col.append(i) 35 | else: 36 | cat_col.append(i) 37 | return num_col,cat_col 38 | 39 | def find_str(self, df,num_col): 40 | """ 41 | 异常值检测之特殊字符串 42 | :param num_col:list 需要检测的变量集 43 | :return:str_set 特殊值序列 44 | """ 45 | str_set = set() 46 | for var in num_col: 47 | for i in df[var].unique(): 48 | try: 49 | if i is np.nan or isinstance(str2num(i),numbers.Real): 50 | pass 51 | except: 52 | str_set.add(i) 53 | return str_set 54 | 55 | def special_char(self,df,feature_col): 56 | """ 57 | 异常值检测之识别特殊符号 58 | :param df: df 59 | :param feature_col: list 特征列表集 60 | :return: special_set 61 | """ 62 | special_set = set() 63 | for col in feature_col: 64 | for s in df[col].unique(): 65 | try: 66 | special = r"\$|\(|\)|\*|\+|\[|\]|\?\\|\^|\||\@|\&|\{|\}|u" 67 | temp_set = set(re.findall(special,str(s))) 68 | special_set.update(temp_set) 69 | except: 70 | pass 71 | return special_set 72 | 73 | 74 | def outlier(self,df, col, low_percent=0.05, up_percent=0.95, cat_percent=0.001, special_value=None): 75 | """ 76 | 异常值处理之删除 77 | :param df:dataframe 数据集 78 | :param col:str 处理的数值变量 或分类变量 79 | :param low_percent:下限分位数 80 | :param up_percent:上限分位数 81 | :param cat_percnet:分类变量单个字段占比 82 | :param special_value: list 特殊值,不参与异常处理 83 | :return: dataframe 删除异常值后的数据 84 | """ 85 | if col in df.select_dtypes(include=["object", "bool"]).columns.tolist(): # 字符型变量 86 | cat_value_percent = df[col].value_counts() / len(df[col]) # 统计分类变量个数占比 87 | del_values = cat_value_percent[cat_value_percent < cat_percent].index.tolist() # 删除值 88 | if [i for i in del_values if i not in special_value]: 89 | for i in del_values: 90 | df = df.loc[df[col] != i, :] # 删除异常值 91 | else: 92 | Q1 = df[col].quantile(low_percent) # 下分为点 93 | Q2 = df[col].quantile(up_percent) # 上分为点 94 | # 异常值替换 95 | print("low:",Q1,"up:",Q2) 96 | df[col] = df[col].apply(lambda x:Q1 if x < Q1 else Q2 if x > Q2 else x) 97 | return df 98 | 99 | 100 | def replace_str(self,df,old_list=[],new_list=[],replace_dict=None): 101 | """ 102 | 异常值处理之替换 103 | :param old_list: list,待替换序列 104 | :param new_list:list 替换序列 105 | :param replace_dict:dict 替换字典 106 | :return: 替换结果 107 | """ 108 | df_new = df.copy() 109 | if len(old_list) != len(new_list): 110 | warnings.warn("old_list char num not equal new") 111 | return df_new 112 | if replace_dict: 113 | df_new = df_new.replace(replace_dict) 114 | elif old_list: 115 | df_new = df_new.replace(old_list,new_list) 116 | return df_new 117 | 118 | def drop_dupl(self,df,axis=0): 119 | """ 120 | 删除重复行或重复列 121 | :param axis: 0 or 1 0表示删除重复行,1表示删除重复列 122 | :return: 删除结果 123 | """ 124 | if axis == 0: 125 | # 删除重复行 126 | df_new = df.drop_duplicates() 127 | else: # 删除重复列 128 | df_new = df.T.drop_duplicates().T 129 | return df_new 130 | 131 | def cnt_miss_mode(self,df,col_list,mode=None): 132 | """ 133 | 缺失处理-缺失率和众数率统计 134 | :param col_list:统计字段 135 | :param model: 返回结果, 默认 None,option miss,mode 136 | :return: dict 缺失率和众数率字典 137 | """ 138 | miss_mode = {} 139 | miss_mode["miss_rate"],miss_mode["mode_rate"] = {},{} 140 | 141 | for i in col_list: 142 | miss_rate = df[i].isnull().sum() / len(df) # 缺失率 143 | miss_mode["miss_rate"][i] = miss_rate 144 | try: 145 | mode_values = df[i].mode()[0] # 众数 146 | except: 147 | mode_values = None 148 | 149 | mode_rate = len(df.loc[df[i] == mode_values, :]) / len(df) # 众数率 150 | miss_mode["mode_rate"][i] = mode_rate 151 | # 输出模式 152 | if mode=="miss": 153 | return miss_mode["miss_rate"] 154 | elif mode =="mode": 155 | return miss_mode["mode_rate"] 156 | else: 157 | return miss_mode 158 | 159 | 160 | def drop_nan_mode(self,df,nan_percent,mode_percent,col_list,drop=False): 161 | """ 162 | 缺失处理-删除缺失率和众数率较大的变量,如果只删除其中一种,只需将另一种设置为1即可 163 | :param nan_percent: float 0~1 缺失率 164 | :param mode_percent: float 0~1 众数率 165 | :param col_list: list 作用变量列表 166 | :param drop:bool 是否删除 167 | :return: DataFrame 168 | """ 169 | del_col = set() 170 | miss_model_rate = self.cnt_miss_mode(df=df,col_list=col_list) 171 | for i in col_list: 172 | miss_rate = miss_model_rate["miss_rate"][i] # 缺失率 173 | mode_rate = miss_model_rate["mode_rate"][i] # 众数率 174 | if miss_rate >= nan_percent: 175 | del_col.add(i) 176 | elif mode_rate >= mode_percent: 177 | del_col.add(i) 178 | 179 | # 是否删除操作 180 | if drop: 181 | for i in del_col: 182 | try: 183 | del df[i] 184 | except: 185 | print("变量:{}已删除".format(i)) 186 | 187 | return df, del_col 188 | else: 189 | return del_col 190 | 191 | def fill_nan(self,df,value=None,method=None,col_list=None,min_miss_rate=0.05): 192 | """ 193 | 缺失处理-缺失值填充 194 | :param value:dict 列填充值字典 默认 None 195 | :param method:str,方法 backfill, bfill, pad, ffill, None, mode,mean,special_value,采用均值填充的字段必须为连续变量 196 | :param col_list:填充列,特殊方式填充 197 | :param min_miss_rate,最小缺失率 198 | :return: dataframe 199 | """ 200 | if method in ["backfill", "bfill", "pad", "ffill"]: 201 | df = df.fillnan(method=method) 202 | 203 | elif method=="special_value": # 特殊值填充或列字典填充 204 | if value: 205 | df = df.fillnan(value=value) 206 | else: 207 | df = df.fillnan(value='-9999') 208 | 209 | elif method in ["mode","mean"] and len(col_list)>0: # 特殊填充方法 210 | miss_model_rate = self.cnt_miss_mode(df=df,col_list=col_list) 211 | # 特殊填充方法 212 | for i in col_list: 213 | try: 214 | mode = df[i].mode()[0] 215 | except: 216 | mode = value 217 | 218 | if value: 219 | if miss_model_rate["miss_rate"][i] > min_miss_rate: 220 | df[i] = df[i].fillna(value=value) 221 | elif method =="mode": 222 | df[i] = df[i].fillna(value=mode) 223 | elif method =="mean": 224 | df[i] = df[i].fillna(value=df[i].mean()) 225 | elif not value: 226 | if method == "mode": 227 | df[i] = df[i].fillna(value=mode) 228 | elif method =="mean": 229 | df[i] = df[i].fillna(value=df[i].mean()) 230 | else: 231 | df = df.fillnan('-9999') # 任何都不填的情况下 232 | return df 233 | 234 | 235 | def factor_map(self,df,col_list=None): 236 | """ 237 | 字符变量数值化 238 | :param col_list:需要数值化字段 239 | :return:df, factor_dict 数值化映射 240 | """ 241 | factorize_dict = {} 242 | if not col_list: 243 | col_list = df.select_dtypes(include=["object","bool"]).columns.tolist() 244 | for var in col_list: 245 | factorize_dict[var] = {} 246 | for i in np.arange(len(pd.factorize(df[var])[1])): 247 | factorize_dict[var][pd.factorize(df[var])[1][i]] = i # 数值化映射字典 248 | df[var] = pd.factorize(df[var])[0] # 数值化 249 | return df,factorize_dict # 数值数据, 映射字典 250 | 251 | 252 | 253 | class DataSample(object): 254 | """ 255 | 数据抽样 256 | """ 257 | def __init__(self,df,target='target'): 258 | self._df = df 259 | self._target = target 260 | 261 | def random_sample(self,n=None,fra=0.5): 262 | """ 263 | 随机抽样 264 | :param n:抽样样本量 265 | :param fra: float,抽样样本占比 266 | :return:dataframe 抽样结果 267 | """ 268 | if n: 269 | new_df = self._df.sample(n = n) 270 | elif fra: 271 | new_df = self._df.sample(frac=fra) 272 | else: 273 | new_df = self._df 274 | return new_df 275 | 276 | def balance_sample(self,odd,cl="b"): 277 | """ 278 | 根据好坏比抽样 279 | :param odd:int,float 好坏比 280 | :param label:str 标签 281 | :param cl: option b or g 282 | :return: df 抽样后新数据集 283 | """ 284 | bad_df = self._df.loc[self._df[self._target]==1,:] # 坏样本 285 | good_df = self._df.loc[self._df[self._target]==0,:] # 好样本 286 | bad = len(bad_df) # 坏样本数 287 | good = len(good_df) # 好样本数 288 | if cl == "g": 289 | good_new = int(bad*odd) # 所需好样本数 290 | if good_new > good: 291 | good_df_new = good_df.sample(n=good_new,replace=True) 292 | else: 293 | good_df_new = good_df.sample(n = good_new,replace=False) 294 | 295 | good_df = good_df_new 296 | else: 297 | bad_new = int(good*odd) # 所需坏样本数 298 | if bad_new > bad: 299 | bad_df_new = bad_df.sample(n=bad_new,replace=True) 300 | else: 301 | bad_df_new = bad_df.sample(n=bad_new,replace=False) 302 | 303 | bad_df = bad_df_new 304 | 305 | df = pd.concat([bad_df,good_df],axis=0) # 合并新样本 306 | df = shuffle(df) # 打乱顺序 307 | return df 308 | 309 | def over_sample(self,method="BorderLine",sampling_strategy="minority",random_state=42, 310 | k_neighbors=5,n_neighbors=10,kind="borderline-1"): 311 | """ 312 | 过采样方法 313 | :param method: str, option: ADASYN, BorderLine,KMeans,Random,SVM 314 | :param sampling_strategy:str or dict, option: 'minority','not majority','all','auto', {1:n,0:m} 315 | :param random_state:int 316 | :param k_neighbors:int 317 | :param n_neighbors:int 318 | :param kind:str, borderline-1,borderline-2 319 | :return:df 320 | """ 321 | feature_name = self._df.columns.difference(["id",self._target]).tolist() 322 | X = self._df[feature_name].values 323 | y = self._df[self._target].values 324 | 325 | print("Original label shape {}".format(Counter(y))) 326 | 327 | if method == "ADASYN": 328 | overSm = ADASYN(sampling_strategy=sampling_strategy,random_state=random_state,n_neighbors=k_neighbors) 329 | elif method == "BorderLine": 330 | overSm = BorderlineSMOTE(sampling_strategy=sampling_strategy,random_state=random_state,k_neighbors=k_neighbors,m_neighbors=n_neighbors,kind=kind) 331 | elif method == "KMeans": 332 | overSm = KMeansSMOTE(sampling_strategy=sampling_strategy,random_state=random_state,k_neighbors=k_neighbors) 333 | elif method == "Random": 334 | overSm = RandomOverSampler(sampling_strategy=sampling_strategy,random_state=random_state) 335 | elif method == "SVM": 336 | overSm = SVMSMOTE(sampling_strategy=sampling_strategy,random_state=random_state,k_neighbors=k_neighbors,m_neighbors=n_neighbors,out_step=0.5) 337 | else: 338 | print("不支持{}该抽样方法".format(method)) 339 | return self._df 340 | 341 | X_res,y_res = overSm.fit_resample(X,y) 342 | print("overSample label shape {}".format(Counter(y_res))) 343 | _data = np.concatenate([X_res, y_res.reshape(len(X_res), 1)], axis=1) 344 | df_new = pd.DataFrame(data=_data,columns=feature_name+[self._target]) 345 | return df_new 346 | 347 | def ensemble_sample(self,method = "BalanceCascade",sampling_strategy="majority",random_state=42,replacement=True): 348 | """ 349 | 下采样方法 350 | :param method: str, option:'EasyEnsemble','BalanceCascade' 351 | :param sampling_strategy: 采样策略, str, dict, 'majority','not minority','not majority','all','auto' 352 | :param random_state:int 353 | :param replacement: bool 354 | :return:df 355 | """ 356 | feature_name = self._df.columns.difference(["id",self._target]).tolist() 357 | X = self._df[feature_name].values 358 | y = self._df[self._target].values 359 | 360 | print("Original label shape {}".format(Counter(y))) 361 | 362 | if method == "EasyEnsemble": 363 | enS = EasyEnsemble(sampling_strategy=sampling_strategy,random_state=random_state,replacement=replacement) 364 | elif method == "BalanceCascade": 365 | enS = BalanceCascade(sampling_strategy=sampling_strategy,random_state=random_state) 366 | else: 367 | print("不支持{}该抽样方法".format(method)) 368 | return self._df 369 | 370 | X_res, y_res = enS.fit_resample(X, y) 371 | 372 | print("enSample label shape {}".format(Counter(y_res))) 373 | _data = np.concatenate([X_res, y_res.reshape(len(X_res), 1)], axis=1) 374 | df_new = pd.DataFrame(data=_data, columns=feature_name + [self._target]) 375 | return df_new -------------------------------------------------------------------------------- /riskscore/RiskStragety.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 风险策略,稳定性评估PSI,评分决策表,KS 3 | # 单变量PSI 4 | 5 | import pandas as pd 6 | import numpy as np 7 | from .utils import InputCondition as IC 8 | import warnings 9 | 10 | def caculate_ks(df, score:str, target:str): 11 | """ 12 | 指标KS,不分组情况下 13 | :param data:dataframe 14 | :param score:str 分数或概率 15 | :param target:str 好坏定义,0,1 16 | :return:KS 17 | """ 18 | df = IC.check_df(df) 19 | score = IC.check_str(score) 20 | target = IC.check_str(target) 21 | 22 | df = df.sort_values(by=score) 23 | total_good = len(df.loc[df[target] == 0, :]) 24 | total_bad = len(df.loc[df[target] == 1, :]) 25 | df_good, df_bad = pd.DataFrame(), pd.DataFrame() 26 | 27 | df_good["score"] = df.loc[df[target] == 0, score] 28 | df_good["sum_good"] = [i + 1 for i in range(len(df.loc[df[target] == 0, target]))] 29 | df_good["sum_good_rate"] = df_good["sum_good"] / total_good 30 | df_good = df_good.drop_duplicates(["score"], keep="last") 31 | 32 | df_bad["sum_bad"] = df.loc[df[target] == 1, target].cumsum() 33 | df_bad["score"] = df.loc[df[target] == 1, score] 34 | df_bad["sum_bad_rate"] = df_bad["sum_bad"] / total_bad 35 | df_bad = df_bad.drop_duplicates(["score"], keep="last") 36 | 37 | df = pd.merge(df_bad, df_good, how="outer", on="score") 38 | df = df.sort_values(by="score") 39 | df = df.fillna(method="ffill") 40 | df = df.fillna(0) 41 | 42 | df["KS"] = df["sum_bad_rate"] - df["sum_good_rate"] 43 | KS = abs(df["KS"]).max() 44 | return KS, df 45 | 46 | def var_psi(select_feature:list,trainData,testData): 47 | """ 48 | 入模变量PSI 49 | :param select_feature:list 入模变量 50 | :param trainData:dataframe 训练集 51 | :param testData: dataframe 测试集 52 | :return: 变量psi 53 | """ 54 | select_feature = IC.check_list(select_feature) 55 | trainData = IC.check_df(trainData) 56 | testData = IC.check_df(testData) 57 | psi_dict = {} 58 | for var in select_feature: 59 | psi_dict[var] = ((trainData[var].value_counts()/len(trainData))-(testData[var].value_counts()/len(testData)))*\ 60 | (np.log((trainData[var].value_counts()/len(trainData))/(testData[var].value_counts()/len(testData)))) 61 | psi_dict_var = {} 62 | # 输出每个变量的PSI 63 | for var in select_feature: 64 | psi_dict_var[var] = psi_dict[var].sum() 65 | var_psi_df = pd.DataFrame(psi_dict_var,index=[0]).T 66 | return var_psi_df 67 | 68 | 69 | def bin_list(low_line:int,up_line:int,step:int): 70 | """ 71 | 分组 72 | :param low_line:int 下限 73 | :param up_line: int 上限 74 | :param step: int 步长 75 | :return: list 分组列表 76 | """ 77 | bin = [] # 分组方法 78 | bin.append(float("-inf")) # 极小值 79 | for i in range(int(round((up_line-low_line)/step,0))): 80 | bin.append(low_line+step*i) 81 | bin.append(up_line) 82 | bin.append(float("inf")) # 极大值 83 | 84 | return bin 85 | 86 | 87 | def stragety_score(score_df,step:int,score:str="score",label:str="target", 88 | amount:int=5000,tenor:int=6,IRR:float=0.3,capital_cost:float=0.08, 89 | guest_cost:int=100,data_cost:int=30,bad_loss:float=0.5): 90 | """ 91 | 风险决策报表 92 | :param score_df:dataframe 包括score 和target 93 | :param score: str 分数字段 默认 score 94 | :param label : str 目标字段 默认 target 95 | :param step int 步长 96 | :param amount: int 授信额度(支用额度) 97 | :param tenor: int 期数 98 | :param IRR: float 实际年化 99 | :param capital_cost: float 资金成本 100 | :param guest_cost: int 获客成本 101 | :param data_cost:int 数据成本 102 | :param bad_loss: float 坏客户损失率 103 | :return: 分组结果 104 | """ 105 | score_df = IC.check_df(score_df) 106 | if len([c for c in [score,label] if c in score_df.columns])<2: 107 | raise Exception("There score and label not in input data columns") 108 | 109 | if not isinstance(amount,int) or not isinstance(tenor,int) or not isinstance(guest_cost,int) or not isinstance(data_cost,int): 110 | warnings.warn("The amount, tenor,guest_cost,data_cost params should be int") 111 | 112 | if not isinstance(IRR,float) or IRR >=1 or IRR <= 0: 113 | warnings.warn("The IRR params accepted range is 0~1, params was set to default 0.3") 114 | IRR = 0.3 115 | 116 | if not isinstance(capital_cost,float) or capital_cost >=1 or capital_cost <= 0: 117 | warnings.warn("The capital_cost params accepted range is 0~1, params was set to default 0.08") 118 | capital_cost = 0.08 119 | 120 | if not isinstance(bad_loss,float) or bad_loss > 1 or bad_loss <= 0: 121 | warnings.warn("The bad_loss params accepted range is 0~1, params was set to default 0.5") 122 | bad_loss = 0.7 123 | 124 | #分组 125 | low_line = int(score_df[score].min() + step) 126 | up_line = int(score_df[score].max() - step) 127 | 128 | bin = bin_list(low_line,up_line,step) #分组列表 129 | # total 每个分数段总计 130 | temp_df = score_df[score] 131 | total_cut = pd.cut(x=temp_df,bins=bin,right=False) 132 | total_df = pd.value_counts(total_cut) # 每个分数段总计数 133 | total_sum = len(total_cut) # 总计 134 | 135 | # good 每个分数段的好客户 136 | temp_df = score_df.loc[score_df[label]==0,score] 137 | good_cut = pd.cut(x=temp_df,bins=bin,right=False) 138 | good_df = pd.value_counts(good_cut) # 每个分数段好客户计数 139 | good_sum = len(good_cut) # 好客户总数 140 | 141 | # bad 每个分数段的坏客户 142 | temp_df = score_df.loc[score_df[label]==1,score] 143 | bad_cut = pd.cut(x=temp_df,bins=bin,right=False) 144 | bad_df = pd.value_counts(bad_cut) # 每个分数段的坏客户计数 145 | bad_sum = len(bad_cut) # 坏客户总数 146 | 147 | # 总计,好,坏 计数合并 148 | df = pd.concat([total_df,good_df,bad_df],axis=1) 149 | df.columns = ["total","good","bad"] 150 | # 累积 151 | df_cumsum = df.cumsum() 152 | df_cumsum.columns = ["sum_total","sum_good","sum_bad"] # 累积总,累积好,累积坏 计数 153 | df = pd.concat([df,df_cumsum],axis=1) 154 | 155 | # 累积率 156 | df["sum_total_rate"] = df["sum_total"]/total_sum # 累积坏比率 157 | df["sum_good_rate"] = df["sum_good"]/good_sum # 累积好比率 158 | df["sum_bad_rate"] = df["sum_bad"]/bad_sum # 累积坏比率 159 | df["KS"] = df["sum_bad_rate"] - df["sum_good_rate"] # 区分度ks 160 | df["bad_rate"] = df["bad"]/df["total"] # 每个分数段的坏比率 161 | df["good_rate"] = df["good"]/df["total"] #每个分数段的好比率 162 | # 利润 = 好客户获利金额+坏客户回收本金-坏客户损失-资金成本-获客成本-数据成本 163 | df["profit"] = df["good"]*amount*(tenor/12)*IRR + df["bad"]*amount*(1-2*bad_loss) \ 164 | - df["total"]*amount*capital_cost - df["total"]*(guest_cost+data_cost) 165 | 166 | return df 167 | 168 | 169 | def score_psi(trainScore,testScore,low_line:int,up_line:int,step:int,score="score"): 170 | 171 | """ 172 | 分数PSI 173 | :param trainScore:df 训练集分数 174 | :param testScore:df 测试集分数 175 | :param low_line:int 下限 176 | :param up_line: int 上限 177 | :param step: int 步长 178 | :param score: str 分数字段,默认score 179 | :return: 返回PSI计算表及PSI指标 180 | """ 181 | trainScore = IC.check_df(trainScore) 182 | testScore = IC.check_df(testScore) 183 | 184 | #分组 185 | bin = bin_list(low_line=low_line,up_line=up_line,step=step) 186 | train_len = len(trainScore) # 训练集总数 187 | test_len = len(testScore) # 测试集总数 188 | 189 | train_cut = pd.cut(x=trainScore[score],bins=bin,right=False) # 训练集分组 190 | test_cut = pd.cut(x=testScore[score],bins=bin,right=False) # 测试集分组结果 191 | train_df = pd.value_counts(train_cut) # 训练集分组计数 192 | test_df = pd.value_counts(test_cut) # 测试集分组计数 193 | df = pd.concat([train_df,test_df],axis=1) # 合并 194 | df.columns = ["train","test"] 195 | df["train_percent"] = df["train"]/train_len #每个分数段的计数比例 196 | df["test_percent"] = df["test"]/test_len 197 | df["PSI"] = (df["train_percent"]-df["test_percent"])*np.log(df["train_percent"]/df["test_percent"]) #每个分段的psi 198 | PSI = df["PSI"].sum() 199 | return PSI,df 200 | -------------------------------------------------------------------------------- /riskscore/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | __version__ = '1.2' 4 | 5 | # 类 6 | from .ETL import DbRead # 从数据库中读取数据 7 | from .EDA import BasicStat,Plot # 数据探索分析 8 | from .Preprocessing import Preprocess,DataSample # 数据清洗, 数据抽样 9 | from .FeatureEngineering import FeatureBin,WoeBinPlot,SelectFeature # 特征分箱,特征分箱可视化,特征选择 10 | from .Modeling import SearchParam,TrainLr,ScoreCard # 超参调优,模型训练,评分卡构建 11 | from .utils.PltFunction import PlotModel # 模型作图,ks,roc曲线 12 | from .data.germancredit import Germancredit # 导入数据 13 | 14 | # 方法 15 | from .FeatureEngineering import woe_trans # woe映射 16 | from .utils.tools import model_norm,Prob2Score # 模型指标,概率转换为分数 17 | from .RiskStragety import caculate_ks,stragety_score,score_psi,var_psi # ks计算,决策报告,评分psi,变量psi 18 | # 样例数据 19 | -------------------------------------------------------------------------------- /riskscore/data/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangxianyang/creditmodel/36971a49797739230089596acce0b402c6f2cea6/riskscore/data/__init__.py -------------------------------------------------------------------------------- /riskscore/data/german.txt: -------------------------------------------------------------------------------- 1 | Description of the German credit dataset. 2 | 3 | 1. Title: German Credit data 4 | 5 | 2. Source Information 6 | 7 | Professor Dr. Hans Hofmann 8 | Institut f"ur Statistik und "Okonometrie 9 | Universit"at Hamburg 10 | FB Wirtschaftswissenschaften 11 | Von-Melle-Park 5 12 | 2000 Hamburg 13 13 | 14 | 3. Number of Instances: 1000 15 | 16 | Two datasets are provided. the original dataset, in the form provided 17 | by Prof. Hofmann, contains categorical/symbolic attributes and 18 | is in the file "germancredit.csv". 19 | 20 | 6. Number of Attributes german: 20 (7 numerical, 13 categorical) 21 | 22 | 23 | 7. Attribute description for german 24 | 25 | Attribute 1: (qualitative) 26 | Status of existing checking account 27 | 现有的支票状态,有序离散 28 | A11 : ... < 0 DM 29 | A12 : 0 <= ... < 200 DM 30 | A13 : ... >= 200 DM / 31 | salary assignments for at least 1 year 32 | A14 : no checking account 33 | 34 | Attribute 2: (numerical) 35 | Duration in month 36 | 持续时间,连续变量 37 | 38 | Attribute 3: (qualitative) 39 | Credit history 40 | 信用历史,有序离散 41 | A30 : no credits taken/ 42 | all credits paid back duly 43 | A31 : all credits at this bank paid back duly 44 | A32 : existing credits paid back duly till now 45 | A33 : delay in paying off in the past 46 | A34 : critical account/ 47 | other credits existing (not at this bank) 48 | 49 | Attribute 4: (qualitative) 50 | Purpose 51 | 用途,无序离散 52 | A40 : car (new) 53 | A41 : car (used) 54 | A42 : furniture/equipment 55 | A43 : radio/television 56 | A44 : domestic appliances 57 | A45 : repairs 58 | A46 : education 59 | A47 : (vacation - does not exist?) 60 | A48 : retraining 61 | A49 : business 62 | A410 : others 63 | 64 | Attribute 5: (numerical) 65 | Credit amount 66 | 信用额度,连续变量 67 | 68 | Attibute 6: (qualitative) 69 | Savings account/bonds 70 | 储蓄卡存储额度,有序离散 71 | A61 : ... < 100 DM 72 | A62 : 100 <= ... < 500 DM 73 | A63 : 500 <= ... < 1000 DM 74 | A64 : .. >= 1000 DM 75 | A65 : unknown/ no savings account 76 | 77 | Attribute 7: (qualitative) 78 | Present employment since 79 | 工作至今年限,有序离散 80 | A71 : unemployed 81 | A72 : ... < 1 year 82 | A73 : 1 <= ... < 4 years 83 | A74 : 4 <= ... < 7 years 84 | A75 : .. >= 7 years 85 | 86 | Attribute 8: (numerical) 87 | Installment rate in percentage of disposable income 88 | 分期付款利率占可支配收入百分比,连续 89 | 90 | Attribute 9: (qualitative) 91 | Personal status and sex 92 | 婚姻状态和性别,无序离散 93 | A91 : male : divorced/separated 94 | A92 : female : divorced/separated/married 95 | A93 : male : single 96 | A94 : male : married/widowed 97 | A95 : female : single 98 | 99 | Attribute 10: (qualitative) 100 | Other debtors / guarantors 101 | 其他债务或担保,无序离散 102 | A101 : none 103 | A102 : co-applicant 104 | A103 : guarantor 105 | 106 | Attribute 11: (numerical) 107 | Present residence since 108 | 居住至今年限, 连续 109 | 110 | Attribute 12: (qualitative) 111 | Property 112 | 属性,离散 113 | A121 : real estate 114 | A122 : if not A121 : building society savings agreement/ 115 | life insurance 116 | A123 : if not A121/A122 : car or other, not in attribute 6 117 | A124 : unknown / no property 118 | 119 | Attribute 13: (numerical) 120 | Age in years 121 | 年龄,连续 122 | 123 | Attribute 14: (qualitative) 124 | Other installment plans 125 | 其他分期计划,离散 126 | A141 : bank 127 | A142 : stores 128 | A143 : none 129 | 130 | Attribute 15: (qualitative) 131 | Housing 132 | 住房情况,离散 133 | A151 : rent 134 | A152 : own 135 | A153 : for free 136 | 137 | Attribute 16: (numerical) 138 | Number of existing credits at this bank 139 | 现有银行积分数,连续 140 | 141 | Attribute 17: (qualitative) 142 | Job 143 | 工作,有序离散 144 | A171 : unemployed/ unskilled - non-resident 145 | A172 : unskilled - resident 146 | A173 : skilled employee / official 147 | A174 : management/ self-employed/ 148 | highly qualified employee/ officer 149 | 150 | Attribute 18: (numerical) 151 | Number of people being liable to provide maintenance for 152 | 维护服务人数,连续 153 | 154 | Attribute 19: (qualitative) 155 | Telephone 156 | 电话,离散 157 | A191 : none 158 | A192 : yes, registered under the customers name 159 | 160 | Attribute 20: (qualitative) 161 | foreign worker 162 | 国外工作经历,离散 163 | A201 : yes 164 | A202 : no 165 | 166 | 167 | 168 | 8. Cost Matrix 169 | 170 | This dataset requires use of a cost matrix (see below) 171 | 172 | 173 | 1 2 174 | ---------------------------- 175 | 1 0 1 176 | ----------------------- 177 | 2 5 0 178 | 179 | (1 = Good, 2 = Bad) 180 | 181 | the rows represent the actual classification and the columns 182 | the predicted classification. 183 | 184 | It is worse to class a customer as good when they are bad (5), 185 | than it is to class a customer as bad when they are good (1). 186 | 187 | -------------------------------------------------------------------------------- /riskscore/data/germancredit.csv: -------------------------------------------------------------------------------- 1 | A11 6 A34 A43 1169 A65 A75 4 A93 A101 4 A121 67 A143 A152 2 A173 1 A192 A201 1 2 | A12 48 A32 A43 5951 A61 A73 2 A92 A101 2 A121 22 A143 A152 1 A173 1 A191 A201 2 3 | A14 12 A34 A46 2096 A61 A74 2 A93 A101 3 A121 49 A143 A152 1 A172 2 A191 A201 1 4 | A11 42 A32 A42 7882 A61 A74 2 A93 A103 4 A122 45 A143 A153 1 A173 2 A191 A201 1 5 | A11 24 A33 A40 4870 A61 A73 3 A93 A101 4 A124 53 A143 A153 2 A173 2 A191 A201 2 6 | A14 36 A32 A46 9055 A65 A73 2 A93 A101 4 A124 35 A143 A153 1 A172 2 A192 A201 1 7 | A14 24 A32 A42 2835 A63 A75 3 A93 A101 4 A122 53 A143 A152 1 A173 1 A191 A201 1 8 | A12 36 A32 A41 6948 A61 A73 2 A93 A101 2 A123 35 A143 A151 1 A174 1 A192 A201 1 9 | A14 12 A32 A43 3059 A64 A74 2 A91 A101 4 A121 61 A143 A152 1 A172 1 A191 A201 1 10 | A12 30 A34 A40 5234 A61 A71 4 A94 A101 2 A123 28 A143 A152 2 A174 1 A191 A201 2 11 | A12 12 A32 A40 1295 A61 A72 3 A92 A101 1 A123 25 A143 A151 1 A173 1 A191 A201 2 12 | A11 48 A32 A49 4308 A61 A72 3 A92 A101 4 A122 24 A143 A151 1 A173 1 A191 A201 2 13 | A12 12 A32 A43 1567 A61 A73 1 A92 A101 1 A123 22 A143 A152 1 A173 1 A192 A201 1 14 | A11 24 A34 A40 1199 A61 A75 4 A93 A101 4 A123 60 A143 A152 2 A172 1 A191 A201 2 15 | A11 15 A32 A40 1403 A61 A73 2 A92 A101 4 A123 28 A143 A151 1 A173 1 A191 A201 1 16 | A11 24 A32 A43 1282 A62 A73 4 A92 A101 2 A123 32 A143 A152 1 A172 1 A191 A201 2 17 | A14 24 A34 A43 2424 A65 A75 4 A93 A101 4 A122 53 A143 A152 2 A173 1 A191 A201 1 18 | A11 30 A30 A49 8072 A65 A72 2 A93 A101 3 A123 25 A141 A152 3 A173 1 A191 A201 1 19 | A12 24 A32 A41 12579 A61 A75 4 A92 A101 2 A124 44 A143 A153 1 A174 1 A192 A201 2 20 | A14 24 A32 A43 3430 A63 A75 3 A93 A101 2 A123 31 A143 A152 1 A173 2 A192 A201 1 21 | A14 9 A34 A40 2134 A61 A73 4 A93 A101 4 A123 48 A143 A152 3 A173 1 A192 A201 1 22 | A11 6 A32 A43 2647 A63 A73 2 A93 A101 3 A121 44 A143 A151 1 A173 2 A191 A201 1 23 | A11 10 A34 A40 2241 A61 A72 1 A93 A101 3 A121 48 A143 A151 2 A172 2 A191 A202 1 24 | A12 12 A34 A41 1804 A62 A72 3 A93 A101 4 A122 44 A143 A152 1 A173 1 A191 A201 1 25 | A14 10 A34 A42 2069 A65 A73 2 A94 A101 1 A123 26 A143 A152 2 A173 1 A191 A202 1 26 | A11 6 A32 A42 1374 A61 A73 1 A93 A101 2 A121 36 A141 A152 1 A172 1 A192 A201 1 27 | A14 6 A30 A43 426 A61 A75 4 A94 A101 4 A123 39 A143 A152 1 A172 1 A191 A201 1 28 | A13 12 A31 A43 409 A64 A73 3 A92 A101 3 A121 42 A143 A151 2 A173 1 A191 A201 1 29 | A12 7 A32 A43 2415 A61 A73 3 A93 A103 2 A121 34 A143 A152 1 A173 1 A191 A201 1 30 | A11 60 A33 A49 6836 A61 A75 3 A93 A101 4 A124 63 A143 A152 2 A173 1 A192 A201 2 31 | A12 18 A32 A49 1913 A64 A72 3 A94 A101 3 A121 36 A141 A152 1 A173 1 A192 A201 1 32 | A11 24 A32 A42 4020 A61 A73 2 A93 A101 2 A123 27 A142 A152 1 A173 1 A191 A201 1 33 | A12 18 A32 A40 5866 A62 A73 2 A93 A101 2 A123 30 A143 A152 2 A173 1 A192 A201 1 34 | A14 12 A34 A49 1264 A65 A75 4 A93 A101 4 A124 57 A143 A151 1 A172 1 A191 A201 1 35 | A13 12 A32 A42 1474 A61 A72 4 A92 A101 1 A122 33 A141 A152 1 A174 1 A192 A201 1 36 | A12 45 A34 A43 4746 A61 A72 4 A93 A101 2 A122 25 A143 A152 2 A172 1 A191 A201 2 37 | A14 48 A34 A46 6110 A61 A73 1 A93 A101 3 A124 31 A141 A153 1 A173 1 A192 A201 1 38 | A13 18 A32 A43 2100 A61 A73 4 A93 A102 2 A121 37 A142 A152 1 A173 1 A191 A201 2 39 | A13 10 A32 A44 1225 A61 A73 2 A93 A101 2 A123 37 A143 A152 1 A173 1 A192 A201 1 40 | A12 9 A32 A43 458 A61 A73 4 A93 A101 3 A121 24 A143 A152 1 A173 1 A191 A201 1 41 | A14 30 A32 A43 2333 A63 A75 4 A93 A101 2 A123 30 A141 A152 1 A174 1 A191 A201 1 42 | A12 12 A32 A43 1158 A63 A73 3 A91 A101 1 A123 26 A143 A152 1 A173 1 A192 A201 1 43 | A12 18 A33 A45 6204 A61 A73 2 A93 A101 4 A121 44 A143 A152 1 A172 2 A192 A201 1 44 | A11 30 A34 A41 6187 A62 A74 1 A94 A101 4 A123 24 A143 A151 2 A173 1 A191 A201 1 45 | A11 48 A34 A41 6143 A61 A75 4 A92 A101 4 A124 58 A142 A153 2 A172 1 A191 A201 2 46 | A14 11 A34 A40 1393 A61 A72 4 A92 A101 4 A123 35 A143 A152 2 A174 1 A191 A201 1 47 | A14 36 A32 A43 2299 A63 A75 4 A93 A101 4 A123 39 A143 A152 1 A173 1 A191 A201 1 48 | A11 6 A32 A41 1352 A63 A71 1 A92 A101 2 A122 23 A143 A151 1 A171 1 A192 A201 1 49 | A14 11 A34 A40 7228 A61 A73 1 A93 A101 4 A122 39 A143 A152 2 A172 1 A191 A201 1 50 | A14 12 A32 A43 2073 A62 A73 4 A92 A102 2 A121 28 A143 A152 1 A173 1 A191 A201 1 51 | A12 24 A33 A42 2333 A65 A72 4 A93 A101 2 A122 29 A141 A152 1 A172 1 A191 A201 1 52 | A12 27 A33 A41 5965 A61 A75 1 A93 A101 2 A123 30 A143 A152 2 A174 1 A192 A201 1 53 | A14 12 A32 A43 1262 A61 A73 3 A93 A101 2 A123 25 A143 A152 1 A173 1 A191 A201 1 54 | A14 18 A32 A41 3378 A65 A73 2 A93 A101 1 A122 31 A143 A152 1 A173 1 A192 A201 1 55 | A12 36 A33 A40 2225 A61 A75 4 A93 A101 4 A124 57 A141 A153 2 A173 1 A192 A201 2 56 | A14 6 A31 A40 783 A65 A73 1 A93 A103 2 A121 26 A142 A152 1 A172 2 A191 A201 1 57 | A12 12 A32 A43 6468 A65 A71 2 A93 A101 1 A124 52 A143 A152 1 A174 1 A192 A201 2 58 | A14 36 A34 A43 9566 A61 A73 2 A92 A101 2 A123 31 A142 A152 2 A173 1 A191 A201 1 59 | A13 18 A32 A40 1961 A61 A75 3 A92 A101 2 A123 23 A143 A152 1 A174 1 A191 A201 1 60 | A11 36 A34 A42 6229 A61 A72 4 A92 A102 4 A124 23 A143 A151 2 A172 1 A192 A201 2 61 | A12 9 A32 A49 1391 A61 A73 2 A94 A101 1 A121 27 A141 A152 1 A173 1 A192 A201 1 62 | A12 15 A34 A43 1537 A65 A75 4 A93 A103 4 A121 50 A143 A152 2 A173 1 A192 A201 1 63 | A12 36 A30 A49 1953 A61 A75 4 A93 A101 4 A124 61 A143 A153 1 A174 1 A192 A201 2 64 | A12 48 A30 A49 14421 A61 A73 2 A93 A101 2 A123 25 A143 A152 1 A173 1 A192 A201 2 65 | A14 24 A32 A43 3181 A61 A72 4 A92 A101 4 A122 26 A143 A152 1 A173 1 A192 A201 1 66 | A14 27 A32 A45 5190 A65 A75 4 A93 A101 4 A122 48 A143 A152 4 A173 2 A192 A201 1 67 | A14 12 A32 A43 2171 A61 A72 2 A92 A101 2 A123 29 A141 A152 1 A173 1 A191 A201 1 68 | A12 12 A32 A40 1007 A64 A73 4 A94 A101 1 A121 22 A143 A152 1 A173 1 A191 A201 1 69 | A14 36 A32 A46 1819 A61 A73 4 A93 A101 4 A124 37 A142 A153 1 A173 1 A192 A201 2 70 | A14 36 A32 A43 2394 A65 A73 4 A92 A101 4 A123 25 A143 A152 1 A173 1 A191 A201 1 71 | A14 36 A32 A41 8133 A61 A73 1 A92 A101 2 A122 30 A141 A152 1 A173 1 A191 A201 1 72 | A14 7 A34 A43 730 A65 A75 4 A93 A101 2 A122 46 A143 A151 2 A172 1 A192 A201 1 73 | A11 8 A34 A410 1164 A61 A75 3 A93 A101 4 A124 51 A141 A153 2 A174 2 A192 A201 1 74 | A12 42 A34 A49 5954 A61 A74 2 A92 A101 1 A121 41 A141 A152 2 A172 1 A191 A201 1 75 | A11 36 A32 A46 1977 A65 A75 4 A93 A101 4 A124 40 A143 A152 1 A174 1 A192 A201 2 76 | A11 12 A34 A41 1526 A61 A75 4 A93 A101 4 A124 66 A143 A153 2 A174 1 A191 A201 1 77 | A11 42 A32 A43 3965 A61 A72 4 A93 A101 3 A123 34 A143 A152 1 A173 1 A191 A201 2 78 | A12 11 A33 A43 4771 A61 A74 2 A93 A101 4 A122 51 A143 A152 1 A173 1 A191 A201 1 79 | A14 54 A30 A41 9436 A65 A73 2 A93 A101 2 A122 39 A143 A152 1 A172 2 A191 A201 1 80 | A12 30 A32 A42 3832 A61 A72 2 A94 A101 1 A122 22 A143 A152 1 A173 1 A191 A201 1 81 | A14 24 A32 A43 5943 A65 A72 1 A92 A101 1 A123 44 A143 A152 2 A173 1 A192 A201 2 82 | A14 15 A32 A43 1213 A63 A75 4 A93 A101 3 A122 47 A142 A152 1 A173 1 A192 A201 1 83 | A14 18 A32 A49 1568 A62 A73 3 A92 A101 4 A122 24 A143 A151 1 A172 1 A191 A201 1 84 | A11 24 A32 A410 1755 A61 A75 4 A92 A103 4 A121 58 A143 A152 1 A172 1 A192 A201 1 85 | A11 10 A32 A43 2315 A61 A75 3 A93 A101 4 A121 52 A143 A152 1 A172 1 A191 A201 1 86 | A14 12 A34 A49 1412 A61 A73 4 A92 A103 2 A121 29 A143 A152 2 A174 1 A192 A201 1 87 | A12 18 A34 A42 1295 A61 A72 4 A92 A101 1 A122 27 A143 A152 2 A173 1 A191 A201 1 88 | A12 36 A32 A46 12612 A62 A73 1 A93 A101 4 A124 47 A143 A153 1 A173 2 A192 A201 2 89 | A11 18 A32 A40 2249 A62 A74 4 A93 A101 3 A123 30 A143 A152 1 A174 2 A192 A201 1 90 | A11 12 A30 A45 1108 A61 A74 4 A93 A101 3 A121 28 A143 A152 2 A173 1 A191 A201 2 91 | A14 12 A34 A43 618 A61 A75 4 A93 A101 4 A121 56 A143 A152 1 A173 1 A191 A201 1 92 | A11 12 A34 A41 1409 A61 A75 4 A93 A101 3 A121 54 A143 A152 1 A173 1 A191 A201 1 93 | A14 12 A34 A43 797 A65 A75 4 A92 A101 3 A122 33 A141 A152 1 A172 2 A191 A201 2 94 | A13 24 A34 A42 3617 A65 A75 4 A93 A102 4 A124 20 A143 A151 2 A173 1 A191 A201 1 95 | A12 12 A32 A40 1318 A64 A75 4 A93 A101 4 A121 54 A143 A152 1 A173 1 A192 A201 1 96 | A12 54 A30 A49 15945 A61 A72 3 A93 A101 4 A124 58 A143 A151 1 A173 1 A192 A201 2 97 | A14 12 A34 A46 2012 A65 A74 4 A92 A101 2 A123 61 A143 A152 1 A173 1 A191 A201 1 98 | A12 18 A32 A49 2622 A62 A73 4 A93 A101 4 A123 34 A143 A152 1 A173 1 A191 A201 1 99 | A12 36 A34 A43 2337 A61 A75 4 A93 A101 4 A121 36 A143 A152 1 A173 1 A191 A201 1 100 | A12 20 A33 A41 7057 A65 A74 3 A93 A101 4 A122 36 A141 A151 2 A174 2 A192 A201 1 101 | A14 24 A32 A40 1469 A62 A75 4 A94 A101 4 A121 41 A143 A151 1 A172 1 A191 A201 1 102 | A12 36 A32 A43 2323 A61 A74 4 A93 A101 4 A123 24 A143 A151 1 A173 1 A191 A201 1 103 | A14 6 A33 A43 932 A61 A73 3 A92 A101 2 A121 24 A143 A152 1 A173 1 A191 A201 1 104 | A12 9 A34 A42 1919 A61 A74 4 A93 A101 3 A123 35 A143 A151 1 A173 1 A192 A201 1 105 | A14 12 A32 A41 2445 A65 A72 2 A94 A101 4 A123 26 A143 A151 1 A173 1 A192 A201 1 106 | A12 24 A34 A410 11938 A61 A73 2 A93 A102 3 A123 39 A143 A152 2 A174 2 A192 A201 2 107 | A14 18 A31 A40 6458 A61 A75 2 A93 A101 4 A124 39 A141 A152 2 A174 2 A192 A201 2 108 | A12 12 A32 A40 6078 A61 A74 2 A93 A101 2 A123 32 A143 A152 1 A173 1 A191 A201 1 109 | A11 24 A32 A42 7721 A65 A72 1 A92 A101 2 A122 30 A143 A152 1 A173 1 A192 A202 1 110 | A12 14 A32 A49 1410 A63 A75 1 A94 A101 2 A121 35 A143 A152 1 A173 1 A192 A201 1 111 | A12 6 A33 A49 1449 A62 A75 1 A91 A101 2 A123 31 A141 A152 2 A173 2 A191 A201 1 112 | A13 15 A32 A46 392 A61 A72 4 A92 A101 4 A122 23 A143 A151 1 A173 1 A192 A201 1 113 | A12 18 A32 A40 6260 A61 A74 3 A93 A101 3 A121 28 A143 A151 1 A172 1 A191 A201 1 114 | A14 36 A34 A40 7855 A61 A73 4 A92 A101 2 A121 25 A142 A152 2 A173 1 A192 A201 2 115 | A11 12 A32 A43 1680 A63 A75 3 A94 A101 1 A121 35 A143 A152 1 A173 1 A191 A201 1 116 | A14 48 A34 A43 3578 A65 A75 4 A93 A101 1 A121 47 A143 A152 1 A173 1 A192 A201 1 117 | A11 42 A32 A43 7174 A65 A74 4 A92 A101 3 A123 30 A143 A152 1 A174 1 A192 A201 2 118 | A11 10 A34 A42 2132 A65 A72 2 A92 A102 3 A121 27 A143 A151 2 A173 1 A191 A202 1 119 | A11 33 A34 A42 4281 A63 A73 1 A92 A101 4 A123 23 A143 A152 2 A173 1 A191 A201 2 120 | A12 12 A34 A40 2366 A63 A74 3 A91 A101 3 A123 36 A143 A152 1 A174 1 A192 A201 1 121 | A11 21 A32 A43 1835 A61 A73 3 A92 A101 2 A121 25 A143 A152 2 A173 1 A192 A201 2 122 | A14 24 A34 A41 3868 A61 A75 4 A92 A101 2 A123 41 A143 A151 2 A174 1 A192 A201 1 123 | A14 12 A32 A42 1768 A61 A73 3 A93 A101 2 A121 24 A143 A151 1 A172 1 A191 A201 1 124 | A13 10 A34 A40 781 A61 A75 4 A93 A101 4 A124 63 A143 A153 2 A173 1 A192 A201 1 125 | A12 18 A32 A42 1924 A65 A72 4 A92 A101 3 A121 27 A143 A151 1 A173 1 A191 A201 2 126 | A11 12 A34 A40 2121 A61 A73 4 A93 A101 2 A122 30 A143 A152 2 A173 1 A191 A201 1 127 | A11 12 A32 A43 701 A61 A73 4 A94 A101 2 A121 40 A143 A152 1 A172 1 A191 A201 1 128 | A12 12 A32 A45 639 A61 A73 4 A93 A101 2 A123 30 A143 A152 1 A173 1 A191 A201 2 129 | A12 12 A34 A41 1860 A61 A71 4 A93 A101 2 A123 34 A143 A152 2 A174 1 A192 A201 1 130 | A11 12 A34 A40 3499 A61 A73 3 A92 A102 2 A121 29 A143 A152 2 A173 1 A191 A201 2 131 | A12 48 A32 A40 8487 A65 A74 1 A92 A101 2 A123 24 A143 A152 1 A173 1 A191 A201 1 132 | A11 36 A33 A46 6887 A61 A73 4 A93 A101 3 A122 29 A142 A152 1 A173 1 A192 A201 2 133 | A14 15 A32 A42 2708 A61 A72 2 A93 A101 3 A122 27 A141 A152 2 A172 1 A191 A201 1 134 | A14 18 A32 A42 1984 A61 A73 4 A93 A101 4 A124 47 A141 A153 2 A173 1 A191 A201 1 135 | A14 60 A32 A43 10144 A62 A74 2 A92 A101 4 A121 21 A143 A152 1 A173 1 A192 A201 1 136 | A14 12 A34 A43 1240 A65 A75 4 A92 A101 2 A121 38 A143 A152 2 A173 1 A192 A201 1 137 | A14 27 A33 A41 8613 A64 A73 2 A93 A101 2 A123 27 A143 A152 2 A173 1 A191 A201 1 138 | A12 12 A32 A43 766 A63 A73 4 A93 A101 3 A121 66 A143 A152 1 A172 1 A191 A201 2 139 | A12 15 A34 A43 2728 A65 A74 4 A93 A103 2 A121 35 A141 A152 3 A173 1 A192 A201 1 140 | A13 12 A32 A43 1881 A61 A73 2 A92 A101 2 A123 44 A143 A151 1 A172 1 A192 A201 1 141 | A13 6 A32 A40 709 A64 A72 2 A94 A101 2 A121 27 A143 A152 1 A171 1 A191 A202 1 142 | A12 36 A32 A43 4795 A61 A72 4 A92 A101 1 A124 30 A143 A152 1 A174 1 A192 A201 1 143 | A11 27 A32 A43 3416 A61 A73 3 A93 A101 2 A123 27 A143 A152 1 A174 1 A191 A201 1 144 | A11 18 A32 A42 2462 A61 A73 2 A93 A101 2 A123 22 A143 A152 1 A173 1 A191 A201 2 145 | A14 21 A34 A42 2288 A61 A72 4 A92 A101 4 A122 23 A143 A152 1 A173 1 A192 A201 1 146 | A12 48 A31 A49 3566 A62 A74 4 A93 A101 2 A123 30 A143 A152 1 A173 1 A191 A201 1 147 | A11 6 A34 A40 860 A61 A75 1 A92 A101 4 A124 39 A143 A152 2 A173 1 A192 A201 1 148 | A14 12 A34 A40 682 A62 A74 4 A92 A101 3 A123 51 A143 A152 2 A173 1 A192 A201 1 149 | A11 36 A34 A42 5371 A61 A73 3 A93 A103 2 A122 28 A143 A152 2 A173 1 A191 A201 1 150 | A14 18 A34 A43 1582 A64 A75 4 A93 A101 4 A123 46 A143 A152 2 A173 1 A191 A201 1 151 | A14 6 A32 A43 1346 A62 A75 2 A93 A101 4 A124 42 A141 A153 1 A173 2 A192 A201 1 152 | A14 10 A32 A43 1924 A61 A73 1 A93 A101 4 A122 38 A143 A152 1 A173 1 A192 A202 1 153 | A13 36 A32 A43 5848 A61 A73 4 A93 A101 1 A123 24 A143 A152 1 A173 1 A191 A201 1 154 | A12 24 A34 A41 7758 A64 A75 2 A92 A101 4 A124 29 A143 A151 1 A173 1 A191 A201 1 155 | A12 24 A33 A49 6967 A62 A74 4 A93 A101 4 A123 36 A143 A151 1 A174 1 A192 A201 1 156 | A11 12 A32 A42 1282 A61 A73 2 A92 A101 4 A123 20 A143 A151 1 A173 1 A191 A201 2 157 | A11 9 A34 A45 1288 A62 A75 3 A93 A103 4 A121 48 A143 A152 2 A173 2 A191 A202 1 158 | A11 12 A31 A48 339 A61 A75 4 A94 A101 1 A123 45 A141 A152 1 A172 1 A191 A201 1 159 | A12 24 A32 A40 3512 A62 A74 2 A93 A101 3 A123 38 A141 A152 2 A173 1 A192 A201 1 160 | A14 6 A34 A43 1898 A65 A73 1 A93 A101 2 A121 34 A143 A152 2 A172 2 A191 A201 1 161 | A14 24 A34 A43 2872 A62 A75 3 A93 A101 4 A121 36 A143 A152 1 A173 2 A192 A201 1 162 | A14 18 A34 A40 1055 A61 A72 4 A92 A101 1 A122 30 A143 A152 2 A173 1 A191 A201 1 163 | A14 15 A32 A44 1262 A63 A74 4 A93 A101 3 A122 36 A143 A152 2 A173 1 A192 A201 1 164 | A12 10 A32 A40 7308 A61 A71 2 A93 A101 4 A124 70 A141 A153 1 A174 1 A192 A201 1 165 | A14 36 A32 A40 909 A63 A75 4 A93 A101 4 A122 36 A143 A152 1 A173 1 A191 A201 1 166 | A14 6 A32 A42 2978 A63 A73 1 A93 A101 2 A123 32 A143 A152 1 A173 1 A192 A201 1 167 | A11 18 A32 A42 1131 A61 A71 4 A92 A101 2 A123 33 A143 A152 1 A173 1 A191 A201 2 168 | A12 11 A32 A42 1577 A64 A72 4 A92 A101 1 A121 20 A143 A152 1 A173 1 A191 A201 1 169 | A14 24 A32 A42 3972 A61 A74 2 A92 A101 4 A122 25 A143 A151 1 A173 1 A192 A201 1 170 | A12 24 A34 A49 1935 A61 A75 4 A91 A101 4 A121 31 A143 A152 2 A173 1 A192 A201 2 171 | A11 15 A30 A40 950 A61 A75 4 A93 A101 3 A123 33 A143 A151 2 A173 2 A191 A201 2 172 | A14 12 A32 A42 763 A61 A73 4 A92 A101 1 A121 26 A143 A152 1 A173 1 A192 A201 1 173 | A12 24 A33 A42 2064 A61 A71 3 A92 A101 2 A122 34 A143 A152 1 A174 1 A192 A201 2 174 | A12 8 A32 A43 1414 A61 A73 4 A93 A103 2 A121 33 A143 A152 1 A173 1 A191 A202 1 175 | A11 21 A33 A46 3414 A61 A72 2 A93 A101 1 A122 26 A143 A152 2 A173 1 A191 A201 2 176 | A14 30 A31 A41 7485 A65 A71 4 A92 A101 1 A121 53 A141 A152 1 A174 1 A192 A201 2 177 | A11 12 A32 A42 2577 A61 A73 2 A91 A101 1 A123 42 A143 A152 1 A173 1 A191 A201 1 178 | A11 6 A34 A43 338 A63 A75 4 A93 A101 4 A123 52 A143 A152 2 A173 1 A191 A201 1 179 | A14 12 A32 A43 1963 A61 A74 4 A93 A101 2 A123 31 A143 A151 2 A174 2 A192 A201 1 180 | A11 21 A34 A40 571 A61 A75 4 A93 A101 4 A121 65 A143 A152 2 A173 1 A191 A201 1 181 | A14 36 A33 A49 9572 A61 A72 1 A91 A101 1 A123 28 A143 A152 2 A173 1 A191 A201 2 182 | A12 36 A33 A49 4455 A61 A73 2 A91 A101 2 A121 30 A142 A152 2 A174 1 A192 A201 2 183 | A11 21 A31 A40 1647 A65 A73 4 A93 A101 2 A122 40 A143 A152 2 A172 2 A191 A201 2 184 | A14 24 A34 A42 3777 A64 A73 4 A93 A101 4 A121 50 A143 A152 1 A173 1 A192 A201 1 185 | A12 18 A34 A40 884 A61 A75 4 A93 A101 4 A123 36 A141 A152 1 A173 2 A192 A201 2 186 | A14 15 A34 A43 1360 A61 A73 4 A93 A101 2 A122 31 A143 A152 2 A173 1 A191 A201 1 187 | A12 9 A31 A41 5129 A61 A75 2 A92 A101 4 A124 74 A141 A153 1 A174 2 A192 A201 2 188 | A12 16 A34 A40 1175 A61 A71 2 A93 A101 3 A123 68 A143 A153 3 A171 1 A192 A201 1 189 | A11 12 A32 A43 674 A62 A74 4 A94 A101 1 A122 20 A143 A152 1 A173 1 A191 A201 2 190 | A12 18 A30 A42 3244 A61 A73 1 A92 A101 4 A123 33 A141 A152 2 A173 1 A192 A201 1 191 | A14 24 A32 A49 4591 A64 A73 2 A93 A101 3 A122 54 A143 A152 3 A174 1 A192 A201 2 192 | A12 48 A30 A49 3844 A62 A74 4 A93 A101 4 A124 34 A143 A153 1 A172 2 A191 A201 2 193 | A12 27 A32 A49 3915 A61 A73 4 A93 A101 2 A123 36 A143 A152 1 A173 2 A192 A201 2 194 | A14 6 A32 A43 2108 A61 A74 2 A94 A101 2 A121 29 A143 A151 1 A173 1 A191 A201 1 195 | A12 45 A32 A43 3031 A62 A73 4 A93 A103 4 A122 21 A143 A151 1 A173 1 A191 A201 2 196 | A12 9 A34 A46 1501 A61 A75 2 A92 A101 3 A123 34 A143 A152 2 A174 1 A192 A201 2 197 | A14 6 A34 A43 1382 A61 A73 1 A92 A101 1 A123 28 A143 A152 2 A173 1 A192 A201 1 198 | A12 12 A32 A42 951 A62 A72 4 A92 A101 4 A123 27 A141 A151 4 A173 1 A191 A201 2 199 | A12 24 A32 A41 2760 A65 A75 4 A93 A101 4 A124 36 A141 A153 1 A173 1 A192 A201 1 200 | A12 18 A33 A42 4297 A61 A75 4 A91 A101 3 A124 40 A143 A152 1 A174 1 A192 A201 2 201 | A14 9 A34 A46 936 A63 A75 4 A93 A101 2 A123 52 A143 A152 2 A173 1 A192 A201 1 202 | A11 12 A32 A40 1168 A61 A73 4 A94 A101 3 A121 27 A143 A152 1 A172 1 A191 A201 1 203 | A14 27 A33 A49 5117 A61 A74 3 A93 A101 4 A123 26 A143 A152 2 A173 1 A191 A201 1 204 | A11 12 A32 A48 902 A61 A74 4 A94 A101 4 A122 21 A143 A151 1 A173 1 A191 A201 2 205 | A14 12 A34 A40 1495 A61 A75 4 A93 A101 1 A121 38 A143 A152 2 A172 2 A191 A201 1 206 | A11 30 A34 A41 10623 A61 A75 3 A93 A101 4 A124 38 A143 A153 3 A174 2 A192 A201 1 207 | A14 12 A34 A42 1935 A61 A75 4 A93 A101 4 A121 43 A143 A152 3 A173 1 A192 A201 1 208 | A12 12 A34 A44 1424 A61 A74 4 A93 A101 3 A122 26 A143 A152 1 A173 1 A191 A201 1 209 | A11 24 A32 A49 6568 A61 A73 2 A94 A101 2 A123 21 A142 A152 1 A172 1 A191 A201 1 210 | A14 12 A32 A41 1413 A64 A74 3 A93 A101 2 A122 55 A143 A152 1 A173 1 A191 A202 1 211 | A14 9 A34 A43 3074 A65 A73 1 A93 A101 2 A121 33 A143 A152 2 A173 2 A191 A201 1 212 | A14 36 A32 A43 3835 A65 A75 2 A92 A101 4 A121 45 A143 A152 1 A172 1 A192 A201 1 213 | A11 27 A30 A49 5293 A61 A71 2 A93 A101 4 A122 50 A142 A152 2 A173 1 A192 A201 2 214 | A13 30 A33 A49 1908 A61 A75 4 A93 A101 4 A121 66 A143 A152 1 A174 1 A192 A201 2 215 | A14 36 A34 A43 3342 A65 A75 4 A93 A101 2 A123 51 A143 A152 1 A173 1 A192 A201 1 216 | A12 6 A34 A48 932 A65 A74 1 A92 A101 3 A122 39 A143 A152 2 A172 1 A191 A201 1 217 | A11 18 A30 A49 3104 A61 A74 3 A93 A101 1 A122 31 A141 A152 1 A173 1 A192 A201 1 218 | A13 36 A32 A43 3913 A61 A73 2 A93 A101 2 A121 23 A143 A152 1 A173 1 A192 A201 1 219 | A11 24 A32 A42 3021 A61 A73 2 A91 A101 2 A121 24 A143 A151 1 A172 1 A191 A201 1 220 | A14 10 A32 A40 1364 A61 A73 2 A92 A101 4 A123 64 A143 A152 1 A173 1 A192 A201 1 221 | A12 12 A32 A43 625 A61 A72 4 A94 A103 1 A121 26 A141 A152 1 A172 1 A191 A201 1 222 | A11 12 A32 A46 1200 A65 A73 4 A92 A101 4 A122 23 A141 A151 1 A173 1 A192 A201 1 223 | A14 12 A32 A43 707 A61 A73 4 A93 A101 2 A121 30 A141 A152 2 A173 1 A191 A201 1 224 | A14 24 A33 A49 2978 A65 A73 4 A93 A101 4 A121 32 A143 A152 2 A173 2 A192 A201 1 225 | A14 15 A32 A41 4657 A61 A73 3 A93 A101 2 A123 30 A143 A152 1 A173 1 A192 A201 1 226 | A14 36 A30 A45 2613 A61 A73 4 A93 A101 2 A123 27 A143 A152 2 A173 1 A191 A201 1 227 | A12 48 A32 A43 10961 A64 A74 1 A93 A102 2 A124 27 A141 A152 2 A173 1 A192 A201 2 228 | A11 12 A32 A42 7865 A61 A75 4 A93 A101 4 A124 53 A143 A153 1 A174 1 A192 A201 2 229 | A14 9 A32 A43 1478 A61 A74 4 A93 A101 2 A123 22 A143 A152 1 A173 1 A191 A201 2 230 | A11 24 A32 A42 3149 A61 A72 4 A93 A101 1 A124 22 A141 A153 1 A173 1 A191 A201 1 231 | A13 36 A32 A43 4210 A61 A73 4 A93 A101 2 A123 26 A143 A152 1 A173 1 A191 A201 2 232 | A14 9 A32 A40 2507 A63 A75 2 A93 A101 4 A124 51 A143 A153 1 A172 1 A191 A201 1 233 | A14 12 A32 A43 2141 A62 A74 3 A93 A101 1 A124 35 A143 A152 1 A173 1 A191 A201 1 234 | A12 18 A32 A43 866 A61 A73 4 A94 A103 2 A121 25 A143 A152 1 A172 1 A191 A201 1 235 | A14 4 A34 A43 1544 A61 A74 2 A93 A101 1 A121 42 A143 A152 3 A172 2 A191 A201 1 236 | A11 24 A32 A43 1823 A61 A71 4 A93 A101 2 A123 30 A142 A152 1 A174 2 A191 A201 2 237 | A12 6 A32 A40 14555 A65 A71 1 A93 A101 2 A122 23 A143 A152 1 A171 1 A192 A201 2 238 | A12 21 A32 A49 2767 A62 A75 4 A91 A101 2 A123 61 A141 A151 2 A172 1 A191 A201 2 239 | A14 12 A34 A43 1291 A61 A73 4 A92 A101 2 A122 35 A143 A152 2 A173 1 A191 A201 1 240 | A11 30 A32 A43 2522 A61 A75 1 A93 A103 3 A122 39 A143 A152 1 A173 2 A191 A201 1 241 | A11 24 A32 A40 915 A65 A75 4 A92 A101 2 A123 29 A141 A152 1 A173 1 A191 A201 2 242 | A14 6 A32 A43 1595 A61 A74 3 A93 A101 2 A122 51 A143 A152 1 A173 2 A191 A201 1 243 | A11 48 A30 A41 4605 A61 A75 3 A93 A101 4 A124 24 A143 A153 2 A173 2 A191 A201 2 244 | A14 12 A34 A49 1185 A61 A73 3 A92 A101 2 A121 27 A143 A152 2 A173 1 A191 A201 1 245 | A14 12 A31 A48 3447 A63 A73 4 A92 A101 3 A121 35 A143 A152 1 A172 2 A191 A201 1 246 | A14 24 A32 A49 1258 A61 A74 4 A93 A101 1 A121 25 A143 A152 1 A173 1 A192 A201 1 247 | A14 12 A34 A43 717 A61 A75 4 A93 A101 4 A121 52 A143 A152 3 A173 1 A191 A201 1 248 | A14 6 A30 A40 1204 A62 A73 4 A93 A101 1 A124 35 A141 A151 1 A173 1 A191 A202 1 249 | A13 24 A32 A42 1925 A61 A73 2 A93 A101 2 A121 26 A143 A152 1 A173 1 A191 A201 1 250 | A14 18 A32 A43 433 A61 A71 3 A92 A102 4 A121 22 A143 A151 1 A173 1 A191 A201 2 251 | A11 6 A34 A40 666 A64 A74 3 A92 A101 4 A121 39 A143 A152 2 A172 1 A192 A201 1 252 | A13 12 A32 A42 2251 A61 A73 1 A92 A101 2 A123 46 A143 A152 1 A172 1 A191 A201 1 253 | A12 30 A32 A40 2150 A61 A73 4 A92 A103 2 A124 24 A141 A152 1 A173 1 A191 A201 2 254 | A14 24 A33 A42 4151 A62 A73 2 A93 A101 3 A122 35 A143 A152 2 A173 1 A191 A201 1 255 | A12 9 A32 A42 2030 A65 A74 2 A93 A101 1 A123 24 A143 A152 1 A173 1 A192 A201 1 256 | A12 60 A33 A43 7418 A65 A73 1 A93 A101 1 A121 27 A143 A152 1 A172 1 A191 A201 1 257 | A14 24 A34 A43 2684 A61 A73 4 A93 A101 2 A121 35 A143 A152 2 A172 1 A191 A201 1 258 | A11 12 A31 A43 2149 A61 A73 4 A91 A101 1 A124 29 A143 A153 1 A173 1 A191 A201 2 259 | A14 15 A32 A41 3812 A62 A72 1 A92 A101 4 A123 23 A143 A152 1 A173 1 A192 A201 1 260 | A14 11 A34 A43 1154 A62 A71 4 A92 A101 4 A121 57 A143 A152 3 A172 1 A191 A201 1 261 | A11 12 A32 A42 1657 A61 A73 2 A93 A101 2 A121 27 A143 A152 1 A173 1 A191 A201 1 262 | A11 24 A32 A43 1603 A61 A75 4 A92 A101 4 A123 55 A143 A152 1 A173 1 A191 A201 1 263 | A11 18 A34 A40 5302 A61 A75 2 A93 A101 4 A124 36 A143 A153 3 A174 1 A192 A201 1 264 | A14 12 A34 A46 2748 A61 A75 2 A92 A101 4 A124 57 A141 A153 3 A172 1 A191 A201 1 265 | A14 10 A34 A40 1231 A61 A75 3 A93 A101 4 A121 32 A143 A152 2 A172 2 A191 A202 1 266 | A12 15 A32 A43 802 A61 A75 4 A93 A101 3 A123 37 A143 A152 1 A173 2 A191 A201 2 267 | A14 36 A34 A49 6304 A65 A75 4 A93 A101 4 A121 36 A143 A152 2 A173 1 A191 A201 1 268 | A14 24 A32 A43 1533 A61 A72 4 A92 A101 3 A123 38 A142 A152 1 A173 1 A192 A201 1 269 | A11 14 A32 A40 8978 A61 A75 1 A91 A101 4 A122 45 A143 A152 1 A174 1 A192 A202 2 270 | A14 24 A32 A43 999 A65 A75 4 A93 A101 2 A123 25 A143 A152 2 A173 1 A191 A201 1 271 | A14 18 A32 A40 2662 A65 A74 4 A93 A101 3 A122 32 A143 A152 1 A173 1 A191 A202 1 272 | A14 12 A34 A42 1402 A63 A74 3 A92 A101 4 A123 37 A143 A151 1 A173 1 A192 A201 1 273 | A12 48 A31 A40 12169 A65 A71 4 A93 A102 4 A124 36 A143 A153 1 A174 1 A192 A201 1 274 | A12 48 A32 A43 3060 A61 A74 4 A93 A101 4 A121 28 A143 A152 2 A173 1 A191 A201 2 275 | A11 30 A32 A45 11998 A61 A72 1 A91 A101 1 A124 34 A143 A152 1 A172 1 A192 A201 2 276 | A14 9 A32 A43 2697 A61 A73 1 A93 A101 2 A121 32 A143 A152 1 A173 2 A191 A201 1 277 | A14 18 A34 A43 2404 A61 A73 2 A92 A101 2 A123 26 A143 A152 2 A173 1 A191 A201 1 278 | A11 12 A32 A42 1262 A65 A75 2 A91 A101 4 A122 49 A143 A152 1 A172 1 A192 A201 1 279 | A14 6 A32 A42 4611 A61 A72 1 A92 A101 4 A122 32 A143 A152 1 A173 1 A191 A201 2 280 | A14 24 A32 A43 1901 A62 A73 4 A93 A101 4 A123 29 A143 A151 1 A174 1 A192 A201 1 281 | A14 15 A34 A41 3368 A64 A75 3 A93 A101 4 A124 23 A143 A151 2 A173 1 A192 A201 1 282 | A14 12 A32 A42 1574 A61 A73 4 A93 A101 2 A121 50 A143 A152 1 A173 1 A191 A201 1 283 | A13 18 A31 A43 1445 A65 A74 4 A93 A101 4 A123 49 A141 A152 1 A172 1 A191 A201 1 284 | A14 15 A34 A42 1520 A65 A75 4 A93 A101 4 A122 63 A143 A152 1 A173 1 A191 A201 1 285 | A12 24 A34 A40 3878 A62 A72 4 A91 A101 2 A123 37 A143 A152 1 A173 1 A192 A201 1 286 | A11 47 A32 A40 10722 A61 A72 1 A92 A101 1 A121 35 A143 A152 1 A172 1 A192 A201 1 287 | A11 48 A32 A41 4788 A61 A74 4 A93 A101 3 A122 26 A143 A152 1 A173 2 A191 A201 1 288 | A12 48 A33 A410 7582 A62 A71 2 A93 A101 4 A124 31 A143 A153 1 A174 1 A192 A201 1 289 | A12 12 A32 A43 1092 A61 A73 4 A92 A103 4 A121 49 A143 A152 2 A173 1 A192 A201 1 290 | A11 24 A33 A43 1024 A61 A72 4 A94 A101 4 A121 48 A142 A152 1 A173 1 A191 A201 2 291 | A14 12 A32 A49 1076 A61 A73 2 A94 A101 2 A121 26 A143 A152 1 A173 1 A192 A202 1 292 | A12 36 A32 A41 9398 A61 A72 1 A94 A101 4 A123 28 A143 A151 1 A174 1 A192 A201 2 293 | A11 24 A34 A41 6419 A61 A75 2 A92 A101 4 A124 44 A143 A153 2 A174 2 A192 A201 1 294 | A13 42 A34 A41 4796 A61 A75 4 A93 A101 4 A124 56 A143 A153 1 A173 1 A191 A201 1 295 | A14 48 A34 A49 7629 A65 A75 4 A91 A101 2 A123 46 A141 A152 2 A174 2 A191 A201 1 296 | A12 48 A32 A42 9960 A61 A72 1 A92 A101 2 A123 26 A143 A152 1 A173 1 A192 A201 2 297 | A14 12 A32 A41 4675 A65 A72 1 A92 A101 4 A123 20 A143 A151 1 A173 1 A191 A201 1 298 | A14 10 A32 A40 1287 A65 A75 4 A93 A102 2 A122 45 A143 A152 1 A172 1 A191 A202 1 299 | A14 18 A32 A42 2515 A61 A73 3 A93 A101 4 A121 43 A143 A152 1 A173 1 A192 A201 1 300 | A12 21 A34 A42 2745 A64 A74 3 A93 A101 2 A123 32 A143 A152 2 A173 1 A192 A201 1 301 | A14 6 A32 A40 672 A61 A71 1 A92 A101 4 A121 54 A143 A152 1 A171 1 A192 A201 1 302 | A12 36 A30 A43 3804 A61 A73 4 A92 A101 1 A123 42 A143 A152 1 A173 1 A192 A201 2 303 | A13 24 A34 A40 1344 A65 A74 4 A93 A101 2 A121 37 A141 A152 2 A172 2 A191 A201 2 304 | A11 10 A34 A40 1038 A61 A74 4 A93 A102 3 A122 49 A143 A152 2 A173 1 A192 A201 1 305 | A14 48 A34 A40 10127 A63 A73 2 A93 A101 2 A124 44 A141 A153 1 A173 1 A191 A201 2 306 | A14 6 A32 A42 1543 A64 A73 4 A91 A101 2 A121 33 A143 A152 1 A173 1 A191 A201 1 307 | A14 30 A32 A41 4811 A65 A74 2 A92 A101 4 A122 24 A142 A151 1 A172 1 A191 A201 1 308 | A11 12 A32 A43 727 A62 A72 4 A94 A101 3 A124 33 A143 A152 1 A172 1 A192 A201 2 309 | A12 8 A32 A42 1237 A61 A73 3 A92 A101 4 A121 24 A143 A152 1 A173 1 A191 A201 2 310 | A12 9 A32 A40 276 A61 A73 4 A94 A101 4 A121 22 A143 A151 1 A172 1 A191 A201 1 311 | A12 48 A32 A410 5381 A65 A71 3 A93 A101 4 A124 40 A141 A153 1 A171 1 A192 A201 1 312 | A14 24 A32 A42 5511 A62 A73 4 A93 A101 1 A123 25 A142 A152 1 A173 1 A191 A201 1 313 | A13 24 A32 A42 3749 A61 A72 2 A92 A101 4 A123 26 A143 A152 1 A173 1 A191 A201 1 314 | A12 12 A32 A40 685 A61 A74 2 A94 A101 3 A123 25 A141 A152 1 A172 1 A191 A201 2 315 | A13 4 A32 A40 1494 A65 A72 1 A93 A101 2 A121 29 A143 A152 1 A172 2 A191 A202 1 316 | A11 36 A31 A42 2746 A61 A75 4 A93 A101 4 A123 31 A141 A152 1 A173 1 A191 A201 2 317 | A11 12 A32 A42 708 A61 A73 2 A93 A103 3 A122 38 A143 A152 1 A172 2 A191 A201 1 318 | A12 24 A32 A42 4351 A65 A73 1 A92 A101 4 A122 48 A143 A152 1 A172 1 A192 A201 1 319 | A14 12 A34 A46 701 A61 A73 4 A93 A101 2 A123 32 A143 A152 2 A173 1 A191 A201 1 320 | A11 15 A33 A42 3643 A61 A75 1 A92 A101 4 A122 27 A143 A152 2 A172 1 A191 A201 1 321 | A12 30 A34 A40 4249 A61 A71 4 A94 A101 2 A123 28 A143 A152 2 A174 1 A191 A201 2 322 | A11 24 A32 A43 1938 A61 A72 4 A91 A101 3 A122 32 A143 A152 1 A173 1 A191 A201 2 323 | A11 24 A32 A41 2910 A61 A74 2 A93 A101 1 A124 34 A143 A153 1 A174 1 A192 A201 1 324 | A11 18 A32 A42 2659 A64 A73 4 A93 A101 2 A123 28 A143 A152 1 A173 1 A191 A201 1 325 | A14 18 A34 A40 1028 A61 A73 4 A92 A101 3 A121 36 A143 A152 2 A173 1 A191 A201 1 326 | A11 8 A34 A40 3398 A61 A74 1 A93 A101 4 A121 39 A143 A152 2 A172 1 A191 A202 1 327 | A14 12 A34 A42 5801 A65 A75 2 A93 A101 4 A122 49 A143 A151 1 A173 1 A192 A201 1 328 | A14 24 A32 A40 1525 A64 A74 4 A92 A101 3 A123 34 A143 A152 1 A173 2 A192 A201 1 329 | A13 36 A32 A43 4473 A61 A75 4 A93 A101 2 A123 31 A143 A152 1 A173 1 A191 A201 1 330 | A12 6 A32 A43 1068 A61 A75 4 A93 A101 4 A123 28 A143 A152 1 A173 2 A191 A201 1 331 | A11 24 A34 A41 6615 A61 A71 2 A93 A101 4 A124 75 A143 A153 2 A174 1 A192 A201 1 332 | A14 18 A34 A46 1864 A62 A73 4 A92 A101 2 A121 30 A143 A152 2 A173 1 A191 A201 2 333 | A12 60 A32 A40 7408 A62 A72 4 A92 A101 2 A122 24 A143 A152 1 A174 1 A191 A201 2 334 | A14 48 A34 A41 11590 A62 A73 2 A92 A101 4 A123 24 A141 A151 2 A172 1 A191 A201 2 335 | A11 24 A30 A42 4110 A61 A75 3 A93 A101 4 A124 23 A141 A151 2 A173 2 A191 A201 2 336 | A11 6 A34 A42 3384 A61 A73 1 A91 A101 4 A121 44 A143 A151 1 A174 1 A192 A201 2 337 | A12 13 A32 A43 2101 A61 A72 2 A92 A103 4 A122 23 A143 A152 1 A172 1 A191 A201 1 338 | A11 15 A32 A44 1275 A65 A73 4 A92 A101 2 A123 24 A143 A151 1 A173 1 A191 A201 2 339 | A11 24 A32 A42 4169 A61 A73 4 A93 A101 4 A122 28 A143 A152 1 A173 1 A191 A201 1 340 | A12 10 A32 A42 1521 A61 A73 4 A91 A101 2 A123 31 A143 A152 1 A172 1 A191 A201 1 341 | A12 24 A34 A46 5743 A61 A72 2 A92 A101 4 A124 24 A143 A153 2 A173 1 A192 A201 1 342 | A11 21 A32 A42 3599 A61 A74 1 A92 A101 4 A123 26 A143 A151 1 A172 1 A191 A201 1 343 | A12 18 A32 A43 3213 A63 A72 1 A94 A101 3 A121 25 A143 A151 1 A173 1 A191 A201 1 344 | A12 18 A32 A49 4439 A61 A75 1 A93 A102 1 A121 33 A141 A152 1 A174 1 A192 A201 1 345 | A13 10 A32 A40 3949 A61 A72 1 A93 A103 1 A122 37 A143 A152 1 A172 2 A191 A201 1 346 | A14 15 A34 A43 1459 A61 A73 4 A92 A101 2 A123 43 A143 A152 1 A172 1 A191 A201 1 347 | A12 13 A34 A43 882 A61 A72 4 A93 A103 4 A121 23 A143 A152 2 A173 1 A191 A201 1 348 | A12 24 A32 A43 3758 A63 A71 1 A92 A101 4 A124 23 A143 A151 1 A171 1 A191 A201 1 349 | A14 6 A33 A49 1743 A62 A73 1 A93 A101 2 A121 34 A143 A152 2 A172 1 A191 A201 1 350 | A12 9 A34 A46 1136 A64 A75 4 A93 A101 3 A124 32 A143 A153 2 A173 2 A191 A201 2 351 | A14 9 A32 A44 1236 A61 A72 1 A92 A101 4 A121 23 A143 A151 1 A173 1 A192 A201 1 352 | A12 9 A32 A42 959 A61 A73 1 A92 A101 2 A123 29 A143 A152 1 A173 1 A191 A202 2 353 | A14 18 A34 A41 3229 A65 A71 2 A93 A101 4 A124 38 A143 A152 1 A174 1 A192 A201 1 354 | A11 12 A30 A43 6199 A61 A73 4 A93 A101 2 A122 28 A143 A151 2 A173 1 A192 A201 2 355 | A14 10 A32 A46 727 A63 A75 4 A93 A101 4 A124 46 A143 A153 1 A173 1 A192 A201 1 356 | A12 24 A32 A40 1246 A61 A72 4 A93 A101 2 A121 23 A142 A152 1 A172 1 A191 A201 2 357 | A14 12 A34 A43 2331 A65 A75 1 A93 A102 4 A121 49 A143 A152 1 A173 1 A192 A201 1 358 | A14 36 A33 A43 4463 A61 A73 4 A93 A101 2 A123 26 A143 A152 2 A174 1 A192 A201 2 359 | A14 12 A32 A43 776 A61 A73 4 A94 A101 2 A121 28 A143 A152 1 A173 1 A191 A201 1 360 | A11 30 A32 A42 2406 A61 A74 4 A92 A101 4 A121 23 A143 A151 1 A173 1 A191 A201 2 361 | A12 18 A32 A46 1239 A65 A73 4 A93 A101 4 A124 61 A143 A153 1 A173 1 A191 A201 1 362 | A13 12 A32 A43 3399 A65 A75 2 A93 A101 3 A123 37 A143 A152 1 A174 1 A191 A201 1 363 | A13 12 A33 A40 2247 A61 A73 2 A92 A101 2 A123 36 A142 A152 2 A173 1 A192 A201 1 364 | A14 6 A32 A42 1766 A61 A73 1 A94 A101 2 A122 21 A143 A151 1 A173 1 A191 A201 1 365 | A11 18 A32 A42 2473 A61 A71 4 A93 A101 1 A123 25 A143 A152 1 A171 1 A191 A201 2 366 | A14 12 A32 A49 1542 A61 A74 2 A93 A101 4 A123 36 A143 A152 1 A173 1 A192 A201 1 367 | A14 18 A34 A41 3850 A61 A74 3 A93 A101 1 A123 27 A143 A152 2 A173 1 A191 A201 1 368 | A11 18 A32 A42 3650 A61 A72 1 A92 A101 4 A123 22 A143 A151 1 A173 1 A191 A201 1 369 | A11 36 A32 A42 3446 A61 A75 4 A93 A101 2 A123 42 A143 A152 1 A173 2 A191 A201 2 370 | A12 18 A32 A42 3001 A61 A74 2 A92 A101 4 A121 40 A143 A151 1 A173 1 A191 A201 1 371 | A14 36 A32 A40 3079 A65 A73 4 A93 A101 4 A121 36 A143 A152 1 A173 1 A191 A201 1 372 | A14 18 A34 A43 6070 A61 A75 3 A93 A101 4 A123 33 A143 A152 2 A173 1 A192 A201 1 373 | A14 10 A34 A42 2146 A61 A72 1 A92 A101 3 A121 23 A143 A151 2 A173 1 A191 A201 1 374 | A14 60 A34 A40 13756 A65 A75 2 A93 A101 4 A124 63 A141 A153 1 A174 1 A192 A201 1 375 | A12 60 A31 A410 14782 A62 A75 3 A92 A101 4 A124 60 A141 A153 2 A174 1 A192 A201 2 376 | A11 48 A31 A49 7685 A61 A74 2 A92 A103 4 A123 37 A143 A151 1 A173 1 A191 A201 2 377 | A14 18 A33 A43 2320 A61 A71 2 A94 A101 3 A121 34 A143 A152 2 A173 1 A191 A201 1 378 | A14 7 A33 A43 846 A65 A75 3 A93 A101 4 A124 36 A143 A153 1 A173 1 A191 A201 1 379 | A12 36 A32 A40 14318 A61 A75 4 A93 A101 2 A124 57 A143 A153 1 A174 1 A192 A201 2 380 | A14 6 A34 A40 362 A62 A73 4 A92 A101 4 A123 52 A143 A152 2 A172 1 A191 A201 1 381 | A11 20 A32 A42 2212 A65 A74 4 A93 A101 4 A123 39 A143 A152 1 A173 1 A192 A201 1 382 | A12 18 A32 A41 12976 A61 A71 3 A92 A101 4 A124 38 A143 A153 1 A174 1 A192 A201 2 383 | A14 22 A32 A40 1283 A65 A74 4 A92 A101 4 A122 25 A143 A151 1 A173 1 A191 A201 1 384 | A13 12 A32 A40 1330 A61 A72 4 A93 A101 1 A121 26 A143 A152 1 A173 1 A191 A201 1 385 | A14 30 A33 A49 4272 A62 A73 2 A93 A101 2 A122 26 A143 A152 2 A172 1 A191 A201 1 386 | A14 18 A34 A43 2238 A61 A73 2 A92 A101 1 A123 25 A143 A152 2 A173 1 A191 A201 1 387 | A14 18 A32 A43 1126 A65 A72 4 A92 A101 2 A121 21 A143 A151 1 A173 1 A192 A201 1 388 | A12 18 A34 A42 7374 A61 A71 4 A93 A101 4 A122 40 A142 A152 2 A174 1 A192 A201 1 389 | A12 15 A34 A49 2326 A63 A73 2 A93 A101 4 A123 27 A141 A152 1 A173 1 A191 A201 1 390 | A14 9 A32 A49 1449 A61 A74 3 A92 A101 2 A123 27 A143 A152 2 A173 1 A191 A201 1 391 | A14 18 A32 A40 1820 A61 A73 2 A94 A101 2 A122 30 A143 A152 1 A174 1 A192 A201 1 392 | A12 12 A32 A42 983 A64 A72 1 A92 A101 4 A121 19 A143 A151 1 A172 1 A191 A201 1 393 | A11 36 A32 A40 3249 A61 A74 2 A93 A101 4 A124 39 A141 A153 1 A174 2 A192 A201 1 394 | A11 6 A34 A43 1957 A61 A74 1 A92 A101 4 A123 31 A143 A152 1 A173 1 A191 A201 1 395 | A14 9 A34 A42 2406 A61 A71 2 A93 A101 3 A123 31 A143 A152 1 A174 1 A191 A201 1 396 | A12 39 A33 A46 11760 A62 A74 2 A93 A101 3 A124 32 A143 A151 1 A173 1 A192 A201 1 397 | A11 12 A32 A42 2578 A61 A71 3 A92 A101 4 A124 55 A143 A153 1 A174 1 A191 A201 1 398 | A11 36 A34 A42 2348 A61 A73 3 A94 A101 2 A122 46 A143 A152 2 A173 1 A192 A201 1 399 | A12 12 A32 A40 1223 A61 A75 1 A91 A101 1 A121 46 A143 A151 2 A173 1 A191 A201 2 400 | A14 24 A34 A43 1516 A64 A73 4 A92 A101 1 A121 43 A143 A152 2 A172 1 A191 A201 1 401 | A14 18 A32 A43 1473 A61 A72 3 A94 A101 4 A121 39 A143 A152 1 A173 1 A192 A201 1 402 | A12 18 A34 A49 1887 A65 A73 4 A94 A101 4 A121 28 A141 A152 2 A173 1 A191 A201 1 403 | A14 24 A33 A49 8648 A61 A72 2 A93 A101 2 A123 27 A141 A152 2 A173 1 A192 A201 2 404 | A14 14 A33 A40 802 A61 A73 4 A93 A101 2 A123 27 A143 A152 2 A172 1 A191 A201 1 405 | A12 18 A33 A40 2899 A65 A75 4 A93 A101 4 A123 43 A143 A152 1 A173 2 A191 A201 1 406 | A12 24 A32 A43 2039 A61 A72 1 A94 A101 1 A122 22 A143 A152 1 A173 1 A192 A201 2 407 | A14 24 A34 A41 2197 A65 A74 4 A93 A101 4 A123 43 A143 A152 2 A173 2 A192 A201 1 408 | A11 15 A32 A43 1053 A61 A72 4 A94 A101 2 A121 27 A143 A152 1 A173 1 A191 A202 1 409 | A14 24 A32 A43 3235 A63 A75 3 A91 A101 2 A123 26 A143 A152 1 A174 1 A192 A201 1 410 | A13 12 A34 A40 939 A63 A74 4 A94 A101 2 A121 28 A143 A152 3 A173 1 A192 A201 2 411 | A12 24 A32 A43 1967 A61 A75 4 A92 A101 4 A123 20 A143 A152 1 A173 1 A192 A201 1 412 | A14 33 A34 A41 7253 A61 A74 3 A93 A101 2 A123 35 A143 A152 2 A174 1 A192 A201 1 413 | A14 12 A34 A49 2292 A61 A71 4 A93 A101 2 A123 42 A142 A152 2 A174 1 A192 A201 2 414 | A14 10 A32 A40 1597 A63 A73 3 A93 A101 2 A124 40 A143 A151 1 A172 2 A191 A202 1 415 | A11 24 A32 A40 1381 A65 A73 4 A92 A101 2 A122 35 A143 A152 1 A173 1 A191 A201 2 416 | A14 36 A34 A41 5842 A61 A75 2 A93 A101 2 A122 35 A143 A152 2 A173 2 A192 A201 1 417 | A11 12 A32 A40 2579 A61 A72 4 A93 A101 1 A121 33 A143 A152 1 A172 2 A191 A201 2 418 | A11 18 A33 A46 8471 A65 A73 1 A92 A101 2 A123 23 A143 A151 2 A173 1 A192 A201 1 419 | A14 21 A32 A40 2782 A63 A74 1 A92 A101 2 A123 31 A141 A152 1 A174 1 A191 A201 1 420 | A12 18 A32 A40 1042 A65 A73 4 A92 A101 2 A122 33 A143 A152 1 A173 1 A191 A201 2 421 | A14 15 A32 A40 3186 A64 A74 2 A92 A101 3 A123 20 A143 A151 1 A173 1 A191 A201 1 422 | A12 12 A32 A41 2028 A65 A73 4 A93 A101 2 A123 30 A143 A152 1 A173 1 A191 A201 1 423 | A12 12 A34 A40 958 A61 A74 2 A93 A101 3 A121 47 A143 A152 2 A172 2 A191 A201 1 424 | A14 21 A33 A42 1591 A62 A74 4 A93 A101 3 A121 34 A143 A152 2 A174 1 A191 A201 1 425 | A12 12 A32 A42 2762 A65 A75 1 A92 A101 2 A122 25 A141 A152 1 A173 1 A192 A201 2 426 | A12 18 A32 A41 2779 A61 A73 1 A94 A101 3 A123 21 A143 A151 1 A173 1 A192 A201 1 427 | A14 28 A34 A43 2743 A61 A75 4 A93 A101 2 A123 29 A143 A152 2 A173 1 A191 A201 1 428 | A14 18 A34 A43 1149 A64 A73 4 A93 A101 3 A121 46 A143 A152 2 A173 1 A191 A201 1 429 | A14 9 A32 A42 1313 A61 A75 1 A93 A101 4 A123 20 A143 A152 1 A173 1 A191 A201 1 430 | A11 18 A34 A45 1190 A61 A71 2 A92 A101 4 A124 55 A143 A153 3 A171 2 A191 A201 2 431 | A14 5 A32 A49 3448 A61 A74 1 A93 A101 4 A121 74 A143 A152 1 A172 1 A191 A201 1 432 | A12 24 A32 A410 11328 A61 A73 2 A93 A102 3 A123 29 A141 A152 2 A174 1 A192 A201 2 433 | A11 6 A34 A42 1872 A61 A71 4 A93 A101 4 A124 36 A143 A153 3 A174 1 A192 A201 1 434 | A14 24 A34 A45 2058 A61 A73 4 A91 A101 2 A121 33 A143 A152 2 A173 1 A192 A201 1 435 | A11 9 A32 A42 2136 A61 A73 3 A93 A101 2 A121 25 A143 A152 1 A173 1 A191 A201 1 436 | A12 12 A32 A43 1484 A65 A73 2 A94 A101 1 A121 25 A143 A152 1 A173 1 A192 A201 2 437 | A14 6 A32 A45 660 A63 A74 2 A94 A101 4 A121 23 A143 A151 1 A172 1 A191 A201 1 438 | A14 24 A34 A40 1287 A64 A75 4 A92 A101 4 A121 37 A143 A152 2 A173 1 A192 A201 1 439 | A11 42 A34 A45 3394 A61 A71 4 A93 A102 4 A123 65 A143 A152 2 A171 1 A191 A201 1 440 | A13 12 A31 A49 609 A61 A72 4 A92 A101 1 A121 26 A143 A152 1 A171 1 A191 A201 2 441 | A14 12 A32 A40 1884 A61 A75 4 A93 A101 4 A123 39 A143 A152 1 A174 1 A192 A201 1 442 | A11 12 A32 A42 1620 A61 A73 2 A92 A102 3 A122 30 A143 A152 1 A173 1 A191 A201 1 443 | A12 20 A33 A410 2629 A61 A73 2 A93 A101 3 A123 29 A141 A152 2 A173 1 A192 A201 1 444 | A14 12 A32 A46 719 A61 A75 4 A93 A101 4 A123 41 A141 A152 1 A172 2 A191 A201 2 445 | A12 48 A34 A42 5096 A61 A73 2 A92 A101 3 A123 30 A143 A152 1 A174 1 A192 A201 2 446 | A14 9 A34 A46 1244 A65 A75 4 A92 A101 4 A122 41 A143 A151 2 A172 1 A191 A201 1 447 | A11 36 A32 A40 1842 A61 A72 4 A92 A101 4 A123 34 A143 A152 1 A173 1 A192 A201 2 448 | A12 7 A32 A43 2576 A61 A73 2 A93 A103 2 A121 35 A143 A152 1 A173 1 A191 A201 1 449 | A13 12 A32 A42 1424 A65 A75 3 A92 A101 4 A121 55 A143 A152 1 A174 1 A192 A201 1 450 | A12 15 A33 A45 1512 A64 A73 3 A94 A101 3 A122 61 A142 A152 2 A173 1 A191 A201 2 451 | A14 36 A34 A41 11054 A65 A73 4 A93 A101 2 A123 30 A143 A152 1 A174 1 A192 A201 1 452 | A14 6 A32 A43 518 A61 A73 3 A92 A101 1 A121 29 A143 A152 1 A173 1 A191 A201 1 453 | A14 12 A30 A42 2759 A61 A75 2 A93 A101 4 A122 34 A143 A152 2 A173 1 A191 A201 1 454 | A14 24 A32 A41 2670 A61 A75 4 A93 A101 4 A123 35 A143 A152 1 A174 1 A192 A201 1 455 | A11 24 A32 A40 4817 A61 A74 2 A93 A102 3 A122 31 A143 A152 1 A173 1 A192 A201 2 456 | A14 24 A32 A41 2679 A61 A72 4 A92 A101 1 A124 29 A143 A152 1 A174 1 A192 A201 1 457 | A11 11 A34 A40 3905 A61 A73 2 A93 A101 2 A121 36 A143 A151 2 A173 2 A191 A201 1 458 | A11 12 A32 A41 3386 A61 A75 3 A93 A101 4 A124 35 A143 A153 1 A173 1 A192 A201 2 459 | A11 6 A32 A44 343 A61 A72 4 A92 A101 1 A121 27 A143 A152 1 A173 1 A191 A201 1 460 | A14 18 A32 A43 4594 A61 A72 3 A93 A101 2 A123 32 A143 A152 1 A173 1 A192 A201 1 461 | A11 36 A32 A42 3620 A61 A73 1 A93 A103 2 A122 37 A143 A152 1 A173 2 A191 A201 1 462 | A11 15 A32 A40 1721 A61 A72 2 A93 A101 3 A121 36 A143 A152 1 A173 1 A191 A201 1 463 | A12 12 A32 A42 3017 A61 A72 3 A92 A101 1 A121 34 A143 A151 1 A174 1 A191 A201 1 464 | A12 12 A32 A48 754 A65 A75 4 A93 A101 4 A122 38 A143 A152 2 A173 1 A191 A201 1 465 | A14 18 A32 A49 1950 A61 A74 4 A93 A101 1 A123 34 A142 A152 2 A173 1 A192 A201 1 466 | A11 24 A32 A41 2924 A61 A73 3 A93 A103 4 A124 63 A141 A152 1 A173 2 A192 A201 1 467 | A11 24 A33 A43 1659 A61 A72 4 A92 A101 2 A123 29 A143 A151 1 A172 1 A192 A201 2 468 | A14 48 A33 A43 7238 A65 A75 3 A93 A101 3 A123 32 A141 A152 2 A173 2 A191 A201 1 469 | A14 33 A33 A49 2764 A61 A73 2 A92 A101 2 A123 26 A143 A152 2 A173 1 A192 A201 1 470 | A14 24 A33 A41 4679 A61 A74 3 A93 A101 3 A123 35 A143 A152 2 A172 1 A192 A201 1 471 | A12 24 A32 A43 3092 A62 A72 3 A94 A101 2 A123 22 A143 A151 1 A173 1 A192 A201 2 472 | A11 6 A32 A46 448 A61 A72 4 A92 A101 4 A122 23 A143 A152 1 A173 1 A191 A201 2 473 | A11 9 A32 A40 654 A61 A73 4 A93 A101 3 A123 28 A143 A152 1 A172 1 A191 A201 2 474 | A14 6 A32 A48 1238 A65 A71 4 A93 A101 4 A122 36 A143 A152 1 A174 2 A192 A201 1 475 | A12 18 A34 A43 1245 A61 A73 4 A94 A101 2 A123 33 A143 A152 1 A173 1 A191 A201 2 476 | A11 18 A30 A42 3114 A61 A72 1 A92 A101 4 A122 26 A143 A151 1 A173 1 A191 A201 2 477 | A14 39 A32 A41 2569 A63 A73 4 A93 A101 4 A123 24 A143 A152 1 A173 1 A191 A201 1 478 | A13 24 A32 A43 5152 A61 A74 4 A93 A101 2 A123 25 A141 A152 1 A173 1 A191 A201 1 479 | A12 12 A32 A49 1037 A62 A74 3 A93 A101 4 A121 39 A143 A152 1 A172 1 A191 A201 1 480 | A11 15 A34 A42 1478 A61 A75 4 A93 A101 4 A123 44 A143 A152 2 A173 2 A192 A201 1 481 | A12 12 A34 A43 3573 A61 A73 1 A92 A101 1 A121 23 A143 A152 1 A172 1 A191 A201 1 482 | A12 24 A32 A40 1201 A61 A72 4 A93 A101 1 A122 26 A143 A152 1 A173 1 A191 A201 1 483 | A11 30 A32 A42 3622 A64 A75 4 A92 A101 4 A122 57 A143 A151 2 A173 1 A192 A201 1 484 | A14 15 A33 A42 960 A64 A74 3 A92 A101 2 A122 30 A143 A152 2 A173 1 A191 A201 1 485 | A14 12 A34 A40 1163 A63 A73 4 A93 A101 4 A121 44 A143 A152 1 A173 1 A192 A201 1 486 | A12 6 A33 A40 1209 A61 A71 4 A93 A101 4 A122 47 A143 A152 1 A174 1 A192 A201 2 487 | A14 12 A32 A43 3077 A61 A73 2 A93 A101 4 A123 52 A143 A152 1 A173 1 A192 A201 1 488 | A14 24 A32 A40 3757 A61 A75 4 A92 A102 4 A124 62 A143 A153 1 A173 1 A192 A201 1 489 | A14 10 A32 A40 1418 A62 A73 3 A93 A101 2 A121 35 A143 A151 1 A172 1 A191 A202 1 490 | A14 6 A32 A40 3518 A61 A73 2 A93 A103 3 A122 26 A143 A151 1 A173 1 A191 A201 1 491 | A14 12 A34 A43 1934 A61 A75 2 A93 A101 2 A124 26 A143 A152 2 A173 1 A191 A201 1 492 | A12 27 A30 A49 8318 A61 A75 2 A92 A101 4 A124 42 A143 A153 2 A174 1 A192 A201 2 493 | A14 6 A34 A43 1237 A62 A73 1 A92 A101 1 A122 27 A143 A152 2 A173 1 A191 A201 1 494 | A12 6 A32 A43 368 A65 A75 4 A93 A101 4 A122 38 A143 A152 1 A173 1 A191 A201 1 495 | A11 12 A34 A40 2122 A61 A73 3 A93 A101 2 A121 39 A143 A151 2 A172 2 A191 A202 1 496 | A11 24 A32 A42 2996 A65 A73 2 A94 A101 4 A123 20 A143 A152 1 A173 1 A191 A201 2 497 | A12 36 A32 A42 9034 A62 A72 4 A93 A102 1 A124 29 A143 A151 1 A174 1 A192 A201 2 498 | A14 24 A34 A42 1585 A61 A74 4 A93 A101 3 A122 40 A143 A152 2 A173 1 A191 A201 1 499 | A12 18 A32 A43 1301 A61 A75 4 A94 A103 2 A121 32 A143 A152 1 A172 1 A191 A201 1 500 | A13 6 A34 A40 1323 A62 A75 2 A91 A101 4 A123 28 A143 A152 2 A173 2 A192 A201 1 501 | A11 24 A32 A40 3123 A61 A72 4 A92 A101 1 A122 27 A143 A152 1 A173 1 A191 A201 2 502 | A11 36 A32 A41 5493 A61 A75 2 A93 A101 4 A124 42 A143 A153 1 A173 2 A191 A201 1 503 | A13 9 A32 A43 1126 A62 A75 2 A91 A101 4 A121 49 A143 A152 1 A173 1 A191 A201 1 504 | A12 24 A34 A43 1216 A62 A72 4 A93 A101 4 A124 38 A141 A152 2 A173 2 A191 A201 2 505 | A11 24 A32 A40 1207 A61 A72 4 A92 A101 4 A122 24 A143 A151 1 A173 1 A191 A201 2 506 | A14 10 A32 A40 1309 A65 A73 4 A93 A103 4 A122 27 A143 A152 1 A172 1 A191 A201 2 507 | A13 15 A34 A41 2360 A63 A73 2 A93 A101 2 A123 36 A143 A152 1 A173 1 A192 A201 1 508 | A12 15 A31 A40 6850 A62 A71 1 A93 A101 2 A122 34 A143 A152 1 A174 2 A192 A201 2 509 | A14 24 A32 A43 1413 A61 A73 4 A94 A101 2 A122 28 A143 A152 1 A173 1 A191 A201 1 510 | A14 39 A32 A41 8588 A62 A75 4 A93 A101 2 A123 45 A143 A152 1 A174 1 A192 A201 1 511 | A11 12 A32 A40 759 A61 A74 4 A93 A101 2 A121 26 A143 A152 1 A173 1 A191 A201 2 512 | A14 36 A32 A41 4686 A61 A73 2 A93 A101 2 A124 32 A143 A153 1 A174 1 A192 A201 1 513 | A13 15 A32 A49 2687 A61 A74 2 A93 A101 4 A122 26 A143 A151 1 A173 1 A192 A201 1 514 | A12 12 A33 A43 585 A61 A73 4 A94 A102 4 A121 20 A143 A151 2 A173 1 A191 A201 1 515 | A14 24 A32 A40 2255 A65 A72 4 A93 A101 1 A122 54 A143 A152 1 A173 1 A191 A201 1 516 | A11 6 A34 A40 609 A61 A74 4 A92 A101 3 A122 37 A143 A152 2 A173 1 A191 A202 1 517 | A11 6 A34 A40 1361 A61 A72 2 A93 A101 4 A121 40 A143 A152 1 A172 2 A191 A202 1 518 | A14 36 A34 A42 7127 A61 A72 2 A92 A101 4 A122 23 A143 A151 2 A173 1 A192 A201 2 519 | A11 6 A32 A40 1203 A62 A75 3 A93 A101 2 A122 43 A143 A152 1 A173 1 A192 A201 1 520 | A14 6 A34 A43 700 A65 A75 4 A93 A101 4 A124 36 A143 A153 2 A173 1 A191 A201 1 521 | A14 24 A34 A45 5507 A61 A75 3 A93 A101 4 A124 44 A143 A153 2 A173 1 A191 A201 1 522 | A11 18 A32 A43 3190 A61 A73 2 A92 A101 2 A121 24 A143 A152 1 A173 1 A191 A201 2 523 | A11 48 A30 A42 7119 A61 A73 3 A93 A101 4 A124 53 A143 A153 2 A173 2 A191 A201 2 524 | A14 24 A32 A41 3488 A62 A74 3 A92 A101 4 A123 23 A143 A152 1 A173 1 A191 A201 1 525 | A12 18 A32 A43 1113 A61 A73 4 A92 A103 4 A121 26 A143 A152 1 A172 2 A191 A201 1 526 | A12 26 A32 A41 7966 A61 A72 2 A93 A101 3 A123 30 A143 A152 2 A173 1 A191 A201 1 527 | A14 15 A34 A46 1532 A62 A73 4 A92 A101 3 A123 31 A143 A152 1 A173 1 A191 A201 1 528 | A14 4 A34 A43 1503 A61 A74 2 A93 A101 1 A121 42 A143 A152 2 A172 2 A191 A201 1 529 | A11 36 A32 A43 2302 A61 A73 4 A91 A101 4 A123 31 A143 A151 1 A173 1 A191 A201 2 530 | A11 6 A32 A40 662 A61 A72 3 A93 A101 4 A121 41 A143 A152 1 A172 2 A192 A201 1 531 | A12 36 A32 A46 2273 A61 A74 3 A93 A101 1 A123 32 A143 A152 2 A173 2 A191 A201 1 532 | A12 15 A32 A40 2631 A62 A73 2 A92 A101 4 A123 28 A143 A151 2 A173 1 A192 A201 2 533 | A14 12 A33 A41 1503 A61 A73 4 A94 A101 4 A121 41 A143 A151 1 A173 1 A191 A201 1 534 | A14 24 A32 A43 1311 A62 A74 4 A94 A101 3 A122 26 A143 A152 1 A173 1 A192 A201 1 535 | A14 24 A32 A43 3105 A65 A72 4 A93 A101 2 A123 25 A143 A152 2 A173 1 A191 A201 1 536 | A13 21 A34 A46 2319 A61 A72 2 A91 A101 1 A123 33 A143 A151 1 A173 1 A191 A201 2 537 | A11 6 A32 A40 1374 A65 A71 4 A92 A101 3 A122 75 A143 A152 1 A174 1 A192 A201 1 538 | A12 18 A34 A42 3612 A61 A75 3 A92 A101 4 A122 37 A143 A152 1 A173 1 A192 A201 1 539 | A11 48 A32 A40 7763 A61 A75 4 A93 A101 4 A124 42 A141 A153 1 A174 1 A191 A201 2 540 | A13 18 A32 A42 3049 A61 A72 1 A92 A101 1 A122 45 A142 A152 1 A172 1 A191 A201 1 541 | A12 12 A32 A43 1534 A61 A72 1 A94 A101 1 A121 23 A143 A151 1 A173 1 A191 A201 2 542 | A14 24 A33 A40 2032 A61 A75 4 A93 A101 4 A124 60 A143 A153 2 A173 1 A192 A201 1 543 | A11 30 A32 A42 6350 A65 A75 4 A93 A101 4 A122 31 A143 A152 1 A173 1 A191 A201 2 544 | A13 18 A32 A42 2864 A61 A73 2 A93 A101 1 A121 34 A143 A152 1 A172 2 A191 A201 2 545 | A14 12 A34 A40 1255 A61 A75 4 A93 A101 4 A121 61 A143 A152 2 A172 1 A191 A201 1 546 | A11 24 A33 A40 1333 A61 A71 4 A93 A101 2 A121 43 A143 A153 2 A173 2 A191 A201 2 547 | A14 24 A34 A40 2022 A61 A73 4 A92 A101 4 A123 37 A143 A152 1 A173 1 A192 A201 1 548 | A14 24 A32 A43 1552 A61 A74 3 A93 A101 1 A123 32 A141 A152 1 A173 2 A191 A201 1 549 | A11 12 A31 A43 626 A61 A73 4 A92 A101 4 A121 24 A141 A152 1 A172 1 A191 A201 2 550 | A14 48 A34 A41 8858 A65 A74 2 A93 A101 1 A124 35 A143 A153 2 A173 1 A192 A201 1 551 | A14 12 A34 A45 996 A65 A74 4 A92 A101 4 A121 23 A143 A152 2 A173 1 A191 A201 1 552 | A14 6 A31 A43 1750 A63 A75 2 A93 A101 4 A122 45 A141 A152 1 A172 2 A191 A201 1 553 | A11 48 A32 A43 6999 A61 A74 1 A94 A103 1 A121 34 A143 A152 2 A173 1 A192 A201 2 554 | A12 12 A34 A40 1995 A62 A72 4 A93 A101 1 A123 27 A143 A152 1 A173 1 A191 A201 1 555 | A12 9 A32 A46 1199 A61 A74 4 A92 A101 4 A122 67 A143 A152 2 A174 1 A192 A201 1 556 | A12 12 A32 A43 1331 A61 A72 2 A93 A101 1 A123 22 A142 A152 1 A173 1 A191 A201 2 557 | A12 18 A30 A40 2278 A62 A72 3 A92 A101 3 A123 28 A143 A152 2 A173 1 A191 A201 2 558 | A14 21 A30 A40 5003 A65 A73 1 A92 A101 4 A122 29 A141 A152 2 A173 1 A192 A201 2 559 | A11 24 A31 A42 3552 A61 A74 3 A93 A101 4 A123 27 A141 A152 1 A173 1 A191 A201 2 560 | A12 18 A34 A42 1928 A61 A72 2 A93 A101 2 A121 31 A143 A152 2 A172 1 A191 A201 2 561 | A11 24 A32 A41 2964 A65 A75 4 A93 A101 4 A124 49 A141 A153 1 A173 2 A192 A201 1 562 | A11 24 A31 A43 1546 A61 A74 4 A93 A103 4 A123 24 A141 A151 1 A172 1 A191 A201 2 563 | A13 6 A33 A43 683 A61 A72 2 A92 A101 1 A122 29 A141 A152 1 A173 1 A191 A201 1 564 | A12 36 A32 A40 12389 A65 A73 1 A93 A101 4 A124 37 A143 A153 1 A173 1 A192 A201 2 565 | A12 24 A33 A49 4712 A65 A73 4 A93 A101 2 A122 37 A141 A152 2 A174 1 A192 A201 1 566 | A12 24 A33 A43 1553 A62 A74 3 A92 A101 2 A122 23 A143 A151 2 A173 1 A192 A201 1 567 | A11 12 A32 A40 1372 A61 A74 2 A91 A101 3 A123 36 A143 A152 1 A173 1 A191 A201 2 568 | A14 24 A34 A43 2578 A64 A75 2 A93 A101 2 A123 34 A143 A152 1 A173 1 A191 A201 1 569 | A12 48 A32 A43 3979 A65 A74 4 A93 A101 1 A123 41 A143 A152 2 A173 2 A192 A201 1 570 | A11 48 A32 A43 6758 A61 A73 3 A92 A101 2 A123 31 A143 A152 1 A173 1 A192 A201 2 571 | A11 24 A32 A42 3234 A61 A72 4 A92 A101 4 A121 23 A143 A151 1 A172 1 A192 A201 2 572 | A14 30 A34 A43 5954 A61 A74 3 A93 A102 2 A123 38 A143 A152 1 A173 1 A191 A201 1 573 | A14 24 A32 A41 5433 A65 A71 2 A92 A101 4 A122 26 A143 A151 1 A174 1 A192 A201 1 574 | A11 15 A32 A49 806 A61 A73 4 A92 A101 4 A122 22 A143 A152 1 A172 1 A191 A201 1 575 | A12 9 A32 A43 1082 A61 A75 4 A93 A101 4 A123 27 A143 A152 2 A172 1 A191 A201 1 576 | A14 15 A34 A42 2788 A61 A74 2 A92 A102 3 A123 24 A141 A152 2 A173 1 A191 A201 1 577 | A12 12 A32 A43 2930 A61 A74 2 A92 A101 1 A121 27 A143 A152 1 A173 1 A191 A201 1 578 | A14 24 A34 A46 1927 A65 A73 3 A92 A101 2 A123 33 A143 A152 2 A173 1 A192 A201 1 579 | A12 36 A34 A40 2820 A61 A72 4 A91 A101 4 A123 27 A143 A152 2 A173 1 A191 A201 2 580 | A14 24 A32 A48 937 A61 A72 4 A94 A101 3 A123 27 A143 A152 2 A172 1 A191 A201 1 581 | A12 18 A34 A40 1056 A61 A75 3 A93 A103 3 A121 30 A141 A152 2 A173 1 A191 A201 2 582 | A12 12 A34 A40 3124 A61 A72 1 A93 A101 3 A121 49 A141 A152 2 A172 2 A191 A201 1 583 | A14 9 A32 A42 1388 A61 A73 4 A92 A101 2 A121 26 A143 A151 1 A173 1 A191 A201 1 584 | A12 36 A32 A45 2384 A61 A72 4 A93 A101 1 A124 33 A143 A151 1 A172 1 A191 A201 2 585 | A14 12 A32 A40 2133 A65 A75 4 A92 A101 4 A124 52 A143 A153 1 A174 1 A192 A201 1 586 | A11 18 A32 A42 2039 A61 A73 1 A92 A101 4 A121 20 A141 A151 1 A173 1 A191 A201 2 587 | A11 9 A34 A40 2799 A61 A73 2 A93 A101 2 A121 36 A143 A151 2 A173 2 A191 A201 1 588 | A11 12 A32 A42 1289 A61 A73 4 A93 A103 1 A122 21 A143 A152 1 A172 1 A191 A201 1 589 | A11 18 A32 A44 1217 A61 A73 4 A94 A101 3 A121 47 A143 A152 1 A172 1 A192 A201 2 590 | A11 12 A34 A42 2246 A61 A75 3 A93 A101 3 A122 60 A143 A152 2 A173 1 A191 A201 2 591 | A11 12 A34 A43 385 A61 A74 4 A92 A101 3 A121 58 A143 A152 4 A172 1 A192 A201 1 592 | A12 24 A33 A40 1965 A65 A73 4 A92 A101 4 A123 42 A143 A151 2 A173 1 A192 A201 1 593 | A14 21 A32 A49 1572 A64 A75 4 A92 A101 4 A121 36 A141 A152 1 A172 1 A191 A201 1 594 | A12 24 A32 A40 2718 A61 A73 3 A92 A101 4 A122 20 A143 A151 1 A172 1 A192 A201 2 595 | A11 24 A31 A410 1358 A65 A75 4 A93 A101 3 A123 40 A142 A152 1 A174 1 A192 A201 2 596 | A12 6 A31 A40 931 A62 A72 1 A92 A101 1 A122 32 A142 A152 1 A172 1 A191 A201 2 597 | A11 24 A32 A40 1442 A61 A74 4 A92 A101 4 A123 23 A143 A151 2 A173 1 A191 A201 2 598 | A12 24 A30 A49 4241 A61 A73 1 A93 A101 4 A121 36 A143 A152 3 A172 1 A192 A201 2 599 | A14 18 A34 A40 2775 A61 A74 2 A93 A101 2 A122 31 A141 A152 2 A173 1 A191 A201 2 600 | A14 24 A33 A49 3863 A61 A73 1 A93 A101 2 A124 32 A143 A153 1 A173 1 A191 A201 1 601 | A12 7 A32 A43 2329 A61 A72 1 A92 A103 1 A121 45 A143 A152 1 A173 1 A191 A201 1 602 | A12 9 A32 A42 918 A61 A73 4 A92 A101 1 A122 30 A143 A152 1 A173 1 A191 A201 2 603 | A12 24 A31 A46 1837 A61 A74 4 A92 A101 4 A124 34 A141 A153 1 A172 1 A191 A201 2 604 | A14 36 A32 A42 3349 A61 A73 4 A92 A101 2 A123 28 A143 A152 1 A174 1 A192 A201 2 605 | A13 10 A32 A42 1275 A61 A72 4 A92 A101 2 A122 23 A143 A152 1 A173 1 A191 A201 1 606 | A11 24 A31 A42 2828 A63 A73 4 A93 A101 4 A121 22 A142 A152 1 A173 1 A192 A201 1 607 | A14 24 A34 A49 4526 A61 A73 3 A93 A101 2 A121 74 A143 A152 1 A174 1 A192 A201 1 608 | A12 36 A32 A43 2671 A62 A73 4 A92 A102 4 A124 50 A143 A153 1 A173 1 A191 A201 2 609 | A14 18 A32 A43 2051 A61 A72 4 A93 A101 1 A121 33 A143 A152 1 A173 1 A191 A201 1 610 | A14 15 A32 A41 1300 A65 A75 4 A93 A101 4 A124 45 A141 A153 1 A173 2 A191 A201 1 611 | A11 12 A32 A44 741 A62 A71 4 A92 A101 3 A122 22 A143 A152 1 A173 1 A191 A201 2 612 | A13 10 A32 A40 1240 A62 A75 1 A92 A101 4 A124 48 A143 A153 1 A172 2 A191 A201 2 613 | A11 21 A32 A43 3357 A64 A72 4 A92 A101 2 A123 29 A141 A152 1 A173 1 A191 A201 1 614 | A11 24 A31 A41 3632 A61 A73 1 A92 A103 4 A123 22 A141 A151 1 A173 1 A191 A202 1 615 | A14 18 A33 A42 1808 A61 A74 4 A92 A101 1 A121 22 A143 A152 1 A173 1 A191 A201 2 616 | A12 48 A30 A49 12204 A65 A73 2 A93 A101 2 A123 48 A141 A152 1 A174 1 A192 A201 1 617 | A12 60 A33 A43 9157 A65 A73 2 A93 A101 2 A124 27 A143 A153 1 A174 1 A191 A201 1 618 | A11 6 A34 A40 3676 A61 A73 1 A93 A101 3 A121 37 A143 A151 3 A173 2 A191 A201 1 619 | A12 30 A32 A42 3441 A62 A73 2 A92 A102 4 A123 21 A143 A151 1 A173 1 A191 A201 2 620 | A14 12 A32 A40 640 A61 A73 4 A91 A101 2 A121 49 A143 A152 1 A172 1 A191 A201 1 621 | A12 21 A34 A49 3652 A61 A74 2 A93 A101 3 A122 27 A143 A152 2 A173 1 A191 A201 1 622 | A14 18 A34 A40 1530 A61 A73 3 A93 A101 2 A122 32 A141 A152 2 A173 1 A191 A201 2 623 | A14 48 A32 A49 3914 A65 A73 4 A91 A101 2 A121 38 A141 A152 1 A173 1 A191 A201 2 624 | A11 12 A32 A42 1858 A61 A72 4 A92 A101 1 A123 22 A143 A151 1 A173 1 A191 A201 1 625 | A11 18 A32 A43 2600 A61 A73 4 A93 A101 4 A124 65 A143 A153 2 A173 1 A191 A201 2 626 | A14 15 A32 A43 1979 A65 A75 4 A93 A101 2 A123 35 A143 A152 1 A173 1 A191 A201 1 627 | A13 6 A32 A42 2116 A61 A73 2 A93 A101 2 A121 41 A143 A152 1 A173 1 A192 A201 1 628 | A12 9 A31 A40 1437 A62 A74 2 A93 A101 3 A124 29 A143 A152 1 A173 1 A191 A201 2 629 | A14 42 A34 A42 4042 A63 A73 4 A93 A101 4 A121 36 A143 A152 2 A173 1 A192 A201 1 630 | A14 9 A32 A46 3832 A65 A75 1 A93 A101 4 A121 64 A143 A152 1 A172 1 A191 A201 1 631 | A11 24 A32 A43 3660 A61 A73 2 A92 A101 4 A123 28 A143 A152 1 A173 1 A191 A201 1 632 | A11 18 A31 A42 1553 A61 A73 4 A93 A101 3 A123 44 A141 A152 1 A173 1 A191 A201 2 633 | A12 15 A32 A43 1444 A65 A72 4 A93 A101 1 A122 23 A143 A152 1 A173 1 A191 A201 1 634 | A14 9 A32 A42 1980 A61 A72 2 A92 A102 2 A123 19 A143 A151 2 A173 1 A191 A201 2 635 | A12 24 A32 A40 1355 A61 A72 3 A92 A101 4 A123 25 A143 A152 1 A172 1 A192 A201 2 636 | A14 12 A32 A46 1393 A61 A75 4 A93 A101 4 A122 47 A141 A152 3 A173 2 A192 A201 1 637 | A14 24 A32 A43 1376 A63 A74 4 A92 A101 1 A123 28 A143 A152 1 A173 1 A191 A201 1 638 | A14 60 A33 A43 15653 A61 A74 2 A93 A101 4 A123 21 A143 A152 2 A173 1 A192 A201 1 639 | A14 12 A32 A43 1493 A61 A72 4 A92 A101 3 A123 34 A143 A152 1 A173 2 A191 A201 1 640 | A11 42 A33 A43 4370 A61 A74 3 A93 A101 2 A122 26 A141 A152 2 A173 2 A192 A201 2 641 | A11 18 A32 A46 750 A61 A71 4 A92 A101 1 A121 27 A143 A152 1 A171 1 A191 A201 2 642 | A12 15 A32 A45 1308 A61 A75 4 A93 A101 4 A123 38 A143 A152 2 A172 1 A191 A201 1 643 | A14 15 A32 A46 4623 A62 A73 3 A93 A101 2 A122 40 A143 A152 1 A174 1 A192 A201 2 644 | A14 24 A34 A43 1851 A61 A74 4 A94 A103 2 A123 33 A143 A152 2 A173 1 A192 A201 1 645 | A11 18 A34 A43 1880 A61 A74 4 A94 A101 1 A122 32 A143 A152 2 A174 1 A192 A201 1 646 | A14 36 A33 A49 7980 A65 A72 4 A93 A101 4 A123 27 A143 A151 2 A173 1 A192 A201 2 647 | A11 30 A30 A42 4583 A61 A73 2 A91 A103 2 A121 32 A143 A152 2 A173 1 A191 A201 1 648 | A14 12 A32 A40 1386 A63 A73 2 A92 A101 2 A122 26 A143 A152 1 A173 1 A191 A201 2 649 | A13 24 A32 A40 947 A61 A74 4 A93 A101 3 A124 38 A141 A153 1 A173 2 A191 A201 2 650 | A11 12 A32 A46 684 A61 A73 4 A93 A101 4 A123 40 A143 A151 1 A172 2 A191 A201 2 651 | A11 48 A32 A46 7476 A61 A74 4 A93 A101 1 A124 50 A143 A153 1 A174 1 A192 A201 1 652 | A12 12 A32 A42 1922 A61 A73 4 A93 A101 2 A122 37 A143 A152 1 A172 1 A191 A201 2 653 | A11 24 A32 A40 2303 A61 A75 4 A93 A102 1 A121 45 A143 A152 1 A173 1 A191 A201 2 654 | A12 36 A33 A40 8086 A62 A75 2 A93 A101 4 A123 42 A143 A152 4 A174 1 A192 A201 2 655 | A14 24 A34 A41 2346 A61 A74 4 A93 A101 3 A123 35 A143 A152 2 A173 1 A192 A201 1 656 | A11 14 A32 A40 3973 A61 A71 1 A93 A101 4 A124 22 A143 A153 1 A173 1 A191 A201 1 657 | A12 12 A32 A40 888 A61 A75 4 A93 A101 4 A123 41 A141 A152 1 A172 2 A191 A201 2 658 | A14 48 A32 A43 10222 A65 A74 4 A93 A101 3 A123 37 A142 A152 1 A173 1 A192 A201 1 659 | A12 30 A30 A49 4221 A61 A73 2 A92 A101 1 A123 28 A143 A152 2 A173 1 A191 A201 1 660 | A12 18 A34 A42 6361 A61 A75 2 A93 A101 1 A124 41 A143 A152 1 A173 1 A192 A201 1 661 | A13 12 A32 A43 1297 A61 A73 3 A94 A101 4 A121 23 A143 A151 1 A173 1 A191 A201 1 662 | A11 12 A32 A40 900 A65 A73 4 A94 A101 2 A123 23 A143 A152 1 A173 1 A191 A201 2 663 | A14 21 A32 A42 2241 A61 A75 4 A93 A101 2 A121 50 A143 A152 2 A173 1 A191 A201 1 664 | A12 6 A33 A42 1050 A61 A71 4 A93 A101 1 A122 35 A142 A152 2 A174 1 A192 A201 1 665 | A13 6 A34 A46 1047 A61 A73 2 A92 A101 4 A122 50 A143 A152 1 A172 1 A191 A201 1 666 | A14 24 A34 A410 6314 A61 A71 4 A93 A102 2 A124 27 A141 A152 2 A174 1 A192 A201 1 667 | A12 30 A31 A42 3496 A64 A73 4 A93 A101 2 A123 34 A142 A152 1 A173 2 A192 A201 1 668 | A14 48 A31 A49 3609 A61 A73 1 A92 A101 1 A121 27 A142 A152 1 A173 1 A191 A201 1 669 | A11 12 A34 A40 4843 A61 A75 3 A93 A102 4 A122 43 A143 A151 2 A173 1 A192 A201 2 670 | A13 30 A34 A43 3017 A61 A75 4 A93 A101 4 A122 47 A143 A152 1 A173 1 A191 A201 1 671 | A14 24 A34 A49 4139 A62 A73 3 A93 A101 3 A122 27 A143 A152 2 A172 1 A192 A201 1 672 | A14 36 A32 A49 5742 A62 A74 2 A93 A101 2 A123 31 A143 A152 2 A173 1 A192 A201 1 673 | A14 60 A32 A40 10366 A61 A75 2 A93 A101 4 A122 42 A143 A152 1 A174 1 A192 A201 1 674 | A14 6 A34 A40 2080 A63 A73 1 A94 A101 2 A123 24 A143 A152 1 A173 1 A191 A201 1 675 | A14 21 A33 A49 2580 A63 A72 4 A93 A101 2 A121 41 A141 A152 1 A172 2 A191 A201 2 676 | A14 30 A34 A43 4530 A61 A74 4 A92 A101 4 A123 26 A143 A151 1 A174 1 A192 A201 1 677 | A14 24 A34 A42 5150 A61 A75 4 A93 A101 4 A123 33 A143 A152 1 A173 1 A192 A201 1 678 | A12 72 A32 A43 5595 A62 A73 2 A94 A101 2 A123 24 A143 A152 1 A173 1 A191 A201 2 679 | A11 24 A32 A43 2384 A61 A75 4 A93 A101 4 A121 64 A141 A151 1 A172 1 A191 A201 1 680 | A14 18 A32 A43 1453 A61 A72 3 A92 A101 1 A121 26 A143 A152 1 A173 1 A191 A201 1 681 | A14 6 A32 A46 1538 A61 A72 1 A92 A101 2 A124 56 A143 A152 1 A173 1 A191 A201 1 682 | A14 12 A32 A43 2279 A65 A73 4 A93 A101 4 A124 37 A143 A153 1 A173 1 A192 A201 1 683 | A14 15 A33 A43 1478 A61 A73 4 A94 A101 3 A121 33 A141 A152 2 A173 1 A191 A201 1 684 | A14 24 A34 A43 5103 A61 A72 3 A94 A101 3 A124 47 A143 A153 3 A173 1 A192 A201 1 685 | A12 36 A33 A49 9857 A62 A74 1 A93 A101 3 A122 31 A143 A152 2 A172 2 A192 A201 1 686 | A14 60 A32 A40 6527 A65 A73 4 A93 A101 4 A124 34 A143 A153 1 A173 2 A192 A201 1 687 | A13 10 A34 A43 1347 A65 A74 4 A93 A101 2 A122 27 A143 A152 2 A173 1 A192 A201 1 688 | A12 36 A33 A40 2862 A62 A75 4 A93 A101 3 A124 30 A143 A153 1 A173 1 A191 A201 1 689 | A14 9 A32 A43 2753 A62 A75 3 A93 A102 4 A123 35 A143 A152 1 A173 1 A192 A201 1 690 | A11 12 A32 A40 3651 A64 A73 1 A93 A101 3 A122 31 A143 A152 1 A173 2 A191 A201 1 691 | A11 15 A34 A42 975 A61 A73 2 A91 A101 3 A122 25 A143 A152 2 A173 1 A191 A201 1 692 | A12 15 A32 A45 2631 A62 A73 3 A92 A101 2 A121 25 A143 A152 1 A172 1 A191 A201 1 693 | A12 24 A32 A43 2896 A62 A72 2 A93 A101 1 A123 29 A143 A152 1 A173 1 A191 A201 1 694 | A11 6 A34 A40 4716 A65 A72 1 A93 A101 3 A121 44 A143 A152 2 A172 2 A191 A201 1 695 | A14 24 A32 A43 2284 A61 A74 4 A93 A101 2 A123 28 A143 A152 1 A173 1 A192 A201 1 696 | A14 6 A32 A41 1236 A63 A73 2 A93 A101 4 A122 50 A143 A151 1 A173 1 A191 A201 1 697 | A12 12 A32 A43 1103 A61 A74 4 A93 A103 3 A121 29 A143 A152 2 A173 1 A191 A202 1 698 | A14 12 A34 A40 926 A61 A71 1 A92 A101 2 A122 38 A143 A152 1 A171 1 A191 A201 1 699 | A14 18 A34 A43 1800 A61 A73 4 A93 A101 2 A123 24 A143 A152 2 A173 1 A191 A201 1 700 | A13 15 A32 A46 1905 A61 A75 4 A93 A101 4 A123 40 A143 A151 1 A174 1 A192 A201 1 701 | A14 12 A32 A42 1123 A63 A73 4 A92 A101 4 A123 29 A143 A151 1 A172 1 A191 A201 2 702 | A11 48 A34 A41 6331 A61 A75 4 A93 A101 4 A124 46 A143 A153 2 A173 1 A192 A201 2 703 | A13 24 A32 A43 1377 A62 A75 4 A92 A101 2 A124 47 A143 A153 1 A173 1 A192 A201 1 704 | A12 30 A33 A49 2503 A62 A75 4 A93 A101 2 A122 41 A142 A152 2 A173 1 A191 A201 1 705 | A12 27 A32 A49 2528 A61 A72 4 A92 A101 1 A122 32 A143 A152 1 A173 2 A192 A201 1 706 | A14 15 A32 A40 5324 A63 A75 1 A92 A101 4 A124 35 A143 A153 1 A173 1 A191 A201 1 707 | A12 48 A32 A40 6560 A62 A74 3 A93 A101 2 A122 24 A143 A152 1 A173 1 A191 A201 2 708 | A12 12 A30 A42 2969 A61 A72 4 A92 A101 3 A122 25 A143 A151 2 A173 1 A191 A201 2 709 | A12 9 A32 A43 1206 A61 A75 4 A92 A101 4 A121 25 A143 A152 1 A173 1 A191 A201 1 710 | A12 9 A32 A43 2118 A61 A73 2 A93 A101 2 A121 37 A143 A152 1 A172 2 A191 A201 1 711 | A14 18 A34 A43 629 A63 A75 4 A93 A101 3 A122 32 A141 A152 2 A174 1 A192 A201 1 712 | A11 6 A31 A46 1198 A61 A75 4 A92 A101 4 A124 35 A143 A153 1 A173 1 A191 A201 2 713 | A14 21 A32 A41 2476 A65 A75 4 A93 A101 4 A121 46 A143 A152 1 A174 1 A192 A201 1 714 | A11 9 A34 A43 1138 A61 A73 4 A93 A101 4 A121 25 A143 A152 2 A172 1 A191 A201 1 715 | A12 60 A32 A40 14027 A61 A74 4 A93 A101 2 A124 27 A143 A152 1 A174 1 A192 A201 2 716 | A14 30 A34 A41 7596 A65 A75 1 A93 A101 4 A123 63 A143 A152 2 A173 1 A191 A201 1 717 | A14 30 A34 A43 3077 A65 A75 3 A93 A101 2 A123 40 A143 A152 2 A173 2 A192 A201 1 718 | A14 18 A32 A43 1505 A61 A73 4 A93 A101 2 A124 32 A143 A153 1 A174 1 A192 A201 1 719 | A13 24 A34 A43 3148 A65 A73 3 A93 A101 2 A123 31 A143 A152 2 A173 1 A192 A201 1 720 | A12 20 A30 A41 6148 A62 A75 3 A94 A101 4 A123 31 A141 A152 2 A173 1 A192 A201 1 721 | A13 9 A30 A43 1337 A61 A72 4 A93 A101 2 A123 34 A143 A152 2 A174 1 A192 A201 2 722 | A12 6 A31 A46 433 A64 A72 4 A92 A101 2 A122 24 A141 A151 1 A173 2 A191 A201 2 723 | A11 12 A32 A40 1228 A61 A73 4 A92 A101 2 A121 24 A143 A152 1 A172 1 A191 A201 2 724 | A12 9 A32 A43 790 A63 A73 4 A92 A101 3 A121 66 A143 A152 1 A172 1 A191 A201 1 725 | A14 27 A32 A40 2570 A61 A73 3 A92 A101 3 A121 21 A143 A151 1 A173 1 A191 A201 2 726 | A14 6 A34 A40 250 A64 A73 2 A92 A101 2 A121 41 A141 A152 2 A172 1 A191 A201 1 727 | A14 15 A34 A43 1316 A63 A73 2 A94 A101 2 A122 47 A143 A152 2 A172 1 A191 A201 1 728 | A11 18 A32 A43 1882 A61 A73 4 A92 A101 4 A123 25 A141 A151 2 A173 1 A191 A201 2 729 | A12 48 A31 A49 6416 A61 A75 4 A92 A101 3 A124 59 A143 A151 1 A173 1 A191 A201 2 730 | A13 24 A34 A49 1275 A64 A73 2 A91 A101 4 A121 36 A143 A152 2 A173 1 A192 A201 1 731 | A12 24 A33 A43 6403 A61 A72 1 A93 A101 2 A123 33 A143 A152 1 A173 1 A191 A201 1 732 | A11 24 A32 A43 1987 A61 A73 2 A93 A101 4 A121 21 A143 A151 1 A172 2 A191 A201 2 733 | A12 8 A32 A43 760 A61 A74 4 A92 A103 2 A121 44 A143 A152 1 A172 1 A191 A201 1 734 | A14 24 A32 A41 2603 A64 A73 2 A92 A101 4 A123 28 A143 A151 1 A173 1 A192 A201 1 735 | A14 4 A34 A40 3380 A61 A74 1 A92 A101 1 A121 37 A143 A152 1 A173 2 A191 A201 1 736 | A12 36 A31 A44 3990 A65 A72 3 A92 A101 2 A124 29 A141 A152 1 A171 1 A191 A201 1 737 | A12 24 A32 A41 11560 A61 A73 1 A92 A101 4 A123 23 A143 A151 2 A174 1 A191 A201 2 738 | A11 18 A32 A40 4380 A62 A73 3 A93 A101 4 A123 35 A143 A152 1 A172 2 A192 A201 1 739 | A14 6 A34 A40 6761 A61 A74 1 A93 A101 3 A124 45 A143 A152 2 A174 2 A192 A201 1 740 | A12 30 A30 A49 4280 A62 A73 4 A92 A101 4 A123 26 A143 A151 2 A172 1 A191 A201 2 741 | A11 24 A31 A40 2325 A62 A74 2 A93 A101 3 A123 32 A141 A152 1 A173 1 A191 A201 1 742 | A12 10 A31 A43 1048 A61 A73 4 A93 A101 4 A121 23 A142 A152 1 A172 1 A191 A201 1 743 | A14 21 A32 A43 3160 A65 A75 4 A93 A101 3 A122 41 A143 A152 1 A173 1 A192 A201 1 744 | A11 24 A31 A42 2483 A63 A73 4 A93 A101 4 A121 22 A142 A152 1 A173 1 A192 A201 1 745 | A11 39 A34 A42 14179 A65 A74 4 A93 A101 4 A122 30 A143 A152 2 A174 1 A192 A201 1 746 | A11 13 A34 A49 1797 A61 A72 3 A93 A101 1 A122 28 A141 A152 2 A172 1 A191 A201 1 747 | A11 15 A32 A40 2511 A61 A71 1 A92 A101 4 A123 23 A143 A151 1 A173 1 A191 A201 1 748 | A11 12 A32 A40 1274 A61 A72 3 A92 A101 1 A121 37 A143 A152 1 A172 1 A191 A201 2 749 | A14 21 A32 A41 5248 A65 A73 1 A93 A101 3 A123 26 A143 A152 1 A173 1 A191 A201 1 750 | A14 15 A32 A41 3029 A61 A74 2 A93 A101 2 A123 33 A143 A152 1 A173 1 A191 A201 1 751 | A11 6 A32 A42 428 A61 A75 2 A92 A101 1 A122 49 A141 A152 1 A173 1 A192 A201 1 752 | A11 18 A32 A40 976 A61 A72 1 A92 A101 2 A123 23 A143 A152 1 A172 1 A191 A201 2 753 | A12 12 A32 A49 841 A62 A74 2 A92 A101 4 A121 23 A143 A151 1 A172 1 A191 A201 1 754 | A14 30 A34 A43 5771 A61 A74 4 A92 A101 2 A123 25 A143 A152 2 A173 1 A191 A201 1 755 | A14 12 A33 A45 1555 A64 A75 4 A93 A101 4 A124 55 A143 A153 2 A173 2 A191 A201 2 756 | A11 24 A32 A40 1285 A65 A74 4 A92 A101 4 A124 32 A143 A151 1 A173 1 A191 A201 2 757 | A13 6 A34 A40 1299 A61 A73 1 A93 A101 1 A121 74 A143 A152 3 A171 2 A191 A202 1 758 | A13 15 A34 A43 1271 A65 A73 3 A93 A101 4 A124 39 A143 A153 2 A173 1 A192 A201 2 759 | A14 24 A32 A40 1393 A61 A73 2 A93 A103 2 A121 31 A143 A152 1 A173 1 A192 A201 1 760 | A11 12 A34 A40 691 A61 A75 4 A93 A101 3 A122 35 A143 A152 2 A173 1 A191 A201 2 761 | A14 15 A34 A40 5045 A65 A75 1 A92 A101 4 A123 59 A143 A152 1 A173 1 A192 A201 1 762 | A11 18 A34 A42 2124 A61 A73 4 A92 A101 4 A121 24 A143 A151 2 A173 1 A191 A201 2 763 | A11 12 A32 A43 2214 A61 A73 4 A93 A101 3 A122 24 A143 A152 1 A172 1 A191 A201 1 764 | A14 21 A34 A40 12680 A65 A75 4 A93 A101 4 A124 30 A143 A153 1 A174 1 A192 A201 2 765 | A14 24 A34 A40 2463 A62 A74 4 A94 A101 3 A122 27 A143 A152 2 A173 1 A192 A201 1 766 | A12 12 A32 A43 1155 A61 A75 3 A94 A103 3 A121 40 A141 A152 2 A172 1 A191 A201 1 767 | A11 30 A32 A42 3108 A61 A72 2 A91 A101 4 A122 31 A143 A152 1 A172 1 A191 A201 2 768 | A14 10 A32 A41 2901 A65 A72 1 A92 A101 4 A121 31 A143 A151 1 A173 1 A191 A201 1 769 | A12 12 A34 A42 3617 A61 A75 1 A93 A101 4 A123 28 A143 A151 3 A173 1 A192 A201 1 770 | A14 12 A34 A43 1655 A61 A75 2 A93 A101 4 A121 63 A143 A152 2 A172 1 A192 A201 1 771 | A11 24 A32 A41 2812 A65 A75 2 A92 A101 4 A121 26 A143 A151 1 A173 1 A191 A201 1 772 | A11 36 A34 A46 8065 A61 A73 3 A92 A101 2 A124 25 A143 A152 2 A174 1 A192 A201 2 773 | A14 21 A34 A41 3275 A61 A75 1 A93 A101 4 A123 36 A143 A152 1 A174 1 A192 A201 1 774 | A14 24 A34 A43 2223 A62 A75 4 A93 A101 4 A122 52 A141 A152 2 A173 1 A191 A201 1 775 | A13 12 A34 A40 1480 A63 A71 2 A93 A101 4 A124 66 A141 A153 3 A171 1 A191 A201 1 776 | A11 24 A32 A40 1371 A65 A73 4 A92 A101 4 A121 25 A143 A151 1 A173 1 A191 A201 2 777 | A14 36 A34 A40 3535 A61 A74 4 A93 A101 4 A123 37 A143 A152 2 A173 1 A192 A201 1 778 | A11 18 A32 A43 3509 A61 A74 4 A92 A103 1 A121 25 A143 A152 1 A173 1 A191 A201 1 779 | A14 36 A34 A41 5711 A64 A75 4 A93 A101 2 A123 38 A143 A152 2 A174 1 A192 A201 1 780 | A12 18 A32 A45 3872 A61 A71 2 A92 A101 4 A123 67 A143 A152 1 A173 1 A192 A201 1 781 | A12 39 A34 A43 4933 A61 A74 2 A93 A103 2 A121 25 A143 A152 2 A173 1 A191 A201 2 782 | A14 24 A34 A40 1940 A64 A75 4 A93 A101 4 A121 60 A143 A152 1 A173 1 A192 A201 1 783 | A12 12 A30 A48 1410 A61 A73 2 A93 A101 2 A121 31 A143 A152 1 A172 1 A192 A201 1 784 | A12 12 A32 A40 836 A62 A72 4 A92 A101 2 A122 23 A141 A152 1 A172 1 A191 A201 2 785 | A12 20 A32 A41 6468 A65 A71 1 A91 A101 4 A121 60 A143 A152 1 A174 1 A192 A201 1 786 | A12 18 A32 A49 1941 A64 A73 4 A93 A101 2 A122 35 A143 A152 1 A172 1 A192 A201 1 787 | A14 22 A32 A43 2675 A63 A75 3 A93 A101 4 A123 40 A143 A152 1 A173 1 A191 A201 1 788 | A14 48 A34 A41 2751 A65 A75 4 A93 A101 3 A123 38 A143 A152 2 A173 2 A192 A201 1 789 | A12 48 A33 A46 6224 A61 A75 4 A93 A101 4 A124 50 A143 A153 1 A173 1 A191 A201 2 790 | A11 40 A34 A46 5998 A61 A73 4 A93 A101 3 A124 27 A141 A152 1 A173 1 A192 A201 2 791 | A12 21 A32 A49 1188 A61 A75 2 A92 A101 4 A122 39 A143 A152 1 A173 2 A191 A201 2 792 | A14 24 A32 A41 6313 A65 A75 3 A93 A101 4 A123 41 A143 A152 1 A174 2 A192 A201 1 793 | A14 6 A34 A42 1221 A65 A73 1 A94 A101 2 A122 27 A143 A152 2 A173 1 A191 A201 1 794 | A13 24 A32 A42 2892 A61 A75 3 A91 A101 4 A124 51 A143 A153 1 A173 1 A191 A201 1 795 | A14 24 A32 A42 3062 A63 A75 4 A93 A101 3 A124 32 A143 A151 1 A173 1 A192 A201 1 796 | A14 9 A32 A42 2301 A62 A72 2 A92 A101 4 A122 22 A143 A151 1 A173 1 A191 A201 1 797 | A11 18 A32 A41 7511 A65 A75 1 A93 A101 4 A122 51 A143 A153 1 A173 2 A192 A201 2 798 | A14 12 A34 A42 1258 A61 A72 2 A92 A101 4 A122 22 A143 A151 2 A172 1 A191 A201 1 799 | A14 24 A33 A40 717 A65 A75 4 A94 A101 4 A123 54 A143 A152 2 A173 1 A192 A201 1 800 | A12 9 A32 A40 1549 A65 A72 4 A93 A101 2 A121 35 A143 A152 1 A171 1 A191 A201 1 801 | A14 24 A34 A46 1597 A61 A75 4 A93 A101 4 A124 54 A143 A153 2 A173 2 A191 A201 1 802 | A12 18 A34 A43 1795 A61 A75 3 A92 A103 4 A121 48 A141 A151 2 A172 1 A192 A201 1 803 | A11 20 A34 A42 4272 A61 A75 1 A92 A101 4 A122 24 A143 A152 2 A173 1 A191 A201 1 804 | A14 12 A34 A43 976 A65 A75 4 A93 A101 4 A123 35 A143 A152 2 A173 1 A191 A201 1 805 | A12 12 A32 A40 7472 A65 A71 1 A92 A101 2 A121 24 A143 A151 1 A171 1 A191 A201 1 806 | A11 36 A32 A40 9271 A61 A74 2 A93 A101 1 A123 24 A143 A152 1 A173 1 A192 A201 2 807 | A12 6 A32 A43 590 A61 A72 3 A94 A101 3 A121 26 A143 A152 1 A172 1 A191 A202 1 808 | A14 12 A34 A43 930 A65 A75 4 A93 A101 4 A121 65 A143 A152 4 A173 1 A191 A201 1 809 | A12 42 A31 A41 9283 A61 A71 1 A93 A101 2 A124 55 A141 A153 1 A174 1 A192 A201 1 810 | A12 15 A30 A40 1778 A61 A72 2 A92 A101 1 A121 26 A143 A151 2 A171 1 A191 A201 2 811 | A12 8 A32 A49 907 A61 A72 3 A94 A101 2 A121 26 A143 A152 1 A173 1 A192 A201 1 812 | A12 6 A32 A43 484 A61 A74 3 A94 A103 3 A121 28 A141 A152 1 A172 1 A191 A201 1 813 | A11 36 A34 A41 9629 A61 A74 4 A93 A101 4 A123 24 A143 A152 2 A173 1 A192 A201 2 814 | A11 48 A32 A44 3051 A61 A73 3 A93 A101 4 A123 54 A143 A152 1 A173 1 A191 A201 2 815 | A11 48 A32 A40 3931 A61 A74 4 A93 A101 4 A124 46 A143 A153 1 A173 2 A191 A201 2 816 | A12 36 A33 A40 7432 A61 A73 2 A92 A101 2 A122 54 A143 A151 1 A173 1 A191 A201 1 817 | A14 6 A32 A44 1338 A63 A73 1 A91 A101 4 A121 62 A143 A152 1 A173 1 A191 A201 1 818 | A14 6 A34 A43 1554 A61 A74 1 A92 A101 2 A123 24 A143 A151 2 A173 1 A192 A201 1 819 | A11 36 A32 A410 15857 A61 A71 2 A91 A102 3 A123 43 A143 A152 1 A174 1 A191 A201 1 820 | A11 18 A32 A43 1345 A61 A73 4 A94 A101 3 A121 26 A141 A152 1 A173 1 A191 A201 2 821 | A14 12 A32 A40 1101 A61 A73 3 A94 A101 2 A121 27 A143 A152 2 A173 1 A192 A201 1 822 | A13 12 A32 A43 3016 A61 A73 3 A94 A101 1 A123 24 A143 A152 1 A173 1 A191 A201 1 823 | A11 36 A32 A42 2712 A61 A75 2 A93 A101 2 A122 41 A141 A152 1 A173 2 A191 A201 2 824 | A11 8 A34 A40 731 A61 A75 4 A93 A101 4 A121 47 A143 A152 2 A172 1 A191 A201 1 825 | A14 18 A34 A42 3780 A61 A72 3 A91 A101 2 A123 35 A143 A152 2 A174 1 A192 A201 1 826 | A11 21 A34 A40 1602 A61 A75 4 A94 A101 3 A123 30 A143 A152 2 A173 1 A192 A201 1 827 | A11 18 A34 A40 3966 A61 A75 1 A92 A101 4 A121 33 A141 A151 3 A173 1 A192 A201 2 828 | A14 18 A30 A49 4165 A61 A73 2 A93 A101 2 A123 36 A142 A152 2 A173 2 A191 A201 2 829 | A11 36 A32 A41 8335 A65 A75 3 A93 A101 4 A124 47 A143 A153 1 A173 1 A191 A201 2 830 | A12 48 A33 A49 6681 A65 A73 4 A93 A101 4 A124 38 A143 A153 1 A173 2 A192 A201 1 831 | A14 24 A33 A49 2375 A63 A73 4 A93 A101 2 A123 44 A143 A152 2 A173 2 A192 A201 1 832 | A11 18 A32 A40 1216 A61 A72 4 A92 A101 3 A123 23 A143 A151 1 A173 1 A192 A201 2 833 | A11 45 A30 A49 11816 A61 A75 2 A93 A101 4 A123 29 A143 A151 2 A173 1 A191 A201 2 834 | A12 24 A32 A43 5084 A65 A75 2 A92 A101 4 A123 42 A143 A152 1 A173 1 A192 A201 1 835 | A13 15 A32 A43 2327 A61 A72 2 A92 A101 3 A121 25 A143 A152 1 A172 1 A191 A201 2 836 | A11 12 A30 A40 1082 A61 A73 4 A93 A101 4 A123 48 A141 A152 2 A173 1 A191 A201 2 837 | A14 12 A32 A43 886 A65 A73 4 A92 A101 2 A123 21 A143 A152 1 A173 1 A191 A201 1 838 | A14 4 A32 A42 601 A61 A72 1 A92 A101 3 A121 23 A143 A151 1 A172 2 A191 A201 1 839 | A11 24 A34 A41 2957 A61 A75 4 A93 A101 4 A122 63 A143 A152 2 A173 1 A192 A201 1 840 | A14 24 A34 A43 2611 A61 A75 4 A94 A102 3 A121 46 A143 A152 2 A173 1 A191 A201 1 841 | A11 36 A32 A42 5179 A61 A74 4 A93 A101 2 A122 29 A143 A152 1 A173 1 A191 A201 2 842 | A14 21 A33 A41 2993 A61 A73 3 A93 A101 2 A121 28 A142 A152 2 A172 1 A191 A201 1 843 | A14 18 A32 A45 1943 A61 A72 4 A92 A101 4 A121 23 A143 A152 1 A173 1 A191 A201 2 844 | A14 24 A31 A49 1559 A61 A74 4 A93 A101 4 A123 50 A141 A152 1 A173 1 A192 A201 1 845 | A14 18 A32 A42 3422 A61 A75 4 A93 A101 4 A122 47 A141 A152 3 A173 2 A192 A201 1 846 | A12 21 A32 A42 3976 A65 A74 2 A93 A101 3 A123 35 A143 A152 1 A173 1 A192 A201 1 847 | A14 18 A32 A40 6761 A65 A73 2 A93 A101 4 A123 68 A143 A151 2 A173 1 A191 A201 2 848 | A14 24 A32 A40 1249 A61 A72 4 A94 A101 2 A121 28 A143 A152 1 A173 1 A191 A201 1 849 | A11 9 A32 A43 1364 A61 A74 3 A93 A101 4 A121 59 A143 A152 1 A173 1 A191 A201 1 850 | A11 12 A32 A43 709 A61 A75 4 A93 A101 4 A121 57 A142 A152 1 A172 1 A191 A201 2 851 | A11 20 A34 A40 2235 A61 A73 4 A94 A103 2 A122 33 A141 A151 2 A173 1 A191 A202 2 852 | A14 24 A34 A41 4042 A65 A74 3 A93 A101 4 A122 43 A143 A152 2 A173 1 A192 A201 1 853 | A14 15 A34 A43 1471 A61 A73 4 A93 A101 4 A124 35 A143 A153 2 A173 1 A192 A201 1 854 | A11 18 A31 A40 1442 A61 A74 4 A93 A101 4 A124 32 A143 A153 2 A172 2 A191 A201 2 855 | A14 36 A33 A40 10875 A61 A75 2 A93 A101 2 A123 45 A143 A152 2 A173 2 A192 A201 1 856 | A14 24 A32 A40 1474 A62 A72 4 A94 A101 3 A121 33 A143 A152 1 A173 1 A192 A201 1 857 | A14 10 A32 A48 894 A65 A74 4 A92 A101 3 A122 40 A143 A152 1 A173 1 A192 A201 1 858 | A14 15 A34 A42 3343 A61 A73 4 A93 A101 2 A124 28 A143 A153 1 A173 1 A192 A201 1 859 | A11 15 A32 A40 3959 A61 A73 3 A92 A101 2 A122 29 A143 A152 1 A173 1 A192 A201 2 860 | A14 9 A32 A40 3577 A62 A73 1 A93 A103 2 A121 26 A143 A151 1 A173 2 A191 A202 1 861 | A14 24 A34 A41 5804 A64 A73 4 A93 A101 2 A121 27 A143 A152 2 A173 1 A191 A201 1 862 | A14 18 A33 A49 2169 A61 A73 4 A94 A101 2 A123 28 A143 A152 1 A173 1 A192 A201 2 863 | A11 24 A32 A43 2439 A61 A72 4 A92 A101 4 A121 35 A143 A152 1 A173 1 A192 A201 2 864 | A14 27 A34 A42 4526 A64 A72 4 A93 A101 2 A121 32 A142 A152 2 A172 2 A192 A201 1 865 | A14 10 A32 A42 2210 A61 A73 2 A93 A101 2 A121 25 A141 A151 1 A172 1 A191 A201 2 866 | A14 15 A32 A42 2221 A63 A73 2 A92 A101 4 A123 20 A143 A151 1 A173 1 A191 A201 1 867 | A11 18 A32 A43 2389 A61 A72 4 A92 A101 1 A123 27 A142 A152 1 A173 1 A191 A201 1 868 | A14 12 A34 A42 3331 A61 A75 2 A93 A101 4 A122 42 A142 A152 1 A173 1 A191 A201 1 869 | A14 36 A32 A49 7409 A65 A75 3 A93 A101 2 A122 37 A143 A152 2 A173 1 A191 A201 1 870 | A11 12 A32 A42 652 A61 A75 4 A92 A101 4 A122 24 A143 A151 1 A173 1 A191 A201 1 871 | A14 36 A33 A42 7678 A63 A74 2 A92 A101 4 A123 40 A143 A152 2 A173 1 A192 A201 1 872 | A13 6 A34 A40 1343 A61 A75 1 A93 A101 4 A121 46 A143 A152 2 A173 2 A191 A202 1 873 | A11 24 A34 A49 1382 A62 A74 4 A93 A101 1 A121 26 A143 A152 2 A173 1 A192 A201 1 874 | A14 15 A32 A44 874 A65 A72 4 A92 A101 1 A121 24 A143 A152 1 A173 1 A191 A201 1 875 | A11 12 A32 A42 3590 A61 A73 2 A93 A102 2 A122 29 A143 A152 1 A172 2 A191 A201 1 876 | A12 11 A34 A40 1322 A64 A73 4 A92 A101 4 A123 40 A143 A152 2 A173 1 A191 A201 1 877 | A11 18 A31 A43 1940 A61 A72 3 A93 A102 4 A124 36 A141 A153 1 A174 1 A192 A201 1 878 | A14 36 A32 A43 3595 A61 A75 4 A93 A101 2 A123 28 A143 A152 1 A173 1 A191 A201 1 879 | A11 9 A32 A40 1422 A61 A72 3 A93 A101 2 A124 27 A143 A153 1 A174 1 A192 A201 2 880 | A14 30 A34 A43 6742 A65 A74 2 A93 A101 3 A122 36 A143 A152 2 A173 1 A191 A201 1 881 | A14 24 A32 A41 7814 A61 A74 3 A93 A101 3 A123 38 A143 A152 1 A174 1 A192 A201 1 882 | A14 24 A32 A41 9277 A65 A73 2 A91 A101 4 A124 48 A143 A153 1 A173 1 A192 A201 1 883 | A12 30 A34 A40 2181 A65 A75 4 A93 A101 4 A121 36 A143 A152 2 A173 1 A191 A201 1 884 | A14 18 A34 A43 1098 A61 A71 4 A92 A101 4 A123 65 A143 A152 2 A171 1 A191 A201 1 885 | A12 24 A32 A42 4057 A61 A74 3 A91 A101 3 A123 43 A143 A152 1 A173 1 A192 A201 2 886 | A11 12 A32 A46 795 A61 A72 4 A92 A101 4 A122 53 A143 A152 1 A173 1 A191 A201 2 887 | A12 24 A34 A49 2825 A65 A74 4 A93 A101 3 A124 34 A143 A152 2 A173 2 A192 A201 1 888 | A12 48 A32 A49 15672 A61 A73 2 A93 A101 2 A123 23 A143 A152 1 A173 1 A192 A201 2 889 | A14 36 A34 A40 6614 A61 A75 4 A93 A101 4 A123 34 A143 A152 2 A174 1 A192 A201 1 890 | A14 28 A31 A41 7824 A65 A72 3 A93 A103 4 A121 40 A141 A151 2 A173 2 A192 A201 1 891 | A11 27 A34 A49 2442 A61 A75 4 A93 A101 4 A123 43 A142 A152 4 A174 2 A192 A201 1 892 | A14 15 A34 A43 1829 A61 A75 4 A93 A101 4 A123 46 A143 A152 2 A173 1 A192 A201 1 893 | A11 12 A34 A40 2171 A61 A73 4 A93 A101 4 A122 38 A141 A152 2 A172 1 A191 A202 1 894 | A12 36 A34 A41 5800 A61 A73 3 A93 A101 4 A123 34 A143 A152 2 A173 1 A192 A201 1 895 | A14 18 A34 A43 1169 A65 A73 4 A93 A101 3 A122 29 A143 A152 2 A173 1 A192 A201 1 896 | A14 36 A33 A41 8947 A65 A74 3 A93 A101 2 A123 31 A142 A152 1 A174 2 A192 A201 1 897 | A11 21 A32 A43 2606 A61 A72 4 A92 A101 4 A122 28 A143 A151 1 A174 1 A192 A201 1 898 | A14 12 A34 A42 1592 A64 A74 3 A92 A101 2 A122 35 A143 A152 1 A173 1 A191 A202 1 899 | A14 15 A32 A42 2186 A65 A74 1 A92 A101 4 A121 33 A141 A151 1 A172 1 A191 A201 1 900 | A11 18 A32 A42 4153 A61 A73 2 A93 A102 3 A123 42 A143 A152 1 A173 1 A191 A201 2 901 | A11 16 A34 A40 2625 A61 A75 2 A93 A103 4 A122 43 A141 A151 1 A173 1 A192 A201 2 902 | A14 20 A34 A40 3485 A65 A72 2 A91 A101 4 A121 44 A143 A152 2 A173 1 A192 A201 1 903 | A14 36 A34 A41 10477 A65 A75 2 A93 A101 4 A124 42 A143 A153 2 A173 1 A191 A201 1 904 | A14 15 A32 A43 1386 A65 A73 4 A94 A101 2 A121 40 A143 A151 1 A173 1 A192 A201 1 905 | A14 24 A32 A43 1278 A61 A75 4 A93 A101 1 A121 36 A143 A152 1 A174 1 A192 A201 1 906 | A11 12 A32 A43 1107 A61 A73 2 A93 A101 2 A121 20 A143 A151 1 A174 2 A192 A201 1 907 | A11 21 A32 A40 3763 A65 A74 2 A93 A102 2 A121 24 A143 A152 1 A172 1 A191 A202 1 908 | A12 36 A32 A46 3711 A65 A73 2 A94 A101 2 A123 27 A143 A152 1 A173 1 A191 A201 1 909 | A14 15 A33 A41 3594 A61 A72 1 A92 A101 2 A122 46 A143 A152 2 A172 1 A191 A201 1 910 | A12 9 A32 A40 3195 A65 A73 1 A92 A101 2 A121 33 A143 A152 1 A172 1 A191 A201 1 911 | A14 36 A33 A43 4454 A61 A73 4 A92 A101 4 A121 34 A143 A152 2 A173 1 A191 A201 1 912 | A12 24 A34 A42 4736 A61 A72 2 A92 A101 4 A123 25 A141 A152 1 A172 1 A191 A201 2 913 | A12 30 A32 A43 2991 A65 A75 2 A92 A101 4 A123 25 A143 A152 1 A173 1 A191 A201 1 914 | A14 11 A32 A49 2142 A64 A75 1 A91 A101 2 A121 28 A143 A152 1 A173 1 A192 A201 1 915 | A11 24 A31 A49 3161 A61 A73 4 A93 A101 2 A122 31 A143 A151 1 A173 1 A192 A201 2 916 | A12 48 A30 A410 18424 A61 A73 1 A92 A101 2 A122 32 A141 A152 1 A174 1 A192 A202 2 917 | A14 10 A32 A41 2848 A62 A73 1 A93 A102 2 A121 32 A143 A152 1 A173 2 A191 A201 1 918 | A11 6 A32 A40 14896 A61 A75 1 A93 A101 4 A124 68 A141 A152 1 A174 1 A192 A201 2 919 | A11 24 A32 A42 2359 A62 A71 1 A91 A101 1 A122 33 A143 A152 1 A173 1 A191 A201 2 920 | A11 24 A32 A42 3345 A61 A75 4 A93 A101 2 A122 39 A143 A151 1 A174 1 A192 A201 2 921 | A14 18 A34 A42 1817 A61 A73 4 A92 A101 2 A124 28 A143 A152 2 A173 1 A191 A201 1 922 | A14 48 A33 A43 12749 A63 A74 4 A93 A101 1 A123 37 A143 A152 1 A174 1 A192 A201 1 923 | A11 9 A32 A43 1366 A61 A72 3 A92 A101 4 A122 22 A143 A151 1 A173 1 A191 A201 2 924 | A12 12 A32 A40 2002 A61 A74 3 A93 A101 4 A122 30 A143 A151 1 A173 2 A192 A201 1 925 | A11 24 A31 A42 6872 A61 A72 2 A91 A101 1 A122 55 A141 A152 1 A173 1 A192 A201 2 926 | A11 12 A31 A40 697 A61 A72 4 A93 A101 2 A123 46 A141 A152 2 A173 1 A192 A201 2 927 | A11 18 A34 A42 1049 A61 A72 4 A92 A101 4 A122 21 A143 A151 1 A173 1 A191 A201 1 928 | A11 48 A32 A41 10297 A61 A74 4 A93 A101 4 A124 39 A142 A153 3 A173 2 A192 A201 2 929 | A14 30 A32 A43 1867 A65 A75 4 A93 A101 4 A123 58 A143 A152 1 A173 1 A192 A201 1 930 | A11 12 A33 A40 1344 A61 A73 4 A93 A101 2 A121 43 A143 A152 2 A172 2 A191 A201 1 931 | A11 24 A32 A42 1747 A61 A72 4 A93 A102 1 A122 24 A143 A152 1 A172 1 A191 A202 1 932 | A12 9 A32 A43 1670 A61 A72 4 A92 A101 2 A123 22 A143 A152 1 A173 1 A192 A201 2 933 | A14 9 A34 A40 1224 A61 A73 3 A93 A101 1 A121 30 A143 A152 2 A173 1 A191 A201 1 934 | A14 12 A34 A43 522 A63 A75 4 A93 A101 4 A122 42 A143 A152 2 A173 2 A192 A201 1 935 | A11 12 A32 A43 1498 A61 A73 4 A92 A101 1 A123 23 A141 A152 1 A173 1 A191 A201 1 936 | A12 30 A33 A43 1919 A62 A72 4 A93 A101 3 A124 30 A142 A152 2 A174 1 A191 A201 2 937 | A13 9 A32 A43 745 A61 A73 3 A92 A101 2 A121 28 A143 A152 1 A172 1 A191 A201 2 938 | A12 6 A32 A43 2063 A61 A72 4 A94 A101 3 A123 30 A143 A151 1 A174 1 A192 A201 1 939 | A12 60 A32 A46 6288 A61 A73 4 A93 A101 4 A124 42 A143 A153 1 A173 1 A191 A201 2 940 | A14 24 A34 A41 6842 A65 A73 2 A93 A101 4 A122 46 A143 A152 2 A174 2 A192 A201 1 941 | A14 12 A32 A40 3527 A65 A72 2 A93 A101 3 A122 45 A143 A152 1 A174 2 A192 A201 1 942 | A14 10 A32 A40 1546 A61 A73 3 A93 A101 2 A121 31 A143 A152 1 A172 2 A191 A202 1 943 | A14 24 A32 A42 929 A65 A74 4 A93 A101 2 A123 31 A142 A152 1 A173 1 A192 A201 1 944 | A14 4 A34 A40 1455 A61 A74 2 A93 A101 1 A121 42 A143 A152 3 A172 2 A191 A201 1 945 | A11 15 A32 A42 1845 A61 A72 4 A92 A103 1 A122 46 A143 A151 1 A173 1 A191 A201 1 946 | A12 48 A30 A40 8358 A63 A72 1 A92 A101 1 A123 30 A143 A152 2 A173 1 A191 A201 1 947 | A11 24 A31 A42 3349 A63 A72 4 A93 A101 4 A124 30 A143 A153 1 A173 2 A192 A201 2 948 | A14 12 A32 A40 2859 A65 A71 4 A93 A101 4 A124 38 A143 A152 1 A174 1 A192 A201 1 949 | A14 18 A32 A42 1533 A61 A72 4 A94 A102 1 A122 43 A143 A152 1 A172 2 A191 A201 2 950 | A14 24 A32 A43 3621 A62 A75 2 A93 A101 4 A123 31 A143 A152 2 A173 1 A191 A201 2 951 | A12 18 A34 A49 3590 A61 A71 3 A94 A101 3 A123 40 A143 A152 3 A171 2 A192 A201 1 952 | A11 36 A33 A49 2145 A61 A74 2 A93 A101 1 A123 24 A143 A152 2 A173 1 A192 A201 2 953 | A12 24 A32 A41 4113 A63 A72 3 A92 A101 4 A123 28 A143 A151 1 A173 1 A191 A201 2 954 | A14 36 A32 A42 10974 A61 A71 4 A92 A101 2 A123 26 A143 A152 2 A174 1 A192 A201 2 955 | A11 12 A32 A40 1893 A61 A73 4 A92 A103 4 A122 29 A143 A152 1 A173 1 A192 A201 1 956 | A11 24 A34 A43 1231 A64 A75 4 A92 A101 4 A122 57 A143 A151 2 A174 1 A192 A201 1 957 | A13 30 A34 A43 3656 A65 A75 4 A93 A101 4 A122 49 A142 A152 2 A172 1 A191 A201 1 958 | A12 9 A34 A43 1154 A61 A75 2 A93 A101 4 A121 37 A143 A152 3 A172 1 A191 A201 1 959 | A11 28 A32 A40 4006 A61 A73 3 A93 A101 2 A123 45 A143 A152 1 A172 1 A191 A201 2 960 | A12 24 A32 A42 3069 A62 A75 4 A93 A101 4 A124 30 A143 A153 1 A173 1 A191 A201 1 961 | A14 6 A34 A43 1740 A61 A75 2 A94 A101 2 A121 30 A143 A151 2 A173 1 A191 A201 1 962 | A12 21 A33 A40 2353 A61 A73 1 A91 A101 4 A122 47 A143 A152 2 A173 1 A191 A201 1 963 | A14 15 A32 A40 3556 A65 A73 3 A93 A101 2 A124 29 A143 A152 1 A173 1 A191 A201 1 964 | A14 24 A32 A43 2397 A63 A75 3 A93 A101 2 A123 35 A141 A152 2 A173 1 A192 A201 2 965 | A12 6 A32 A45 454 A61 A72 3 A94 A101 1 A122 22 A143 A152 1 A172 1 A191 A201 1 966 | A12 30 A32 A43 1715 A65 A73 4 A92 A101 1 A123 26 A143 A152 1 A173 1 A191 A201 1 967 | A12 27 A34 A43 2520 A63 A73 4 A93 A101 2 A122 23 A143 A152 2 A172 1 A191 A201 2 968 | A14 15 A32 A43 3568 A61 A75 4 A92 A101 2 A123 54 A141 A151 1 A174 1 A192 A201 1 969 | A14 42 A32 A43 7166 A65 A74 2 A94 A101 4 A122 29 A143 A151 1 A173 1 A192 A201 1 970 | A11 11 A34 A40 3939 A61 A73 1 A93 A101 2 A121 40 A143 A152 2 A172 2 A191 A201 1 971 | A12 15 A32 A45 1514 A62 A73 4 A93 A103 2 A121 22 A143 A152 1 A173 1 A191 A201 1 972 | A14 24 A32 A40 7393 A61 A73 1 A93 A101 4 A122 43 A143 A152 1 A172 2 A191 A201 1 973 | A11 24 A31 A40 1193 A61 A71 1 A92 A102 4 A124 29 A143 A151 2 A171 1 A191 A201 2 974 | A11 60 A32 A49 7297 A61 A75 4 A93 A102 4 A124 36 A143 A151 1 A173 1 A191 A201 2 975 | A14 30 A34 A43 2831 A61 A73 4 A92 A101 2 A123 33 A143 A152 1 A173 1 A192 A201 1 976 | A13 24 A32 A43 1258 A63 A73 3 A92 A101 3 A123 57 A143 A152 1 A172 1 A191 A201 1 977 | A12 6 A32 A43 753 A61 A73 2 A92 A103 3 A121 64 A143 A152 1 A173 1 A191 A201 1 978 | A12 18 A33 A49 2427 A65 A75 4 A93 A101 2 A122 42 A143 A152 2 A173 1 A191 A201 1 979 | A14 24 A33 A40 2538 A61 A75 4 A93 A101 4 A123 47 A143 A152 2 A172 2 A191 A201 2 980 | A12 15 A31 A40 1264 A62 A73 2 A94 A101 2 A122 25 A143 A151 1 A173 1 A191 A201 2 981 | A12 30 A34 A42 8386 A61 A74 2 A93 A101 2 A122 49 A143 A152 1 A173 1 A191 A201 2 982 | A14 48 A32 A49 4844 A61 A71 3 A93 A101 2 A123 33 A141 A151 1 A174 1 A192 A201 2 983 | A13 21 A32 A40 2923 A62 A73 1 A92 A101 1 A123 28 A141 A152 1 A174 1 A192 A201 1 984 | A11 36 A32 A41 8229 A61 A73 2 A93 A101 2 A122 26 A143 A152 1 A173 2 A191 A201 2 985 | A14 24 A34 A42 2028 A61 A74 2 A93 A101 2 A122 30 A143 A152 2 A172 1 A191 A201 1 986 | A11 15 A34 A42 1433 A61 A73 4 A92 A101 3 A122 25 A143 A151 2 A173 1 A191 A201 1 987 | A13 42 A30 A49 6289 A61 A72 2 A91 A101 1 A122 33 A143 A152 2 A173 1 A191 A201 1 988 | A14 13 A32 A43 1409 A62 A71 2 A92 A101 4 A121 64 A143 A152 1 A173 1 A191 A201 1 989 | A11 24 A32 A41 6579 A61 A71 4 A93 A101 2 A124 29 A143 A153 1 A174 1 A192 A201 1 990 | A12 24 A34 A43 1743 A61 A75 4 A93 A101 2 A122 48 A143 A152 2 A172 1 A191 A201 1 991 | A14 12 A34 A46 3565 A65 A72 2 A93 A101 1 A122 37 A143 A152 2 A172 2 A191 A201 1 992 | A14 15 A31 A43 1569 A62 A75 4 A93 A101 4 A123 34 A141 A152 1 A172 2 A191 A201 1 993 | A11 18 A32 A43 1936 A65 A74 2 A94 A101 4 A123 23 A143 A151 2 A172 1 A191 A201 1 994 | A11 36 A32 A42 3959 A61 A71 4 A93 A101 3 A122 30 A143 A152 1 A174 1 A192 A201 1 995 | A14 12 A32 A40 2390 A65 A75 4 A93 A101 3 A123 50 A143 A152 1 A173 1 A192 A201 1 996 | A14 12 A32 A42 1736 A61 A74 3 A92 A101 4 A121 31 A143 A152 1 A172 1 A191 A201 1 997 | A11 30 A32 A41 3857 A61 A73 4 A91 A101 4 A122 40 A143 A152 1 A174 1 A192 A201 1 998 | A14 12 A32 A43 804 A61 A75 4 A93 A101 4 A123 38 A143 A152 1 A173 1 A191 A201 1 999 | A11 45 A32 A43 1845 A61 A73 4 A93 A101 4 A124 23 A143 A153 1 A173 1 A192 A201 2 1000 | A12 45 A34 A41 4576 A62 A71 3 A93 A101 4 A123 27 A143 A152 1 A173 1 A191 A201 1 1001 | -------------------------------------------------------------------------------- /riskscore/data/germancredit.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import pandas as pd 4 | import pkg_resources 5 | import numpy as np 6 | 7 | class Germancredit(object): 8 | # 所有变量 9 | def __init__(self): 10 | self.all_feature = ['status_of_existing_checking_account', 'duration_in_month', 'credit_history', 'purpose', 'credit_amount', 11 | 'savings_account_and_bonds', 12 | 'present_employment_since', 'installment_rate_in_percentage_of_disposable_income', 13 | 'personal_status_and_sex', 'other_debtors_or_guarantors', 14 | 'present_residence_since', 'property', 'age_in_years', 'other_installment_plans', 'housing', 15 | 'number_of_existing_credits_at_this_bank', 'job', 16 | 'number_of_people_being_liable_to_provide_maintenance_for', 'telephone', 'foreign_worker'] 17 | # 类别变量 18 | self.cat_col = ['purpose', 'personal_status_and_sex', 'other_debtors_or_guarantors', 'property', 'other_installment_plans', 19 | 'housing', 'telephone', 'foreign_worker'] 20 | # 数值变量 21 | self.num_col = [i for i in self.all_feature if i not in self.cat_col] 22 | # 整型变量 23 | self.int_col = self.cat_col + ['status_of_existing_checking_account', 'credit_history', 'savings_account_and_bonds', 'job'] 24 | # 需要处理的数值变量 25 | self.sub_col = ['status_of_existing_checking_account', 'credit_history', 'savings_account_and_bonds', 26 | 'present_employment_since', 'job'] 27 | # 需要替换的变量 28 | self.rep_dict = { 29 | 'status_of_existing_checking_account': {4: 0}, 30 | 'savings_account_and_bonds': {5: np.nan}, 31 | 'purpose': {'A124': np.nan} 32 | } 33 | 34 | def get_data(self): 35 | DATA_FILE = pkg_resources.resource_filename('riskscore', 'data/germancredit.csv') 36 | data = pd.read_csv(DATA_FILE,encoding='utf-8', sep=' ', header=None,names= self.all_feature + ["target"]) 37 | return data 38 | 39 | def get_describe(self): 40 | DATA_FILE = pkg_resources.resource_filename('riskscore', 'data/german.txt') 41 | with open(DATA_FILE,'r+') as f: 42 | print(f.read()) 43 | -------------------------------------------------------------------------------- /riskscore/utils/InputCondition.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | 入参合法性检验 4 | """ 5 | 6 | import pandas as pd 7 | import numpy as np 8 | from pandas.api.types import is_numeric_dtype 9 | 10 | 11 | # check str 12 | def check_str(params): 13 | if params is not None: 14 | if not isinstance(params, str): 15 | raise Exception("Incorrect inputs; parameter should be a str.") 16 | return params 17 | 18 | 19 | # check bool 20 | def check_bool(params): 21 | if params is not None: 22 | if not isinstance(params, bool): 23 | raise Exception("Incorrect inputs; parameter should be a bool.") 24 | return params 25 | 26 | 27 | # check float 28 | def check_float(params): 29 | if params is not None: 30 | if not isinstance(params, float): 31 | raise Exception("Incorrect inputs; parameter should be a float.") 32 | return params 33 | 34 | 35 | # check int 36 | def check_int(params): 37 | if params is not None: 38 | if not isinstance(params, int): 39 | raise Exception("Incorrect inputs; parameter should be an int.") 40 | return params 41 | 42 | 43 | # check tuple 44 | def check_tuple(params): 45 | if params is not None: 46 | if not isinstance(params, tuple): 47 | raise Exception("Incorrect inputs; parameter should be a tuple.") 48 | return params 49 | 50 | 51 | # check list 52 | def check_list(params): 53 | if params is not None: 54 | if not isinstance(params, list): 55 | raise Exception("Incorrect inputs; parameter should be a list.") 56 | return params 57 | 58 | 59 | # check dict 60 | def check_dict(params): 61 | if params is not None: 62 | if not isinstance(params, dict): 63 | raise Exception("Incorrect inputs; parameter should be a dict.") 64 | return params 65 | 66 | 67 | # check df 68 | def check_df(params): 69 | if params is not None: 70 | if not isinstance(params, pd.DataFrame): 71 | raise Exception("Incorrect inputs; parameter should be a DataFrame.") 72 | return params 73 | 74 | # check like-list 75 | def check_array(params): 76 | if params is not None: 77 | if not isinstance(params,np.ndarray): 78 | raise Exception("Incorrect inputs; parameter should be a ndarray.") 79 | return params 80 | 81 | # check str or list 82 | def check_str_list(params): 83 | if params is not None: 84 | # is string 85 | if isinstance(params, str): 86 | param_list = eval(params) 87 | # is not list 88 | if not isinstance(params, list): 89 | raise Exception("Incorrect inputs; parameter should be a list or str.") 90 | return params 91 | 92 | 93 | # check list-like of float 默认 None,[0.25,0.5,0.75] 94 | def check_float_list(params): 95 | new_list = [] 96 | if params is not None: 97 | # is not list 98 | if not isinstance(params, list): 99 | raise Exception("Incorrect inputs; parameter should be a list-like of float.") 100 | else: 101 | for each in params: 102 | if not isinstance(each, float) and not isinstance(each,int): 103 | new_list.append(each) 104 | if new_list: 105 | raise Exception("Incorrect inputs; parameter should be a list-like of float.") 106 | return params 107 | 108 | 109 | # check list-like of dtypes or None 110 | def check_dtypes_list(params): 111 | if params is not None: 112 | if params=='all': 113 | return params 114 | # is not list 115 | if not isinstance(params, list): 116 | raise Exception("Incorrect inputs; parameter should be a list-like of dtypes.") 117 | else: 118 | for each in params: 119 | if not is_numeric_dtype(each): 120 | raise Exception("Incorrect inputs; parameter should be a list-like of dtypes.") 121 | return params 122 | 123 | 124 | # check special_values: list, dict 125 | def check_special_values(special_values): 126 | if special_values is not None: 127 | if not isinstance(special_values, list) and not isinstance(special_values, dict): 128 | raise Exception("Incorrect inputs; special_values should be a list or dict.") 129 | return special_values 130 | 131 | 132 | # check list of dict 133 | def check_list_of_dict(params): 134 | new_list = [] 135 | if params is not None: 136 | if not isinstance(params, dict): 137 | raise Exception("Incorrect inputs; special_values should be a list of dict.") 138 | else: 139 | for each in params.values(): 140 | if not isinstance(each, list) and not isinstance(each,tuple): 141 | new_list.append(each) 142 | if new_list: 143 | raise Exception("Incorrect inputs; parameter should be a list(tuple) of dict.") 144 | return params 145 | 146 | 147 | # check dict of df 148 | def check_dict_of_df(params): 149 | new_list = [] 150 | if params is not None: 151 | if not isinstance(params, dict): 152 | raise Exception("Incorrect inputs; special_values should be a dict of df.") 153 | else: 154 | for each in params.values(): 155 | if not isinstance(each, pd.DataFrame): 156 | new_list.append(each) 157 | if new_list: 158 | raise Exception("Incorrect inputs; parameter should be a dict of df.") 159 | return params 160 | 161 | # check dict of df 162 | def check_dict_of_float(params): 163 | new_list = [] 164 | if params is not None: 165 | if not isinstance(params, dict): 166 | raise Exception("Incorrect inputs; special_values should be a dict of float.") 167 | else: 168 | for each in params.values(): 169 | if not isinstance(each, float): 170 | new_list.append(each) 171 | if new_list: 172 | raise Exception("Incorrect inputs; parameter should be a dict of float.") 173 | return params -------------------------------------------------------------------------------- /riskscore/utils/PltFunction.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | 画图函数 4 | """ 5 | 6 | import numpy as np 7 | import matplotlib.pyplot as plt 8 | plt.rcParams['backend'] = 'Agg' 9 | import seaborn as sns 10 | from sklearn.metrics import roc_curve,confusion_matrix,auc 11 | import itertools 12 | from .tools import best_prob 13 | 14 | class PlotFeatureEn(object): 15 | """特征工程作图函数""" 16 | 17 | def draw_IV(self,IV_dict, path, xlabel=None, figsize=(15, 7), is_save=False): 18 | """ 19 | 信息值IV柱状图 20 | --------------------- 21 | param 22 | IV_dict: dict IV值字典 23 | path: str 文件存储地址 24 | xlabel: list x轴标签 25 | figsize: tupe 图片大小 26 | _____________________ 27 | return 28 | draw_iv 29 | """ 30 | IV_dict_sorted = sorted(IV_dict.items(), key=lambda x: x[1], reverse=True) 31 | ivlist = [i[1] for i in IV_dict_sorted] 32 | index = [i[0] for i in IV_dict_sorted] 33 | fig1 = plt.figure(figsize=figsize) 34 | ax1 = fig1.add_subplot(1, 1, 1) 35 | x = np.arange(len(index)) + 1 36 | ax1.bar(x, ivlist, width=0.5) # 生成柱状图 37 | ax1.set_xticks(x) 38 | if xlabel: 39 | ax1.set_xticklabels(index, rotation=0, fontsize=8) 40 | 41 | ax1.set_ylabel('IV(Information Value)', fontsize=14) 42 | # 在柱状图上添加数字标签 43 | for a, b in zip(x, ivlist): 44 | plt.text(a, b + 0.01, '%.4f' % b, ha='center', va='bottom', fontsize=10) 45 | 46 | if is_save: 47 | plt.savefig(path + "high_iv.png") 48 | 49 | def draw_importance(self,importance, features, figsize, path): 50 | """特征重要度""" 51 | plt.style.use('fivethirtyeight') 52 | plt.rcParams['figure.figsize'] = figsize 53 | sns.set_style("darkgrid", {"font.sans-serif": ["simhei", "Arial"]}) 54 | indices = np.argsort(importance)[::-1] 55 | plt.figure(figsize=figsize) 56 | plt.title("feature importance") 57 | plt.bar(range(len(indices)), importance[indices], color='lightblue', align="center") 58 | plt.step(range(len(indices)), np.cumsum(importance[indices]), where='mid', label='Cumulative') 59 | plt.xticks(range(len(indices)), features[indices], rotation='vertical', fontsize=14) 60 | plt.xlim([-1, len(indices)]) 61 | plt.savefig(path + "feature_importance.png") 62 | 63 | def draw_corr(self,df, figsize: tuple, path: str): 64 | """ 65 | 特征相关系数 66 | ------------------------ 67 | parameter: 68 | data_new: dataFrame,columns must be number 69 | figsize: tupe,two number 70 | return: 71 | heatmap 72 | """ 73 | # 相关系数分析 74 | colormap = plt.cm.viridis 75 | plt.figure(figsize=figsize) 76 | plt.title('皮尔森相关性系数', y=1.05, size=8) 77 | mask = np.zeros_like(df.corr(), dtype=np.bool) 78 | mask[np.triu_indices_from(mask)] = True 79 | sns.heatmap(df.corr(), linewidths=0.1, vmax=1.0, square=True, cmap=colormap, linecolor='white', annot=True, 80 | mask=mask) 81 | plt.savefig(path + "feature_corr.png") 82 | 83 | class PlotModel(object): 84 | """模型作图""" 85 | def __init__(self,y_true,y_prob): 86 | """ 87 | :param y_true:array, 真实y 88 | :param y_prob: array, 预测概率y 89 | """ 90 | self.y_true = y_true 91 | self.y_prob = y_prob 92 | 93 | def plot_roc_curve(self,filename='./'): 94 | """ 95 | """ 96 | fpr, tpr, _ = roc_curve(self.y_true, self.y_prob) 97 | c_stats = auc(fpr, tpr) 98 | plt.plot([0, 1], [0, 1], 'r--') 99 | plt.plot(fpr, tpr, label="ROC curve") 100 | auc_value = "AUC = %.3f" % c_stats 101 | plt.text(0.8, 0.2, auc_value, bbox=dict(facecolor='r', alpha=0.5)) 102 | plt.xlabel('False positive rate') # 假正率 103 | plt.ylabel('True positive rate') # 真正率 104 | plt.title('ROC curve') # ROC 曲线 105 | plt.legend(loc='best') 106 | plt.savefig(filename+"roc_curve.png") 107 | # plt.show() 108 | plt.close() 109 | return auc_value 110 | 111 | def plot_ks_curve(self,filename='./'): 112 | """ 113 | """ 114 | fpr, tpr, thr = roc_curve(self.y_true, self.y_prob) # 假正率 真正率 概率阈值 115 | thr = np.array(sorted(thr)) 116 | ks = abs(fpr - tpr) # ks 序列 117 | ks_value = "KS = %.3f" % max(ks) # ks值 118 | cut_prob = best_prob(self.y_true, self.y_prob) # 最佳切分概率 119 | plt.plot(thr, fpr, label='cum_good', color='blue', linestyle='-', linewidth=2) # 假正率 累计好 120 | plt.plot(thr, tpr, label='cum_bad', color='red', linestyle='-', linewidth=2) # 真正率,累计坏 121 | plt.plot(thr, ks, label='ks', color='green', linestyle='-', linewidth=2) # ks曲线 122 | plt.plot(thr, [max(ks)] * len(thr), color='green', linestyle='--') # ks值直线 123 | plt.axvline(cut_prob, color='gray', linestyle='--') # 最佳切分概率直线 124 | plt.title('{}'.format(ks_value), fontsize=15) 125 | plt.xlim((0.1, 1)) 126 | plt.savefig(filename+"ks_curve.png") # 保存 127 | # plt.show() 128 | plt.close() 129 | return ks_value 130 | 131 | def plot_confusion_matrix(self,labels:list,normalize=False,filename='./'): 132 | """ 133 | 混淆矩阵 134 | ------------------------------------------ 135 | Params 136 | labels: list, labels class 137 | normalize: bool, True means trans results to percent 138 | """ 139 | cut_prob = best_prob(self.y_true, self.y_prob) # 最佳切分概率 140 | y_pred = np.array([1 if i >= cut_prob else 0 for i in self.y_prob]) 141 | matrix = confusion_matrix(y_true=self.y_true, y_pred=y_pred, labels=labels) 142 | plt.imshow(matrix, interpolation='nearest', cmap=plt.cm.Blues_r) # 在指定的轴上展示图像 143 | plt.colorbar() # 增加色柱 144 | tick_marks = np.arange(len(labels)) 145 | plt.xticks(tick_marks, labels, rotation=45) # 设置坐标轴标签 146 | plt.yticks(tick_marks, labels) 147 | 148 | if normalize: 149 | matrix = matrix.astype('float') / matrix.sum(axis=1)[:, np.newaxis] 150 | thresh = matrix.max() / 2. 151 | for i, j in itertools.product(range(matrix.shape[0]), range(matrix.shape[1])): 152 | plt.text(j, i, matrix[i, j], fontsize=12, 153 | horizontalalignment="center", 154 | color="white" if matrix[i, j] > thresh else "black") 155 | 156 | plt.tight_layout() 157 | plt.ylabel('True') 158 | plt.xlabel('Predicted') 159 | plt.title("confusion matrix") 160 | plt.savefig(filename+"matrix.png") 161 | # plt.show() 162 | plt.close() -------------------------------------------------------------------------------- /riskscore/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangxianyang/creditmodel/36971a49797739230089596acce0b402c6f2cea6/riskscore/utils/__init__.py -------------------------------------------------------------------------------- /riskscore/utils/btl/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangxianyang/creditmodel/36971a49797739230089596acce0b402c6f2cea6/riskscore/utils/btl/__init__.py -------------------------------------------------------------------------------- /riskscore/utils/btl/merge.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | 单调性检验之合并,分箱 4 | """ 5 | 6 | from .monotous import (_FeatureMonotone,_BinBadRate,_BadRateMonotone,_AssignBin) 7 | import numpy as np 8 | 9 | def monotone_merge(df, target, col): 10 | ''' 11 | 合并方案 12 | :return:将数据集df中,不满足坏样本率单调性的变量col进行合并,使得合并后的新的变量中,坏样本率单调,输出合并方案。 13 | 例如,col=[Bin0, Bin1, Bin2, Bin3, Bin4]是不满足坏样本率单调性的。合并后的col是: 14 | [Bin0&Bin1, Bin2, Bin3, Bin4]. 15 | 合并只能在相邻的箱中进行。 16 | 迭代地寻找最优合并方案。每一步迭代时,都尝试将所有非单调的箱进行合并,每一次尝试的合并都是跟前后箱进行合并再做比较 17 | ''' 18 | def _merge_matrix(m, i,j,k): 19 | ''' 20 | :param m: 需要合并行的矩阵 21 | :param i,j: 合并第i和j行 22 | :param k: 删除第k行 23 | :return: 合并后的矩阵 24 | ''' 25 | m[i, :] = m[i, :] + m[j, :] 26 | m = np.delete(m, k, axis=0) 27 | return m 28 | 29 | def _merge_adjacent_rows(i, bad_by_bin_current, bins_list_current, not_monotone_count_current): 30 | ''' 31 | :param i: 需要将第i行与前、后的行分别进行合并,比较哪种合并方案最佳。判断准则是,合并后非单调性程度减轻,且更加均匀 32 | :param bad_by_bin_current:合并前的分箱矩阵,包括每一箱的样本个数、坏样本个数和坏样本率 33 | :param bins_list_current: 合并前的分箱方案 34 | :param not_monotone_count_current:合并前的非单调性元素个数 35 | :return:分箱后的分箱矩阵、分箱方案、非单调性元素个数和衡量均匀性的指标balance 36 | ''' 37 | i_prev = i - 1 38 | i_next = i + 1 39 | bins_list = bins_list_current.copy() 40 | bad_by_bin = bad_by_bin_current.copy() 41 | #合并方案a:将第i箱与前一箱进行合并 42 | bad_by_bin2a = _merge_matrix(bad_by_bin.copy(), i_prev, i, i) 43 | bad_by_bin2a[i_prev, -1] = bad_by_bin2a[i_prev, -2] / bad_by_bin2a[i_prev, -3] 44 | not_monotone_count2a = _FeatureMonotone(bad_by_bin2a[:, -1])['count_of_nonmonotone'] 45 | # 合并方案b:将第i行与后一行进行合并 46 | bad_by_bin2b = _merge_matrix(bad_by_bin.copy(), i, i_next, i_next) 47 | bad_by_bin2b[i, -1] = bad_by_bin2b[i, -2] / bad_by_bin2b[i, -3] 48 | not_monotone_count2b = _FeatureMonotone(bad_by_bin2b[:, -1])['count_of_nonmonotone'] 49 | # balance = ((bad_by_bin[:, 1] / N).T * (bad_by_bin[:, 1] / N))[0, 0] 50 | balance_a = ((bad_by_bin2a[:, 1] / N).T * (bad_by_bin2a[:, 1] / N))[0, 0] 51 | balance_b = ((bad_by_bin2b[:, 1] / N).T * (bad_by_bin2b[:, 1] / N))[0, 0] 52 | # 满足下述2种情况时返回方案a:(1)方案a能减轻非单调性而方案b不能;(2)方案a和b都能减轻非单调性,但是方案a的样本均匀性优于方案b 53 | if not_monotone_count2a < not_monotone_count_current and not_monotone_count2b >= not_monotone_count_current or \ 54 | not_monotone_count2a < not_monotone_count_current and not_monotone_count2b < not_monotone_count_current and balance_a < balance_b: 55 | bins_list[i_prev] = bins_list[i_prev] + bins_list[i] 56 | bins_list.remove(bins_list[i]) 57 | bad_by_bin = bad_by_bin2a 58 | not_monotone_count = not_monotone_count2a 59 | balance = balance_a 60 | # 同样地,满足下述2种情况时返回方案b:(1)方案b能减轻非单调性而方案a不能;(2)方案a和b都能减轻非单调性,但是方案b的样本均匀性优于方案a 61 | elif not_monotone_count2a >= not_monotone_count_current and not_monotone_count2b < not_monotone_count_current or \ 62 | not_monotone_count2a < not_monotone_count_current and not_monotone_count2b < not_monotone_count_current and balance_a > balance_b: 63 | bins_list[i] = bins_list[i] + bins_list[i_next] 64 | bins_list.remove(bins_list[i_next]) 65 | bad_by_bin = bad_by_bin2b 66 | not_monotone_count = not_monotone_count2b 67 | balance = balance_b 68 | # 如果方案a和b都不能减轻非单调性,返回均匀性更优的合并方案 69 | else: 70 | if balance_a< balance_b: 71 | bins_list[i] = bins_list[i] + bins_list[i_next] 72 | bins_list.remove(bins_list[i_next]) 73 | bad_by_bin = bad_by_bin2b 74 | not_monotone_count = not_monotone_count2b 75 | balance = balance_b 76 | else: 77 | bins_list[i] = bins_list[i] + bins_list[i_next] 78 | bins_list.remove(bins_list[i_next]) 79 | bad_by_bin = bad_by_bin2b 80 | not_monotone_count = not_monotone_count2b 81 | balance = balance_b 82 | return {'bins_list': bins_list, 'bad_by_bin': bad_by_bin, 'not_monotone_count': not_monotone_count, 83 | 'balance': balance} 84 | 85 | 86 | N = df.shape[0] 87 | [badrate_bin, bad_by_bin] = _BinBadRate(df, col, target) 88 | bins = list(bad_by_bin[col]) 89 | bins_list = [[i] for i in bins] 90 | badRate = sorted(badrate_bin.items(), key=lambda x: x[0]) 91 | badRate = [i[1] for i in badRate] 92 | not_monotone_count, not_monotone_position = _FeatureMonotone(badRate)['count_of_nonmonotone'], _FeatureMonotone(badRate)['index_of_nonmonotone'] 93 | # 迭代地寻找最优合并方案,终止条件是:当前的坏样本率已经单调,或者当前只有2箱 94 | while (not_monotone_count > 0 and len(bins_list)>2): 95 | # 当非单调的箱的个数超过1个时,每一次迭代中都尝试每一个箱的最优合并方案 96 | all_possible_merging = [] 97 | for i in not_monotone_position: 98 | merge_adjacent_rows = _merge_adjacent_rows(i, np.mat(bad_by_bin), bins_list, not_monotone_count) 99 | all_possible_merging.append(merge_adjacent_rows) 100 | balance_list = [i['balance'] for i in all_possible_merging] 101 | not_monotone_count_new = [i['not_monotone_count'] for i in all_possible_merging] 102 | # 如果所有的合并方案都不能减轻当前的非单调性,就选择更加均匀的合并方案 103 | if min(not_monotone_count_new) >= not_monotone_count: 104 | best_merging_position = balance_list.index(min(balance_list)) 105 | # 如果有多个合并方案都能减轻当前的非单调性,也选择更加均匀的合并方案 106 | else: 107 | better_merging_index = [i for i in range(len(not_monotone_count_new)) if not_monotone_count_new[i] < not_monotone_count] 108 | better_balance = [balance_list[i] for i in better_merging_index] 109 | best_balance_index = better_balance.index(min(better_balance)) 110 | best_merging_position = better_merging_index[best_balance_index] 111 | bins_list = all_possible_merging[best_merging_position]['bins_list'] 112 | bad_by_bin = all_possible_merging[best_merging_position]['bad_by_bin'] 113 | not_monotone_count = all_possible_merging[best_merging_position]['not_monotone_count'] 114 | not_monotone_position = _FeatureMonotone(bad_by_bin[:, 3])['index_of_nonmonotone'] 115 | return bins_list 116 | 117 | 118 | def monotonous_bin(df,col,cutOffPoints,target,special_values): 119 | 120 | #单调性检验 121 | var_cutoff = {} 122 | col1 = col + '_Bin' # 检验单调性 123 | df[col1] = df[col].map(lambda x: _AssignBin(x, cutOffPoints=cutOffPoints,special_attribute=special_values)) 124 | BRM = _BadRateMonotone(df, col1, target, special_attribute=special_values) # 是否单调 125 | if not BRM: 126 | # 合并方案 127 | if special_values == []: 128 | bin_merged = monotone_merge(df, target, col1) 129 | removed_index = [] 130 | for bin in bin_merged: 131 | if len(bin) > 1: 132 | indices = [int(b.replace('Bin', '')) for b in bin] 133 | removed_index = removed_index + indices[0:-1] 134 | removed_point = [cutOffPoints[k] for k in range(len(cutOffPoints)) if k in removed_index] 135 | # removed_point = [cutOffPoints[k] for k in removed_index] 136 | for p in removed_point: 137 | cutOffPoints.remove(p) 138 | var_cutoff[col] = cutOffPoints 139 | else: 140 | cutOffPoints2 = [i for i in cutOffPoints if i not in special_values] 141 | temp = df.loc[~df[col].isin(special_values)] 142 | bin_merged = monotone_merge(temp, target, col1) 143 | removed_index = [] 144 | for bin in bin_merged: 145 | if len(bin) > 1: 146 | indices = [int(b.replace('Bin', '')) for b in bin] 147 | removed_index = removed_index + indices[0:-1] 148 | # print("removed_index:",removed_index) # 调试 149 | # print("cutOffPoints2:",cutOffPoints2) # 调试 150 | removed_point = [cutOffPoints2[k] for k in range(len(cutOffPoints2)) if k in removed_index] 151 | # removed_point = [cutOffPoints2[k] for k in removed_index] 152 | for p in removed_point: 153 | cutOffPoints2.remove(p) 154 | # cutOffPoints2 = cutOffPoints2 + special_values 155 | var_cutoff[col] = cutOffPoints2 # 单调性检验结果 156 | return var_cutoff 157 | -------------------------------------------------------------------------------- /riskscore/utils/btl/monotous.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | 单调性检验工具 4 | """ 5 | 6 | import pandas as pd 7 | 8 | 9 | def _AssignBin(x, cutOffPoints, special_attribute=None): 10 | 11 | ''' 12 | 分箱转换 13 | :param x: 某个变量的某个取值 14 | :param cutOffPoints: list 上述变量的分箱结果,用切分点表示 15 | :param special_attribute:list 不参与分箱的特殊取值 16 | :return: 分箱后的对应的第几个箱,从0开始 17 | 例如, cutOffPoints = [10,20,30], 对于 x = 7, 返回 Bin0;对于x=23,返回Bin2; 对于x = 35, return Bin3。 18 | 对于特殊值,返回的序列数前加"-" 19 | ''' 20 | cutOffPoints2 = [i for i in cutOffPoints if i not in special_attribute] 21 | numBin = len(cutOffPoints2) 22 | if x in special_attribute: 23 | i = special_attribute.index(x)+1 24 | return 'Bin{}'.format(0-i) 25 | if x<=cutOffPoints2[0]: 26 | return 'Bin0' 27 | elif x > cutOffPoints2[-1]: 28 | return 'Bin{}'.format(numBin) 29 | else: 30 | for i in range(0,numBin): 31 | if cutOffPoints2[i] < x <= cutOffPoints2[i+1]: 32 | return 'Bin{}'.format(i+1) 33 | 34 | 35 | def _FeatureMonotone(x): 36 | ''' 37 | 检验BadRate单调性 38 | Param x: list cut off list 39 | :return: 返回序列x中有几个元素不满足单调性,以及这些元素的位置。 40 | 例如,x=[1,3,2,5], 元素3比前后两个元素都大,不满足单调性;元素2比前后两个元素都小,也不满足单调性。 41 | 故返回的不满足单调性的元素个数为2,位置为1和2. 42 | ''' 43 | monotone = [x[i]x[i+1] and x[i] > x[i-1] for i in range(1,len(x)-1)] 44 | index_of_nonmonotone = [i+1 for i in range(len(monotone)) if monotone[i]] 45 | return {'count_of_nonmonotone':monotone.count(True), 'index_of_nonmonotone':index_of_nonmonotone} 46 | 47 | 48 | def _BinBadRate(df, col, target, grantRateIndicator=0): 49 | ''' 50 | 标签类别计数统计 51 | :param df: 需要计算好坏比率的数据集 52 | :param col: 需要计算好坏比率的特征 53 | :param target: 好坏标签 54 | :param grantRateIndicator: 1返回总体的坏样本率,0不返回 55 | :return: 每箱的坏样本率,以及总体的坏样本率(当grantRateIndicator==1时) 56 | ''' 57 | total = df.groupby([col])[target].count() 58 | total = pd.DataFrame({'total': total}) 59 | bad = df.groupby([col])[target].sum() 60 | bad = pd.DataFrame({'bad': bad}) 61 | regroup = total.merge(bad, left_index=True, right_index=True, how='left') 62 | regroup.reset_index(drop=False, inplace=True) 63 | regroup['bad_rate'] = regroup.apply(lambda x: x.bad / x.total, axis=1) 64 | dicts = dict(zip(regroup[col],regroup['bad_rate'])) 65 | if grantRateIndicator==0: 66 | return (dicts, regroup) 67 | N = sum(regroup['total']) 68 | B = sum(regroup['bad']) 69 | overallRate = B * 1.0 / N 70 | return (dicts, regroup, overallRate) 71 | 72 | def _BadRateMonotone(df, sortByVar, target,special_attribute = []): 73 | ''' 74 | 返回是否单调结果 75 | :param df: 包含检验坏样本率的变量,和目标变量 76 | :param sortByVar: 需要检验坏样本率的变量 77 | :param target: 目标变量,0、1表示好、坏 78 | :param special_attribute: 不参与检验的特殊值 79 | :return: 坏样本率单调与否 80 | ''' 81 | df2 = df.loc[~df[sortByVar].isin(special_attribute)] 82 | if len(set(df2[sortByVar])) <= 2: 83 | return True 84 | regroup = _BinBadRate(df2, sortByVar, target)[1] 85 | combined = zip(regroup['total'],regroup['bad']) 86 | badRate = [x[1]*1.0/x[0] for x in combined] 87 | badRateNotMonotone = _FeatureMonotone(badRate)['count_of_nonmonotone'] 88 | if badRateNotMonotone > 0: 89 | return False 90 | else: 91 | return True -------------------------------------------------------------------------------- /riskscore/utils/tools.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | 小工具类 4 | """ 5 | import math 6 | import pandas as pd 7 | import numpy as np 8 | from sklearn.metrics import roc_curve,auc,f1_score,recall_score,precision_score,accuracy_score 9 | 10 | def Prob2Score(prob, basePoint=600,PDO=50): 11 | # 将概率转化成分数且为正整数 12 | y = np.log(prob/(1-prob)) 13 | y2 = basePoint+PDO/np.log(2)*(-y) 14 | score = y2.astype("int") 15 | return score 16 | 17 | def str2num(s): 18 | """ 19 | 字符转换为数字 20 | """ 21 | try: 22 | if '.' in str(s): 23 | return float(s) 24 | else: 25 | return int(s) 26 | except: 27 | return s 28 | 29 | def feature_subgroup(df, index,columns): 30 | 31 | """ 32 | 特征分组 33 | :param index:list 34 | :param columns:list 35 | :return: df 36 | """ 37 | g = df.groupby(index).agg({col: 'nunique' for col in columns}) 38 | if g[g > 1].dropna().shape[0] != 0: 39 | print("index非唯一值.") 40 | return df.groupby(index).agg({col: 'max' for col in columns}) 41 | 42 | def groupby_key(df,user_id,content,label): 43 | """ 44 | 根据user_id 合并content 和label列 45 | :param data: dataframe 46 | :param user_id: 47 | :param content: 文本列 48 | :param label:目标文件 49 | :return: dataframe 50 | """ 51 | df[content] = df[content].astype("str") 52 | content_Series = df.groupby(by=user_id)[content].sum() 53 | content_df = pd.DataFrame({"user_id":content_Series.index,"content":content_Series.values}) 54 | label_df = df[[user_id,label]].drop_duplicates() 55 | df= pd.merge(content_df,label_df,on=user_id,how="inner") 56 | return df 57 | 58 | 59 | def best_prob(y_true,y_prob): 60 | """ 61 | cut best prob 62 | :param y_prob: y of prediction 63 | :param y_true: real y 64 | :return: ks_value and draw ks 65 | """ 66 | fpr, tpr, thr = roc_curve(y_true, y_prob) 67 | max_ks = 0 68 | cut_prob = 0.5 69 | for i in range(len(thr)): 70 | if abs(fpr[i] - tpr[i]) > max_ks: 71 | max_ks = abs(fpr[i] - tpr[i]) 72 | cut_prob = thr[i] 73 | return cut_prob 74 | 75 | def model_norm(y_true,y_prob): 76 | """ 77 | 计算模型指标,auc,ks,f1,recall,precision,accuracy,cut_prob 78 | :param y_true:like-array 79 | :param y_prob:like-array 80 | :return:norm dict 81 | """ 82 | norm = dict() 83 | fpr, tpr, thr = roc_curve(y_true, y_prob) 84 | KS = 0 85 | cut_prob = 0.5 86 | for i in range(len(thr)): 87 | if abs(fpr[i] - tpr[i]) > KS: 88 | KS = abs(fpr[i] - tpr[i]) 89 | cut_prob = thr[i] 90 | norm["AUC"] = auc(fpr, tpr) 91 | norm["KS"] = KS 92 | norm["cut_prob"] = cut_prob 93 | y_pred = np.array([1 if i > cut_prob else 0 for i in y_prob]) 94 | norm["recall"] = recall_score(y_true=y_true,y_pred=y_pred,pos_label=1,average='binary') 95 | norm["precision"] = precision_score(y_true=y_true,y_pred=y_pred,pos_label=1,average='binary') 96 | norm["accuracy"] = accuracy_score(y_true=y_true,y_pred=y_pred) 97 | norm["f1"] = f1_score(y_true=y_true,y_pred=y_pred,pos_label=1,average='binary') 98 | return norm 99 | 100 | def calculate_AIC(X,y_true,y_prob): 101 | """ 102 | 赤池信息准则AIC计算 103 | :param X: like-array 104 | :param y_true:like-array 105 | :param y_prob:like-array 106 | :return: float AIC 107 | """ 108 | aic = 2 * X.shape[1] + X.shape[0] * math.log(pow(y_true - y_prob,2).sum() / X.shape[1]) 109 | return aic 110 | 111 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | ############################################# 4 | # Author: huangsir 5 | # Mail: hxysir@163.com 6 | # Created Time: 2019-10-22 18:00 7 | ############################################# 8 | 9 | from setuptools import setup,find_packages 10 | 11 | setup( 12 | name = "riskscore", 13 | version = "1.3", 14 | author="huangsir", 15 | author_email="hxysir@163.com", 16 | url = "https://github.com/huangxianyang/creditmodel", 17 | license = "MIT License", 18 | description = "build credit score model", 19 | long_description = "build credit score model, include etl eda preprocessing featureengineering trainmodel riskstragety eg", 20 | platforms = "any", 21 | keywords = ["pip", "EDA","Preprocessing", "FeatureEngineering", "Modeling","RiskStragety"], 22 | 23 | packages = find_packages(),# 所有包含__init__.py文件的目录 24 | package_data={'riskscore':['data/*.csv','data/*.txt']}, 25 | # include_package_data = True, 26 | install_requires = ['pymongo==3.7.2','PyMySQL==0.9.3','missingno==0.4.1','pandas-profiling==1.4.1','numpy==1.15.0', 27 | 'matplotlib==3.0.2','pandas==0.23.4','scikit-learn==0.21.3','imbalanced-learn==0.5.0', 28 | 'statsmodels==0.9.0','scorecardpy==0.1.7','seaborn==0.9.0','bayesian-optimization==1.0.1'] 29 | ) --------------------------------------------------------------------------------