特征开发总结

(1)数据结构的选择

在进行特征开发时经常会遇到数据的增删改查的问题,为了代码更高效的运行,需要选择合适的数据结构:

  1. 写数据时,用列表list

  2. 查找数据时,用字典dict (或集合set)

(2)如果枚举型参数较多,可以将参数写入列表或者字典中,避免平铺,使得代码更简练

需要进行大量的判断时或者重复性操作时,也可以将参数写入列表中,改为循环,避免平铺,使得代码简练

例如:

def degree_1_overdue_rate():
	#统计用户的一度联系人中,逾期天数分别超过7,15,30和60日的人数以及占比
    degree_1_sp_overdue_day_list = np.random.randint(-1,50,20)
    overdue_labels = [7, 15,30, 60]
    result = {}
    for overdue_label in overdue_labels:
        result[overdue_label] = {}
        result[overdue_label]["num"]=0.0
        result[overdue_label]["rate"]=0.0
    total_count = sum(degree_1_sp_overdue_day_list>=0)
    for overdue_label in overdue_labels:
        result[overdue_label]["num"] = sum(degree_1_sp_overdue_day_list>=overdue_label)
        result[overdue_label]["rate"] = result[overdue_label]["num"] / total_count
    i = 1000
    fmap = {}
    for overdue_label in overdue_labels:
        for j in ["num", "rate"]:
            fmap[i] = result[overdue_label][j]
            i += 1
    for k in fmap:
        print k, fmap[k]

(3)将代码模块化|工具化

将代码中频繁用到的步骤,抽出来写到方法中,这样编码即简练又能解耦合

例如:

初始化操作,初始化字典

除法操作

数据格式化

连接数据库

从字典中查数据

计算列表的统计值:统计列表的最大值、最小值、平均值等

给定订单的应还款日期和已还款日期,计算逾期天数

字符串转化为日期

字符串转化为时间戳

(4)挖掘特征时,要将特征有条理的分类,将每一类特征写到一个函数中,函数中可以包含子函数

def init(self):

        self.feature_map = {

        400000:self.category_feature,        #商品类别名称相关

        400001:self.shouquan_feature,        #授权实名相关

        400100:self.order_list_feature,      #购物订单相关

        400110:self.asserts_basic_feature,    #用户资产相关

	}

def order_list_feature(self):

        def make_time_feature(xx,xx):

             .....

        def make_month_feature(xx,xx):

             ......

        ........

                fmap = make_time_feature(fmap, num_all)

                fmap = make_month_feature(fmap, month_money)

                fmap = make_address_feature(fmap, phone_dict, address_dict,person_dict,real_name, order_phone, address_money_dict, cate_finsh_all,cate_address_num)
         

(5)集合的浅拷贝和深拷贝

参考:https://www.cnblogs.com/Pengdachui-1/p/10995635.html

集合的复制:浅拷贝,只拷贝第一层,第二层的数据区域是一样的

深拷贝:拷贝所有的数据,不管是第一层还是第N层,两个变量的数据域互不影响。

import copy

copy.deepcopy()

(6)用户的profile信息如何衍生特征:

  • 性别
  • 年龄分段
  • 手机的运营商(前三位)
  • 用户所属的省ID
  • 用户所属的城市的级别(第N线城市)
  • 用户所属的省市是否含“自治”
  • 是否填写了某些可选项
  • 交叉验证
  • 紧急联系人跟本人的关系(是否有同姓的)
  • 用户的渠道来源
  • 用户的profile是否在黑名单中
  • sp授权时间与申请订单的时间的差
  • 行业编号
  • 职业编号
  • sos类型编号

(7)使用numpy 和pandas进行数据的统计和转化

pandas不仅替代了循环操作,使得代码更简洁,而且向量运算也加快了代码的执行效率,更重要的是pandas中大量的数据统计的方法使编程更简单

例如:

统计序列中每个值出现的次数

统计序列去重后的个数

统计序列的最小值、最大值、分位数等

序列的分桶pd.cut() | pd.qcut()

序列的one-hot编码

筛选某几列特征

 	arr = np.random.randint(0,10,size=(10,5))

    df = DataFrame(arr)

    df.columns = list("abcde")

    #批量统计某几列的特征

    sub_cols = ["a","c","d"]

    df_2 = df[sub_cols].apply(lambda k:sum(k==1), axis=0)

    #print df_2
    
	#统计序列中某些值出现的次数以及占比

    total_count = df["a"].size

    print "total_count:", total_count

    df_3 = df["a"].value_counts()

    dic = df_3.to_dict()

    for k in [1,3,5]:

        count = dic.get(k, 0)

        rate = count / total_count

        print k, count, rate   

(8)分层批量计算特征:

以device_call_feature.py为例:

层级结构如下:

sos1|sos2

​ —>phone|name

​ —>最近半年|最近3个月

​ -----> 时段:工作日|周末|白天|黑夜|凌晨

​ ---->通话类型:打入|打出|未接通|挂断|全部

​ ---->联系密度:次数|时长

​ —>统计:最高时长|最低时长|平均时长

​ ---->统计:距离订单申请的最远天数|最近天数

分类型

  1. 通话次数相关:

​ (全部 |最近一个月)(打入|打出|未接通|挂断|全部)的通话(次数|占比)

​ (全部 |最近一个月)(固话| 手机号| 大于11位号码 | 小于11位号码)的(打入|打出|未接通|挂断|全部)通话(次数| 占比)

​ (全部 |最近一个月)(工作日|周末|白天|黑夜|凌晨)的(打入|打出|未接通|挂断|全部)通话(次数|占比)

​ (全部 |最近一个月) 通话时长为0 的(打入|打出|未接通|挂断|全部)通话(次数|占比)

​ (全部 |最近一个月)(有通讯录姓名| 无通讯录姓名)的(打入|打出|未接通|挂断|全部)通话(次数|占比)

​ (全部 |最近一个月)(打入|打出|未接通|挂断|全部)的 催收电话 的通话次数、占固话的比例

  1. 通话时长相关:

​ 最近一个月(打入|打出|未接通|挂断|全部)的(最大、最小、平均)通话时长


#计算特征值

  def feature_generator(self, all_call):

        fmap = self.init()

        if all_call == None or len(all_call) == 0:

            return fmap

        tmp_dic = { 0:[], 1:[], 2:[], 3:[]}

        feature_list = ['week_days','weekends','day','night','midnight']

        feature_list2 = ['max','min','average']

        feature_list3 = [ "persons", "persons_times",  "persons_duration", "persons_average" , "max_persons_duration", "max_persons_average"]

        feature_list4 = [ "long", "v_long", "short", "self_phone", "comp_phone", "long_ratio", "v_long_ratio", "short_ratio", "self_phone_ratio", "comp_phone_ratio"]

        feature_list5 = [ 'name', 'anonymous', 'name_ratio', 'anonymous_ratio' ]

        type_list = [0,1,2,3]

        #区分通话类型构造字典

        for line in all_call:

            if int(line['type']) in tmp_dic:

                tmp_dic[ int(line['type']) ].append( line)

        for line in  type_list:

            #统计每种通话类型的(全部|电话类型|长短号码|正常异常姓名)的通话次数|时长|占比

            tmp_dura = self.duration( tmp_dic[ line ] )

            tmp_week = self.week_weekends( tmp_dic[ line ] )

            tmp_per = self.personal_info(  tmp_dic[ line ] )

            tmp_len = self.phone_len( tmp_dic[ line ] )

            tmp_name = self.name_check( tmp_dic[ line ] )

        for word in feature_list:

                fmap[ str(line)+'_'+str(word) ] = ( str(tmp_week[word]) )

            for word in feature_list2:

                fmap[ str(line)+'_'+str(word) ] = ( str(tmp_dura[word]) )

            for word in feature_list3:

                fmap[ str(line)+'_'+str(word) ] = ( str(tmp_per[word]) )

            for word in feature_list4:

                fmap[ str(line)+'_'+str(word) ] = ( str(tmp_len[word]) )

            for word in feature_list5:

                fmap[ str(line)+'_'+str(word) ] = ( str(tmp_name[word]) )

        return fmap

(9)交叉验证

用户是否跟填写的紧急联系人有短信联系,几个有联系

用户是否跟填写的紧急联系人通话记录,一般在哪个时段联系,通话的时长、次数等

用户填写的紧急联系人是否异常,是否是代办|中介,是否也在平台接过款,是否正在借款中

用户填写的紧急联系人的关系是否是亲属关系,如果是的话,是否同姓?

用户填写的居住城市是否在gps出现过的城市中

(10)数据格式化(数据清洗)

  1. 一般原始的数据是不能直接使用的,需要首先转化为我们需要的格式类型
  2. 原始数据中有一些脏数据比如空值、异常值等,需要剔除掉
  3. 原始数据中不是所有的字段都是必要的,需要提取我们需要的字段

注意:

  1. 在格式化数据时,在函数的开头必须先初始化,否则会报错
  2. 从字典中取数时,一定要用get()设置默认值,否则容易抛异常
  3. 在函数的开头最好表明:原始数据输入格式,格式化后的输出格式, 以及过滤的规则

数据格式化后,需要根据时间对格式化数据排序,然后提取序列数据

然后对序列数据进行统计,如计算最大值、最小值、均值、方差、中位数、个数等

(11)在计算特征时,最好根据特征的类型,分开计算

例如:

区分字符串型特征| 枚举型特征| 数值型特征,便于后续格式化特征

字符串特征:手机的型号、手机的品牌、用户的居住城市

枚举型特征:是否root、使用该设备的用户是否唯一、当前使用的网络是否是wifi

连续型特征:用户更换的 设备数、该设备登录过几个用户

def init(self):

        self.feature_map = {
            100: self.device_string_feature, #字符串性特征
            101: self.device_enumerated_feature, #枚举型特征 
            102: self.device_continuous_feature, #连续型特征
        }

(12)时间型数据处理–时间窗口化

在特征工程中,近期的数据一般包含的信息量更准确,往往需要按时间窗口筛选数据,例如:筛选最近一个月的通话数据、最近3个月的订单数据、最近半年的多平台数等

dateutil模块的relativedelta()函数有年月周日时分秒的参数,来设置时间窗口,在实际中使用较广。而datetime模块的timedelta()仅有周、日的参数,有局限。

import datetime
from dateutil.relativedelta import relativedelta
now_date = datetime.datetime.now()
print "now_date:", now_date
two_year_bf = now_date - relativedelta(months=24)
print two_year_bf
print now_date + datetime.timedelta(days=2)
print now_date + datetime.timedelta(weeks=1)
print "**************************"
print now_date + relativedelta(months=1)
print now_date + relativedelta(years=1)
print now_date + relativedelta(days=1)
print now_date + relativedelta(weeks=1)
print now_date + relativedelta(minutes=1)
print now_date + relativedelta(hours=1)
print now_date + relativedelta(seconds=1)
--------------------result-------------------
now_date: 2020-01-07 19:36:32.935444
2020-01-08 19:36:32.935444
2020-01-14 19:36:32.935444
******************************
2021-01-07 19:36:32.935444
2020-02-07 19:36:32.935444
2020-01-08 19:36:32.935444
2020-01-14 19:36:32.935444
2020-01-07 20:36:32.935444
2020-01-07 19:37:32.935444
2020-01-07 19:36:33.935444

(13)时间型数据的处理1-转化为离散型

在挖掘特征时,需要查看用户的行为发生时间是否异常 或者 用户在网页或者app上的浏览偏好,如:订单的创建日期等 是工作日、周末、白天、前半夜还是后半夜,那么今天就来看看如何计算这些特征

如何计算某个日期是周几呢?有以下两种方法:

(1)date_.strptime("%w")

(2)date_.isoweekday()

获取某个日期的小时,也有两种方法:

(1)date_.strptime("%H")

(2)date_.hour

from dateutil.relativedelta import relativedelta
import datetime

#判断是否是工作日、周末、白天、前半夜、后半夜等
def week_weekends():
    call_list = []
    for i in range(10):
        call_list.append(datetime.datetime.now()+relativedelta(hours=i))

    tmp_dic = {
        'week_days' : 0,
        'weekends' : 0,
        'day' : 0,
        'night' : 0,
        'midnight': 0
            }
    for tmp_time in call_list:
        print tmp_time
        #if tmp_time.strptime("%w") in [1,2,3,4,5]:
        if tmp_time.isoweekday() in [1,2,3,4,5]:  
            tmp_dic['week_days'] += 1
        #elif tmp_time.strptime("%w") in [1,2,3,4,5]:
        elif tmp_time.isoweekday() in [6,7]:
            tmp_dic['weekends'] += 1
        if tmp_time.hour in range(6,18):
            tmp_dic['day'] += 1
        elif tmp_time.hour in range(18,24):
            tmp_dic['night'] += 1
        elif tmp_time.hour in range(0,6):
            tmp_dic['midnight'] += 1
            
    for k,v in tmp_dic.items():
        print k,v

week_weekends()

(14)时间型数据的处理2-转化为连续型

什么是序列性数据?指有时间先后顺序的行为数据

例如:

  1. 用户在某电商平台的浏览行为列表,每个浏览行为包括:时间戳、页面ID、埋点ID(页面上某个链接、按钮等的ID)
  2. 用户在互联网金融借贷平台上的浏览行为

如何挖掘特征呢?

首先获取用户最近一段时间内的用户序列数据,并按照时间先后排序

前后两个浏览行为的时间差,作为用户在某个埋点|页面的停留时长;注意需要设置可以接受的最大时间间隔;若行为之间间隔大于改值时,认为已经离开app,且该时间间隔不算在内

统计用户在每个埋点|页面的点击次数和停留(浏览)总时长作为特征

统计用户首次编辑身份证等号码类的时间间隔

(15)特征的编码规范

采用10位编码

1-3位:标识数据源

​ -----> 4-6位: 标识特征组

​ -----> 7-10位:标识具体特征项

对于特征组:

  • 000:字符串型特征:取值没有可比性,类型:string
  • 001-099:整数枚举型特征:原始取值大小是可比较的,如:"xx等级|级别’:1,2,3 分别对应低级、中级、高级,类型:int
  • 100-999:连续型特征,类型:int or float

对于特征:

对于(-1, 1)之间的浮点数,保留小数点后8位

对于<-1 || >1的浮点数,保留小数点后4位

对于整数,不保留小数点

err:特征生成代码的错误

nan:缺失

inv:无效

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值