首页 > 数据库 > 数据库开发技术 > 竞赛姿势必会:特征工程中的奇淫技巧
结合之前的手动特征工程Part1更好理解特征解释性 & Featuretools自动特征工程,这是特征工程系列的第三篇文章了,这里将会介绍一些奇淫技巧,这些方法其实已经占据我们特征工程工作中的绝大部分,希望对你有所帮助。
如果给出np.nan到LGBM,然后在每个树节点上拆分,它会拆分非nan值,然后根据哪个最好将所有nan发送给左子节点或右子节点。因此,nan在每个节点上都得到了特殊处理,可能会出现过拟合。通过简单地将所有NAN转换为小于所有非NAN值的负数(例如-999);
df = pd.concat([train[col],test[col]],axis=0)
df[col].fillna(-999, inplace=True)
那么LGBM将不再过度处理NAN。相反,它将给予它和其他数字一样的关注。过拟合虽然不是很好,但是两种方法都试一试,看看哪一种线下分数反馈最高。
目标编码(分解)将一个(string,categroy,object)列转换为整数。之后,您可以根据max是否小于128,小于32768,或者更大,将其强制转换为int8, int16或int32。分解会减少内存,并将NAN变成一个影响如上所述的CV和LB的数字(即-1)。分解还可以选择将类别变量视为如下所述的数值;
df[col],_ = df[col].factorize()
if df[col].max()<128: df[col] = df[col].astype('int8')
elif df[col].max()<32768: df[col] = df[col].astype('int16')
else: df[col].astype('int32')
此外,为了减少内存,人们在其他列上使用流行的memory_reduce函数。一种更简单、更安全的方法是将所有float64转换为float32,将所有int64转换为int32。
for col in df.columns:
if df[col].dtype=='float64': df[col] = df[col].astype('float32')
if df[col].dtype=='int64': df[col] = df[col].astype('int32')
或者使用封装好的memory_reduce函数
def reduce_mem(df):
start_mem = df.memory_usage().sum() / 1024 ** 2
for col in df.columns:
col_type = df[col].dtypes
if col_type != object:
c_min = df[col].min()
c_max = df[col].max()
if str(col_type)[:3] == 'int':
if c_min > np.iinfo(np.int8).min and c_max < np.iinfo(np.int8).max:
df[col] = df[col].astype(np.int8)
elif c_min > np.iinfo(np.int16).min and c_max < np.iinfo(np.int16).max:
df[col] = df[col].astype(np.int16)
elif c_min > np.iinfo(np.int32).min and c_max < np.iinfo(np.int32).max:
df[col] = df[col].astype(np.int32)
elif c_min > np.iinfo(np.int64).min and c_max < np.iinfo(np.int64).max:
df[col] = df[col].astype(np.int64)
else:
if c_min > np.finfo(np.float16).min and c_max < np.finfo(np.float16).max:
df[col] = df[col].astype(np.float16)
elif c_min > np.finfo(np.float32).min and c_max < np.finfo(np.float32).max:
df[col] = df[col].astype(np.float32)
else:
df[col] = df[col].astype(np.float64)
end_mem = df.memory_usage().sum() / 1024 ** 2
print('{:.2f} Mb, {:.2f} Mb ({:.2f} %)'.format(start_mem, end_mem, 100 * (start_mem - end_mem) / start_mem))
gc.collect()
return df
对于类别变量,你可以选择告诉LGBM它们是类别的,或者你可以告诉LGBM把它当作数字(事先标记编码)。无论哪种方式,LGBM都可以提取类别特征。两种方法都试一试,看看哪一种线下反馈最高。在目标编码之后,对category执行以下操作,或者对numeric将其保留为int
df[col] = df[col].astype('category')
单列(字符串或数字)可以通过拆分变成两列。
例如字符串列“id_30”(如Mac OS X 10_9_5)可以分为操作系统“Mac OS X”和版本号“Version 10_9_5”(这就造出两个特征)。
例如数字列TransactionAmt 1230.45可以分为美元1230和美分45。
LGBM不能单独看到这些碎片,你需要把它们分开。
两个(字符串或数字)列可以合并成一个列。例如card1和card2可以成为一个新的列
df['uid'] = df[‘card1’].astype(str)+’_’+df[‘card2’].astype(str)
这有助于LGBM模型学习,因为card1和card2本身可能与目标不相关,因此LGBM不会在树节点上分割它们。但是交叉特征uid = card1_card2可能与target相关,现在LGBM将对其进行拆分。
数字列可以与加、减、乘、除等相结合。一个数字例子是:
df['x1_x2'] = df['x1'] * df['x2']
频率编码是一种强大的技术,它允许LGBM查看列值是罕见的还是常见的(即频率高低)。例如,如果你想让LGBM学习到哪些信用卡不常使用,比如
temp = df['card1'].value_counts().to_dict()
df['card1_counts'] = df['card1'].map(temp)
为LGBM提供组的统计信息允许LGBM确定某个值对于特定组是常见的还是罕见的。通过pandas提供3个变量来计算组的统计信息,例如
temp = df.groupby('card1')['TransactionAmt'].agg(['mean'])
.rename({'mean':'TransactionAmt_card1_mean'},axis=1)
df = pd.merge(df,temp,on='card1',how='left')
这里的特征向每行添加该行card1组的平均TransactionAmt。因此,LGBM现在可以判断一行是否有其card1组的异常TransactionAmt。
对列本身归一化
df[col] = ( df[col]-df[col].mean() ) / df[col].std()
或者你可以将一列与另一列标准化。比如,创建了一个组的统计信息来表示D3的平均值。然后你可以通过下面计算,新变量D3_remove_time不再随着时间的推移而增加,因为我们已经根据时间的影响对其进行了规范化。
df['D3_remove_time'] = df['D3'] - df['D3_week_mean']
通常情况下,我们希望消除数据中的异常值,因为他们会影响我们的模型学习能力。这个时候可以考虑使用平滑技术,这个方法背后的思想是确定和删除不常见的值。
如果这对你的工作生活有帮助,点赞关注一键三连,支持我的工作。
原文来自知乎,点击阅读原文,获取更多内容。
扫码加我好友进微信群|QQ群


原文作者:ChallengeHub;如有侵权,请联系管理员删除。