文章目錄
一、賽題說明
1.1題目說明
賽題鏈接:“未來杯” 城市-房產租金預測
比賽要求參賽選手根據給定的數據集,建立模型,預測房屋租金。
數據集中的數據類別包括租賃房源、小區、二手房、配套、新房、土地、人口、客戶、真實租金等。
這是典型的迴歸預測。
1.2 預測指標
迴歸結果評價標準採用R-Square
R2(R-Square)的公式爲:
殘差平方和:
總平均值:
其中 表示 𝑦 的平均值, 得到 表達式爲:
用於度量因變量的變異中可由自變量解釋部分所佔的比例,取值範圍是 0~1,越接近1,表明迴歸平方和佔總平方和的比例越大,迴歸線與各觀測點越接近,用x的變化來解釋y值變化的部分就越多,迴歸的擬合程度就越好。所以 也稱爲擬合優度(Goodness of Fit)的統計量。
y_{i}表示真實值,表示預測值,表示樣本均值。得分越高擬合效果越好。
1.3 數據概況
1.租賃基本信息:
- ID——房屋編號
- area——房屋面積
- rentType——出租方式:整租/合租/未知
- houseType——房型
- houseFloor——房間所在樓層:高/中/低
- totalFloor——房間所在的總樓層數
- houseToward——房間朝向
- houseDecoration——房屋裝修
- tradeTime——成交日期
- tradeMoney——成交租金
2.小區信息:
- CommunityName——小區名稱
- city——城市
- region——地區
- plate——區域板塊
- buildYear——小區建築年代
- saleSecHouseNum——該板塊當月二手房掛牌房源數
3.配套設施:
- subwayStationNum——該板塊地鐵站數量
- busStationNum——該板塊公交站數量
- interSchoolNum——該板塊國際學校的數量
- schoolNum——該板塊公立學校的數量
- privateSchoolNum——該板塊私立學校數量
- hospitalNum——該板塊綜合醫院數量
- DrugStoreNum——該板塊藥房數量
- gymNum——該板塊健身中心數量
- bankNum——該板塊銀行數量
- shopNum——該板塊商店數量
- parkNum——該板塊公園數量
- mallNum——該板塊購物中心數量
- superMarketNum——該板塊超市數量
4.其他信息:
- totalTradeMoney——該板塊當月二手房成交總金額
- totalTradeArea——該板塊二手房成交總面積
- tradeMeanPrice——該板塊二手房成交均價
- tradeSecNum——該板塊當月二手房成交套數
- totalNewTradeMoney——該板塊當月新房成交總金額
- totalNewTradeArea——該板塊當月新房成交的總面積
- totalNewMeanPrice——該板塊當月新房成交均價
- tradeNewNum——該板塊當月新房成交套數
- remainNewNum——該板塊當月新房未成交套數
- supplyNewNum——該板塊當月新房供應套數
- supplyLandNum——該板塊當月土地供應幅數
- supplyLandArea——該板塊當月土地供應面積
- tradeLandNum——該板塊當月土地成交幅數
- tradeLandArea——該板塊當月土地成交面積
- landTotalPrice——該板塊當月土地成交總價
- landMeanPrice——該板塊當月樓板價(元/m^{2})
- totalWorkers——當前板塊現有的辦公人數
- newWorkers——該板塊當月流入人口數(現招聘的人員)
- residentPopulation——該板塊常住人口
- pv——該板塊當月租客瀏覽網頁次數
- uv——該板塊當月租客瀏覽網頁總人數
- lookNum——線下看房次數
二、數據分析
2.1 總體瀏覽數據
#coding:utf-8
#導入warnings包,利用過濾器來實現忽略警告語句。
import warnings
warnings.filterwarnings('ignore')
# GBDT
from sklearn.ensemble import GradientBoostingRegressor
# XGBoost
import xgboost as xgb
# LightGBM
import lightgbm as lgb
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
#載入數據
data_train = pd.read_csv('F:\\實驗\\比賽\\房價預測\\數據集\\train_data.csv')
data_train['Type'] = 'Train'
data_test = pd.read_csv('F:\\實驗\\比賽\\房價預測\\數據集\\test_a.csv')
data_test['Type'] = 'Test'
data_all = pd.concat([data_train, data_test], ignore_index=True)
# 總體情況
print(data_train.info())
print(data_train.describe())
data_train.head()
結果(截取部分):
簡要分析:
- 該份訓練集包含 41440行×52列數據;
- 目標變量是 真實房租價格- tradeMoney;
- 大多數數據都是int或float型;有部分字段是object型,即文本型中文或英文的,如rentType字段,這些字段在之後需要做處理。
2.2 分類特徵和連續型特徵
# 根據特徵含義和特徵一覽,大致可以判斷出數值型和類別型特徵如下
categorical_feas = ['rentType', 'houseType', 'houseFloor', 'region', 'plate', 'houseToward', 'houseDecoration',
'communityName','city','buildYear']
numerical_feas=['ID','area','totalFloor','saleSecHouseNum','subwayStationNum',
'busStationNum','interSchoolNum','schoolNum','privateSchoolNum','hospitalNum',
'drugStoreNum','gymNum','bankNum','shopNum','parkNum','mallNum','superMarketNum',
'totalTradeMoney','totalTradeArea','tradeMeanPrice','tradeSecNum','totalNewTradeMoney',
'totalNewTradeArea','tradeNewMeanPrice','tradeNewNum','remainNewNum','supplyNewNum',
'supplyLandNum','supplyLandArea','tradeLandNum','tradeLandArea','landTotalPrice',
'landMeanPrice','totalWorkers','newWorkers','residentPopulation','pv','uv','lookNum']
2.3 缺失值分析
def missing_values(df):
alldata_na = pd.DataFrame(df.isnull().sum(), columns={'missingNum'})
alldata_na['existNum'] = len(df) - alldata_na['missingNum']
alldata_na['sum'] = len(df)
alldata_na['missingRatio'] = alldata_na['missingNum']/len(df)*100
alldata_na['dtype'] = df.dtypes
#ascending:默認True升序排列;False降序排列
alldata_na = alldata_na[alldata_na['missingNum']>0].reset_index().sort_values(by=['missingNum','index'],ascending=[False,True])
alldata_na.set_index('index',inplace=True)
return alldata_na
missing_values(data_train)
簡要分析:
- 這裏採用編寫函數的方式來直接獲取結果(這種方式會在之後反覆用到,建議大家儘早養成函數式編寫的習慣);
- 其實在總體情況一覽中,info()函數也能看出來。
- 結果是,僅有pv、uv存在缺失值,後面再探究會發現缺失的都是屬於同一個plate,可能是官方直接刪除了該plate的pv、uv。
2.4 單調特徵列分析
#是否有單調特徵列(單調的特徵列很大可能是時間)
def incresing(vals):
cnt = 0
len_ = len(vals)
for i in range(len_-1):
if vals[i+1] > vals[i]:
cnt += 1
return cnt
fea_cols = [col for col in data_train.columns]
for col in fea_cols:
cnt = incresing(data_train[col].values)
if cnt / data_train.shape[0] >= 0.55:
print('單調特徵:',col)
print('單調特徵值個數:', cnt)
print('單調特徵值比例:', cnt / data_train.shape[0])
單調特徵: tradeTime
單調特徵值個數: 24085
單調特徵值比例: 0.5812017374517374
簡要分析:
- 先編寫判斷單調的函數 incresing, 然後再應用到每列上;
- 單調特徵是 tradeTime,爲時間列。
- 多說句額外的,時間列在特徵工程的時候,不同的情況下能有很多的變種形式,比如按年月日分箱,或者按不同的維度在時間上聚合分組,等等。
2.5 特徵nunique分佈
# 特徵nunique分佈
for feature in categorical_feas:
print(feature + "的特徵分佈如下:")
print(data_train[feature].value_counts())
if feature != 'communityName': # communityName值太多,暫且不看圖表
plt.hist(data_all[feature], bins=3)
plt.show()
結果(部分截圖):
print(data_train['communityName'].value_counts())
print(data_test['communityName'].value_counts())
結果:
XQ01834 358
XQ01274 192
XQ02273 188
XQ03110 185
XQ02337 173
...
XQ01302 1
XQ02870 1
XQ01972 1
XQ02671 1
XQ01787 1
Name: communityName, Length: 4236, dtype: int64
XQ03306 1
XQ00629 1
XQ01830 1
XQ00534 1
XQ02021 1
..
XQ03596 1
XQ03784 1
XQ00332 1
XQ01140 1
XQ00239 1
Name: communityName, Length: 2469, dtype: int64
簡要分析:
- 用自帶函數value_counts() 來得到每個分類變量的種類分佈; 並且簡單畫出柱狀圖。
- rentType:4種,且絕大多數是無用的未知方式;
- houseType:104種,絕大多數在3室及以下;
- houseFloor:3種,分佈較爲均勻;
- region: 15種;
- plate: 66種;
- houseToward: 10種;
- houseDecoration: 4種,一大半是其他;
- buildYear: 80種;
- communityName:4236種,且分佈較爲稀疏;
- 此步驟是爲之後數據處理和特徵工程做準備,先理解每個字段的含義以及分佈,之後需要根據實際含義對分類變量做不同的處理。
2.6 統計特徵值頻次大於100的特徵
# 統計特徵值出現頻次大於100的特徵
for feature in categorical_feas:
df_value_counts = pd.DataFrame(data_train[feature].value_counts())
df_value_counts = df_value_counts.reset_index()
df_value_counts.columns = [feature, 'counts'] # change column names
print(df_value_counts[df_value_counts['counts'] >= 100])
結果(部分截圖):
簡要分析:
- 此步驟和特徵nunique分佈結合步驟結合起來看,有一些小於100的是可以直接統一歸類爲其他的
2.7 Label分佈
# Labe 分佈
fig,axes = plt.subplots(2,3,figsize=(20,5))
fig.set_size_inches(20,12)
sns.distplot(data_train['tradeMoney'],ax=axes[0][0])
sns.distplot(data_train[(data_train['tradeMoney']<=20000)]['tradeMoney'],ax=axes[0][1])
sns.distplot(data_train[(data_train['tradeMoney']>20000)&(data_train['tradeMoney']<=50000)]['tradeMoney'],ax=axes[0][2])
sns.distplot(data_train[(data_train['tradeMoney']>50000)&(data_train['tradeMoney']<=100000)]['tradeMoney'],ax=axes[1][0])
sns.distplot(data_train[(data_train['tradeMoney']>100000)]['tradeMoney'],ax=axes[1][1])
結果截圖:
print("money<=10000",len(data_train[(data_train['tradeMoney']<=10000)]['tradeMoney']))
print("10000<money<=20000",len(data_train[(data_train['tradeMoney']>10000)&(data_train['tradeMoney']<=20000)]['tradeMoney']))
print("20000<money<=50000",len(data_train[(data_train['tradeMoney']>20000)&(data_train['tradeMoney']<=50000)]['tradeMoney']))
print("50000<money<=100000",len(data_train[(data_train['tradeMoney']>50000)&(data_train['tradeMoney']<=100000)]['tradeMoney']))
print("100000<money",len(data_train[(data_train['tradeMoney']>100000)]['tradeMoney']))
結果:
money<=10000 38964
10000<money<=20000 1985
20000<money<=50000 433
50000<money<=100000 39
100000<money 19
簡要分析:
- 將目標變量tradeMoney分組,並查看每組間的分佈; 可以看出絕大多數都是集中在10000元以內的,並且從圖中可以看到該分佈是右偏的。
- 這裏只是一種實現方式,完全可以將tradeMoney和其他字段一起結合起來查看,比如樓層高低,地區板塊。