Task3 特征工程

“數(shù)據(jù)決定了機(jī)器學(xué)習(xí)的上限,而算法只是盡可能逼近這個(gè)上限”砾跃,這里的數(shù)據(jù)指的就是經(jīng)過特征工程得到的數(shù)據(jù)骏啰。
????????特征工程指的是把原始數(shù)據(jù)轉(zhuǎn)變?yōu)槟P偷挠?xùn)練數(shù)據(jù)的過程,它的目的就是獲取更好的訓(xùn)練數(shù)據(jù)特征抽高,使得機(jī)器學(xué)習(xí)模型逼近這個(gè)上限判耕。

“特征工程是將原始數(shù)據(jù)轉(zhuǎn)化為特征的過程,這些特征可以更好地向預(yù)測(cè)模型描述潛在問題翘骂,從而提高模型對(duì)未見數(shù)據(jù)的準(zhǔn)確性驳癌。”???? --Jason Brownlee博士

特征工程從概念上一般包括如下幾個(gè)方法:

  1. 特征構(gòu)建:特征構(gòu)建是指從原始數(shù)據(jù)中人工的找出一些具有實(shí)際意義的特征紫皇。需要花時(shí)間去觀察原始數(shù)據(jù)漾岳,思考問題的潛在形式和數(shù)據(jù)結(jié)構(gòu),對(duì)數(shù)據(jù)敏感性和機(jī)器學(xué)習(xí)實(shí)戰(zhàn)經(jīng)驗(yàn)?zāi)軒椭卣鳂?gòu)建莹桅。除此之外昌执,屬性分割和結(jié)合是特征構(gòu)建時(shí)常使用的方法。特征構(gòu)建是個(gè)非常麻煩的問題诈泼,書里面也很少提到具體的方法懂拾,需要對(duì)問題有比較深入的理解。
  2. 特征抽取:一些觀測(cè)數(shù)據(jù)如果直接建模铐达,其原始狀態(tài)的數(shù)據(jù)太多岖赋。像圖像、音頻和文本數(shù)據(jù)瓮孙,如果將其看做是表格數(shù)據(jù)唐断,那么其中包含了數(shù)以千計(jì)的屬性汁汗。特征抽取是自動(dòng)地對(duì)原始觀測(cè)降維,使其特征集合小到可以進(jìn)行建模的過程栗涂。通持疲可采用主成分分析(PCA)、線性判別分析(LDA))等方法;對(duì)于圖像數(shù)據(jù)斤程,可以進(jìn)行線(line)或邊緣(edge)的提取;根據(jù)相應(yīng)的領(lǐng)域角寸,圖像、視頻和音頻數(shù)據(jù)可以有很多數(shù)字信號(hào)處理的方法對(duì)其進(jìn)行處理忿墅。
  3. 特征選擇:不同的特征對(duì)模型的準(zhǔn)確度的影響不同扁藕,有些特征與要解決的問題不相關(guān),有些特征是冗余信息疚脐,這些特征都應(yīng)該被移除掉亿柑。特征選擇是自動(dòng)地選擇出對(duì)于問題最重要的那些特征子集的過程。常用的特征選擇方法可以分為3類:過濾式(filter)棍弄、包裹式(wrapper)和嵌入式(embedding)望薄。

小結(jié)
  總的來說,數(shù)據(jù)會(huì)存在各種各樣的問題呼畸,針對(duì)這些問題我們的特征工程給出了相應(yīng)的解決辦法:1.特征解釋能力不足痕支,我們可以嘗試使用特征構(gòu)建,對(duì)數(shù)據(jù)進(jìn)行升維來提升特征解釋能力;2.特征冗余蛮原,維度太高卧须,噪聲太多,我們可以通過特征抽取和特征選擇儒陨,來對(duì)數(shù)據(jù)進(jìn)行降維去噪花嘶,提煉特征。當(dāng)然還有其他的特征處理方法蹦漠,一般需要根據(jù)具體問題而定椭员。

特征工程從過程上一般包括如下幾個(gè)過程:

  1. 數(shù)據(jù)預(yù)處理:數(shù)據(jù)預(yù)處理是特征工程中最為重要的一個(gè)環(huán)節(jié),良好的數(shù)據(jù)預(yù)處理可以使模型的訓(xùn)練達(dá)到事半功倍的效果津辩。數(shù)據(jù)預(yù)處理旨在通過歸一化拆撼、標(biāo)準(zhǔn)化容劳、正則化等方式改進(jìn)不完整喘沿、不一致、無法直接使用的數(shù)據(jù)竭贩。
    原始數(shù)據(jù)的問題一般有:存在異常值蚜印、存在缺失值、不屬于同一量綱留量、信息冗余窄赋、定性特征不能直接使用哟冬、信息利用率低等問題。
    具體解決的方法一般有:歸一化忆绰、標(biāo)準(zhǔn)化浩峡、離散化、二值化错敢、啞編碼等方法翰灾。
  2. 特征選擇:不同的特征對(duì)模型的影響程度不同,我們要自動(dòng)地選擇出對(duì)問題重要的一些特征稚茅,移除與問題相關(guān)性不是很大的特征纸淮,這個(gè)過程就叫做特征選擇。特征的選擇在特征工程中十分重要亚享,往往可以直接決定最后模型訓(xùn)練效果的好壞咽块。常用的特征選擇方法有:過濾式(filter)、包裹式(wrapper)欺税、嵌入式(embedding)侈沪。
  3. 降維:如果拿特征選擇后的數(shù)據(jù)直接進(jìn)行模型的訓(xùn)練,由于數(shù)據(jù)的特征矩陣維度大晚凿,可能會(huì)存在數(shù)據(jù)難以理解峭竣、計(jì)算量增大、訓(xùn)練時(shí)間過長等問題晃虫,因此我們要對(duì)數(shù)據(jù)進(jìn)行降維皆撩。降維是指把原始高維空間的特征投影到低維度的空間,進(jìn)行特征的重組哲银,以減少數(shù)據(jù)的維度扛吞。降維與特征選擇最大的不同在于,特征選擇是進(jìn)行特征的剔除荆责、刪減滥比,而降維是做特征的重組構(gòu)成新的特征,原始特征全部“消失”了做院,性質(zhì)發(fā)生了根本的變化盲泛。常見的降維方法有:主成分分析法(PCA)和線性判別分析法(LDA)。

小結(jié)
具體方法的學(xué)習(xí)可以參考以下博客:
https://www.cnblogs.com/jasonfreak/p/5448385.html
https://zhuanlan.zhihu.com/p/36503570
https://www.ituring.com.cn/book/tupubarticle/23033#

代碼實(shí)戰(zhàn):

導(dǎo)入數(shù)據(jù)

import pandas as pd
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import seaborn as sns
from operator import itemgetter

%matplotlib inline

path = './datalab/231784/'
Train_data = pd.read_csv(path+'used_car_train_20200313.csv', sep=' ')
Test_data = pd.read_csv(path+'used_car_testA_20200313.csv', sep=' ')
print(Train_data.shape)
print(Test_data.shape)
  1. 異常值處理
# 這里我包裝了一個(gè)異常值處理的代碼键耕,可以隨便調(diào)用寺滚。
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

# 我們可以刪掉一些異常數(shù)據(jù)村视,以 power 為例。  
# 這里刪不刪同學(xué)可以自行判斷
# 但是要注意 test 的數(shù)據(jù)不能刪 = = 不能掩耳盜鈴是不是

Train_data = outliers_proc(Train_data, 'power', scale=3)
  1. 特征構(gòu)造
    2.1 適合樹模型的特征構(gòu)造
# 訓(xùn)練集和測(cè)試集放在一起酒奶,方便構(gòu)造特征
Train_data['train']=1
Test_data['train']=0
data = pd.concat([Train_data, Test_data], ignore_index=True)

# 使用時(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

# 看一下空數(shù)據(jù)站刑,有 15k 個(gè)樣本的時(shí)間是有問題的,我們可以選擇刪除鼻百,也可以選擇放著笛钝。
# 但是這里不建議刪除,因?yàn)閯h除缺失數(shù)據(jù)占總樣本量過大愕宋,7.5%
# 我們可以先放著玻靡,因?yàn)槿绻覀?XGBoost 之類的決策樹,其本身就能處理缺失值中贝,所以可以不用管囤捻;
data['used_time'].isnull().sum()

# 從郵編中提取城市信息,相當(dāng)于加入了先驗(yàn)知識(shí)
data['city'] = data['regionCode'].apply(lambda x : str(x)[:-3])
data = data

# 計(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')

# 數(shù)據(jù)分桶 以 power 為例
# 這時(shí)候我們的缺失值也進(jìn)桶了蝎土,
# 為什么要做數(shù)據(jù)分桶呢,原因有很多绣否,= =
# 1. 離散后稀疏向量內(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長了一歲就變化

# 當(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()

# 刪除不需要的數(shù)據(jù)
data = data.drop(['creatDate', 'regDate', 'regionCode'], axis=1)

# 目前的數(shù)據(jù)其實(shí)已經(jīng)可以給樹模型使用了,所以我們導(dǎo)出一下
data.to_csv('data_for_tree.csv', index=0)

2.2 適合LR模型的特征構(gòu)造

# 我們可以再構(gòu)造一份特征給 LR NN 之類的模型用
# 之所以分開構(gòu)造是因?yàn)榻菘荩煌P蛯?duì)數(shù)據(jù)集的要求不同
# 我們看下數(shù)據(jù)分布:
data['power'].plot.hist()

# 我們剛剛已經(jīng)對(duì) train 進(jìn)行異常值處理了滚秩,但是現(xiàn)在還有這么奇怪的分布是因?yàn)?test 中的 power 異常值,
# 所以我們其實(shí)剛剛 train 中的 power 異常值不刪為好淮捆,可以用長尾分布截?cái)鄟泶?Train_data['power'].plot.hist()

# 我們對(duì)其取 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()

# km 的比較正常,應(yīng)該是已經(jīng)做過分桶了
data['kilometer'].plot.hist()

# 所以我們可以直接做歸一化
data['kilometer'] = ((data['kilometer'] - np.min(data['kilometer'])) / 
                        (np.max(data['kilometer']) - np.min(data['kilometer'])))
data['kilometer'].plot.hist()

# 除此之外 還有我們剛剛構(gòu)造的統(tǒng)計(jì)量特征:
# 'brand_amount', 'brand_price_average', 'brand_price_max',
# 'brand_price_median', 'brand_price_min', 'brand_price_std',
# 'brand_price_sum'
# 這里不再一一舉例分析了攀痊,直接做變換桐腌,
def max_min(x):
    return (x - np.min(x)) / (np.max(x) - np.min(x))

data['brand_amount'] = ((data['brand_amount'] - np.min(data['brand_amount'])) / 
                        (np.max(data['brand_amount']) - np.min(data['brand_amount'])))
data['brand_price_average'] = ((data['brand_price_average'] - np.min(data['brand_price_average'])) / 
                               (np.max(data['brand_price_average']) - np.min(data['brand_price_average'])))
data['brand_price_max'] = ((data['brand_price_max'] - np.min(data['brand_price_max'])) / 
                           (np.max(data['brand_price_max']) - np.min(data['brand_price_max'])))
data['brand_price_median'] = ((data['brand_price_median'] - np.min(data['brand_price_median'])) /
                              (np.max(data['brand_price_median']) - np.min(data['brand_price_median'])))
data['brand_price_min'] = ((data['brand_price_min'] - np.min(data['brand_price_min'])) / 
                           (np.max(data['brand_price_min']) - np.min(data['brand_price_min'])))
data['brand_price_std'] = ((data['brand_price_std'] - np.min(data['brand_price_std'])) / 
                           (np.max(data['brand_price_std']) - np.min(data['brand_price_std'])))
data['brand_price_sum'] = ((data['brand_price_sum'] - np.min(data['brand_price_sum'])) / 
                           (np.max(data['brand_price_sum']) - np.min(data['brand_price_sum'])))

# 對(duì)類別特征進(jìn)行 OneEncoder
data = pd.get_dummies(data, columns=['model', 'brand', 'bodyType', 'fuelType',
                                     'gearbox', 'notRepairedDamage', 'power_bin'])

# 這份數(shù)據(jù)可以給 LR 用
data.to_csv('data_for_lr.csv', index=0)
  1. 特征選擇
    3.1 過濾式
# 相關(guān)性分析
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'))

# 當(dāng)然也可以直接看圖
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 Numeric Features with Price',y=1,size=16)
sns.heatmap(correlation,square = True,  vmax=0.8)

3.2 包裹式

!pip install mlxtend  # 不要點(diǎn),下載速度很慢

# k_feature 太大會(huì)很難跑苟径,沒服務(wù)器案站,所以提前 interrupt 了
from mlxtend.feature_selection import SequentialFeatureSelector as SFS
from sklearn.linear_model import LinearRegression
sfs = SFS(LinearRegression(),
           k_features=10,
           forward=True,
           floating=False,
           scoring = 'r2',
           cv = 0)
x = data.drop(['price'], axis=1)
x = x.fillna(0)
y = data['price']
sfs.fit(x, y)
sfs.k_feature_names_ 

# 畫出來,可以看到邊際效益
from mlxtend.plotting import plot_sequential_feature_selection as plot_sfs
import matplotlib.pyplot as plt
fig1 = plot_sfs(sfs.get_metric_dict(), kind='std_dev')
plt.grid()
plt.show()

3.3 嵌入式

待更新
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末棘街,一起剝皮案震驚了整個(gè)濱河市蟆盐,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌遭殉,老刑警劉巖石挂,帶你破解...
    沈念sama閱讀 219,539評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異险污,居然都是意外死亡痹愚,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門蛔糯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來里伯,“玉大人,你說我怎么就攤上這事渤闷〖参停” “怎么了?”我有些...
    開封第一講書人閱讀 165,871評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵飒箭,是天一觀的道長狼电。 經(jīng)常有香客問我,道長弦蹂,這世上最難降的妖魔是什么肩碟? 我笑而不...
    開封第一講書人閱讀 58,963評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮凸椿,結(jié)果婚禮上削祈,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好髓抑,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評(píng)論 6 393
  • 文/花漫 我一把揭開白布咙崎。 她就那樣靜靜地躺著,像睡著了一般吨拍。 火紅的嫁衣襯著肌膚如雪褪猛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,763評(píng)論 1 307
  • 那天羹饰,我揣著相機(jī)與錄音伊滋,去河邊找鬼。 笑死队秩,一個(gè)胖子當(dāng)著我的面吹牛笑旺,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播馍资,決...
    沈念sama閱讀 40,468評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼筒主,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了迷帜?” 一聲冷哼從身側(cè)響起物舒,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎戏锹,沒想到半個(gè)月后冠胯,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,850評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡锦针,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評(píng)論 3 338
  • 正文 我和宋清朗相戀三年荠察,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片奈搜。...
    茶點(diǎn)故事閱讀 40,144評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡悉盆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出馋吗,到底是詐尸還是另有隱情焕盟,我是刑警寧澤,帶...
    沈念sama閱讀 35,823評(píng)論 5 346
  • 正文 年R本政府宣布宏粤,位于F島的核電站脚翘,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏绍哎。R本人自食惡果不足惜来农,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望崇堰。 院中可真熱鬧沃于,春花似錦涩咖、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蒋困,卻和暖如春盾似,著一層夾襖步出監(jiān)牢的瞬間敬辣,已是汗流浹背雪标。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留溉跃,地道東北人村刨。 一個(gè)月前我還...
    沈念sama閱讀 48,415評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像撰茎,于是被迫代替她去往敵國和親嵌牺。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容