在sklearn之數據分析中總結了數據分析常用方法,接下來對數據預處理進行總結
當我們拿到數據集后一般需要進行以下步驟:
- (1)明確有數據集有多少特征,哪些是連續的,哪些是類別的
- (2)檢查有沒有缺失值,對缺失的特征選擇恰當的方式進行彌補,使數據完整
- (3)對連續的數值型特征進行標准化
- (4)對類別型的特征進行編碼
- (5)根據實際問題分析是否需要對特征進行相應的函數轉換
依然以房價數據為例,依次進行上述操作
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
housing = pd.read_csv('./datasets/housing/housing.csv')
1. 明確數據集有多少特征,哪些是連續的,哪些是類別的
print(housing.shape)
(20640, 10)
print(housing.info())
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 20640 entries, 0 to 20639
Data columns (total 10 columns):
longitude 20640 non-null float64
latitude 20640 non-null float64
housing_median_age 20640 non-null float64
total_rooms 20640 non-null float64
total_bedrooms 20433 non-null float64
population 20640 non-null float64
households 20640 non-null float64
median_income 20640 non-null float64
median_house_value 20640 non-null float64
ocean_proximity 20640 non-null object
dtypes: float64(9), object(1)
memory usage: 1.6+ MB
None
2. 檢查有沒有缺失值,對缺失的特征選擇恰當的方式進行彌補,使數據完整
通過info()發現除了:
- ocean_proximity屬性類別為object外,其余都為float64類型,則判斷ocean_proximity為標簽,其余為特征值
- total_bedrooms存在缺失值
2.1 缺失值處理方式
(1) 放棄缺失值所在的行
(2) 放棄缺失值所在的屬性,即列
(3) 將缺失值設置為某個值(0,平均值、中位數或使用頻率高的值)
print(housing[housing.isnull().T.any().T][:5]) #打印有NaN的行的前5行
longitude latitude housing_median_age total_rooms total_bedrooms \
290 -122.16 37.77 47.0 1256.0 NaN
341 -122.17 37.75 38.0 992.0 NaN
538 -122.28 37.78 29.0 5154.0 NaN
563 -122.24 37.75 45.0 891.0 NaN
696 -122.10 37.69 41.0 746.0 NaN
population households median_income median_house_value ocean_proximity
290 570.0 218.0 4.3750 161900.0 NEAR BAY
341 732.0 259.0 1.6196 85100.0 NEAR BAY
538 3741.0 1273.0 2.5762 173400.0 NEAR BAY
563 384.0 146.0 4.9489 247100.0 NEAR BAY
696 387.0 161.0 3.9063 178400.0 NEAR BAY
2.1.1 刪除缺失值所在的行
# 刪除行
housing1 = housing.dropna(subset=['total_bedrooms'])
print(housing1.info())
<class 'pandas.core.frame.DataFrame'>
Int64Index: 20433 entries, 0 to 20639
Data columns (total 10 columns):
longitude 20433 non-null float64
latitude 20433 non-null float64
housing_median_age 20433 non-null float64
total_rooms 20433 non-null float64
total_bedrooms 20433 non-null float64
population 20433 non-null float64
households 20433 non-null float64
median_income 20433 non-null float64
median_house_value 20433 non-null float64
ocean_proximity 20433 non-null object
dtypes: float64(9), object(1)
memory usage: 1.7+ MB
None
2.1.2 刪除缺失值所在的列
# 刪除列
housing2 = housing.drop(['total_bedrooms',],axis=1)
print(housing2.info())
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 20640 entries, 0 to 20639
Data columns (total 9 columns):
longitude 20640 non-null float64
latitude 20640 non-null float64
housing_median_age 20640 non-null float64
total_rooms 20640 non-null float64
population 20640 non-null float64
households 20640 non-null float64
median_income 20640 non-null float64
median_house_value 20640 non-null float64
ocean_proximity 20640 non-null object
dtypes: float64(8), object(1)
memory usage: 1.4+ MB
None
2.1.3 替換缺失值
# 使用平均值替換
mean = housing['total_bedrooms'].mean()
print('mean:',mean)
housing3 = housing.fillna({'total_bedrooms':mean})
print(housing3[290:291])
mean: 537.8705525375618
longitude latitude housing_median_age total_rooms total_bedrooms \
290 -122.16 37.77 47.0 1256.0 537.870553
population households median_income median_house_value ocean_proximity
290 570.0 218.0 4.375 161900.0 NEAR BAY
3. 對連續的數值型特征進行標准化
當數據集的數值屬性具有非常大的比例差異,往往導致機器學習的算法表現不佳,當然也有極少數特例。在實際應用中,通過梯度下降法求解的模型通常需要歸一化,包括線性回歸、邏輯回歸、支持向量機、神經網絡等模型。但對於決策樹不使用,以C4.5為例,決策樹在進行節點分裂時主要依據數據集D關於特征X的信息增益比,而信息增益比跟特征是否經過歸一化是無關的
數據標准化常用方法有:
- 最小-最大縮放(又加歸一化),將值重新縮放使其最終范圍在0-1之間,(current - min)/ (max - min)
- 標准化,(current - mean) / var,使得得到的結果分布具備單位方差,相比最小-最大縮放,標准化的方法受異常值的影響更小
4. 對類別型的特征進行編碼
4.1 為什么要進行編碼
在監督學習中,除了決策樹等少數模型外都需要將預測值與實際值(也就是說標簽)進行比較,然后通過算法優化損失函數,這就需要將標簽轉換為數值類型用於計算
4.2 如何編碼
常用的編碼方式有:序號編碼,獨熱編碼,二進制編碼
4.2.1 序號編碼
序號編碼通常用於處理類別間具有大小感謝的數據,例如成績,可以分為低、中、高三檔,並且存在‘高>中>低’的排列順序,序號編碼會按照大小關系對類別型特征賦予一個數值ID,例如高表示3,中表示2,低表示1
4.2.2 獨熱編碼
獨熱編碼通常用於處理類別間不具有大小關系的特征。例如血血型,一共有4個取值(A型血、B型血、AB型血、O型血),獨熱編碼會把血型變成一個4維稀疏向量,A型血表示(1,0,0,0),B型血表示(0,1,0,0),C型血表示(0,0,1,0),D型血表示(0,0,0,1)
對於類別取值較多的情況下使用獨熱編碼需要注意以下問題:
(1) 使用稀疏向量來節省空間
在獨熱編碼下,特征向量只有某一維取1,其他位置均為0,因此可以利用向量的稀疏性表示有效地節省空間,並且目前大部分的算法均接受稀疏向量形式的輸入
(2) 配合特征選擇來降低維度
4.2.3 二進制編碼
二進制編碼本質上就是利用二進制對ID進行哈希映射,最終得到0/1特征向量,且維數少於獨熱編碼,節省了存儲空間
5. 根據實際問題分析是否需要對特征進行相應的函數轉換
當我們對數據集進行一定程度的分析之后,可能會發現不同屬性之間的某些有趣的聯系,特別是跟目標屬性相關的聯系,在准備給機器學習算法輸入數據之前,應該嘗試各種屬性的組合
以上面的房價數據集為例,如果你不知道一個地區有多少個家庭,那么知道一個地區的房間總數也沒什么用,你真正想知道是的一個家庭的房間數量,同樣的,但看卧室總數這個屬性本身,也沒有什么意義,你可能想拿它和房間總數來對比,或者拿來通每個家庭的人口數這個屬性結合
5.1 查看原數據集屬性與房間中位數的相關性
corr_martrix = housing.corr()
print(corr_martrix['median_house_value'].sort_values(ascending=False))
median_house_value 1.000000
median_income 0.688075
total_rooms 0.134153
housing_median_age 0.105623
households 0.065843
total_bedrooms 0.049686
population -0.024650
longitude -0.045967
latitude -0.144160
Name: median_house_value, dtype: float64
5.2 查看屬性組合后與房間中位數的相關性
housing4 = housing.copy()
housing4['rooms_per_household'] = housing4['total_rooms'] / housing4['households']
housing4['bedrooms_per_room'] = housing4['total_bedrooms'] / housing4['total_rooms']
housing4['population_per_household'] = housing4['population'] / housing4['households']
corr_martrix1 = housing.corr()
print(corr_martrix1['median_house_value'].sort_values(ascending=False))
median_house_value 1.000000
median_income 0.688075
total_rooms 0.134153
housing_median_age 0.105623
households 0.065843
total_bedrooms 0.049686
population -0.024650
longitude -0.045967
latitude -0.144160
Name: median_house_value, dtype: float64
可以看出bedrooms_per_room較房間總數或是卧室總數與房價中位數的相關性要高的多,所以在進行屬性組合時可以多多嘗試
6. 使用Sklearn.pipeline實現數據預處理
6.1 代碼實現
from sklearn.preprocessing import Imputer,LabelEncoder,OneHotEncoder,StandardScaler
from sklearn.base import BaseEstimator,TransformerMixin
from sklearn.pipeline import Pipeline,FeatureUnion
class DaraFrameSelector(BaseEstimator,TransformerMixin):
def __init__(self,attr_name):
self.attr_name = attr_name
def fit(self,X,Y=None):
return self
def transform(self,X,Y=None):
return X[self.attr_name].values
features_attr = list(housing.columns[:-1])
labels_attr = [housing.columns[-1]]
feature_pipeline = Pipeline([('selector',DaraFrameSelector(features_attr)),
('imputer',Imputer(strategy='mean')),
('scaler',StandardScaler()),])
label_pipeline = Pipeline([('selector',DaraFrameSelector(labels_attr)),
('encoder',OneHotEncoder()),])
full_pipeline = FeatureUnion(transformer_list=[('feature_pipeline',feature_pipeline),
('label_pipeline',label_pipeline),])
C:\Anaconda3\lib\site-packages\sklearn\utils\deprecation.py:58: DeprecationWarning: Class Imputer is deprecated; Imputer was deprecated in version 0.20 and will be removed in 0.22. Import impute.SimpleImputer from sklearn instead.
warnings.warn(msg, category=DeprecationWarning)
housing_prepared = full_pipeline.fit_transform(housing)
print(housing_prepared.shape)
(20640, 14)
參考資料:
- (1) 《機器學習實戰基於Scikit-Learn和TensorFlow》
- (2) 《白面機器學習》