3
雷鋒網(wǎng) AI 科技評(píng)論按:眨眼間我們就從人工特征、專家系統(tǒng)來到了自動(dòng)特征、深度學(xué)習(xí)的人工智能新時(shí)代,眾多開源測(cè)試數(shù)據(jù)集也大大降低了理論研究的門檻,直接加載數(shù)據(jù)集就可以開始模型訓(xùn)練或者測(cè)試。然而面對(duì)實(shí)際問題時(shí),收集到的數(shù)據(jù)往往不是像數(shù)據(jù)集中那樣整理好的,直接用來跑模型會(huì)帶來各種各樣的問題。這時(shí)候我們就開始回憶起「特征工程」這一組容易被忽略但解決問題時(shí)不可或缺的硬功夫。
數(shù)據(jù)科學(xué)家 Dipanjan Sarkar 近日就發(fā)布了兩篇長(zhǎng)博客介紹了一些基本的特征工程知識(shí)和技巧。這篇為上篇,主要介紹連續(xù)型數(shù)值數(shù)據(jù)的特征工程處理方法。雷鋒網(wǎng) AI 科技評(píng)論全文編譯如下。
「推動(dòng)世界運(yùn)轉(zhuǎn)的是錢」,不論你是否同意這句話,都不能忽視這個(gè)事實(shí)。以今天的數(shù)字化革命時(shí)代而言,更恰當(dāng)?shù)恼f法已經(jīng)成了「推動(dòng)世界運(yùn)轉(zhuǎn)的是數(shù)據(jù)」。確實(shí),無論數(shù)據(jù)的大小和規(guī)模,其已經(jīng)成為企業(yè)、公司和組織的頭等資產(chǎn)。任何智能系統(tǒng)不管其復(fù)雜度如何都需要由數(shù)據(jù)來驅(qū)動(dòng)。在任何智能系統(tǒng)的核心模塊,我們都有一個(gè)或多個(gè)基于機(jī)器學(xué)習(xí)、深度學(xué)習(xí)或統(tǒng)計(jì)方法的算法,這些算法在一段時(shí)間內(nèi)以數(shù)據(jù)為原料收集知識(shí),并提供智能見解。但算法本身非常樸素且不能在原始數(shù)據(jù)上直接得出結(jié)果。因此一個(gè)重要的任務(wù)就是需要從數(shù)據(jù)中設(shè)計(jì)出工程上有意義的特征,即能被這些算法理解和使用的特征。
任何智能系統(tǒng)基本上是由一個(gè)端到端的流程組成,從數(shù)據(jù)原始數(shù)據(jù)開始,利用數(shù)據(jù)處理技術(shù)來加工、處理并從這些數(shù)據(jù)中設(shè)計(jì)出有意義的特征和屬性。然后我們通常利用統(tǒng)計(jì)模型或機(jī)器學(xué)習(xí)模型在這些特征上建模,如果未來要使用的話,就基于眼前要解決的問題部署模型。一個(gè)典型的標(biāo)準(zhǔn)的基于 CRISP-DM(注:跨行業(yè)數(shù)據(jù)挖掘標(biāo)準(zhǔn)流程)工業(yè)標(biāo)準(zhǔn)處理模型的機(jī)器學(xué)習(xí)流程描述如下。
直接輸入原始數(shù)據(jù)并在這些數(shù)據(jù)基礎(chǔ)上直接建模很可能是魯莽的,因?yàn)槲覀兒芸赡懿粫?huì)得到期望的結(jié)果或性能,且算法不夠智能,不能自動(dòng)地從原始數(shù)據(jù)中抽取有意義的特征(雖然有一些某種程度上自動(dòng)抽取特征的技術(shù),比如深度學(xué)習(xí)技術(shù),后文我們會(huì)再談到)。
我們的主要關(guān)注領(lǐng)域放在數(shù)據(jù)準(zhǔn)備方面,正如上圖中所指出的,我們先對(duì)數(shù)據(jù)做一些必要數(shù)據(jù)加工和處理,然后采用各種方法從原始數(shù)據(jù)中抽取有意義的屬性或特征。
特征工程是構(gòu)建任何智能系統(tǒng)的必要部分。即使你有了很多新的方法如深度學(xué)習(xí)和元啟發(fā)式方法來幫助你自動(dòng)進(jìn)行機(jī)器學(xué)習(xí),但每個(gè)問題都是針對(duì)特定領(lǐng)域的,且更好的特征(適合問題的)通常是系統(tǒng)性能的決定性因素。特征工程是一門藝術(shù)也是一門科學(xué),這就是為什么數(shù)據(jù)科學(xué)家在建模前通常花 70% 的時(shí)間用于準(zhǔn)備數(shù)據(jù)。讓我們看看數(shù)據(jù)科學(xué)界領(lǐng)域里一些名人關(guān)于特征工程的言論。
「特征處理是困難的、耗時(shí)的且需要專家知識(shí)?!簩?shí)用化的機(jī)器學(xué)習(xí)』基本上就是特征工程。」
—— 吳恩達(dá)
這些基本加強(qiáng)了我們先前提到的觀點(diǎn):數(shù)據(jù)科學(xué)家將近 80% 的時(shí)間是用在困難且處理耗時(shí)的特征工程上,其過程既需要領(lǐng)域知識(shí)又需要數(shù)學(xué)計(jì)算。
「特征工程是將原始數(shù)據(jù)轉(zhuǎn)化特征的過程,特征要能更好地表示潛在問題并提高預(yù)測(cè)模型在未知數(shù)據(jù)上的準(zhǔn)確率?!?/p>
—— Dr. Jason Brownlee
這讓我們了解到特征工程是將數(shù)據(jù)轉(zhuǎn)換為特征的過程,特征是機(jī)器學(xué)習(xí)模型的輸入,從而更高質(zhì)量的特征有助于提高整體模型的性能。特征的好壞非常地取決于潛在的問題。因此,即使機(jī)器學(xué)習(xí)任務(wù)在不同場(chǎng)景中是相同的,比如將郵件分為垃圾郵件或非垃圾郵件,或?qū)κ謱憯?shù)字字符進(jìn)行分類,這兩個(gè)場(chǎng)景中提取的特征千差萬別。
來自華盛頓大學(xué)的 Pedro Domingos 教授,在這篇名為《A Few Useful Things to Know about Machine Learning》中告訴我們。
「歸根到底,有的機(jī)器學(xué)習(xí)項(xiàng)目成功了, 有的失敗了。為何如此不同呢?我們很容易想到,最重要的因素就是使用的特征?!?/p>
—— Prof. Pedro Domingos
有可能啟發(fā)你的最后一句關(guān)于特征工程的名言來自有名的 Kaggle 比賽選手 Xavier Conort。你們大部分人都知道 Kaggle 上通常會(huì)定期地放一些來自真實(shí)世界中的棘手的機(jī)器學(xué)習(xí)問題,一般對(duì)所有人開放。
「我們使用的算法對(duì) Kaggle 賽手來說都是非常標(biāo)準(zhǔn)的。…我們花費(fèi)大部分精力在特征工程上。... 我們也非常小心地丟棄可能使模型過擬合的特征?!?/p>
—— Xarvier Conort
一個(gè)特征通常是來自原始數(shù)據(jù)的一種特定表示,它是一個(gè)單獨(dú)的、可度量的屬性,通常由數(shù)據(jù)集中的一列來描述??紤]到一個(gè)通用的二維數(shù)據(jù)集,每個(gè)樣本的觀測(cè)值用一行來表示,每種特征用一列來表示,從而每個(gè)樣本的觀測(cè)值中的各種特征都有一個(gè)具體的值。
這樣以來,正如上圖中例子所示,每行通常代表一個(gè)特征向量,整個(gè)特征集包括了所有的觀察值形成了二維的特征矩陣,稱為特征集。這與代表二維數(shù)據(jù)的數(shù)據(jù)框或電子表格相似。機(jī)器學(xué)習(xí)算法通常都是處理這些數(shù)值型矩陣或張量,因此大部分特征工程技術(shù)都將原始數(shù)據(jù)轉(zhuǎn)換為一些數(shù)值型數(shù)來表示,使得它們能更好地被算法理解。
從數(shù)據(jù)集的角度出發(fā),特征可以分為兩種主要的類型。一般地,原始特征是直接從數(shù)據(jù)集中得到,沒有額外的操作或處理。導(dǎo)出特征通常來自于特征工程,即我們從現(xiàn)有數(shù)據(jù)屬性中提取的特征。一個(gè)簡(jiǎn)單的例子是從一個(gè)包含出生日期的雇員數(shù)據(jù)集中創(chuàng)建一個(gè)新的「年齡」特征,只需要將當(dāng)前日期減去出生日期即可。
數(shù)據(jù)的類型和格式各不相同,包括結(jié)構(gòu)化的和非結(jié)構(gòu)化的數(shù)據(jù)。在這篇文章中,我們將討論各種用來處理結(jié)構(gòu)化的連續(xù)型數(shù)值數(shù)據(jù)的特征工程策略。所有的這些例子都是我最近一本書中的一部分《Pratical Mahine Learning with Python》,你可以訪問這篇文章中使用的相關(guān)的數(shù)據(jù)集和代碼,它們放在 GitHub 上。在此著重感謝 Gabriel Moreira ,他在特征工程技術(shù)上提供了一些優(yōu)雅的指針,給了我很大幫助。
數(shù)值型數(shù)據(jù)通常以標(biāo)量的形式表示數(shù)據(jù),描述觀測(cè)值、記錄或者測(cè)量值。本文的數(shù)值型數(shù)據(jù)是指連續(xù)型數(shù)據(jù)而不是離散型數(shù)據(jù),表示不同類目的數(shù)據(jù)就是后者。數(shù)值型數(shù)據(jù)也可以用向量來表示,向量的每個(gè)值或分量代表一個(gè)特征。整數(shù)和浮點(diǎn)數(shù)是連續(xù)型數(shù)值數(shù)據(jù)中最常見也是最常使用的數(shù)值型數(shù)據(jù)類型。即使數(shù)值型數(shù)據(jù)可以直接輸入到機(jī)器學(xué)習(xí)模型中,你仍需要在建模前設(shè)計(jì)與場(chǎng)景、問題和領(lǐng)域相關(guān)的特征。因此仍需要特征工程。讓我們利用 python 來看看在數(shù)值型數(shù)據(jù)上做特征工程的一些策略。我們首先加載下面一些必要的依賴(通常在 Jupyter botebook 上)。
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import scipy.stats as spstats
%matplotlib inline
正如我們先前提到的,根據(jù)上下文和數(shù)據(jù)的格式,原始數(shù)值型數(shù)據(jù)通??芍苯虞斎氲綑C(jī)器學(xué)習(xí)模型中。原始的度量方法通常用數(shù)值型變量來直接表示為特征,而不需要任何形式的變換或特征工程。通常這些特征可以表示一些值或總數(shù)。讓我們加載四個(gè)數(shù)據(jù)集之一的 Pokemon 數(shù)據(jù)集,該數(shù)據(jù)集也在 Kaggle 上公布了。
poke_df = pd.read_csv('datasets/Pokemon.csv', encoding='utf-8')
poke_df.head()
Pokemon 是一個(gè)大型多媒體游戲,包含了各種口袋妖怪(Pokemon)角色。簡(jiǎn)而言之,你可以認(rèn)為他們是帶有超能力的動(dòng)物!這些數(shù)據(jù)集由這些口袋妖怪角色構(gòu)成,每個(gè)角色帶有各種統(tǒng)計(jì)信息。
如果你仔細(xì)地觀察上圖中這些數(shù)據(jù),你會(huì)看到幾個(gè)代表數(shù)值型原始值的屬性,它可以被直接使用。下面的這行代碼挑出了其中一些重點(diǎn)特征。
poke_df[['HP', 'Attack', 'Defense']].head()
這樣,你可以直接將這些屬性作為特征,如上圖所示。這些特征包括 Pokemon 的 HP(血量),Attack (攻擊)和 Defense(防御)狀態(tài)。事實(shí)上,我們也可以基于這些字段計(jì)算出一些基本的統(tǒng)計(jì)量。
poke_df[['HP', 'Attack', 'Defense']].describe()
數(shù)值特征形式的基本描述性統(tǒng)計(jì)量
這樣你就對(duì)特征中的統(tǒng)計(jì)量如總數(shù)、平均值、標(biāo)準(zhǔn)差和四分位數(shù)有了一個(gè)很好的印象。
原始度量的另一種形式包括代表頻率、總數(shù)或特征屬性發(fā)生次數(shù)的特征。讓我們看看 millionsong 數(shù)據(jù)集中的一個(gè)例子,其描述了某一歌曲被各種用戶收聽的總數(shù)或頻數(shù)。
popsong_df = pd.read_csv('datasets/song_views.csv',encoding='utf-8')
popsong_df.head(10)
根據(jù)這張截圖,顯而易見 listen_count 字段可以直接作為基于數(shù)值型特征的頻數(shù)或總數(shù)。
基于要解決的問題構(gòu)建模型時(shí),通常原始頻數(shù)或總數(shù)可能與此不相關(guān)。比如如果我要建立一個(gè)推薦系統(tǒng)用來推薦歌曲,我只希望知道一個(gè)人是否感興趣或是否聽過某歌曲。我不需要知道一首歌被聽過的次數(shù),因?yàn)槲腋P(guān)心的是一個(gè)人所聽過的各種各樣的歌曲。在這個(gè)例子中,二值化的特征比基于計(jì)數(shù)的特征更合適。我們二值化 listen_count 字段如下。
watched = np.array(popsong_df['listen_count'])
watched[watched >= 1] = 1
popsong_df['watched'] = watched
你也可以使用 scikit-learn 中 preprocessing 模塊的 Binarizer 類來執(zhí)行同樣的任務(wù),而不一定使用 numpy 數(shù)組。
from sklearn.preprocessing import Binarizer
bn = Binarizer(threshold=0.9)
pd_watched =bn.transform([popsong_df['listen_count']])[0]
popsong_df['pd_watched'] = pd_watched
popsong_df.head(11)
你可以從上面的截圖中清楚地看到,兩個(gè)方法得到了相同的結(jié)果。因此我們得到了一個(gè)二值化的特征來表示一首歌是否被每個(gè)用戶聽過,并且可以在相關(guān)的模型中使用它。
處理連續(xù)型數(shù)值屬性如比例或百分比時(shí),我們通常不需要高精度的原始數(shù)值。因此通常有必要將這些高精度的百分比舍入為整數(shù)型數(shù)值。這些整數(shù)可以直接作為原始數(shù)值甚至分類型特征(基于離散類的)使用。讓我們?cè)囍鴮⑦@個(gè)觀念應(yīng)用到一個(gè)虛擬數(shù)據(jù)集上,該數(shù)據(jù)集描述了庫(kù)存項(xiàng)和他們的流行度百分比。
items_popularity =pd.read_csv('datasets/item_popularity.csv',encoding='utf-8')
items_popularity['popularity_scale_10'] = np.array(np.round((items_popularity['pop_percent'] * 10)),dtype='int')
items_popularity['popularity_scale_100'] = np.array(np.round((items_popularity['pop_percent'] * 100)),dtype='int')
items_popularity
基于上面的輸出,你可能猜到我們?cè)嚵藘煞N不同的舍入方式。這些特征表明項(xiàng)目流行度的特征現(xiàn)在既有 1-10 的尺度也有 1-100 的尺度?;谶@個(gè)場(chǎng)景或問題你可以使用這些值同時(shí)作為數(shù)值型或分類型特征。
高級(jí)機(jī)器學(xué)習(xí)模型通常會(huì)對(duì)作為輸入特征變量函數(shù)的輸出響應(yīng)建模(離散類別或連續(xù)數(shù)值)。例如,一個(gè)簡(jiǎn)單的線性回歸方程可以表示為
其中輸入特征用變量表示為
權(quán)重或系數(shù)可以分別表示為
目標(biāo)是預(yù)測(cè)響應(yīng) y.
在這個(gè)例子中,僅僅根據(jù)單個(gè)的、分離的輸入特征,這個(gè)簡(jiǎn)單的線性模型描述了輸出與輸入之間的關(guān)系。
然而,在一些真實(shí)場(chǎng)景中,有必要試著捕獲這些輸入特征集一部分的特征變量之間的相關(guān)性。上述帶有相關(guān)特征的線性回歸方程的展開式可以簡(jiǎn)單表示為
此處特征可表示為
表示了相關(guān)特征?,F(xiàn)在讓我們?cè)囍?Pokemon 數(shù)據(jù)集上設(shè)計(jì)一些相關(guān)特征。
atk_def = poke_df[['Attack', 'Defense']]
atk_def.head()
從輸出數(shù)據(jù)框中,我們可以看到我們有兩個(gè)數(shù)值型(連續(xù)的)特征,Attack 和 Defence。現(xiàn)在我們可以利用 scikit-learn 建立二度特征。
pf = PolynomialFeatures(degree=2,
interaction_only=False,include_bias=False)
res = pf.fit_transform(atk_def)
res
Output
------
array([[ 49., 49., 2401., 2401., 2401.],
[ 62., 63., 3844., 3906., 3969.],
[ 82., 83., 6724., 6806., 6889.],
...,
[ 110., 60., 12100., 6600., 3600.],
[ 160., 60., 25600., 9600., 3600.],
[ 110., 120., 12100., 13200., 14400.]])
上面的特征矩陣一共描述了 5 個(gè)特征,其中包括新的相關(guān)特征。我們可以看到上述矩陣中每個(gè)特征的度,如下所示。
pd.DataFrame(pf.powers_, columns=['Attack_degree','Defense_degree'])
基于這個(gè)輸出,現(xiàn)在我們可以通過每個(gè)特征的度知道它實(shí)際上代表什么。在此基礎(chǔ)上,現(xiàn)在我們可以對(duì)每個(gè)特征進(jìn)行命名如下。這僅僅是為了便于理解,你可以給這些特征取更好的、容易使用和簡(jiǎn)單的名字。
intr_features = pd.DataFrame(res, columns=['Attack','Defense','Attack^2','Attack x Defense','Defense^2'])
intr_features.head(5)
因此上述數(shù)據(jù)代表了我們?cè)嫉奶卣饕约八鼈兊南嚓P(guān)特征。
處理原始、連續(xù)的數(shù)值型特征問題通常會(huì)導(dǎo)致這些特征值的分布被破壞。這表明有些值經(jīng)常出現(xiàn)而另一些值出現(xiàn)非常少。除此之外,另一個(gè)問題是這些特征的值的變化范圍。比如某個(gè)音樂視頻的觀看總數(shù)會(huì)非常大(Despacito,說你呢)而一些值會(huì)非常小。直接使用這些特征會(huì)產(chǎn)生很多問題,反而會(huì)影響模型表現(xiàn)。因此出現(xiàn)了處理這些問題的技巧,包括分區(qū)間法和變換。
分區(qū)間(Bining),也叫做量化,用于將連續(xù)型數(shù)值特征轉(zhuǎn)換為離散型特征(類別)??梢哉J(rèn)為這些離散值或數(shù)字是類別或原始的連續(xù)型數(shù)值被分區(qū)間或分組之后的數(shù)目。每個(gè)不同的區(qū)間大小代表某種密度,因此一個(gè)特定范圍的連續(xù)型數(shù)值會(huì)落在里面。對(duì)數(shù)據(jù)做分區(qū)間的具體技巧包括等寬分區(qū)間以及自適應(yīng)分區(qū)間。我們使用從 2016 年 FreeCodeCamp 開發(fā)者和編碼員調(diào)查報(bào)告中抽取出來的一個(gè)子集中的數(shù)據(jù),來討論各種針對(duì)編碼員和軟件開發(fā)者的屬性。
fcc_survey_df =pd.read_csv('datasets/fcc_2016_coder_survey_subset.csv',encoding='utf-8')
fcc_survey_df[['ID.x', 'EmploymentField', 'Age','Income']].head()
對(duì)于每個(gè)參加調(diào)查的編碼員或開發(fā)者,ID.x 變量基本上是一個(gè)唯一的標(biāo)識(shí)符而其他字段是可自我解釋的。
就像名字表明的那樣,在等寬分區(qū)間方法中,每個(gè)區(qū)間都是固定寬度的,通??梢灶A(yù)先分析數(shù)據(jù)進(jìn)行定義?;谝恍╊I(lǐng)域知識(shí)、規(guī)則或約束,每個(gè)區(qū)間有個(gè)預(yù)先固定的值的范圍,只有處于范圍內(nèi)的數(shù)值才被分配到該區(qū)間?;跀?shù)據(jù)舍入操作的分區(qū)間是一種方式,你可以使用數(shù)據(jù)舍入操作來對(duì)原始值進(jìn)行分區(qū)間,我們前面已經(jīng)講過。
現(xiàn)在我們分析編碼員調(diào)查報(bào)告數(shù)據(jù)集的 Age 特征并看看它的分布。
fig, ax = plt.subplots()
fcc_survey_df['Age'].hist(color='#A9C5D3',edgecolor='black',grid=False)
ax.set_title('Developer Age Histogram', fontsize=12)
ax.set_xlabel('Age', fontsize=12)
ax.set_ylabel('Frequency', fontsize=12)
上面的直方圖表明,如預(yù)期那樣,開發(fā)者年齡分布仿佛往左側(cè)傾斜(上年紀(jì)的開發(fā)者偏少)。現(xiàn)在我們根據(jù)下面的模式,將這些原始年齡值分配到特定的區(qū)間。
Age Range: Bin
---------------
0 - 9 : 0
10 - 19 : 1
20 - 29 : 2
30 - 39 : 3
40 - 49 : 4
50 - 59 : 5
60 - 69 : 6
... and so on
我們可以簡(jiǎn)單地使用我們先前學(xué)習(xí)到的數(shù)據(jù)舍入部分知識(shí),先將這些原始年齡值除以 10,然后通過 floor 函數(shù)對(duì)原始年齡數(shù)值進(jìn)行截?cái)唷?/p>
fcc_survey_df['Age_bin_round'] = np.array(np.floor(np.array(fcc_survey_df['Age']) / 10.))
fcc_survey_df[['ID.x', 'Age','Age_bin_round']].iloc[1071:1076]
你可以看到基于數(shù)據(jù)舍入操作的每個(gè)年齡對(duì)應(yīng)的區(qū)間。但是如果我們需要更靈活的操作怎么辦?如果我們想基于我們的規(guī)則或邏輯,確定或修改區(qū)間的寬度怎么辦?基于常用范圍的分區(qū)間方法將幫助我們完成這個(gè)。讓我們來定義一些通用年齡段位,使用下面的方式來對(duì)開發(fā)者年齡分區(qū)間。
Age Range : Bin
---------------
0 - 15 : 1
16 - 30 : 2
31 - 45 : 3
46 - 60 : 4
61 - 75 : 5
75 - 100 : 6
基于這些常用的分區(qū)間方式,我們現(xiàn)在可以對(duì)每個(gè)開發(fā)者年齡值的區(qū)間打標(biāo)簽,我們將存儲(chǔ)區(qū)間的范圍和相應(yīng)的標(biāo)簽。
bin_ranges = [0, 15, 30, 45, 60, 75, 100]
bin_names = [1, 2, 3, 4, 5, 6]
fcc_survey_df['Age_bin_custom_range'] = pd.cut(np.array(fcc_survey_df['Age']),bins=bin_ranges)
fcc_survey_df['Age_bin_custom_label'] = pd.cut(np.array(fcc_survey_df['Age']),bins=bin_ranges, labels=bin_names)
# view the binned features
fcc_survey_df[['ID.x', 'Age', 'Age_bin_round','Age_bin_custom_range','Age_bin_custom_label']].iloc[10a71:1076]
使用等寬分區(qū)間的不足之處在于,我們手動(dòng)決定了區(qū)間的值范圍,而由于落在某個(gè)區(qū)間中的數(shù)據(jù)點(diǎn)或值的數(shù)目是不均勻的,因此可能會(huì)得到不規(guī)則的區(qū)間。一些區(qū)間中的數(shù)據(jù)可能會(huì)非常的密集,一些區(qū)間會(huì)非常稀疏甚至是空的!自適應(yīng)分區(qū)間方法是一個(gè)更安全的策略,在這些場(chǎng)景中,我們讓數(shù)據(jù)自己說話!這樣,我們使用數(shù)據(jù)分布來決定區(qū)間的范圍。
基于分位數(shù)的分區(qū)間方法是自適應(yīng)分箱方法中一個(gè)很好的技巧。量化對(duì)于特定值或切點(diǎn)有助于將特定數(shù)值域的連續(xù)值分布劃分為離散的互相挨著的區(qū)間。因此 q 分位數(shù)有助于將數(shù)值屬性劃分為 q 個(gè)相等的部分。關(guān)于量化比較流行的例子包括 2 分位數(shù),也叫中值,將數(shù)據(jù)分布劃分為2個(gè)相等的區(qū)間;4 分位數(shù),也簡(jiǎn)稱分位數(shù),它將數(shù)據(jù)劃分為 4 個(gè)相等的區(qū)間;以及 10 分位數(shù),也叫十分位數(shù),創(chuàng)建 10 個(gè)相等寬度的區(qū)間,現(xiàn)在讓我們看看開發(fā)者數(shù)據(jù)集的 Income 字段的數(shù)據(jù)分布。
fig, ax = plt.subplots()
fcc_survey_df['Income'].hist(bins=30, color='#A9C5D3',edgecolor='black',grid=False)
ax.set_title('Developer Income Histogram',fontsize=12)
ax.set_xlabel('Developer Income', fontsize=12)
ax.set_ylabel('Frequency', fontsize=12)
描述開發(fā)者收入分布的直方圖
上述的分布描述了一個(gè)在收入上右歪斜的分布,少數(shù)人賺更多的錢,多數(shù)人賺更少的錢。讓我們基于自適應(yīng)分箱方式做一個(gè) 4-分位數(shù)或分位數(shù)。我們可以很容易地得到如下的分位數(shù)。
quantile_list = [0, .25, .5, .75, 1.]
quantiles =
fcc_survey_df['Income'].quantile(quantile_list)
quantiles
Output
------
0.00 6000.0
0.25 20000.0
0.50 37000.0
0.75 60000.0
1.00 200000.0
Name: Income, dtype: float64
現(xiàn)在讓我們?cè)谠嫉姆植贾狈綀D中可視化下這些分位數(shù)。
fig, ax = plt.subplots()
fcc_survey_df['Income'].hist(bins=30, color='#A9C5D3',edgecolor='black',grid=False)
for quantile in quantiles:
qvl = plt.axvline(quantile, color='r')
ax.legend([qvl], ['Quantiles'], fontsize=10)
ax.set_title('Developer Income Histogram with Quantiles',fontsize=12)
ax.set_xlabel('Developer Income', fontsize=12)
ax.set_ylabel('Frequency', fontsize=12)
上面描述的分布中紅色線代表了分位數(shù)值和我們潛在的區(qū)間。讓我們利用這些知識(shí)來構(gòu)建我們基于分區(qū)間策略的分位數(shù)。
quantile_labels = ['0-25Q', '25-50Q', '50-75Q', '75-100Q']
fcc_survey_df['Income_quantile_range'] = pd.qcut(
fcc_survey_df['Income'],q=quantile_list)
fcc_survey_df['Income_quantile_label'] = pd.qcut(
fcc_survey_df['Income'],q=quantile_list,labels=quantile_labels)
fcc_survey_df[['ID.x', 'Age', 'Income','Income_quantile_range',
'Income_quantile_label']].iloc[4:9]
通過這個(gè)例子,你應(yīng)該對(duì)如何做基于分位數(shù)的自適應(yīng)分區(qū)間法有了一個(gè)很好的認(rèn)識(shí)。一個(gè)需要重點(diǎn)記住的是,分區(qū)間的結(jié)果是離散值類型的分類特征,當(dāng)你在模型中使用分類數(shù)據(jù)之前,可能需要額外的特征工程相關(guān)步驟。我們將在接下來的部分簡(jiǎn)要地講述分類數(shù)據(jù)的特征工程技巧。
我們討論下先前簡(jiǎn)單提到過的數(shù)據(jù)分布傾斜的負(fù)面影響?,F(xiàn)在我們可以考慮另一個(gè)特征工程技巧,即利用統(tǒng)計(jì)或數(shù)學(xué)變換。我們?cè)囋嚳?Log 變換和 Box-Cox 變換。這兩種變換函數(shù)都屬于冪變換函數(shù)簇,通常用來創(chuàng)建單調(diào)的數(shù)據(jù)變換。它們的主要作用在于它能幫助穩(wěn)定方差,始終保持分布接近于正態(tài)分布并使得數(shù)據(jù)與分布的平均值無關(guān)。
log 變換屬于冪變換函數(shù)簇。該函數(shù)用數(shù)學(xué)表達(dá)式表示為
讀為以 b 為底 x 的對(duì)數(shù)等于 y。這可以變換為
表示以b為底指數(shù)必須達(dá)到多少才等于x。自然對(duì)數(shù)使用 b=e,e=2.71828,通常叫作歐拉常數(shù)。你可以使用通常在十進(jìn)制系統(tǒng)中使用的 b=10 作為底數(shù)。
當(dāng)應(yīng)用于傾斜分布時(shí) Log 變換是很有用的,因?yàn)樗麄儍A向于拉伸那些落在較低的幅度范圍內(nèi)自變量值的范圍,傾向于壓縮或減少更高幅度范圍內(nèi)的自變量值的范圍。從而使得傾斜分布盡可能的接近正態(tài)分布。讓我們對(duì)先前使用的開發(fā)者數(shù)據(jù)集的 Income 特征上使用log變換。
fcc_survey_df['Income_log'] = np.log((1+fcc_survey_df['Income']))
fcc_survey_df[['ID.x', 'Age', 'Income','Income_log']].iloc[4:9]
Income_log 字段描述了經(jīng)過 log 變換后的特征?,F(xiàn)在讓我們來看看字段變換后數(shù)據(jù)的分布。
基于上面的圖,我們可以清楚地看到與先前傾斜分布相比,該分布更加像正態(tài)分布或高斯分布。
income_log_mean =np.round(np.mean(fcc_survey_df['Income_log']), 2)
fig, ax = plt.subplots()
fcc_survey_df['Income_log'].hist(bins=30,color='#A9C5D3',edgecolor='black',grid=False)
plt.axvline(income_log_mean, color='r')
ax.set_title('Developer Income Histogram after Log Transform',fontsize=12)
ax.set_xlabel('Developer Income (log scale)',fontsize=12)
ax.set_ylabel('Frequency', fontsize=12)
ax.text(11.5, 450, r'$\mu$='+str(income_log_mean),fontsize=10)
經(jīng)過log變換后描述開發(fā)者收入分布的直方圖
Box-Cox 變換是另一個(gè)流行的冪變換函數(shù)簇中的一個(gè)函數(shù)。該函數(shù)有一個(gè)前提條件,即數(shù)值型值必須先變換為正數(shù)(與 log 變換所要求的一樣)。萬一出現(xiàn)數(shù)值是負(fù)的,使用一個(gè)常數(shù)對(duì)數(shù)值進(jìn)行偏移是有幫助的。數(shù)學(xué)上,Box-Cox 變換函數(shù)可以表示如下。
生成的變換后的輸出y是輸入 x 和變換參數(shù)的函數(shù);當(dāng) λ=0 時(shí),該變換就是自然對(duì)數(shù) log 變換,前面我們已經(jīng)提到過了。λ 的最佳取值通常由最大似然或最大對(duì)數(shù)似然確定?,F(xiàn)在讓我們?cè)陂_發(fā)者數(shù)據(jù)集的收入特征上應(yīng)用 Box-Cox 變換。首先我們從數(shù)據(jù)分布中移除非零值得到最佳的值,結(jié)果如下。
income = np.array(fcc_survey_df['Income'])
income_clean = income[~np.isnan(income)]
l, opt_lambda = spstats.boxcox(income_clean)
print('Optimal lambda value:', opt_lambda)
Output
------
Optimal lambda value: 0.117991239456
現(xiàn)在我們得到了最佳的值,讓我們?cè)谌≈禐?0 和 λ(最佳取值 λ )時(shí)使用 Box-Cox 變換對(duì)開發(fā)者收入特征進(jìn)行變換。
fcc_survey_df['Income_boxcox_lambda_0'] = spstats.boxcox((1+fcc_survey_df['Income']),lmbda=0)
fcc_survey_df['Income_boxcox_lambda_opt'] = spstats.boxcox(fcc_survey_df['Income'],lmbda=opt_lambda)
fcc_survey_df[['ID.x', 'Age', 'Income', 'Income_log','Income_boxcox_lambda_0','Income_boxcox_lambda_opt']].iloc[4:9]
變換后的特征在上述數(shù)據(jù)框中描述了。就像我們期望的那樣,Income_log 和 Income_boxcox_lamba_0 具有相同的取值。讓我們看看經(jīng)過最佳λ變換后 Income 特征的分布。
income_boxcox_mean = np.round(np.mean(fcc_survey_df['Income_boxcox_lambda_opt']),2)
fig, ax = plt.subplots()
fcc_survey_df['Income_boxcox_lambda_opt'].hist(bins=30,
color='#A9C5D3',edgecolor='black', grid=False)
plt.axvline(income_boxcox_mean, color='r')
ax.set_title('Developer Income Histogram after Box–Cox Transform',fontsize=12)
ax.set_xlabel('Developer Income (Box–Cox transform)',fontsize=12)
ax.set_ylabel('Frequency', fontsize=12)
ax.text(24, 450, r'$\mu$='+str(income_boxcox_mean),fontsize=10)
經(jīng)過Box-Cox變換后描述開發(fā)者收入分布的直方圖
分布看起來更像是正態(tài)分布,與我們經(jīng)過 log 變換后的分布相似。
特征工程是機(jī)器學(xué)習(xí)和數(shù)據(jù)科學(xué)中的一個(gè)重要方面,永遠(yuǎn)都不應(yīng)該被忽視。雖然我們也有自動(dòng)的機(jī)器學(xué)習(xí)框架,如 AutoML(但該框架也強(qiáng)調(diào)了它需要好的特征才能跑出好的效果?。?。特征工程永不過時(shí),即使對(duì)于自動(dòng)化方法,其中也有一部分經(jīng)常需要根據(jù)數(shù)據(jù)類型、領(lǐng)域和要解決的問題而設(shè)計(jì)特殊的特征。
這篇文章中我們討論了在連續(xù)型數(shù)值數(shù)據(jù)上特征工程的常用策略。在接下來的部分,我們將討論處理離散、分類數(shù)據(jù)的常用策略,在后續(xù)章節(jié)中會(huì)提到非結(jié)構(gòu)化類型數(shù)據(jù)的處理策略。敬請(qǐng)關(guān)注!
這篇文章中使用的所有的代碼和數(shù)據(jù)集都可以從 GitHub 上訪問。
代碼也以 Jupyter notebook 的形式提供了。
via:Understanding Feature Engineering (Part-1) ,雷鋒網(wǎng) AI 科技評(píng)論編譯
相關(guān)文章:
想成為真正的數(shù)據(jù)科學(xué)家,除了資歷你還需要這4個(gè)技能
Kaggle16000份問卷揭示數(shù)據(jù)科學(xué)家平均畫像:30歲,碩士學(xué)位,年薪36萬
數(shù)據(jù)科學(xué)家必須知道的 10 個(gè)深度學(xué)習(xí)架構(gòu)
雷峰網(wǎng)原創(chuàng)文章,未經(jīng)授權(quán)禁止轉(zhuǎn)載。詳情見轉(zhuǎn)載須知。