1. 程式人生 > >資料科學和人工智慧技術筆記 三、資料預處理

資料科學和人工智慧技術筆記 三、資料預處理

三、資料預處理

作者:Chris Albon

譯者:飛龍

協議:CC BY-NC-SA 4.0

為 Scikit-Learn 轉換 Pandas 類別資料

# 匯入所需的庫
from sklearn import preprocessing
import pandas as pd

raw_data = {'patient': [1, 1, 1, 2, 2],
        'obs': [1, 2, 3, 1, 2],
        'treatment': [0, 1, 0, 1, 0],
        'score': ['strong',
'weak', 'normal', 'weak', 'strong']} df = pd.DataFrame(raw_data, columns = ['patient', 'obs', 'treatment', 'score']) # 建立標籤(類別)編碼物件 le = preprocessing.LabelEncoder() # 使編碼器擬合 pandas 列 le.fit(df['score']) # LabelEncoder() # 檢視標籤(如果你希望) list(le.classes_) # ['normal', 'strong', 'weak'] # 將擬合的編碼器應用於 pandas 列
le.transform(df['score']) # array([1, 2, 0, 2, 1]) # 將一些整數轉換為它們的類別名稱 list(le.inverse_transform([2, 2, 1])) # ['weak', 'weak', 'strong']

刪除帶缺失值的觀測

# 載入庫
import numpy as np
import pandas as pd

# 建立特徵矩陣
X = np.array([[1.1, 11.1], 
              [2.2, 22.2], 
              [3.3, 33.3], 
              [
4.4, 44.4], [np.nan, 55]]) # 移除帶缺失值的觀測 X[~np.isnan(X).any(axis=1)] ''' array([[ 1.1, 11.1], [ 2.2, 22.2], [ 3.3, 33.3], [ 4.4, 44.4]]) '''

刪除缺失值

# 載入庫
import numpy as np
import pandas as pd

# 建立特徵矩陣
X = np.array([[1, 2], 
              [6, 3], 
              [8, 4], 
              [9, 5], 
              [np.nan, 4]])

# 移除帶缺失值的觀測
X[~np.isnan(X).any(axis=1)]

array([[ 1.,  2.],
       [ 6.,  3.],
       [ 8.,  4.],
       [ 9.,  5.]]) 

# 將資料載入為資料幀
df = pd.DataFrame(X, columns=['feature_1', 'feature_2'])

# 移除帶缺失值的觀測
df.dropna()
feature_1 feature_2
0 1.0 2.0
1 6.0 3.0
2 8.0 4.0
3 9.0 5.0

檢測離群點

# 載入庫
import numpy as np
from sklearn.covariance import EllipticEnvelope
from sklearn.datasets import make_blobs

# 建立模擬資料
X, _ = make_blobs(n_samples = 10,
                  n_features = 2,
                  centers = 1,
                  random_state = 1)

# 將第一個觀測值替換為異常值
X[0,0] = 10000
X[0,1] = 10000

EllipticEnvelope假設資料是正態分佈的,並且基於該假設,在資料周圍“繪製”橢圓,將橢圓內的任何觀測分類為正常(標記為1),並將橢圓外的任何觀測分類為異常值(標記為-1)。 這種方法的一個主要限制是,需要指定一個contamination引數,該引數是異常觀測值的比例,這是我們不知道的值。

# 建立檢測器
outlier_detector = EllipticEnvelope(contamination=.1)

# 擬合檢測器
outlier_detector.fit(X)

# 預測離群點
outlier_detector.predict(X)

# array([-1,  1,  1,  1,  1,  1,  1,  1,  1,  1]) 

離散化特徵

# 載入庫
from sklearn.preprocessing import Binarizer
import numpy as np

# 建立特徵
age = np.array([[6], 
                [12], 
                [20], 
                [36], 
                [65]])

# 建立二值化器
binarizer = Binarizer(18)

# 轉換特徵
binarizer.fit_transform(age)

'''
array([[0],
       [0],
       [1],
       [1],
       [1]]) 
'''

# 對特徵分箱
np.digitize(age, bins=[20,30,64])

'''
array([[0],
       [0],
       [1],
       [2],
       [3]]) 
'''

編碼序數類別特徵

# 載入庫
import pandas as pd

# 建立特徵
df = pd.DataFrame({'Score': ['Low', 
                             'Low', 
                             'Medium', 
                             'Medium', 
                             'High']})

# 檢視資料幀
df
Score
0 Low
1 Low
2 Medium
3 Medium
4 High

建立比例對映

# 建立對映器
scale_mapper = {'Low':1, 
                'Medium':2,
                'High':3}

# 將特徵值對映為比例
df['Scale'] = df['Score'].replace(scale_mapper)

# 檢視資料幀
df
Score Scale
0 Low 1
1 Low 1
2 Medium 2
3 Medium 2
4 High 3

使用下采樣處理不平衡類

在下采樣中,我們從多數類(即具有更多觀測值的類)中不放回隨機抽樣,來建立與少數類相等的新觀測子集。

# 載入庫
import numpy as np
from sklearn.datasets import load_iris

# 載入鳶尾花資料
iris = load_iris()

# 建立特徵矩陣
X = iris.data

# 建立目標向量
y = iris.target

# 移除前 40 個觀測
X = X[40:,:]
y = y[40:]

# 建立二元目標向量,表示是否是類 0
y = np.where((y == 0), 0, 1)

# 檢視不平衡的目標向量
y

'''
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]) 
'''

# 每個類別的觀測的下標
i_class0 = np.where(y == 0)[0]
i_class1 = np.where(y == 1)[0]

# 每個類別的觀測數量
n_class0 = len(i_class0)
n_class1 = len(i_class1)

# 對於類 0 的每個觀測,隨機從類 1 不放回取樣
i_class1_downsampled = np.random.choice(i_class1, size=n_class0, replace=False)

# 將類 0 的目標向量,和下采樣的類 1 的目標向量連線到一起
np.hstack((y[i_class0], y[i_class1_downsampled]))

# array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]) 

使用上取樣處理不平衡類別

在上取樣中,對於多數類中的每個觀測,我們從少數類中帶放回隨機選擇觀測。 最終結果是來自少數類和多數類的觀測數量相同。

# 載入庫
import numpy as np
from sklearn.datasets import load_iris

# 載入鳶尾花資料
iris = load_iris()

# 建立特徵矩陣
X = iris.data

# 建立目標向量
y = iris.target

# 移除前 40 個觀測
X = X[40:,:]
y = y[40:]

# 建立二元目標向量,表示是否是類 0
y = np.where((y == 0), 0, 1)

# 檢視不平衡的目標向量
y

'''
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]) 
'''

# 每個類別的觀測的下標
i_class0 = np.where(y == 0)[0]
i_class1 = np.where(y == 1)[0]

# 每個類別的觀測數量
n_class0 = len(i_class0)
n_class1 = len(i_class1)

# 對於類 1 中的每個觀測,我們從類 0 中帶放回隨機選擇觀測。
i_class0_upsampled = np.random.choice(i_class0, size=n_class1, replace=True)

# 將類 0 的上取樣的目標向量,和類 1 的目標向量連線到一起
np.concatenate((y[i_class0_upsampled], y[i_class1]))

'''
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]) 
'''

處理離群點

# 載入庫
import pandas as pd

# 建立 DataFrame
houses = pd.DataFrame()
houses['Price'] = [534433, 392333, 293222, 4322032]
houses['Bathrooms'] = [2, 3.5, 2, 116]
houses['Square_Feet'] = [1500, 2500, 1500, 48000]

houses
Price Bathrooms Square_Feet
0 534433 2.0 1500
1 392333 3.5 2500
2 293222 2.0 1500
3 4322032 116.0 48000

選擇 1:丟棄

# 丟棄大於某個值的觀測
houses[houses['Bathrooms'] < 20]
Price Bathrooms Square_Feet
0 534433 2.0 1500
1 392333 3.5 2500
2 293222 2.0 1500

選擇 2:標記

# 載入庫
import numpy as np

# 基於布林條件建立特徵
houses['Outlier'] = np.where(houses['Bathrooms'] < 20, 0, 1)

# 展示資料
houses
Price Bathrooms Square_Feet Outlier
0 534433 2.0 1500 0
1 392333 3.5 2500 0
2 293222 2.0 1500 0
3 4322032 116.0 48000 1

選擇 3:重縮放

# 對數特徵
houses['Log_Of_Square_Feet'] = [np.log(x) for x in houses['Square_Feet']]

# 展示資料
houses
Price Bathrooms Square_Feet Outlier Log_Of_Square_Feet
0 534433 2.0 1500 0 7.313220
1 392333 3.5 2500 0 7.824046
2 293222 2.0 1500 0 7.313220
3 4322032 116.0 48000 1 10.778956

使用均值填充缺失值

均值插補用該特徵/變數的平均值替換缺失值。 平均插補是最“樸素”的插補方法之一,因為不像 k 最近鄰居插補這樣的更復雜的方法,它不會使用觀測的資訊來估計它的值。

import pandas as pd
import numpy as np
from sklearn.preprocessing import Imputer

# 建立空資料集
df = pd.DataFrame()

# 建立兩個變數,叫做 x0 和 x1
# 使 x1 的第一個值為缺失值
df['x0'] = [0.3051,0.4949,0.6974,0.3769,0.2231,0.341,0.4436,0.5897,0.6308,0.5]
df['x1'] = [np.nan,0.2654,0.2615,0.5846,0.4615,0.8308,0.4962,0.3269,0.5346,0.6731]

# 觀察資料集
df
x0 x1
0 0.3051 NaN
1 0.4949 0.2654
2 0.6974 0.2615
3 0.3769 0.5846
4 0.2231 0.4615
5 0.3410 0.8308
6 0.4436 0.4962
7 0.5897 0.3269
8 0.6308 0.5346
9 0.5000 0.6731

擬合填充器

# 建立一個填充器物件,它尋找 NaN 值,之後將它們按列替換為特徵的均值
mean_imputer = Imputer(missing_values='NaN', strategy='mean', axis=0)

# 在 df 資料及上訓練填充器
mean_imputer = mean_imputer.fit(df)

# 將填充器應用於 df 資料集
imputed_df = mean_imputer.transform(df.values)

# 檢視資料
imputed_df

'''
array([[ 0.3051    ,  0.49273333],
       [ 0.4949    ,  0.2654    ],
       [ 0.6974    ,  0.2615    ],
       [ 0.3769    ,  0.5846    ],
       [ 0.2231    ,  0.4615    ],
       [ 0.341     ,  0.8308    ],
       [ 0.4436    ,  0.4962    ],
       [ 0.5897    ,  0.3269    ],
       [ 0.6308    ,  0.5346    ],
       [ 0.5       ,  0.6731    ]]) 
'''

請注意,0.49273333是估算值,取代了np.NaN值。

填充缺失的類標籤

# 載入庫
import numpy as np
from sklearn.preprocessing import Imputer

# 建立帶有類別特徵的特徵矩陣
X = np.array([[0, 2.10, 1.45], 
              [1, 1.18, 1.33], 
              [0, 1.22, 1.27],
              [0, -0.21, -1.19],
              [np.nan, 0.87, 1.31],
              [np.nan, -0.67, -0.22]])

# 建立填充器物件
imputer = Imputer(strategy='most_frequent', axis=0)

# 使用最頻繁的類別填充缺失值
imputer.fit_transform(X)

'''
array([[ 0.  ,  2.1 ,  1.45],
       [ 1.  ,  1.18,  1.33],
       [ 0.  ,  1.22,  1.27],
       [ 0.  , -0.21, -1.19],
       [ 0.  ,  0.87,  1.31],
       [ 0.  , -0.67, -0.22]]) 
'''

使用 KNN 填充缺失類別

# 載入庫
import numpy as np
from sklearn.neighbors import KNeighborsClassifier

# 建立帶有類別特徵的特徵矩陣
X = np.array([[0, 2.10, 1.45], 
              [1, 1.18, 1.33], 
              [0, 1.22, 1.27],
              [1, -0.21, -1.19]])

# 建立類別特徵有缺失的特徵矩陣
X_with_nan = np.array([[np.nan, 0.87, 1.31], 
                       [np.nan, -0.67, -0.22]])

# 訓練 KNN 學習器
clf = KNeighborsClassifier(3, weights='distance')
trained_model = clf.fit(X[:,1:], X[:,0])

# 預測缺失值的類別
imputed_values = trained_model.predict(X_with_nan[:,1:])

# 將預測分類的列和它們的其它特徵連線
X_with_imputed = np.hstack((imputed_values.reshape(-1,1), X_with_nan[:,1:]))

# 連線兩個特徵矩陣
np.vstack((X_with_imputed, X))

'''
array([[ 0.  ,  0.87,  1.31],
       [ 1.  , -0.67, -0.22],
       [ 0.  ,  2.1 ,  1.45],
       [ 1.  ,  1.18,  1.33],
       [ 0.  ,  1.22,  1.27],
       [ 1.  , -0.21, -1.19]]) 
'''

觀測正則化

# 載入庫
from sklearn.preprocessing import Normalizer
import numpy as np

# 建立特徵矩陣
X = np.array([[0.5, 0.5], 
              [1.1, 3.4], 
              [1.5, 20.2], 
              [1.63, 34.4], 
              [10.9, 3.3]])

Normalizer重縮放各個觀側,使其具有單位範數(長度之和為 1)。

# 建立正則化器
normalizer = Normalizer(norm='l2')

# 轉換特徵矩陣
normalizer.transform(X)

'''
array([[ 0.70710678,  0.70710678],
       [ 0.30782029,  0.95144452],
       [ 0.07405353,  0.99725427],
       [ 0.04733062,  0.99887928],
       [ 0.95709822,  0.28976368]]) 
'''

多個標籤的獨熱編碼特徵

# 載入庫
from sklearn.preprocessing import MultiLabelBinarizer
import numpy as np

# 建立 NumPy 陣列
y = [('Texas', 'Florida'), 
    ('California', 'Alabama'), 
    ('Texas', 'Florida'), 
    ('Delware', 'Florida'), 
    ('Texas', 'Alabama')]

# 建立 MultiLabelBinarizer 物件
one_hot = MultiLabelBinarizer()

# 獨熱編碼資料
one_hot.fit_transform(y)

'''
array([[0, 0, 0, 1, 1],
       [1, 1, 0, 0, 0],
       [0, 0, 0, 1, 1],
       [0, 0, 1, 1, 0],
       [1, 0, 0, 0, 1]]) 
'''

# 檢視類別
one_hot.classes_

# array(['Alabama', 'California', 'Delware', 'Florida', 'Texas'], dtype=object) 

獨熱編碼標稱類別特徵

# 載入庫
import numpy as np
import pandas as pd
from sklearn.preprocessing import LabelBinarizer

# 建立 NumPy 陣列
x = np.array([['Texas'], 
              ['California'], 
              ['Texas'], 
              ['Delaware'], 
              ['Texas']])

# 建立 LabelBinzarizer 物件
one_hot = LabelBinarizer()

# 獨熱編碼資料
one_hot.fit_transform(x)

'''
array([[0, 0, 1],
       [1, 0, 0],
       [0, 0, 1],
       [0, 1, 0],
       [0, 0, 1]]) 
'''

# 檢視類別
one_hot.classes_

'''
array(['California', 'Delaware', 'Texas'],
      dtype='<U10') 
'''

# 虛擬特徵
pd.get_dummies(x[:,0])
California Delaware Texas
0 0 0 1
1 1 0 0
2 0 0 1
3 0 1 0
4 0 0 1

預處理類別特徵

通常,機器學習方法(例如