kaggle(02)-房价预测案例(基础版)
房價預測案例
Step 1: 檢視源數據集
import numpy as np import pandas as pd讀入數據
-
一般來說源數據的index那一欄沒什么用,我們可以用來作為我們pandas dataframe的index。這樣之后要是檢索起來也省事兒。
-
有人的地方就有鄙視鏈。跟知乎一樣。Kaggle的也是個處處呵呵的危險地帶。Kaggle上默認把數據放在input文件夾下。所以我們沒事兒寫個教程什么的,也可以依據這個convention來,顯得自己很有逼格。。
檢視源數據
train_df.head() # print(train_df.info) # print(train_df.shape)| 60 | RL | 65.0 | 8450 | Pave | NaN | Reg | Lvl | AllPub | Inside | ... | 0 | NaN | NaN | NaN | 0 | 2 | 2008 | WD | Normal | 208500 |
| 20 | RL | 80.0 | 9600 | Pave | NaN | Reg | Lvl | AllPub | FR2 | ... | 0 | NaN | NaN | NaN | 0 | 5 | 2007 | WD | Normal | 181500 |
| 60 | RL | 68.0 | 11250 | Pave | NaN | IR1 | Lvl | AllPub | Inside | ... | 0 | NaN | NaN | NaN | 0 | 9 | 2008 | WD | Normal | 223500 |
| 70 | RL | 60.0 | 9550 | Pave | NaN | IR1 | Lvl | AllPub | Corner | ... | 0 | NaN | NaN | NaN | 0 | 2 | 2006 | WD | Abnorml | 140000 |
| 60 | RL | 84.0 | 14260 | Pave | NaN | IR1 | Lvl | AllPub | FR2 | ... | 0 | NaN | NaN | NaN | 0 | 12 | 2008 | WD | Normal | 250000 |
5 rows × 80 columns
這時候大概心里可以有數,哪些地方需要人為的處理一下,以做到源數據更加好被process。
Step 2: 合并數據:將測試集和訓練集的數據進行合并,因為要對數據做預處理
這么做主要是為了用DF進行數據預處理的時候更加方便。等所有的需要的預處理進行完之后,我們再把他們分隔開。
首先,SalePrice作為我們的訓練目標,只會出現在訓練集中,不會在測試集中(要不然你測試什么?)。所以,我們先把SalePrice這一列給拿出來,不讓它礙事兒。
我們先看一下SalePrice長什么樣紙:
%matplotlib inline prices = pd.DataFrame({"price":train_df["SalePrice"], "log(price + 1)":np.log1p(train_df["SalePrice"])}) prices.hist() array([[<matplotlib.axes._subplots.AxesSubplot object at 0x7fbf9f8703c8>,<matplotlib.axes._subplots.AxesSubplot object at 0x7fbf97340400>]],dtype=object)可見,label本身并不平滑。為了我們分類器的學習更加準確,我們會首先把label給“平滑化”(正態化)
這一步大部分同學會miss掉,導致自己的結果總是達不到一定標準。
這里我們使用最有逼格的log1p, 也就是 log(x+1),避免了復值的問題。
記住喲,如果我們這里把數據都給平滑化了,那么最后算結果的時候,要記得把預測到的平滑數據給變回去。
按照“怎么來的怎么去”原則,log1p()就需要expm1(); 同理,log()就需要exp(), … etc.
對于分類問題,不需要對標簽進行平滑處理,因為其結果本來就是需要離散化的;但是對于回歸問題,由于需要預測的是一個連續的值,所以需要訓練模型的數據的標簽也是平滑的,而對于訓練集,我們得到的標簽往往又是離散的,所以要先做平滑處理!
注:在比賽中,由于訓練集和測試集已經知道,所以常常為了更大的得到好的結果,常常將測試集和訓練集先混合在一起,然后在整體做數據的預處理;但是在實際的過程中,由于測試集是未知的,所以先對訓練集做處理,再用相同的方法去處理測試集。
y_train = np.log1p(train_df.pop('SalePrice'))然后我們把剩下的部分合并起來
all_df = pd.concat((train_df, test_df), axis=0)此刻,我們可以看到all_df就是我們合在一起的DF
all_df.shape (2919, 79)而y_train則是SalePrice那一列
y_train.head() Id 1 12.247699 2 12.109016 3 12.317171 4 11.849405 5 12.429220 Name: SalePrice, dtype: float64Step 3: 變量轉化
類似『特征工程』。就是把不方便處理或者不unify的數據給統一了。
正確化變量屬性
首先,我們注意到,MSSubClass 的值其實應該是一個category,
但是Pandas是不會懂這些事兒的。使用DF的時候,這類數字符號會被默認記成數字。
這種東西就很有誤導性,我們需要把它變回成string
all_df['MSSubClass'].dtypes dtype('int64') all_df['MSSubClass'] = all_df['MSSubClass'].astype(str) #這個根據給出的數據集的描述可知,該屬性應該是表示的為級別,但是PD在讀取數據的時候會自動將其認為是數字,所以需要轉換為字符串變成str以后,做個統計,就很清楚了
all_df['MSSubClass'].value_counts() 20 1079 60 575 50 287 120 182 30 139 160 128 70 128 80 118 90 109 190 61 85 48 75 23 45 18 180 17 40 6 150 1 Name: MSSubClass, dtype: int64把category的變量轉變成numerical表達形式
當我們用numerical來表達categorical的時候,要注意,數字本身有大小的含義,所以亂用數字會給之后的模型學習帶來麻煩。于是我們可以用One-Hot的方法來表達category。
pandas自帶的get_dummies方法,可以幫你一鍵做到One-Hot。
pd.get_dummies(all_df['MSSubClass'], prefix='MSSubClass').head()#將分類數據轉換為數值型數據| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
| 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
此刻MSSubClass被我們分成了12個column,每一個代表一個category。是就是1,不是就是0。
同理,我們把所有的category數據,都給One-Hot了
all_dummy_df = pd.get_dummies(all_df) all_dummy_df.head()| 65.0 | 8450 | 7 | 5 | 2003 | 2003 | 196.0 | 706.0 | 0.0 | 150.0 | ... | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 |
| 80.0 | 9600 | 6 | 8 | 1976 | 1976 | 0.0 | 978.0 | 0.0 | 284.0 | ... | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 |
| 68.0 | 11250 | 7 | 5 | 2001 | 2002 | 162.0 | 486.0 | 0.0 | 434.0 | ... | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 |
| 60.0 | 9550 | 7 | 5 | 1915 | 1970 | 0.0 | 216.0 | 0.0 | 540.0 | ... | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 |
| 84.0 | 14260 | 8 | 5 | 2000 | 2000 | 350.0 | 655.0 | 0.0 | 490.0 | ... | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 |
5 rows × 303 columns
處理好numerical變量
就算是numerical的變量,也還會有一些小問題。
比如,有一些數據是缺失 的:
all_dummy_df.isnull().sum().sort_values(ascending=False).head(10) #將數據中有缺失項的屬性統計出來并從大到小排序 LotFrontage 486 GarageYrBlt 159 MasVnrArea 23 BsmtHalfBath 2 BsmtFullBath 2 BsmtFinSF2 1 GarageCars 1 TotalBsmtSF 1 BsmtUnfSF 1 GarageArea 1 dtype: int64可以看到,缺失最多的column是LotFrontage
處理這些缺失的信息,得靠好好審題。一般來說,數據集的描述里會寫的很清楚,這些缺失都代表著什么。當然,如果實在沒有的話,也只能靠自己的『想當然』。。
在這里,我們用平均值來填滿這些空缺。
mean_cols = all_dummy_df.mean()#得到所有列的平均值 mean_cols.head(10) #打印出前10列數據的平均值 LotFrontage 69.305795 LotArea 10168.114080 OverallQual 6.089072 OverallCond 5.564577 YearBuilt 1971.312778 YearRemodAdd 1984.264474 MasVnrArea 102.201312 BsmtFinSF1 441.423235 BsmtFinSF2 49.582248 BsmtUnfSF 560.772104 dtype: float64 all_dummy_df = all_dummy_df.fillna(mean_cols) #用每一列的平均值填充每一列中的NAN項看看是不是沒有空缺了?
all_dummy_df.isnull().sum().sum() 0標準化numerical數據
這一步并不是必要,但是得看你想要用的分類器是什么。一般來說,regression的分類器都比較傲嬌,最好是把源數據給放在一個標準分布內。不要讓數據間的差距太大。
這里,我們當然不需要把One-Hot的那些0/1數據給標準化。我們的目標應該是那些本來就是numerical的數據:
先來看看 哪些是numerical的:
numeric_cols = all_df.columns[all_df.dtypes != 'object'] numeric_cols Index(['LotFrontage', 'LotArea', 'OverallQual', 'OverallCond', 'YearBuilt','YearRemodAdd', 'MasVnrArea', 'BsmtFinSF1', 'BsmtFinSF2', 'BsmtUnfSF','TotalBsmtSF', '1stFlrSF', '2ndFlrSF', 'LowQualFinSF', 'GrLivArea','BsmtFullBath', 'BsmtHalfBath', 'FullBath', 'HalfBath', 'BedroomAbvGr','KitchenAbvGr', 'TotRmsAbvGrd', 'Fireplaces', 'GarageYrBlt','GarageCars', 'GarageArea', 'WoodDeckSF', 'OpenPorchSF','EnclosedPorch', '3SsnPorch', 'ScreenPorch', 'PoolArea', 'MiscVal','MoSold', 'YrSold'],dtype='object')計算標準分布:(X-X’)/s
讓我們的數據點更平滑,更便于計算。
注意:我們這里也是可以繼續使用Log的,我只是給大家展示一下多種“使數據平滑”的辦法。
numeric_col_means = all_dummy_df.loc[:, numeric_cols].mean()#算出每一個數值型的屬性下數據的平均值 numeric_col_std = all_dummy_df.loc[:, numeric_cols].std()#算出每一個數值型的屬性下數據的方差 all_dummy_df.loc[:, numeric_cols] = (all_dummy_df.loc[:, numeric_cols] - numeric_col_means) / numeric_col_std #計算標準分布Step 4: 建立模型
把數據集分回 訓練/測試集
dummy_train_df = all_dummy_df.loc[train_df.index] dummy_test_df = all_dummy_df.loc[test_df.index] dummy_train_df.shape, dummy_test_df.shape ((1460, 303), (1459, 303))Ridge Regression
用Ridge Regression模型來跑一遍看看。(對于多因子的數據集,這種模型可以方便的把所有的var都無腦的放進去)
from sklearn.linear_model import Ridge from sklearn.model_selection import cross_val_score這一步不是很必要,只是把DF轉化成Numpy Array,這跟Sklearn更加配
X_train = dummy_train_df.values X_test = dummy_test_df.values用Sklearn自帶的cross validation方法來測試模型
alphas = np.logspace(-3, 2, 50) test_scores = [] for alpha in alphas:clf = Ridge(alpha)test_score = np.sqrt(-cross_val_score(clf, X_train, y_train, cv=10, scoring='neg_mean_squared_error'))test_scores.append(np.mean(test_score))存下所有的CV值,看看哪個alpha值更好(也就是『調參數』)
import matplotlib.pyplot as plt %matplotlib inline plt.plot(alphas, test_scores) plt.title("Alpha vs CV Error");可見,大概alpha=10~20的時候,可以把score達到0.135左右。
Random Forest
from sklearn.ensemble import RandomForestRegressor max_features = [.1, .3, .5, .7, .9, .99] test_scores = [] for max_feat in max_features:clf = RandomForestRegressor(n_estimators=200, max_features=max_feat)test_score = np.sqrt(-cross_val_score(clf, X_train, y_train, cv=5, scoring='neg_mean_squared_error'))test_scores.append(np.mean(test_score)) plt.plot(max_features, test_scores) plt.title("Max Features vs CV Error");用RF的最優值達到了0.137
Step 5: Ensemble
這里我們用一個Stacking的思維來汲取兩種或者多種模型的優點
首先,我們把最好的parameter拿出來,做成我們最終的model
ridge = Ridge(alpha=15) rf = RandomForestRegressor(n_estimators=500, max_features=.3) ridge.fit(X_train, y_train) rf.fit(X_train, y_train) RandomForestRegressor(bootstrap=True, criterion='mse', max_depth=None,max_features=0.3, max_leaf_nodes=None,min_impurity_decrease=0.0, min_impurity_split=None,min_samples_leaf=1, min_samples_split=2,min_weight_fraction_leaf=0.0, n_estimators=500, n_jobs=1,oob_score=False, random_state=None, verbose=0, warm_start=False)上面提到了,因為最前面我們給label做了個log(1+x), 于是這里我們需要把predit的值給exp回去,并且減掉那個"1"
所以就是我們的expm1()函數。
y_ridge = np.expm1(ridge.predict(X_test)) y_rf = np.expm1(rf.predict(X_test))一個正經的Ensemble是把這群model的預測結果作為新的input,再做一次預測。這里我們簡單的方法,就是直接『平均化』。
y_final = (y_ridge + y_rf) / 2 #對模型進行融合Step 6: 提交結果
submission_df = pd.DataFrame(data= {'Id' : test_df.index, 'SalePrice': y_final})我們的submission大概長這樣:
submission_df.head(10)| 1461 | 120284.850938 |
| 1462 | 151366.179663 |
| 1463 | 174792.133120 |
| 1464 | 190099.605945 |
| 1465 | 195500.170436 |
| 1466 | 175876.563502 |
| 1467 | 177675.128914 |
| 1468 | 169318.651321 |
| 1469 | 184796.699921 |
| 1470 | 122328.836416 |
走你~
總結
以上是生活随笔為你收集整理的kaggle(02)-房价预测案例(基础版)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 轻松理解牛顿迭代法且用其求平方根
- 下一篇: PCB设计的基本步骤