數據挖掘入門系列教程(六)之數據集特徵選擇

數據挖掘入門系列教程(六)之數據集特徵選擇

這一篇博客主要來如何介紹從數據集中抽取合適的特徵。html

咱們知道,在數據挖掘中,數據的訓練算法很重要,可是一樣咱們對於數據的前置處理也不可忽視。由於咱們對某個數據集的描述是使用特徵來表示的。在前面的博客中不管咱們是得到商品交易的相關性關係,仍是使用決策樹去對Iris進行分類,咱們都是使用了數據集中全部的特徵。可是實際上咱們獲取的數據真的有這麼好嗎?python

舉個例子,咱們對西瓜進行分類,可是西瓜的編號實際上與訓練毫無關係,所以咱們會訓練以前將去掉西瓜的編號。咱們之因此去掉編號,是由於咱們知道這個編號與西瓜的好壞毫無關係。可是若是給你一個陌生的數據集,有着成百上千的特徵,咱們又如何去除無關的數據特徵獲得有用的特徵而後進行訓練?git

憑感受?這裏有兩個問題:github

  • 數據集太大,這個太耗費人力
  • 若是某個關鍵的特徵取值很相近(或者相同)怎麼辦

這裏說一下第二個問題。舉個例子,西瓜的好壞與西瓜的顏色有關,可是若是你的數據集中西瓜的顏色所有同樣,你還要不要使用這個特徵進行訓練呢?算法

確定有人說,要!!可是若是這種特徵取值類似的特徵有1000個,你還要不要呢?在前面的博客中,咱們能夠看到使用Apriori算法進行計算,一旦\(K\)的值增大,基本上輕薄本就算不動這個數據了!api

簡介

經過前面的學習咱們知道,不管是交易數據,仍是Iris數據,他們都是一個一個的實體(視頻,聲音,文本也是),咱們會經過選擇一個一個的特徵來描述某一個實體,這能夠說是建模,同時這個模型的表示可以讓數據挖掘的算法可以理解。數組

如何選擇一些好的特徵,這個也就是這篇博客要討論的話題(本篇博客是探討探討如何從已有的特徵中選擇好的特徵【也就是簡化模型】,而不是本身去從數據集中(好比說聲音)去尋找特徵)。網絡

這樣作有什麼好處呢?最簡單的一個就是它可以下降真實世界的複雜度。好比說我要描述一個蘋果長什麼樣,我確定不須要去了解蘋果是從哪裏買的。可是一樣也有缺點,由於咱們在簡化的過程當中,可能會忽略某一些特徵,可是這些特徵可能恰好有着某一些有用的信息。app

特徵能夠初略的分爲兩個類型:框架

  • 數值型:數值特徵,好比說Iris的花瓣長度是多少等等
  • 類別型:類別特徵(也能夠稱之爲名義特徵)好比說西瓜的顏色是淺綠色仍是深綠色

對於數值型特徵來講,若是兩個特徵值相差很小,則能夠認爲這兩個特徵很類似,可是對於類別型的特徵值而言,沒辦法說他們是否類似,由於他們要不相同,要不不相同。由於名義特徵沒辦法進行數學上的計算,所以咱們能夠將它們進行二值化變成數值特徵。

一樣反過來,數值型特徵也能夠經過離散化變成類別特徵,好比說花瓣長度大於某一個值爲類別0,反之則爲類別1。可是很明顯,這樣會丟失一些數據細節。

下面將以不一樣的數據集爲例,介紹一些用來簡化模型的算法。

加載數據集Adult

數據集來自這裏,一樣在個人GIthub中也存在這個數據集。咱們下載以下的數據集:

adult.data中的部分數據以下,每一行表明的是一我的的數據,每一列表示的特徵屬性值(至於特徵是什麼,這個在adult.name文件中有介紹):

🆗,如今咱們就可使用python來加載數據集了。使用pandas,這個前面已經介紹了。

import pandas as pd
adult_data = pd.read_csv("Data/adult.data",header=None,names=["age","workclass","fnlwgt","education","education_num","marital-status","occupation","relationship","race","sex","capital-gain","capital-loss","hours-per-week","native-country","money"])

names表示的就是每個特徵的名字。adult_data的數據以下。特徵表明什麼意思,基本上經過特徵名就能夠理解了。若是不理解的能夠看adult.name文件。最後一個特徵爲money,他是分類結果,含義是他每一年的收入是否大於50K

一樣咱們能夠得到某一個特徵的一些數學量(好比說平均值,標準差,等等),以hours-per-week爲例:

一樣咱們能夠得到方差:

也能夠得到方差:

數據分佈以下:

一樣,咱們能夠獲得某一個特徵的全部取值狀況,在這裏咱們查看職業「occupation」的取值有哪一些:

?表明數據缺失。

特徵選擇

如何選擇一個好的特徵,這個是一門技術活,一樣也是一門藝術活,由於特徵的選擇不是惟一的,也不是維持不變的,它須要根據咱們的需求發生改變。好比說咱們判斷一我的的成績好很差,確定不須要知道他的名字。特徵有不少,咱們弱水三千,只取一瓢,緣由以下:

  • 下降複雜度:特徵越小,咱們耗費的計算時間也就越少。
  • 下降噪音:好比說西瓜分類中,西瓜的id就毫無做用。
  • 增長模型的可讀性。

特徵的選擇有不少方法,下面介紹一些經常使用的簡單的方法。

方差

咱們能夠很容易的理解,若是某一個特徵的特徵值都同樣,或者說相互之間都很類似,那麼咱們能夠理解爲這個特徵並無提供什麼有用的信息給咱們,所以咱們能夠去掉這一個特徵。那麼如何判斷是否特徵值是否類似,emm,方差能夠作到這個。

在scikit-learn中提供了VarianceThreshold轉換器用來去除方差小於某一個閾值的列,具體的使用能夠看官網。使用示例以下:

import numpy as np
X = np.arange(30).reshape((10, 3))

建立一個\(10 \times 3\)的矩陣。

而後咱們對矩陣進行更改,將第二列的全部值都設爲1:

X[:,1] = 1

而後咱們使用轉換器對數據集進行處理:

from sklearn.feature_selection import VarianceThreshold
# threshold表明的就是閾值,默認是0.0
vt = VarianceThreshold(threshold=0.0)
Xt = vt.fit_transform(X)

轉換後的數據以下:

咱們能夠看到第二列的已經被去除了。

VarianceThreshold有兩個重要的函數:fittransform,這些說明官網都有,這裏稍微的囉嗦如下。fit函數是去計算array的方差,而transform函數就是去轉換array數組,將反差小於閾值的去除。

咱們能夠經過variances_去查看具體的方差是多少。

以上面的adult.data數據爲例,咱們只使用數值類型數據對money進行預測。

首先,咱們使用原始的數據進行預測:

在下面X的數據所有都是數值類型的數據。而後構建一個決策樹,而後使用交叉驗證獲得預測的準確度。

X = adult_data[["age","education_num","capital-gain","capital-loss","hours-per-week"]]
Y = adult_data["money"]
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import cross_val_score
dtc = DecisionTreeClassifier(random_state=14)
score = cross_val_score(dtc, X, Y, scoring='accuracy')
score.mean()

結果爲:

而後咱們使用轉換器去除閾值小於200的方差。而後再構建一個決策樹。

from sklearn.feature_selection import VarianceThreshold
X = adult_data[["age","education_num","capital-gain","capital-loss","hours-per-week"]].values
Y = adult_data["money"]
vt = VarianceThreshold(threshold=200)
Xt = vt.fit_transform(X)

# 構建決策樹
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import cross_val_score
dtc = DecisionTreeClassifier(random_state=14)
score = cross_val_score(dtc, Xt, Y, scoring='accuracy')
score.mean()

最後預測的準確度爲:

比不處理特徵提升了\(2\%\)左右,還行。不錯不錯~~

選擇最佳特徵

如何選擇最佳的幾個特徵,在Apriori算法中咱們已經見識到了,同時尋找幾個最佳的特徵仍是挺耗費計算資源的,所以咱們能夠換一個方向,一次尋找一個特徵(單變量),而後再選擇幾個比較好的特徵。

在scikit-learn中提供了幾個用於選擇單變量特徵的選擇器。

  • SelectKBest:選擇k個最佳的特徵。
  • SelectPercentile:選擇最佳的前r%g個特徵。

下面將以幾個選擇方法來舉例說明。

卡方驗證\(X^2\)(Chi-Square Test)

卡方驗證是什麼,若是不知道的話很是建議看一看這一位博主的博客:結合平常生活的例子,瞭解什麼是卡方檢驗。簡單點來講,就是能夠驗證咱們的假設是否正確。

計算公式以下:

\[X^2 = \sum\frac{(觀察次數 - 理論次數)^2}{理論次數} \\ 理論次數也就是在假設成立的狀況下,應該發生的次數。 \]

而後咱們就能夠根據自由度\(X^2\),經過卡方表去判斷咱們假設的置信度。總的來講,\(X^2\)越小(在自由度相同的狀況下),表示錯誤決策假設的機率越低。

卡方表以下,\(\alpha\)表示的是錯誤拒絕假設的機率(\(1-\alpha\)也就是假設成立的機率),\(n\)表示的是自由度,紅色框框表示就是\(X^2\)

仍是以上面的數值型數據舉例:

咱們使用卡方驗證從裏面選取前3個最好的特徵。在sklearn中的卡方驗證,作出的\(H_0\)假設(The null hypothesis)默認表明兩個變量之間相互獨立(解釋來自stackoverflow)。這樣也就是說\(X^2\)的值越大也就表明着變量之間越相互依賴,也就是對數據挖掘的做用越大。more userful

X = adult_data[["age","education_num","capital-gain","capital-loss","hours-per-week"]].values
Y = adult_data["money"]

from sklearn.feature_selection import SelectKBest
# 導入卡方驗證
from sklearn.feature_selection import chi2
# 選取前3個最好的節點
transformer = SelectKBest(score_func=chi2,k=3)
X_chi2 = transformer.fit_transform(X,Y)
transformer.scores_

卡方驗證計算的結果以下:

一樣,咱們能夠獲得卡方驗證的最大值的三個特徵(也就是第1,3,4項特徵)去構建決策樹。

from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import cross_val_score
dtc = DecisionTreeClassifier(random_state=14)
score = cross_val_score(dtc, X_chi2, Y, scoring='accuracy')
score.mean()

最後的精準度爲:

emm,比方差的方法稍微差了一點。

皮爾遜相關係數(Pearson Correlation Coefficient)

皮爾遜相關係數具體是什麼能夠參考百度百科,或者其餘博主的博客。在這裏只簡單的介紹一下。 皮爾遜係數主要是描述X與Y之間的關係,其值介於\(-1與1\)之間。

  • 當相關係數爲0時,X和Y兩變量無關係。
  • 當X的值增大(減少),Y值增大(減少),兩個變量爲正相關,相關係數在0.00與1.00之間。
  • 當X的值增大(減少),Y值減少(增大),兩個變量爲負相關,相關係數在-1.00與0.00之間。

計算公式以下:

\[\begin{equation} \begin{aligned} &總體相關係數計算以下 \\ & \rho_{X,Y} = \frac{Cov(X,Y)}{\sqrt{D(X)}\sqrt{D(Y)}} \\ &= \frac{E((X - E(X)(Y-E(Y))}{\sqrt{D(X)}\sqrt{D(Y)}} \\ &其中,E爲數學指望,D爲方差。Cov(X,Y)表示的X與Y的協方差。\\ &基於樣本對協方差和方差進行估計,能夠獲得樣本標準差:\\ & r =\frac{\sum_{i=1}^{n}{[(X_i - E(X)(Y_i-E(Y)]}}{\sqrt{\sum_{i=1}^{n}{(X_i-E(X))^2}}\sqrt{\sum_{i=1}^{n}{(Y_i-E(Y))^2}}} \\ & 等價於 \\ &r = \frac{1}{n-1}\sum_{i=1}^n[(\frac{X_i - E(X)}{\sqrt{D(X)}})(\frac{X_i - E(X)}{\sqrt{D(Y)}})] \end{aligned} \end{equation} \]

至於怎麼實現這個咱們可使用Scipy庫。具體使用以下:

from scipy.stats import pearsonr
def pearsonr_fit(x,y):
    scores=[]
    p_values = []
    for column in range(X.shape[1]):
        # cur_p表示的雙側p值,x[:,column]表示的是X中的某一列
        # 而後計算X中的某一列與y之間獲得關係,返回相關係數和p_value
        cur_score,cur_p = pearsonr(x[:,column],y)
        # 由於相關係數可能爲負數,因此取絕對值
        scores.append(abs(cur_score))
        p_values.append(cur_p)
    return (np.array(scores),np.array(p_values))

而後咱們經過調用這個函數就能夠得到X中的每一列與y之間的關係,而後返回最佳的幾個特徵。

import numpy as np
X = adult_data[["age","education_num","capital-gain","capital-loss","hours-per-week"]].values
# 這裏不使用">50K"等字符串是由於pearsonr不接受字符串數據。
Y = adult_data["money"] == " >50K"
transformer = SelectKBest(score_func=pearsonr_fit,k=3)
X_pearsonr = transformer.fit_transform(X,Y)

具體的皮爾遜係數以下:

在這裏皮爾遜係數越大,表明兩個變量越相關,也就是對於數據挖掘越有做用。所以咱們選擇第1,2,5項特徵。

最後構建決策樹:

from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import cross_val_score
dtc = DecisionTreeClassifier(random_state=14)
score = cross_val_score(dtc, X_pearsonr, Y, scoring='accuracy')
score.mean()

獲得的結果爲:

emm,更低了。

PCA 主成分分析

在前面的幾個方法中,咱們都是從已有的特徵中選擇最佳的一個(或者幾個)特徵而後進行數據挖掘進行訓練。可是咱們沒有考慮一個問題,若是特徵之間聯繫緊密怎麼辦(好比說性別能夠由兩個特徵表示,一個特徵表示是不是男的,另一個特徵表示是不是女)?有人會說,這個有什麼關係,都進行訓練就好了。這樣作確實是沒有關係,可是咱們不得不考慮計算機的計算能力是有限的,若是咱們可以使用最少的信息(去除某一些類似的特徵)儘量的描述數據集的特徵,這樣必將大大的下降數據集的冗餘程度。

這裏咱們使用廣告的數據集:http://archive.ics.uci.edu/ml/machine-learning-databases/internet_ads/,一樣在個人GIthub中也有這個數據集。稍微的解釋一下這個數據集:

這個數據集從0到1557 都是一些網絡圖像的特徵好比說URL,長寬,ALT等等特徵(這些特徵有不少類似的特徵),而後第1558表明着這個圖片是否是廣告。

首先咱們仍是從處理數據集開始:

import pandas as pd
import numpy as np
from collections import defaultdict

def convert_number(x):
    try:
        return float(x)
    except ValueError:
        return np.nan
    
converters = defaultdict()
for i in range(1559 -1):
    converters[i] = convert_number
converters[1558] = lambda x:1 if x.strip() == "ad." else 0

ads_data = pd.read_csv("Data/ad.data",header=None,converters=converters)

首先咱們將數據從字符串轉成float類型,而後將"ad."轉換成1表明有廣告,0表明沒有廣告。可是這裏有一個問題,那就是在前面的一些特徵可能缺失了(使用❓表示),所以咱們使用NaN表示缺失的數據。

處理後的數據集以下:

而後咱們去除爲NaN的數據:

data = ads_data.dropna(axis=0,how='any')
X = data.drop(1558,axis=1).values
Y = data[1558]

取出後的X數據集大小爲:

首先咱們什麼特徵都不去除,使用決策樹進行預測:

from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import cross_val_score
dtc = DecisionTreeClassifier(random_state=14)
score = cross_val_score(dtc, X, Y, scoring='accuracy')
print("準確度是:{}".format(score.mean()))

結果爲:

前面咱們介紹過這個ad的數據集裏面確定有不少的冗餘信息,那麼咱們如何去除冗餘信息,這裏咱們選擇PCA算法(主成分分析算法Principal Component Analysis),目的是用較少的信息描述數據集的特徵組合。具體的PCA算法能夠看一下這個博主的博客:主成分分析(PCA)原理詳解

至於使用,咱們可使用sklearn中自帶的庫進行操做。

from sklearn.decomposition import PCA
# n_components 表示的組成分的數量,默認返回數據集中全部的特徵
pca = PCA(n_components=5)
Xd = pca.fit_transform(X)

返回的結果就是主成分,根據方差的大小從大到小排序。方差越大,表明着這個特徵越可以解釋數據集中的大部分信息。咱們能夠查看每一個特徵的方差:

pca.explained_variance_ratio_

其中第一個特徵的方差對數據集整體方差的貢獻率爲\(85.36\%\)。後面的依次遞減。

用PCA算法處理數據一個很差的地方在於,獲得的主成分每每是其餘幾個特徵的複雜組合,
例如,上述第一個特徵就是經過爲原始數據集的1558個特徵(雖然不少特徵值爲0)分別乘以不
同權重獲得的,前三個特徵的權重依次爲- 0.09二、 - 0.995和- 0.024。通過某種組合獲得的特徵,
若是沒有豐富的研究經驗,理解起來很困難。 ——《Python數據挖掘入門與實踐》

而後是用決策樹進行分類:

from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import cross_val_score
dtc = DecisionTreeClassifier(random_state=14)
score = cross_val_score(dtc, Xd, Y, scoring='accuracy')
print("準確度是:{}".format(score.mean()))

結果爲:

比使用全部特徵的準確度稍微差了一點(差了約\(0.3 \%\)),可是使用的特徵卻大大的減小了(一個是使用了1557個特徵,一個是隻使用了5個特徵)。總的來講結果仍是不錯的。

咱們也能夠經過畫圖來表示前三個特徵與adnoad的關係:

from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
data = ads_data.dropna(axis=0,how='any')
Y = data[1558]
classes = set(Y)
colors = ['red', 'green']
fig = plt.figure()
ax = Axes3D(fig)

for cur_class, color in zip(classes, colors):
    mask = (Y == cur_class).values
    ax.scatter(Xd[mask,0], Xd[mask,1],Xd[mask,2],color=color, label=int(cur_class),marker='o')

plt.legend()
plt.show()

總結

這篇博客主要是介紹怎麼從數據集種提取出好的特徵下降數據集的複雜度和冗餘度。涉及了:

  • 方差
  • 卡方驗證
  • 皮爾遜相關係數
  • PCA算法

看起來實現並不難,那是由於有了不少優秀的框架已經幫咱們作好了這些事情。這樣節約了咱們寫代碼的時間,避免重複造輪子,可是這樣並不表明咱們會用就行 ,咱們真正應該作的是理解裏面的原理和背後的數學知識,知其因此然。

項目地址:GitHub

參考:

  1. 《Python數據挖掘入門與實踐》
  2. 主成分分析(PCA)原理詳解
  3. 百度百科——皮爾森相關係數
  4. 結合平常生活的例子,瞭解什麼是卡方檢驗
  5. Sklearn Chi2 For Feature Selection
相關文章
相關標籤/搜索