特征锦囊:彻底了解一下WOE和IV
今日锦囊
特征锦囊:彻底了解一下WOE和IV
第一次接触这两个名词是在做风控模型的时候,老师教我们可以用IV去做变量筛选,IV(Information Value),中文名是信息值,简单来说这个指标的作用就是来衡量变量的预测能力强弱的,然后IV又是WOE算出来的。姑且先不管原理哈,我们先给出来一下结论。
IV范围 |
变量预测力 |
---|---|
<0.02 |
无预测力? |
0.02~0.10 |
弱? |
0.10~0.30 |
中等? |
`> 0.30 |
强? |
WOE的原理
实际案例
好了,上面的理论也讲了一些了,还是拿一个实际的变量来计算一下。
我们来假设一个场景,我们需要卖茶叶,然后我们不知道从哪里拿来了一份1000人的营销名单(手机号码),然后就批量添加微信好友,最后有600个手机号码可以成功搜索到微信号的,进而进行了好友添加,最终有100人成功添加到好友了。
我们这份名单上,有客户的年龄字段,那么我们可以拿来计算一下这个字段对于是否成功添加好友(响应)有多大的预测能力,我们在Excel中进行实现:
可以看出来,这个变量对于我们是否可以成功加到客户微信好友有着很强的预测能力。
Python实现
我们知道,针对连续型变量,是需要先转换为类别变量才可以进行IV值的计算的,现在我们把数据导入到Python中,原始变量是连续型变量,那么我们如何在Python里实现IV值的计算呢?如下图:(其中target=1代表响应,target=0代表未响应)
核心代码就是下面的:
def iv_count(data_bad, data_good):
'''计算iv值'''
value_list = set(data_bad.unique()) | set(data_good.unique())
iv = 0
len_bad = len(data_bad)
len_good = len(data_good)
for value in value_list:
# 判断是否某类是否为0,避免出现无穷小值和无穷大值
if sum(data_bad == value) == 0:
bad_rate = 1 / len_bad
else:
bad_rate = sum(data_bad == value) / len_bad
if sum(data_good == value) == 0:
good_rate = 1 / len_good
else:
good_rate = sum(data_good == value) / len_good
iv += (good_rate - bad_rate) * math.log(good_rate / bad_rate,2)
print(value,iv)
return iv
那么我们如何使用呢,一步一步来:
Step1:导入数据
测试数据集可以后台回复 'age' 进行获取。
data = pd.read_csv('./data/age.csv')
# 定义必要的参数
feature = data.loc[:,['age']]
labels = data['target']
keep_cols = ['age']
cut_bin_dict = {'age':[0,18,25,30,40,50,100]}
Step2:按照指定阈值分箱
按照我们之前Excel相同的分箱逻辑进行分箱:
cut_bin = cut_bin_dict['age']
# 按照分箱阈值分箱,并将缺失值替换成Blank,区分好坏样本
data_bad = pd.cut(feature['age'], cut_bin, right=False).cat.add_categories(['Blank']).fillna('Blank')[labels == 1]
data_good = pd.cut(feature['age'], cut_bin, right=False
).cat.add_categories(['Blank']).fillna('Blank')[labels == 0]
value_list = set(data_bad.unique()) | set(data_good.unique())
value_list
Step3:调用函数计算IV
iv_series['age'] = iv_count(data_bad, data_good)
iv_series
可以看得出,和我们Excel计算的结果完全一致!
? “我要打10个”版本
嗯,上面针对单个的变量IV计算是会了,那么如果有一堆需要你计算IV的变量,可以如何处理呢?其实,原理很简单,就是写个循环,这里呢已经写好了一个,大家可以参考一下的。这边有一些细节的东西需要说明一下的。
1)注意区分变量类型,数值型变量和类别型变量要区分对待。
2)注意分组后是否出现某组内的响应(未响应)数量为零的情况,如果为零需要处理一下。
代码放上,大家可以试着运行一下:
def get_iv_series(feature, labels, keep_cols=None, cut_bin_dict=None):
'''
计算各变量最大的iv值,get_iv_series方法出入参如下:
------------------------------------------------------------
入参结果如下:
feature: 数据集的特征空间
labels: 数据集的输出空间
keep_cols: 需计算iv值的变量列表
cut_bin_dict: 数值型变量要进行分箱的阈值字典,格式为{'col1':[value1,value2,...], 'col2':[value1,value2,...], ...}
------------------------------------------------------------
入参结果如下:
iv_series: 各变量最大的IV值
'''
def iv_count(data_bad, data_good):
'''计算iv值'''
value_list = set(data_bad.unique()) | set(data_good.unique())
iv = 0
len_bad = len(data_bad)
len_good = len(data_good)
for value in value_list:
# 判断是否某类是否为0,避免出现无穷小值和无穷大值
if sum(data_bad == value) == 0:
bad_rate = 1 / len_bad
else:
bad_rate = sum(data_bad == value) / len_bad
if sum(data_good == value) == 0:
good_rate = 1 / len_good
else:
good_rate = sum(data_good == value) / len_good
iv += (good_rate - bad_rate) * math.log(good_rate / bad_rate,2)
return iv
if keep_cols is None:
keep_cols = sorted(list(feature.columns))
col_types = feature[keep_cols].dtypes
categorical_feature = list(col_types[col_types == 'object'].index)
numerical_feature = list(col_types[col_types != 'object'].index)
iv_series = pd.Series()
# 遍历数值变量计算iv值
for col in numerical_feature:
cut_bin = cut_bin_dict[col]
# 按照分箱阈值分箱,并将缺失值替换成Blank,区分好坏样本
data_bad = pd.cut(feature[col], cut_bin, right=False).cat.add_categories(['Blank']).fillna('Blank')[labels == 1]
data_good = pd.cut(feature[col], cut_bin, right=False
).cat.add_categories(['Blank']).fillna('Blank')[labels == 0]
iv_series[col] = iv_count(data_bad, data_good)
# 遍历类别变量计算iv值
for col in categorical_feature:
# 将缺失值替换成Blank,区分好坏样本
data_bad = feature[col].fillna('Blank')[labels == 1]
data_good = feature[col].fillna('Blank')[labels == 0]
iv_series[col] = iv_count(data_bad, data_good)
return iv_series
调用demo:
iv_series = get_iv_series(feature, labels, keep_cols, cut_bin_dict=cut_bin_dict)
iv_series
# age 0.434409
? 总结一下
记住IV值的预测能力映射:
IV范围 |
变量预测力 |
---|---|
<0.02 |
无预测力? |
0.02~0.10 |
弱? |
0.10~0.30 |
中等? |
`> 0.30 |
强? |
如果想复现代码,可以从我的公号后台输出 'age' 去获取测试集吧,或者拿自己目前的数据集来玩玩也可以,不过得注意一些细节,转换数据格式。 GitHub传送门
https://github.com/Pysamlam/Tips-of-Feature-engineering
- 分布式唯一ID生成器Twitter 的 Snowflake idworker java版本
- 使用 Phoenix-4.11.0连接 Hbase 集群 ,并使用 JDBC 查询测试
- 高并发分布式系统中生成全局唯一Id汇总
- ZooKeeper 可视化监控 zkui
- 关于RBAC(Role-Base Access Control)的理解
- Spring Boot 中使用 Kafka
- 如何评价一段代码
- java系统高并发的解决方案
- Spring Boot 中使用 Redis
- 使用 Jedis 连接操作 Redis
- 浅析ReDoS的原理与实践
- 使用 Executors,ThreadPoolExecutor,创建线程池,源码分析理解
- CentOS+Nginx+Tomcat搭建高性能负载均衡集群
- Java 四种线程池的使用
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- windows安装openssh并通过生成SSH密钥登录Linux服务器
- SSH设置别名访问远程服务器详细介绍
- Linux检查Swap交换空间的五个命令小结
- seaborn数据总体分布的可视化策略
- 配置 Apache 服务器禁止所有非法域名 访问自己的服务器
- Ubuntu16.04源码安装Mininet
- Kotlin基础学习之Deprecated与Suppress注解使用
- Centos 7下利用crontab定时执行任务详解
- 树莓派无线上网时无屏幕下发现树莓派IP的方法
- Ubuntu18.04安装opencv 3.2.0的解决方法
- Android MVP BaseFragment 通用式封装的实现
- 腾讯云服务器Centos挂载数据盘的方法
- CentOS 8.0.1905 安装 ZABBIX4.4版本 (已验证)
- seaborn分类变量的汇总展示
- Linux查看PCIe版本及速率的方法