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資料探勘入門與實踐》