02标准建模流程:特征工程

1.特征构建

1.1 特征延伸

1.1.1 类别特征编码

对于离散特征来说,一般编码后才可输入模型,常见的编码方法如下:
● one-hot编码
将一列文本数据转化成一列或多列只有0和1的数据,如性别取值有男生、女生两种,那one-hot后,男对应的编码为1 0,女对应的编码为0 1
● label encoding
将一列文本数据转化成数值,例如,[red, blue, red, yellow] = [0,2,0,1]
注意事项:
● 当特征数量比较多时,one-hot会造成维度爆炸
● 当类别取值相互独立时优先使用one-hot编码

1.1.2 时间特征处理思路

● 时间戳本身特征
直接使用Pandas的series提取时间戳特征,比如说哪年、哪季度、哪月、哪周、哪日、哪时、哪分、哪秒、年里的第几天、月里的第几天、周里的第几天。
● 0-1特征
一般是与真实场景结合来用,比如说工作日、周末、公众假日(春节、端午节、中秋节等)、X初、X中、X末(X代表年、季度、月、周)、特殊节日(如运营暂停、服务暂停)、日常习惯叫法(如清晨、上午、中午、下午、傍晚、夜晚、深夜、凌晨),从而可以衍生出:
○ 是否工作日
○ 是否春节
○ 是否月初
○ 是否服务期外
○ 是否凌晨
○ 等等等等
● 时间差特征
一般也是与真实场景结合来用,比如说工作日、周末等等,比如:
○ 距离春节还有N天
○ 距离周末还有N天
○ 举例下月初还有N天
○ 等等等等

1.1.3 特征延伸思路

思路一:特征=时间周期+一个或多个修饰词(维度)+原子指标+聚合函数,如30天内xxapp的理财金额总和,其中30天为时间周期,xxapp为维度,理财金额为原子指标,总和为聚合函数。
如:【近1/3/7天】/【近3/7/30天】的【医疗类消费】【均值、累计值、计数】。

● 原子指标:原子指标和度量含义相同,基于某一业务事件行为下的度量,是业务定义中不可再拆分的指标,具有明确业务含义的名词 ,如支付金额。原子指标描述的其实是一种指标的类型,比如授信金额,已用额度等
● 时间周期:不同的时间切片长度,例如近1个月,近3个月,最近一次,最远一次等
● 修饰词(维度):维度是描述事情的角度 ,依赖于指标,如性别,地区,所属机构等
● 聚合函数:聚合函数对一组值执行计算并返回单一的值,例如求和,均值,方差,最大值,最小值,计数等
思路二:特征之间的四则运算,如商品在过去一段时间内点击量在整体的占比/上周商品点击量和本周商品点击量的差值/最近6个月内,最近一次消费到现在的月份数等
思路三:特征之间做比较,如消费金额的排名、地区热门购买产品排行榜等
在这里插入图片描述

2 特征评价

通过评价指标进行特征筛选,以减少无意义的特征,减少入模特征数量,提高建模代码运行速度,提高建模效率。

2.1 常见指标

2.1.1 IV值

刻画了一个特征对好坏用户分布的区分程度
∑ i = 1 N ( g o o d % − b a d % ) ∗ l n ( g o o d % / b a d % ) \sum_{i=1}^N(good\%-bad\%)*ln(good\%/bad\%) i=1N(good%bad%)ln(good%/bad%),其中N为箱子个数,good%是这个箱中的优质客户占所有特征中所有优质客户的比例,bad%是这个箱子里的坏客户占整个特征中所有坏客户的比例。一般选择IV>0.02的特征

import numpy as np
import pandas as pd
import copy

def cut_width(df, inputcol, labelcol='label', bins=10):
    """
    等宽分箱
    :param dataset: DataFrame,计算数据
    :param inputcol: String,待分箱列列名
    :param labelcol: String,目标列列名
    :param bins: int,正整数,分箱数
    :return:
        df: DataFrame,分箱后结果
    """
    df[inputcol] = pd.qcut(x=df[inputcol], q=bins)
    df = pd.crosstab(index=df[inputcol], columns=df[labelcol], margins=False)
    df.columns = ['neg','pos']
    return df

def calculate_woe_iv(dataset):
    """
    对分箱后的特征计算WOE和IV
    :param dataset:DataFrame,计算数据,需要在特征分箱后的数据
    :return:
        iv: float,iv值
        df:DataFrame,woe和IV计算后结果
    """
    df = copy.copy(dataset)
    df['pos_rate'] = (df['pos'] + 1) / df['pos'].sum()  # 计算每个分组内的响应(Y=1)占比,加1为了防止在计算woe时分子分母为0
    df['neg_rate'] = (df['neg'] + 1) / df['neg'].sum()  # 计算每个分组内的未响应(Y=0)占比
    df['woe'] = np.log(df['pos_rate'] / df['neg_rate'])  # 计算每个分组的WOE
    df['iv'] = (df['pos_rate'] - df['neg_rate']) * df['woe']  # 计算每个分组的IV
    iv = df['iv'].sum()
    return iv, df

if __name__ == '__main__':
    data = pd.DataFrame({'uid': list(range(10)), 'label': [0, 0, 1, 1, 0, 1, 0, 0, 0, 1], 'f1': list(range(3, 13))})
    w = cut_width(data,'f1',bins=4)
    iv,df = calculate_woe_iv(w)
    print(iv)

2.1.2 PSI

用于衡量测试集和训练集直接数据分布差异性,是模型稳定性的常见指标,psi过高表示在不同数据集上特征分布表现相差过大,无需入模
∑ i = 1 N ( A c t u a l % − E x p e c t e d % ) ∗ l n ( A c t u a l % / E x p e c t e d % ) \sum_{i=1}^N(Actual\%-Expected\%)*ln(Actual\%/Expected\%) i=1N(Actual%Expected%)ln(Actual%/Expected%),其中,N是分箱数量,Actual是实际占比,Expected是预期占比。一般来说,放弃PSI大于0.25的特征。

import pandas as pd
import numpy as np

def cal_psi(actual, predict, bins=10):
    """
    功能: 计算PSI值,并输出实际和预期占比分布曲线
    :param actual: Array或series,代表训练集特征A分数
    :param predict: Array或series,代表测试集特征A分数
    :param bins: 箱数
    :return:
        psi: float,PSI值
        psi_df:DataFrame
    """
    actual_min = actual.min()  # 训练集中的最小值
    actual_max = actual.max()  # 训练集中的最大值
    binlen = (actual_max - actual_min) / bins
    cuts = [actual_min + i * binlen for i in range(1, bins)]#设定分组
    cuts.insert(0, -float("inf"))
    cuts.append(float("inf"))
    actual_cuts = np.histogram(actual, bins=cuts)#将actual等宽分箱
    predict_cuts = np.histogram(predict, bins=cuts)#将predict按actual的分组等宽分箱
    actual_df = pd.DataFrame(actual_cuts[0],columns=['actual'])
    predict_df = pd.DataFrame(predict_cuts[0], columns=['predict'])
    psi_df = pd.merge(actual_df,predict_df,right_index=True,left_index=True)
    psi_df['actual_rate'] = (psi_df['actual'] + 1) / psi_df['actual'].sum()#计算占比,分子加1,防止计算PSI时分子分母为0
    psi_df['predict_rate'] = (psi_df['predict'] + 1) / psi_df['predict'].sum()
    psi_df['psi'] = (psi_df['actual_rate'] - psi_df['predict_rate']) * np.log(
        psi_df['actual_rate'] / psi_df['predict_rate'])
    psi = psi_df['psi'].sum()
    return psi, psi_df

2.1.3 特征重要性

对于lightgbm.feature_importance(importance_type=‘split’)可输出特征的重要度排名,其中importance_type可选split和gain两种,split(默认)表示特征在所有树中作为划分属性的次数,gain表示特征在作为划分属性时总loss降低量。

print(pd.DataFrame({
        'column': feature_names,
        'importance': lgb_trained_model.feature_importance(),
    }).sort_values(by='importance'))

通过以上代码可得到特征重要度的排名,可根据需要,选择topN的特征

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值