完整代碼: https://github.com/cindycindyhi/kaggle-Titanichtml
特徵工程系列:python
Titanic系列之數據變換github
爲何有的機器學習項目成功了有的卻失敗了呢?畢竟算法是有限的改進也是有限的,最主要的因素就是特徵的選擇了。若是咱們有一些與類別很是相關同時又相互獨立的特徵,學習起來是很容易的,相反就不必定了。一般狀況下,並非直接把原始數據做爲特徵,而是從中構建一些特徵。這是機器學習中的主要工做。在這一步驟中,一般直覺、創造性、魔法和技術同樣重要。機器學習
固然,機器學習的一個終極目標就是將特徵工程過程愈來愈多地自動化。如今常常採用的一種方式是先自動產生大量的候選特徵,而後根據它們與分類類別的信息增益等方法來選取最好的特徵。可是,運行包含大量特徵的學習器來尋找有用的特徵組合太耗時,也容易致使過擬合。仍是須要人爲的介入特徵的選擇中。函數
什麼是派生屬性呢?派生屬性就是從原始數據中獲得的一些屬性,好比上一節從Age屬性通過Factorize獲得的Age_bin屬性就是一個派生屬性,固然這種派生只是很是簡單的派生。爲何要對這些屬性作各類各樣的統計和處理呢,這實際上是特徵工程的一部分,先構建足夠多可能會對結果有意義的屬性,而後再從這些候選集中選擇咱們想要的特徵。特徵工程很是繁瑣,可是對數據挖掘很是重要,通常來講,作一個數據挖掘項目,百分之八十的努力要用在特徵工程上。除了基本的轉換和interaction屬性,咱們也要創造性的從原始屬性中發現新屬性。好比電話號碼,能夠從中提取出來國家和區域特徵。post
一 研究業務邏輯提取特徵學習
Titanic的數據集相對較爲簡單,可是對於一些字符串類型的屬性,好比Name咱們能夠從中提取出來一些能夠揭示其社會地位的稱號。spa
名字的長度也會表明一我的的社會地位,社會地位高的人可能會更容易獲得救生船。
1 df['Names'] = df['Name'].map(lambda x: len(re.split(' ',x)))
對於名字中間的爵位,能夠看到稱號有Mr Mrs Master等,通過統計能夠看到有如下幾種稱號:
這些稱號有法語還有英語,須要依據當時的文化環境將其歸類,如何將其歸類能夠參考這篇文章trevorstephens.com/post/73461351896/titanic-getting-started-with-r-part-4-feature
1 df['Title'] = df['Name'].map(lambda x: re.compile(",(.*?)\.").findall(x)[0]) 2 df['Title'][df.Title=='Jonkheer'] = 'Master' 3 df['Title'][df.Title.isin(['Ms','Mlle'])] = 'Miss' 4 df['Title'][df.Title == 'Mme'] = 'Mrs' 5 df['Title'][df.Title.isin(['Capt', 'Don', 'Major', 'Col', 'Sir'])] = 'Sir' 6 df['Title'][df.Title.isin(['Dona', 'Lady', 'the Countess'])] = 'Lady' 7 df['Title_id'] = pd.factorize(df.Title)[0]+1
對於Ticket屬性也須要處理,能夠看到Ticket字段有的全是數字有的是字母和數字的集合,進一步對數據分析發現約25%的數據有前綴,前綴共有45種,若是把 . 和 / 去掉的話還剩29種,數字部分也有必定的規律:以1開頭的通常是一等艙2開頭的是二等艙3開頭的是三等艙,4-9開頭的大都是三等艙。以上這些數據告訴咱們處理Ticket是有意義的,可以發現其內部蘊涵的信息。
1 def processTicket(): 2 global df 3 df['TicketPrefix'] = df['Ticket'].map(lambda x: getTicketPrefix(x.upper())) 4 df['TicketPrefix'] = df['TicketPrefix'].map(lambda x: re.sub\ 5 ('[\.?\/?]','',x)) 6 df['TicketPrefix'] = df['TicketPrefix'].map(lambda x:re.sub\ 7 ('STON','SOTON',x)) 8 df['TicketPrefix'] = pd.factorize(df['TicketPrefix'])[0] 9 df['TicketNumber'] = df['Ticket'].map(lambda x: getTicketNumber(x) ) 10 df['TicketNumberLength'] = df['TicketNumber'].map(lambda x: len(x)).\ 11 astype(int) 12 df['TicketNumberStart'] = df['TicketNumber'].map(lambda x: x[0:1]).\ 13 astype(int) 14 df['TicketNumber'] = df['TicketNumber'].astype(int) 15 def getTicketPrefix(ticket): 16 match = re.compile("([a-zA-Z\.\/]+)").search(ticket) 17 if match: 18 return match.group() 19 else: 20 return 'U' 21 def getTicketNumber(ticket): 22 match = re.compile("([0-9]+$)").search(ticket) 23 if match: 24 return match.group() 25 else: 26 return '0'
二 簡單組合屬性提取特徵
一些屬性能夠從它自己的數據裏提取一些信息,有些屬性則須要和其餘屬性組合來產生信息。好比對淘寶上的一個商品來講,購買數/點擊率能夠反應商品的轉化率,也是商品的一個很是重要的特徵。
對於Titanic來講,咱們用Age*Pclass組合產生一個屬性,雖然沒有一個名詞來解釋它,可是從結果數據上來看,咱們增大了年紀大的人的權重也提升了高等艙的權重,從最後倖存的結果上看,這個組合仍是有意義的。除了這兩個屬性以外,咱們還能夠對其餘數值屬性進行數學運算,以獲得更大的候選特徵集。
1 numerics = df.loc[:, ['Age_scaled', 'Fare_scaled', 'Pclass_scaled', 'Parch_scaled', 'SibSp_scaled', 2 'Names_scaled', 'CabinNumber_scaled', 'Age_bin_id_scaled', 'Fare_bin_id_scaled']] 3 print "\nFeatures used for automated feature generation:\n", numerics.head(10) 4 5 new_fields_count = 0 6 for i in range(0, numerics.columns.size-1): 7 for j in range(0, numerics.columns.size-1): 8 if i <= j: 9 name = str(numerics.columns.values[i]) + "*" + str(numerics.columns.values[j]) 10 df = pd.concat([df, pd.Series(numerics.iloc[:,i] * numerics.iloc[:,j], name=name)], axis=1) 11 new_fields_count += 1 12 if i < j: 13 name = str(numerics.columns.values[i]) + "+" + str(numerics.columns.values[j]) 14 df = pd.concat([df, pd.Series(numerics.iloc[:,i] + numerics.iloc[:,j], name=name)], axis=1) 15 new_fields_count += 1 16 if not i == j: 17 name = str(numerics.columns.values[i]) + "/" + str(numerics.columns.values[j]) 18 df = pd.concat([df, pd.Series(numerics.iloc[:,i] / numerics.iloc[:,j], name=name)], axis=1) 19 name = str(numerics.columns.values[i]) + "-" + str(numerics.columns.values[j]) 20 df = pd.concat([df, pd.Series(numerics.iloc[:,i] - numerics.iloc[:,j], name=name)], axis=1) 21 new_fields_count += 2 22 23 print "\n", new_fields_count, "new features generated"
這個過程自動產生大量的特徵,這裏用了9個特徵產生了176個特徵,可能這些特徵有些過於多了,可是它只是一個候選集,咱們能夠經過一些處理篩選掉一些特徵。固然有些模型也很適合大量特徵的訓練集,好比隨機森林(有論文驗證,隨機森林是分類算法中表現最好的模型)。
產生的這些特徵可能高度相關於原始特徵,線性模型處理這類特徵時會產生multicollinearity問題,能夠用計算這些特徵的皮爾遜相關係數,篩選相關性特徵。若是用隨機森林模型訓練的話,能夠不須要這個步驟。
三 用PCA進行維歸約
經過上面三個部分的處理,咱們獲得了具備大量特徵的維度很高的數據集,特徵較多不能直接用來做爲模型輸入,一是由於這些特徵間具備多重共線性,可能 會致使空間的不穩定;二是由於高維空間自己具備稀疏性,一維正態分佈有68%的值落於正負標準差之間,而在十維空間上只有0.02%;三是因爲過多的屬性 會使挖掘須要很長時間。對於一些模型來講,好比使用L1(Lasso),當有大量屬性時效果很好,由於它能夠有效忽略掉噪聲變量。而一些模型則容易過擬 合。
數據歸約技術能夠用來獲得數據集的規約表示,它小得多,但仍接近於保持原始數據的完整性。也就是說,在歸約後的數據集上進行數據挖掘將更加有效,仍然產生幾乎相同的數據分析結果。
PCA(主成份分析)是一種維歸約的方法,它搜索k個最能表明數據的n維正交向量,將原始數據投影到一個小的多的空間上,致使維歸約。PCA經過建立一個替換的較小的變量集組合屬性的基本要素。具體原理及python的實現過程能夠參考這篇blog Implementing a Principal Component Analysis (PCA) in Python step by step
咱們能夠直接使用scikit-learn的PCA函數進行維規約
1 X = df.values[:, 1::] 2 y = df.values[:, 0] 3 variance_pct = .99 4 # Create PCA object 5 pca = PCA(n_components=variance_pct) 6 # Transform the initial features 7 X_transformed = pca.fit_transform(X,y) 8 # Create a data frame from the PCA'd data 9 pcaDataFrame = pd.DataFrame(X_transformed)
實驗發現,PCA對於線性模型(不使用Lasso)很是有效,可是對於隨機森林模型沒有提升。