特征工程
目的
1、对于特征进行进一步分析,并对于数据进行处理
2、完成对于特征工程的分析,并对于数据进行一些图表分析
删除异常值
通过用3/4分为数和1/4分为数划分异常范围,之间删除异常值
#删除异常值函数
def outliers_proc(data,col_name,scale=3):
'''
用于清洗数据
:param data : pandas格式数据
:col_name : pandas 列名
:scale : 尺度
return 异常值删除后的data
'''
def box_plot_outliers(data_ser,box_scale):
'''
利用箱线图去除异常值
:data_ser : pandas.Series格式
:box_scale : 尺度
'''
iqr=box_scale*(data_ser.quantile(0.75)-data_ser.quantile(0.25))
val_low=data_ser.quantile(0.25)-iqr
val_up=data_ser.quantile(0.75)+iqr
rule_low=(data_ser<val_low)
rule_up=(data_ser>val_up)
return (rule_low,rule_up),(val_low,val_up)
data_n=data.copy()
data_series=data_n[col_name]
#删除规则
rule,value=box_plot_outliers(data_series,box_scale=scale)
index=np.arange(data_series.shape[0])[rule[0] | rule[1]]
print("delete num of number is:{}".format(len(index)))
#删除异常数据
data_n=data_n.drop(index)
data_n.reset_index(drop=True,inplace=True)
print("remaining number is:{}".format(data_n.shape[0]))
#可视化异常数据对比
index_low=np.arange(data_series.shape[0])[rule[0]]
outliers=data_series.iloc[index_low]
print("the describe of lower is:")
print(pd.Series(outliers).describe())
index_up=np.arange(data_series.shape[0])[rule[1]]
outliers=data_series.iloc[index_up]
print("the describe of larger is:")
print(pd.Series(outliers).describe())
#箱线图可视化
fig,ax=plt.subplots(1,2,figsize=(10,7))
sns.boxplot(y=data[col_name],data=data,palette='Set1',ax=ax[0])
sns.boxplot(y=data_n[col_name],data=data_n,palette='Set1',ax=ax[1])
return data_n
dataTrain=outliers_proc(dataTrain,'power',3)
删除前和删除后对比:
当然还可以对其他列做异常处理,这里只用‘power’列作为例子
特征构造
这个作为特征工程中较为重要的一步,需要针对不同的模型使用不同的特征,以及使用不同的特征构造方法。
本次比赛数据中,
1、构造汽车使用时间、城市信息、品牌信息(并分桶);
2、对数字特征进行数据变换(log变换、归一化等);
3、对类别特征进行one-hot编码;
#合并训练集和测试集
dataTrain['train']=1
dataTest['train']=0
data=pd.concat([dataTrain,dataTest],ignore_index=True,sort=False)
#使用时间数据来做特征,时间与价格成反比
data['used_time'] = (pd.to_datetime(data['creatDate'],
format='%Y%m%d', errors='coerce') -pd.to_datetime(data['regDate'],
format='%Y%m%d', errors='coerce')).dt.days
print("缺失个数:",data['used_time'].isnull().sum())
#有15072个确实值
print("总个数:",len(data))
print('有%.2f%%数据缺失'%(data['used_time'].isnull().sum()/len(data)*100))
#从邮编中获取城市信息,德国
data['city']=data['regionCode'].apply(lambda x:str(x)[:-3])
# print(data['city'])
#计算某品牌的销售统计量
train_gb = dataTrain.groupby('brand')
all_info = {}
for kind, kind_data in train_gb:
info = {}
kind_data = kind_data[kind_data['price'] > 0]
info['brand_amount'] = len(kind_data)
info['brand_price_max'] = kind_data.price.max()
info['brand_price_median'] = kind_data.price.median()
info['brand_price_min'] = kind_data.price.min()
info['brand_price_sum'] = kind_data.price.sum()
info['brand_price_std'] = kind_data.price.std()
info['brand_price_average'] = round(kind_data.price.sum() / (len(kind_data) + 1), 2)
all_info[kind] = info
brand_fe = pd.DataFrame(all_info).T.reset_index().rename(columns={'index':'brand'})
data = data.merge(brand_fe, how='left', on='brand')
#数据分桶
离散后稀疏向量内积乘法运算速度更快,计算结果页方便存储,容易扩展
离散后的特征对异常值更具鲁棒性,如age>30为1否则为0,对于年龄为200的也不会对魔性造成很大的干扰
LR属于广义线性模型,表达能力有限,经过离散化后,每个变量有单独的权重,这相当于引入了非线性,能够提升模型的表达能力,加大拟合
离散后特征可以进行特征交叉,提升表达能力,由M+N个变量变成M*N个变量,进一步引入非线性,提升表达能力
特征离散后模型更稳定,如用户年龄区间不会因为用户增长了一岁就变化
其他原因,例如LightGBM在改进XGBoost时就增加了数据分桶,增强了模型的泛化性
#以power为例
bin=[i*10 for i in range(31)]
data['power_bin']=pd.cut(data['power'],bin,labels=False)
data[['power_bin','power']].head()
#删除原始数据
data=data.drop(['creatDate','regDate','regionCode'],axis=1)
#导出数据,给树模型使用
data.to_csv('data_for_tree.csv',index=0)
#构造LR 数据特征
#不同的模型需要不同的特征
data['power'].plot.hist()
#test数据集中没有做异常值处理
dataTrain['power'].plot.hist()
#对其去log,再归一化
from sklearn import preprocessing
min_max_scaler=preprocessing.MinMaxScaler()
data['power']=np.log(data['power']+1)
data['power']=((data['power'])-np.min(data['power']))/(np.max(data['power'])-np.min(data['power']))
data['power'].plot.hist()
data['kilometer'].plot.hist()
#归一化
data['kilometer']=((data['kilometer']-np.min(data['kilometer']))/
np.max(data['kilometer'])-np.min(data['kilometer']))
data['kilometer'].plot.hist()
#对其他特征做归一化
def max_min(x):
return (x - np.min(x)) / (np.max(x) - np.min(x))
data['brand_amount'] = max_min(data['brand_amount'])
data['brand_price_average'] = max_min(data['brand_price_average'])
data['brand_price_max'] = max_min(data['brand_price_max'])
data['brand_price_median'] = max_min(data['brand_price_median'])
data['brand_price_min'] = max_min(data['brand_price_min'])
data['brand_price_std'] = max_min(data['brand_price_std'])
data['brand_price_sum'] = max_min(data['brand_price_sum'])
#对类别特征进行one-hot编码
data=pd.get_dummies(data,columns=['model','brand','bodyType','fuelType','gearbox','notRepairedDamage','power_bin'])
#导出数据,给lr模型使用
data.to_csv('data_for_lr.csv',index=0)
特征筛选
通过相关性分析,筛选重要的特征,另一种降维方式。
#过滤式
#相关性分析
print(data['power'].corr(data['price'],method='spearman'))
print(data['kilometer'].corr(data['price'],method='spearman'))
print(data['brand_amount'].corr(data['price'],method='spearman'))
print(data['brand_price_average'].corr(data['price'],method='spearman'))
print(data['brand_price_max'].corr(data['price'],method='spearman'))
print(data['brand_price_median'].corr(data['price'],method='spearman'))
#可视化相关性
data_numeric=data[['power','kilometer','brand_amount','brand_price_average',
'brand_price_max','brand_price_median']]
correlation=data_numeric.corr()
f,ax=plt.subplots(figsize=(7,7))
plt.title("correlation of price with numeric features",y=1,size=16)
sns.heatmap(correlation,square=True,vmax=0.8)
总结:
特征工程是比赛中最至关重要的的一块,特别的传统的比赛,大家的模型可能都差不多,调参带来的效果增幅是非常有限的,但特征工程的好坏往往会决定了最终的排名和成绩。
特征工程的主要目的还是在于将数据转换为能更好地表示潜在问题的特征,从而提高机器学习的性能。比如,异常值处理是为了去除噪声,填补缺失值可以加入先验知识等。
特征构造也属于特征工程的一部分,其目的是为了增强数据的表达。有些比赛的特征是匿名特征,这导致我们并不清楚特征相互直接的关联性,这时我们就只有单纯基于特征进行处 理,比如装箱,groupby,agg 等这样一些操作进行一些特征统计,此外还可以对特征进行进一步的 log,exp 等 变换,或者对多个特征进行四则运算(如上面我们算出的使用时长),多项式组合等然后进行筛选。由于特性的 匿名性其实限制了很多对于特征的处理,当然有些时候用 NN 去提取一些特征也会达到意想不到的良好效果。
1、根据可视化数据后,分析使用什么模型;
2、根据不同的模型选用不同的特征工程方法;
3、根据模型训练得到的结果进行调整特征工程方法,以提升精度等。