完整代碼: https://github.com/cindycindyhi/kaggle-Titanic
特征工程系列:
缺失值填充之后,就要對其他格式有問題的屬性進行處理了。比如Sex Embarked這些屬性的值都是字符串類型的,而scikit learn中的模型都只能處理數值型的數據,需要將這些原始的字符串類型的數據轉為數值型數據。所有數據通常可以分成兩種類型:定量與定性。定量的屬性(數值屬性)通常蘊涵着可排序性,比如在泰坦尼克號數據集中,年齡就是一個定量屬性。定性屬性(標稱 序數 二元屬性)的值是一些符號或事務的名稱,每個值代表某種類別編碼或狀態,不是可測量量,是不具有排序意義的,比如Embarked(登船地點)。
一 定性屬性的數據變換
對於字符串型的定性屬性轉換,如果單純的用數字來代替的化,比如對於Embarked的三個值S Q C分別用1 2 3來代替,模型會把它當成是有順序的數值屬性,對於一些根據距離來確定分類的算法來說,就不能准確運行啦。那么應該怎么將定性屬性轉為數字呢?
(1)dummy varibles(不知道中文應該說成啥。。虛設屬性?)
什么是dummy呢,舉個栗子,Emarked屬性的取值有三個S Q C,分別代表三個上船地點。dummy這個屬性呢,就是向數據集里再加入三個屬性暫且命名為Embarked_S Embarkde_Q 和Embarked_C,如果一個人是在S地點上船的,那么這三個屬性的值就是(1,0,0),在Q點上船的就是(0,1,0),每個屬性都是二元屬性,1代表是,0代表否。所以dummy適用於值范圍相對較少的屬性。
1 import pandas as pd
#creat dummy varibles from raw data 2 dummies_df = pd.get_dummies(df.Embarked) 3 #remana the columns to Embarked_S... 4 dummies_df = dummies_df.rename(columns=lambda x:'Embarked_'+str(x)) 5 df = pd.concat([df,dummies_df],axis=1)
這樣就會3個dummy屬性加到數據集里啦,用df.info()看一下:
(2)factorizing(因子分解?)
用dummy可以處理像Embarked這樣的值域范圍較小的標稱屬性。對於Cabin(船艙號,A43 B55這種)這種標稱屬性,用dummy就不好處理了。pandas提供了一個factorize()函數,用以將標稱屬性的字符串值映射為一個數字,相同的字符串映射為同一個數字。不同於dummy,這種映射最后只生成一個屬性。對於Cabin屬性,我們可以將其分成兩部分,字符串+數字,新建兩個屬性。對於字符串(A-E & U),可以用factorize()將其處理成數字。
1 import re 2 df['CabinLetter'] = df['Cabin'].map( lambda x: re.compile("([a-zA-Z]+)").\ 3 search(x).group() ) 4 df['CabinLetter'] = pd.factorize(df.CabinLetter)[0]
上一步呢,只是把Cabin船艙號前面的字母提出來作為一個新的屬性,船艙號中的數字當然也要提出來作為一個新的屬性啦。
1 #plus one for laplace assumption 2 df['CabinNumber'] = df['Cabin'].map( lambda x: getCabinNumber(x) ).\ 3 astype(int) +1 4 def getCabinNumber(cabin): 5 match = re.compile("([0-9]+)").search(cabin) 6 if match: 7 return match.group() 8 else: 9 return 0
二 定量屬性的數據變換
(1)數據規范化
數據規范化通過將數據壓縮到一個范圍內(通常是0-1或者-1-1)賦予所有屬性相等的權重。對於涉及神經網絡的分類算法或者基於距離度量的分類和聚類,規范化特別有用。規范化方法有多種,如rescaling logarithmic normalize等,可以在這里找到各種規范化方法的具體實現。但是有些時候並不需要規范化,比如算法使用相似度函數而不是距離函數的時候,比如隨機森林,它從不比較一個特征與另一個特征,因此也不許要規范化,關於這個問題,詳細信息可以參考這篇文章www.faqs.org/faqs/ai-faq/neural-nets/part2/section-16.html
如果對Age屬性進行規范化的話(看最后分類算法使用哪種再確定要不要規范化,如果要規范化的話,其他屬性也要處理),代碼如下:
1 if keep_scaled: 2 scaler = preprocessing.StandardScaler() 3 df['Age_Scaled'] = scaler.fit_transform(df['Age'])
StandardScaler將數值壓縮到[-1,1]區間,計算公式為(2x - max(x) - min(x)) / (max(x) - min(x)).
(2)Binning
就像直方圖的bin將數據划分成幾塊一樣,我們也可以將數值屬性划分成幾個bin,這是一種連續數據離散化的處理方式。我們使用pandas.qcut()函數來離散化連續數據,它使用分位數對數據進行划分,可以得到大小基本相等的bin。以下以Fare(船票價格)為例,對於其他連續屬性如Age SibSp等也可以划分成bin。
1 def processFare(): 2 global df 3 df['Fare'][df.Fare.isnull()] = df.Fare.dropna().mean() 4 #zero values divide -- laplace 5 df['Fare'][np.where(df['Fare']==0)[0]] = df['Fare'][df.Fare.\ 6 nonzero()[0] ].min() / 10 7 df['Fare_bin'] = pd.qcut(df.Fare, 4)
這樣產生的df['Fare_bin']的值是這樣的,
0 [0.401, 7.91] 3 (31, 512.329]
1 (31, 512.329] 4 (7.91, 14.454]
2 (7.91, 14.454] 5 (7.91, 14.454]
因為是bin,所以屬性都是一個個區間,代表這個數據屬於哪個區間。對於這樣的數據,我們需要factorize下,轉為數值型數據。
1 df['Fare_bin_id'] = pd.factorize(df.Fare_bin)[0]+1 2 scaler = preprocessing.StandardScaler() 3 df['Fare_bin_id_scaled'] = scaler.fit_transform(df.Fare_bin_id)