特征工程概括
- 數(shù)據(jù)理解:主要在EDA階段完成,了解數(shù)據(jù)的性質(zhì):定類,定序胞锰,定距术幔,定比
- 數(shù)據(jù)清洗:提高數(shù)據(jù)質(zhì)量,主要包括
- 特征變換
- 缺失值處理
- 異常值處理
- 其他
- 特征構(gòu)造:增強(qiáng)數(shù)據(jù)表達(dá)砂缩,添加先驗(yàn)知識(shí)
- 特征選擇:降低噪聲
- 類別不平衡:
代碼
2.3 異常值處理
通過箱線圖去掉異常值。原理見https://www.zhihu.com/question/36172806
# 輸入數(shù)據(jù),列名和清洗的尺度挟冠,輸出異常值處理好的數(shù)據(jù)
def outliers_proc(data, col_name, scale=3):
"""
用于清洗異常值,默認(rèn)用 box_plot(scale=3)進(jìn)行清洗
:param data: 接收 pandas 數(shù)據(jù)格式
:param col_name: pandas 列名
:param scale: 尺度
:return:
"""
def box_plot_outliers(data_ser, box_scale):
"""
利用箱線圖去除異常值
:param data_ser: 接收 pandas.Series 數(shù)據(jù)格式
:param box_scale: 箱線圖尺度袍睡,
:return:
"""
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 number is: {}".format(len(index)))
data_n = data_n.drop(index)
data_n.reset_index(drop=True, inplace=True)
print("Now column number is: {}".format(data_n.shape[0]))
index_low = np.arange(data_series.shape[0])[rule[0]]
outliers = data_series.iloc[index_low]
print("Description of data less than the lower bound is:")
print(pd.Series(outliers).describe())
index_up = np.arange(data_series.shape[0])[rule[1]]
outliers = data_series.iloc[index_up]
print("Description of data larger than the upper bound 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
特征構(gòu)造
1. 構(gòu)造統(tǒng)計(jì)量特征
先使用groupby分組知染,之后計(jì)算某列的各種統(tǒng)計(jì)量
# 計(jì)算某品牌的銷售統(tǒng)計(jì)量,同學(xué)們還可以計(jì)算其他特征的統(tǒng)計(jì)量
# 這里要以 train 的數(shù)據(jù)計(jì)算統(tǒng)計(jì)量
Train_gb = Train_data.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')
2. 時(shí)間特征
使用pd.to_datetime
# 使用時(shí)間:data['creatDate'] - data['regDate']斑胜,反應(yīng)汽車使用時(shí)間控淡,一般來說價(jià)格與使用時(shí)間成反比
# 不過要注意,數(shù)據(jù)里有時(shí)間出錯(cuò)的格式止潘,所以我們需要 errors='coerce'
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
3. 地理信息
# 從郵編中提取城市信息掺炭,相當(dāng)于加入了先驗(yàn)知識(shí)
data['city'] = data['regionCode'].apply(lambda x : str(x)[:-3])
data = data
4. 數(shù)據(jù)分桶
使用pd.cut
# 數(shù)據(jù)分桶 以 power 為例
# 這時(shí)候我們的缺失值也進(jìn)桶了,
# 為什么要做數(shù)據(jù)分桶呢凭戴,原因有很多涧狮,= =
# 1. 離散后稀疏向量?jī)?nèi)積乘法運(yùn)算速度更快,計(jì)算結(jié)果也方便存儲(chǔ)么夫,容易擴(kuò)展者冤;
# 2. 離散后的特征對(duì)異常值更具魯棒性,如 age>30 為 1 否則為 0档痪,對(duì)于年齡為 200 的也不會(huì)對(duì)模型造成很大的干擾涉枫;
# 3. LR 屬于廣義線性模型,表達(dá)能力有限钞它,經(jīng)過離散化后拜银,每個(gè)變量有單獨(dú)的權(quán)重殊鞭,這相當(dāng)于引入了非線性,能夠提升模型的表達(dá)能力尼桶,加大擬合操灿;
# 4. 離散后特征可以進(jìn)行特征交叉,提升表達(dá)能力泵督,由 M+N 個(gè)變量編程 M*N 個(gè)變量趾盐,進(jìn)一步引入非線形,提升了表達(dá)能力小腊;
# 5. 特征離散后模型更穩(wěn)定救鲤,如用戶年齡區(qū)間,不會(huì)因?yàn)橛脩裟挲g長(zhǎng)了一歲就變化
# 當(dāng)然還有很多原因秩冈,LightGBM 在改進(jìn) XGBoost 時(shí)就增加了數(shù)據(jù)分桶本缠,增強(qiáng)了模型的泛化性
bin = [i*10 for i in range(31)]
data['power_bin'] = pd.cut(data['power'], bin, labels=False)
data[['power_bin', 'power']].head()
5. 特征歸一化/標(biāo)準(zhǔn)化
對(duì)于NN 和LR模型,還需要把特征歸一化/標(biāo)準(zhǔn)化
使用公式 x - min / max - min