1313from sklearn .utils import check_array
1414from sklearn .metrics import f1_score , recall_score , accuracy_score , precision_score
1515
16- from .processing import feature_bin_stats
16+ from .processing import feature_bin_stats , Combiner
1717
1818
1919def _get_context (X , feature_names ):
@@ -75,6 +75,24 @@ def json2expr(data, max_index, feature_list):
7575
7676class Rule :
7777 def __init__ (self , expr ): # expr 既可以传递字符串,也可以传递dict
78+ """规则集
79+
80+ :param expr: 类似 DataFrame 的 query 方法传参方式即可,目前仅支持数值型变量规则
81+
82+ **参考样例**
83+
84+ >>> from scorecardpipeline import *
85+ >>> target = "creditability"
86+ >>> data = germancredit()
87+ >>> data[target] = data[target].map({"good": 0, "bad": 1})
88+ >>> data = data.select_dtypes("number") # 暂不支持字符型规则
89+ >>> rule1 = Rule("duration_in_month < 10")
90+ >>> rule2 = Rule("credit_amount < 500")
91+ >>> rule1.report(data, target=target)
92+ >>> rule2.report(data, target=target)
93+ >>> (rule1 | rule2).report(data, target=target)
94+ >>> (rule1 & rule2).report(data, target=target)
95+ """
7896 self ._state = RuleState .INITIALIZED
7997 self .expr = expr
8098
@@ -113,9 +131,22 @@ def predict(self, X: DataFrame, part=""): # dict预测对应part_dict 、字符
113131
114132 return result
115133
116- def report (self , datasets , target = "target" , overdue = "overdue" , dpd = - 1 , del_grey = False , valid = None , desc = "" , return_cols = None , prior_rules = None ):
134+ def report (self , datasets , target = "target" , overdue = "overdue" , dpd = - 1 , del_grey = False , desc = "" , return_cols = None , prior_rules = None ) -> pd .DataFrame :
135+ """规则效果报告表格输出
136+
137+ :param datasets: 数据集,需要包含 目标变量 或 逾期天数,当不包含目标变量时,会通过逾期天数计算目标变量,同时需要传入逾期定义的DPD天数
138+ :param target: 目标变量名称,默认 target
139+ :param desc: 规则相关的描述,会出现在返回的表格当中
140+ :param return_cols: 指定返回的字段列表,默认不传
141+ :param prior_rules: 先验规则,可以传入先验规则先筛选数据后再评估规则效果
142+ :param overdue: 逾期天数字读名称
143+ :param dpd: 逾期定义方式,逾期天数 > DPD 为 1,其他为 0,仅 overdue 字段起作用时有用
144+ :param del_grey: 是否删除逾期天数 (0, dpd] 的数据,仅 overdue 字段起作用时有用
145+
146+ :return: pd.DataFrame,规则效果评估表
147+ """
117148 if return_cols is None :
118- return_cols = ['指标名称' , "指标含义" , '分箱' , '样本总数' , '样本占比' , '好样本数' , '好样本占比' , '坏样本数' , '坏样本占比' , '坏样本率' , 'LIFT值' , '分档KS值' ]
149+ return_cols = ['指标名称' , "指标含义" , '分箱' , '样本总数' , '样本占比' , '好样本数' , '好样本占比' , '坏样本数' , '坏样本占比' , '坏样本率' , 'LIFT值' ]
119150 if desc is None or desc == "" and "指标含义" in return_cols :
120151 return_cols .remove ("指标含义" )
121152
@@ -130,14 +161,16 @@ def report(self, datasets, target="target", overdue="overdue", dpd=-1, del_grey=
130161 rule_expr = self .expr
131162
132163 if prior_rules :
133- prior_tables = prior_rules .report (datasets , target = target , overdue = overdue , dpd = dpd , del_grey = del_grey , valid = valid , desc = desc , return_cols = return_cols , prior_rules = None )
164+ prior_tables = prior_rules .report (datasets , target = target , overdue = overdue , dpd = dpd , del_grey = del_grey , desc = desc , return_cols = return_cols , prior_rules = None )
134165 temp = datasets [~ prior_rules .predict (datasets )]
135166 rule_result = pd .DataFrame ({rule_expr : np .where (self .predict (temp ), "命中" , "未命中" ), "target" : temp [target ].tolist ()})
136167 else :
137168 prior_tables = pd .DataFrame (columns = return_cols )
138169 rule_result = pd .DataFrame ({rule_expr : np .where (self .predict (datasets ), "命中" , "未命中" ), "target" : datasets [target ].tolist ()})
139170
140- table = feature_bin_stats (rule_result , rule_expr , rules = [["命中" ], ["未命中" ]], desc = desc , return_cols = return_cols )
171+ combiner = Combiner (target = target )
172+ combiner .load ({rule_expr : [["命中" ], ["未命中" ]]})
173+ table = feature_bin_stats (rule_result , rule_expr , combiner = combiner , desc = desc , return_cols = return_cols )
141174
142175 # 准确率、精确率、召回率、F1分数
143176 metrics = pd .DataFrame ({
@@ -149,14 +182,17 @@ def report(self, datasets, target="target", overdue="overdue", dpd=-1, del_grey=
149182 })
150183 table = table .merge (metrics , on = "分箱" , how = "left" )
151184
185+ # 规则上线后增益评估
186+ # 坏账率变化情况: 上线后拒绝多少比例的坏客户同时拒绝后坏账水平多少,在原始数据基础上换张改善多少
187+ total_bad , total = table ["坏样本数" ].sum (), table ["样本总数" ].sum ()
188+ total_bad_rate = total_bad / total
189+ table ["坏账改善" ] = (total_bad_rate - (total_bad - table ["坏样本数" ]) / (total - table ["样本总数" ])) / total_bad_rate
190+
152191 if prior_rules :
153192 prior_tables .insert (loc = 0 , column = "规则分类" , value = ["先验规则" ] * len (prior_tables ))
193+ prior_tables ["坏账改善" ] = np .nan
154194 table .insert (loc = 0 , column = "规则分类" , value = ["验证规则" ] * len (table ))
155- table = pd .concat ([prior_tables , table ])
156-
157- # 规则上线后增益评估
158- # 通过率变化情况: 上线后拒绝多少客户
159- # 坏账率变化情况: 上线后拒绝多少比例的坏客户同时拒绝后坏账水平多少
195+ table = pd .concat ([prior_tables , table ]).set_index (["规则分类" ])
160196
161197 return table
162198
0 commit comments