1. 程式人生 > >Python資料探勘入門與實踐--用轉換器抽取特徵

Python資料探勘入門與實踐--用轉換器抽取特徵

所使用的資料是描述人及其所處的環境,背景及其生活狀況,挖掘目標是:預測一個人是否年收入要多於5 萬美元

1.特徵抽取:

       特徵抽取是資料探勘中最為重要的一個環節,一般而言,它最終的結果影響要高於資料探勘演算法本身。不幸的是,關於如何選取好的特徵,還沒有嚴格的規定、便捷的規則可循,其實這也是資料探勘科學更像是一門藝術所在。建立好的規則離不開直覺,還需要專業領域的知識和資料探勘經驗,光是這些還是不夠的,還需要不停的嘗試,在試錯中前進,有時多少還要靠點運氣。

#coding=gbk
#使用轉換器抽取特徵
import pandas as pd 
import numpy as np 

# 1. 特徵抽取
names = ['Age','Work-Class','fnlwgt','Education',
         'Education-Num','Marital-Status','Occupation',
         'Relationship','Race','Sex','Capital-gain',
         'Capital-loss','Hours-per-week','Native-Country',
         'Earning-Raw'
         ]
filename = r'D:\datasets\Adult.csv'
adult = pd.read_csv(filename, header=None, names = names)
# print(adult.tail())
print(adult.shape)  #    (32561, 15)
adult.drop_duplicates()
adult.dropna(how='all', inplace=True)
print(adult.shape)  # (32561, 15)

#對於連續性特徵,使用常見的統計方法
print(adult['Hours-per-week'].describe())
# count    32561.000000
# mean        40.437456
# std         12.347429
# min          1.000000
# 25%         40.000000
# 50%         40.000000
# 75%         45.000000
# max         99.000000
# Name: Hours-per-week, dtype: float64
print(adult['Education-Num'].median())  # 10.0 表示受教育的年限
#打印出受教育年限為 16 年的總人數
print(adult['Education-Num'].value_counts()[16])   # 413

#使用unique 方法得到所有的工作情況
print(adult['Work-Class'].unique())    #可以獲得特徵的屬性
# [' State-gov' ' Self-emp-not-inc' ' Private' ' Federal-gov' ' Local-gov'
#  ' ?' ' Self-emp-inc' ' Without-pay' ' Never-worked']

#數值特徵也可以離散化轉換成類別型特徵,但是細節丟失是離散化的不好的情況,也是建模需要考慮的情況
#這裡建立 時長特徵 LongHours, 用它表示一個人是否每週工作超過 40 小時, 將數值型轉換成 類別型
adult['LongHours'] = adult['Hours-per-week'] >40
print(adult['LongHours'][:5].astype(int))   # 將其轉換成數值型 0, 1
# 0    0
# 1    0
# 2    0
# 3    0
# 4    0
# Name: LongHours, dtype: int32

2.特徵選擇:

通常特徵數量很多,我們只選取其中的一小部分。原因如下:

1.降低複雜度:隨著特徵數量的增加,很多資料探勘演算法需要很多的時間和資源。減少特徵數量,是提高演算法執行速度,減少資源使用的好辦法。

2. 降低噪音:增加特徵額外的特徵並不總會提升演算法的表現。額外特徵可能擾亂演算法的正常工作。只選取合適的特徵有助於減少出現沒有實際意義的相關性的機率。

3.增加模型的可讀性:根據成千上萬的特徵建立的模型來解答一個問題,對於計算機是很容易,當模型對我們來說是晦澀難懂的。因此,使用更少的特徵,建立可以理解的模型是很有必要的。

選用乾淨的資料,選取更具有描述性的特徵,對演算法的效果提升很有幫助。如果資料集的特徵都相同,就跟沒提供什麼資訊一樣,挖掘就失去了意義。 

#使用sckit-learn 中的VarianceThreshold 轉換器來刪除特徵值方差達不到最低標準的特徵。
X = np.arange(30).reshape((10,3))   #輸出一個10行 3列 的矩陣,可以將它看成是 10個 個體, 3個特徵
X[:,1] = 1 #將第二列轉變成 1, 因此第1,3列的方差大, 第2 列方差 為0
#使用 轉換器 VarianceThreshold處理資料集
from sklearn.feature_selection import VarianceThreshold
vt = VarianceThreshold(threshold=2) # 可以設定自定義的閾值
Xt = vt.fit_transform(X)
print(Xt)   # 可以看出 第二列的方差是0 , 表示不包含具有區別意義的資訊
# [[ 0  2]
#  [ 3  5]
#  [ 6  8]
#  [ 9 11]
#  [12 14]
#  [15 17]
#  [18 20]
#  [21 23]
#  [24 26]
#  [27 29]]
print(vt.variances_)    # [74.25  0.   74.25] 輸出每一列的方差

選擇最優的特徵:

       特徵很多的情況下,怎麼選取出最優的特徵是很具有難度的工作。他與解決資料探勘問題自身問題相關,計算量很大。隨著特徵數量的增加,尋找子集的任務複雜度呈指數級增加。尋找最佳特徵組合的時間複雜同樣是指數級增加。

    其中一個變通的方法就是不要找表現好的子集,而是去找表現好的單個特徵,單個變數,依據是它們各自能達到的準確度。分類任務一般是這樣做,一般只要測量變數與目標類別之間的某種相關性就可以。

       scikit-learn提供了幾種用於選擇單變數特徵的轉換器,其中SelectKBest 是返回k 個最佳特徵, SelectPercentile 是返回表現最佳的前 r% 個特徵。單個特徵和某一類別之間的相關性的計算方法有很多,最常用的有卡方分佈 ,其他方法還有 資訊熵。

#選擇最佳特徵,使用 SelectKBest 和 chi2, 和 SelectPecentile
X = adult[['Age','Education-Num','Capital-gain','Capital-loss','Hours-per-week']].values
print(X[:5])    # 隨意抽取一些特徵,將其轉換成陣列形式
#將Earning-Raw 稅前收入 是否達到 5 萬美元作為 目標類別
adult['Earning-Raw'] = adult['Earning-Raw'] == ' >50K'   # 多於 50k 美元的為True
adult['Earning-Raw'] = adult['Earning-Raw'].astype(int) # 不轉換成0, 1 都可以
y = adult['Earning-Raw'].values
print(y[:5])    # [0 0 0 0 0]
# y = (adult['Earning-Raw'] == ' >50K').values    # 注意 ' >50K' 字串的格式
 
#使用SelectKBest 轉換器類,用卡方函式打分
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2
 
transformer = SelectKBest(score_func=chi2, k=3) # 使用卡方檢驗, 得到分類效果較好的 3 個特徵
Xt_chi2 = transformer.fit_transform(X, y)
 
# print(Xt_chi2)
print(transformer.scores_)
# [8.60061182e+03 2.40142178e+03 8.21924671e+07 1.37214589e+06
#  6.47640900e+03]  可以看出 1,3,4 列的值最大,相關性最好

#使用 Peaesonr 相關係數
#皮爾遜函式返回2個數組,每個特徵的皮爾遜相關係數和  p 值
#皮爾遜函式的引數有2個數組, 第一個是一維陣列
from scipy.stats import pearsonr
def multivariate_pearsonr(X, y):
    scores, pvalues =[], []
    for column in range(X.shape[1]):
        cur_score, cur_p = pearsonr(X[:,column], y)  # 遍歷X 中的每一列,計算係數和p,將其儲存到列表當中
        scores.append(abs(cur_score))
        pvalues.append(cur_p)
    return (np.array(scores), np.array(pvalues))

transformer1 = SelectKBest(score_func=multivariate_pearsonr, k=3)
Xt_pearsonr = transformer1.fit_transform(X, y)
print(transformer1.scores_)     
# [0.2340371  0.33515395 0.22332882 0.15052631 0.22968907] 得到 1, 2, 5 列的相關性較大

#使用決策樹進行預測
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import cross_val_score
clf = DecisionTreeClassifier(random_state=666)
score_chi2 = cross_val_score(clf, Xt_chi2, y, scoring='accuracy')
score_pearsonr = cross_val_score(clf, Xt_pearsonr, y, scoring='accuracy')
mean_chi2 = np.mean(score_chi2) *100
mean_peasonr = np.mean(score_pearsonr) *100
#可以看出使用卡方檢驗的方法得到的特徵組合效果比較好
print('the chi2 accuracy is %.3f'%mean_chi2 +'%')   # the chi2 accuracy is 82.875%
print('the chi2 accuracy is %.3f'%mean_peasonr +'%')    #the chi2 accuracy is 77.074%

3.建立特徵:

      有時,僅僅選擇已有的特徵是不夠用的,我們就要不同的方法從已有的特徵中發掘新的特徵。

4.建立自己的轉換器

       隨著資料集複雜程度的提高和型別的變化,現成的特徵抽取不能滿足需求了,那就需要自己編寫轉換器了。轉換器類似於轉換函式,它接收一種形式的資料,輸出另外一種的形式。轉換器可以用訓練集訓練,訓練得到的引數可以用來轉換測試資料集。

轉換器API 有2個關鍵函式:

1、fit() :接收訓練資料, 設定內部引數

2、transform() : 轉換過程。接收訓練資料集或相同格式的新的資料集。

fit() 和 transform() 函式的輸入應該是同一種資料型別, 但是transform() 可以是返回不同型別的資料型別。

# 4.建立自己的轉換器,Hours-per-week 特徵使用均值作為二值化的閾值
from sklearn.base import TransformerMixin   #需要繼承的 Mixin 類
from sklearn.utils import as_float_array    # 轉換函式

class MeanDiscrete(TransformerMixin):
    def fit(self, X, y=None):   # 這裡需要傳入3 個引數,否則會報錯
        X = as_float_array(X)
        self.mean = X.mean(axis =0) #儲存每個特徵的均值
        return self
    
    def transform(self, X):
        X = as_float_array(X)        #輸入必須是numpy陣列,還要檢查輸入的資料列數是否一致。
        assert X.shape[1] == self.mean.shape[0] # X 中的特徵數應該與訓練集中的特徵數一致
        return X > self.mean
mean_discrete = MeanDiscrete()
X_mean = mean_discrete.fit_transform(X) # 返回與 X 相同shape大小的 陣列
print(X_mean)

#單元測試
#為了建立單元測試,從numpy 引入testing 模組中的assert_array_equal 函式,檢測2個數組是否相等
def test_meandiscrete():
    X_test = np.array([[0,2],[3,5],[6,8],[9,11],[12,14],
                       [15,17],[18,20],[21,23],[24,26],[27,29]
                       ])
    mean_discrete = MeanDiscrete()
    mean_discrete.fit(X_test)
    assert_array_equal(mean_discrete.mean, np.array([13.5, 15.5])) 
    X_transformed = mean_discrete.transform(X_test)
    X_expected =np.array([[0,0],[0,0],[0,0],[0,0],[0,0],
                          [1,1],[1,1],[1,1],[1,1],[1,1]
                          ])  
    assert_array_equal(X_transformed, X_expected)
    print('test works')
test_meandiscrete()

#組裝起來,使用Pipeline,建立一個流水線,第一步是使用MeanDiscrete轉換器,第二步是使用決策樹,然後進行交叉驗證
from sklearn.pipeline import Pipeline
pipeline = Pipeline([('mean_discrete', MeanDiscrete()),
                     ('classifier',DecisionTreeClassifier(random_state=14))
                     ])
score_mean_discrete = cross_val_score(pipeline, X, y, scoring= 'accuracy')
# print('Mean Discrete performance: {0:.3f}'.format(score_mean_discrete.mean()))
score = np.mean(score_mean_discrete) *100
print('the accuracy is %.3f'%score +'%')    #  the accuracy is 80.271%

參考:《Python資料探勘入門與實踐》