第4章主要學習如何處理數據的特徵向量,包括如何重新調整(使用MinMaxScaler、StandardScaler等方法)、正規化、生成多項式或Interaction Features、處理Outliers、分割特徵向量、根據指定規則分組、處理Missing Values 等。第5章則是學習該如何管理類別資料,包括如何使用One-hot編碼特徵矩陣與Dictionaries、轉換文字類別至數字、處理不平衡類別,以及處理缺失類別的數值。這些基礎的資料處理技術對於建立機器學習模型,尤其在應對大量且多樣化的訓練數據時,都是不可或缺的重要技能。
OS: Windows 11 23H2
CPU: Intel Core i7-13700K
GPU: NVIDIA GeForce RTX 4070 SUPER 12G
RAM: DDR5 64G 5600MT/s
Python Package Manager: uv
Python Version: 3.10.16
Python Package: numpy, pandas, sklearn, rich
IDE: Visual Studio Code
為方便查看數據處理前與處理後的差別,故額外使用rich函式庫建立兩個Function格式化印出指定內容。
4.1 Rescaling a Feature
Code:
Explanation:
這段程式碼使用sklearn的MinMaxScaler將資料縮放到0與1之間。而fit_transform方法先學習原始資料的最小值和最大值,再將所有資料點按照比例轉換到指定範圍內(0~1),使各特徵值保持相對關係但統一尺度。
Discussion:
sklearn提供的MinMaxScaler方法可以在保留特徵之間的相對關係的同時,消除資料中量級差異造成的偏差問題。但若資料中只要存在一個異常的極端值,則會壓縮其餘資料縮放後的範圍。
Result:
4.2 Standardizing a Feature
Code:
Explanation:
左上程式碼使用兩種了不同的資料標準化方法,第一種使用StandardScaler將資料標準化為平均值為0、標準差為1的常態分佈。第二種使用RobustScaler進行標準化,此方法透過中位數和四分位數範圍進行標準化。兩種方法都透過fit_transform方法將原始資料轉換為標準化後的結果。右上程式碼則是印出指定資料中的平均值與標準差。
Discussion:
StandardScaler比較適合用於近似常態分佈的資料,但對極端值非常敏感。RobustScaler則較不受極端值影響,因為它使用中位數和四分位數進行縮放,使其在處理含有極端值的資料集時表現更佳,但在完全常態分佈的資料中可能不如StandardScaler有效。
Result:
4.3 Normalizing Observations
Code:
Explanation:
這段程式碼使用使用L2和L1進行正規化。L2正規化使得正規化後的樣本變成單位向量,其歐幾里得距離等於1;L1正規化使得正規化後的每一個樣本中各元素的絕對值總和變成1。兩種方法都透過Normalizer的transform方法進行向量長度的標準化。
Discussion:
L2正規化可以保持向量的方向。L1正規化則傾向於將較小的值壓縮為零。這兩種方法適合用在文字辨識的部分。
Result:
4.4 Generating Polynomial and Interaction Features
Code:
Explanation:
此程式碼使用PolynomialFeatures將原始特徵轉換為包含原始特徵及其二次項的新特徵矩陣。其中degree參數可指定要產生的多項式特徵的最高次數;而include_bias參數則控制是否在輸出的特徵矩陣中包含常數項1;當interaction_only設為True時,生成的多項式特徵中只包含交互作用項,也就是只考慮不同特徵之間的乘積。兩種方法都透過fit_transform方法進行轉換。
Discussion:
PolynomialFeatures可以幫助我們捕捉特徵與目標間的非線性關係,而交互作用特徵則可以用來描述不同特徵之間互相影響的情況。
Result:
4.5 Transforming Features
Code:
Explanation:
此程式碼將兩種自定義函數套用於資料。首先創建一個一個簡單的add_ten函數,此函數將輸入值加10。接著使用sklearn的FunctionTransformer將此函數套用於整個特徵矩陣,每個元素都會加10。而同樣的功能也可以使用pandas的apply方法實現。
Discussion:
使用FunctionTransformer的優點是可以無縫整合進sklearn的工作流程中,但同樣的功能也可以透過pandas實現。
Result:
4.6 Detecting Outliers
Code:
Explanation:
此程式碼使用兩種檢測離群值的方法。首先使用make_blobs生成10個樣本點,然後手動將第一個樣本設為極端值。第一種方法使用EllipticEnvelope檢測異常值,其中的contamination參數可以告訴演算法預期數據中約有多少%的數值是異常值。透過fit訓練後,它會學習數據的分佈並確定哪些點是異常值。之後再使用predict方法判別異常處。(正常的標記為1,不正常的標記為-1)
第二種方法使用四分位數來找出異常值,透過計算四分位數Q1與Q3獲得的四分位距IQR(Q3-Q1),就可以標記所有在Q1-1.5*IQR以下或Q3+1.5*IQR以上的值為離群值。(統計學用法)
Discussion:
sklearn提供的EllipticEnvelope方法適合球形或橢圓形分布的數據集。它的優點是可以計算特徵間的covariance。而四分位數的方法更加簡單直觀且對分布假設較少,但缺點是無法捕捉特徵間的相互關係,只適用於單變量異常檢測。
Result:
4.7 Handling Outliers
Code:
Explanation:
此程式碼使用兩種處理異常值的方法。第一種方法透過使用np.where函數將浴室數量超過20的房屋標記為異常值(值為1,其餘為0)。第二種方法使用np.log轉換的方法,這個方法可以壓縮較高值的比例,減輕異常值的影響,同時保留這些異常值數據在分析中的存在。
Discussion:
第一種方法保留了原始資料,便於分析異常值的影響,但不改變極端值本身。第二種方法則能有效壓縮數據範圍,減輕異常值的影響,但會改變數據分布特性。
Result:
4.8 Discretizating Features
Code:
Explanation:
此程式碼使用兩種分類數據的方法,第一種方法使用Binarizer將資料根據閾值(18)轉換成0或1,讓小於閾值的數值變成0,大於等於閾值的變成1。而第二種方法則使用np.digitize,此方法可依照多個區間(bins)將數據分成不同類別,其中bins參數定義了區間的邊界,right參數決定了邊界值的歸屬方式。(當right=True時,區間是左開右閉[a,b),如20這個值在第二個分組方式中會被分到第0類而非第1類)
Discussion:
這兩種方法的優點是能將連續變數轉換為分類變數。其中Binarizer適合簡單的二分問題,而np.digitize則更適合多區間分類。
Result:
4.9 Grouping Observations Using Clustering
Code:
Explanation:
這段程式碼使用K-means演算法對數據進行分類,透過建立一個KMeans模型,其中的第一個參數為n_clusters,可以指定要分為幾個群組,第二個參數random_state則為一個seed,確保每次運行時都可以透過相同的seed獲得相同的結果。使用fit方法訓練模型,最後透過predict方法為每個資料點分配所屬群組,並將結果加入DataFrame的新列"group"中。
Discussion:
K-means的優點是實現簡單且計算效率高,能有效處理大數據集。但其結果可能因random_state參數的不同而變動。
Result:
4.10 Deleting Observations with Missing Values
Code:
Explanation:
這段程式碼使用兩種方法移除含有NaN的資料列。第一種方法使用NumPy的isnan來判斷數據中每個元素是否有NaN值,並回傳一個布林陣列,有NaN的為Ture,其餘為False,再透過any方法來查看每一row中是否有NaN,若有則在最外處透過 ~ 符號將True改為False以直接移除該row,只保留其餘非NaN的部份。
第二種方法透過將數據轉換為DataFrame,直接使用pandas提供的dropna方法移除含有NaN的row。
Discussion:
此程式碼中兩種刪除NaN的方法優點是簡單直接,能迅速獲得完整的資料集,適合NaN比例較低時使用。然而,此方法會減少資料量,可能引入偏差,特別是當NaN並非隨機分布時,可能會喪失重要的資訊。
Result:
4.11 Imputing Missing Values
Code:
Explanation:
這段程式碼使用兩種處理缺失值的方法,首先創建模擬資料並標準化,接著人為將一個值設為NaN作為缺失值。第一種方法使用KNNImputer,透過尋找5個最近鄰近的值,並計算它們的平均值來填補缺失值。而第二種方法使用SimpleImputer,其使用整個數據中的平均值來填補NaN。其中true_value儲存原始值,用於後續與填補值比較。
Discussion:
KNNImputer的優點是考慮了資料點之間的相似性,通常能提供更準確的估計值,尤其是在資料具有局部相關性時。而SimpleImputer計算速度快且實現簡單,但忽略了資料點間的相關性,可能無法保留資料的分布特性。
Result:
5.1 Encoding Nominal Categorical Features
Code:
Explanation:
此程式碼使用sklearn提供的兩種方法進行One-hot編碼。它將分類特徵(如州名)轉換成數值特徵矩陣。此程式處理兩種情況:單一標籤特徵(例如一個州名)和多標籤特徵(例如一個物件屬於多個州)。LabelBinarizer用於單一標籤轉換,而MultiLabelBinarizer則處理多標籤的情況。
同時,上述的功能也可透過使用pandas提供的get_dummues做到。
Discussion:
One-hot編碼方法可以方便處理分類資料,但會增加特徵數量,可能導致維度過高。當類別很多時,會產生稀疏矩陣,增加計算負擔。
Result:
5.2 Encoding Ordinal Categorical Features
Code:
Explanation:
此程式碼將分類資料(文字)轉換為數值。透過自定義字典scale_mapper將不同文字對應到數值。最後使用pandas的replace方法將DataFrame中的文字等級替換為相應的數值,保留了資料的順序性質。
Discussion:
此方法的優點是保留了分類資料的序列關係。
目前pandas提供的replace方法即將在未來版本中失效,故會額外出現以下提醒資訊,可透過在程式碼前方加上pd.set_option('future.no_silent_downcasting', True)或是加上result.infer_objects(copy=False)以關閉此訊息。
FutureWarning: Downcasting behavior in `replace` is deprecated and will be removed in a future version. To retain the old behavior, explicitly call `result.infer_objects(copy=False)`. To opt-in to the future behavior, set `pd.set_option('future.no_silent_downcasting', True)` dataframe["Score"].replace(scale_mapper)
Result:
5.3 Encoding Dictionaries of Features
Code:
Explanation:
此程式碼使用DictVectorizer將字典轉換為數值特徵矩陣。此工具接收一系列字典(每個字典代表一個觀測值,鍵是特徵名稱,值是特徵數值),然後創建一個矩陣,其中每列代表一個觀測值,每行對應一個特徵。其中的sparse參數可指定是否要輸出為稀疏矩陣,這邊設定False則代表不輸出為稀疏矩陣。後續使用get_feature_names_out方法可獲取轉換後的數值特徵矩陣中的特徵名稱。
後方則使用了多個不同的字典,並將其合為單一陣列進行轉換為數值特徵矩陣。
Discussion:
此方法的優點是能有效處理稀疏資料。它能夠自動處理不同字典中缺失的特徵。然而產生的數值特徵矩陣可能非常大且稀疏。
課本中使用的get_feature_names已經無法使用,需要使用新版本中的get_feature_names_out才可正確獲取特徵名稱
Result:
5.4 Imputing Missing Class Values
Code:
Explanation:
此程式碼使用兩種處理缺失分類資料的方法。第一種方法使用KNeighborsClassifier,先用已知資料(X)的其他特徵(第二與第三個元素)預測缺失的分類值(第一項),然後先使用np.hstack將X_with_nan中未分類的部分(NaN值)使用預測值替代,再使用np.vstack將X_with_nan與原始資料合併。第二種方法則使用SimpleImputer直接以最常見值填補缺失資料。
Discussion:
KNeighborsClassifier方法的優點是利用特徵間相關性進行預測,能產生更符合資料分布的填補值。而SimpleImputer雖然簡單快速,但僅使用已知值的統計值,可能會忽略特徵間的關係,填補結果較為粗略。
Result:
5.5 Handling Imbalanced Classes
Code:
Explanation:
此程式碼使用三種處理不平衡類別資料的方法。首先,為了創建不平衡資料,這邊使用了鳶尾花資料集,並從中移除前40個觀測值,創造了類別不平衡的情況,並將target變數轉為二分類(0代表setosa種類,1代表其他種類)。
第一種方法使用RandomForestClassifier並指定自定義權重,另外還有使用RandomForestClassifier的"balanced"參數自動平衡權重,其中平衡後的資料不會直接顯示於結果,這邊透過feature_importances_ 方法來獲取模型重視哪些特徵。
第二種方法透過使用np.random.choice進行Downsampling的方法,透過隨機選取多數類別資料進行刪除直到匹配少數類別數量
第三種方法同樣使用np.random.choice進行Upsampling的方法,透過重複選取少數類別樣本進行複製以增加其數量,並達到與多數類別相同的樣本數。
Discussion:
這些處理不平衡資料的方法各有優缺點。RandomForestClassifier易於使用,但可能無法處理極端不平衡情況。Downsampling簡單有效,但捨棄了部分資料,可能損失重要資訊。Upsampling保留所有原始資料,但複製樣本可能導致重複資料過多,尤其在少數類別樣本特徵多樣性不足時。
Result:
Difficulties Encountered and Solutions
pandas庫即將失效之函數處理
Difficulties: 使用replace函數時,會出現提示訊息警告此方法將在未來失效。
Solution: 可透過在程式碼前方加上pd.set_option('future.no_silent_downcasting', True)或是加上result.infer_objects(copy=False)以關閉此訊息。
2. sklearn庫已失效函數處理
Difficulties: 原先課本中使用的feature_names = dictvectorizer.get_feature_names()無法正常使用。
Solution: 可透過將程式碼更改為feature_names = dictvectorizer.get_feature_names_out()以解決此問題。
References
[1] Machine Learning with Python Cookbook-2nd, by Kyle Gallatin and Chris Albon, O'Reilly, 2023, ISBN: 9781098135720
[2] 機器學習入門:使用Scikit-Learn與TensorFlow, 黃建庭, 碁峰, 2021, ISBN: 9786263240285
[3] 精通機器學習:使用Scikit-Learn, Keras與TensorFlow(第三版), Aurélien Géron, 歐萊禮, 2024, ISBN: 9786263246676
[4] scikit-learn 機器學習實戰, 鄧立國 郭雅秋 陳子堯 鄧淇文, 清華大學, 2022, ISBN: 9787302604396
[5] Python資料科學學習手冊(第二版), Jake VanderPlas , 歐萊禮, 2023, ISBN: 9786263246843