「顧客分群」詳解

本案例將帶領我們進入「非監督式學習 (Unsupervised Learning)」的領域。與之前的預測模型不同,這次我們的資料沒有「正確答案」(如 Churn 欄位)。我們的目標是讓機器自動從數據中找出潛在的結構與群體,這個過程稱為分群 (Clustering)

1. 環境設定:中文字型

核心概念: matplotlibseaborn 套件預設並未包含中文字型,若直接繪圖,圖表中的中文(如標題、座標軸標籤)將會顯示為無法辨識的方塊亂碼(暱稱為豆腐)。因此,繪製中文圖表前的第一步,就是手動安裝並設定字型。

本案例應用重點:

  • !wget ...: 我們在 Colab 中使用 wget 這個指令,從網路上下載一個開源的思源黑體字型檔 (.otf 檔),並將其存放在系統的字型路徑中。
  • import matplotlib.font_manager as fm: 引入字型管理器,用來讀取我們剛下載的字型檔。
  • plt.rcParams['font.family'] = ...: 這是最關鍵的一步,透過 rcParams (runtime configuration parameters) 這個設定,我們明確地告知 matplotlib:「接下來所有的圖表,請一律使用我們剛剛指定的那個中文字型來繪製。」

2. 載入數據

  • files.upload(): 執行這行程式碼時,Colab 會在您的瀏覽器中跳出一個檔案上傳的對話視窗,讓您可以從本機電腦選擇要上傳的檔案
  • uploaded = ...: 當您選擇檔案並完成上傳後,files.upload() 會回傳一個 Python 字典 (dictionary),裡面包含了您上傳的檔案資訊(檔名作為 key,檔案內容作為 value)。這個字典會被存放在 uploaded 這個變數中。
  • try: ... except Exception as e: ...:這是一個錯誤處理的結構。
    • try:: 程式會嘗試執行 try 區塊內的所有程式碼。
    • except Exception as e:: 如果在 try 的過程中,發生任何錯誤(例如,上傳的檔案不是 CSV 格式、檔案已損毀等),程式不會因此崩潰,而是會跳到 except 區塊,執行裡面的程式碼來處理這個例外狀況。
  • file_name = next(iter(uploaded)):因為 uploaded 是一個字典,我們需要從中取出檔名。
    • iter(uploaded) 會建立一個字典鍵(也就是檔名)的迭代器。
    • next(...) 則會從這個迭代器中取出第一個項目。由於我們一次只上傳一個檔案,所以這樣就能成功取得該檔案的檔名。
如果使用本地端的編輯器執行Python腳本(例如Spyder,VSCode),只要設定好工作目錄,就可以直接使用pandas read_csv() 方法載入數據集。upload()是用在當你使用colab環境時。

3. Scikit-learn – 資料標準化 (Standardization)

核心概念: K-Means 演算法是透過計算「距離」來進行分群的。如果我們的特徵單位或尺度差異過大(例如「年收入」的數值遠大於「消費分數」),那麼尺度較大的特徵將會主導整個分群過程,導致結果失準。因此,在進行分群前,我們必須先將所有特徵「標準化」,讓它們處於同一個比較基準上。

本案例應用重點:

  • from sklearn.preprocessing import StandardScaler: 從 scikit-learn 中匯入標準化工具。
  • StandardScaler(): 初始化一個標準化轉換器。
  • .fit_transform(): 這是標準化的核心步驟。它會自動計算資料的平均值與標準差,然後對資料進行轉換,讓轉換後的資料平均值為 0,標準差為 1。

4. Scikit-learn – 使用 K-Means 演算法進行分群

核心概念: K-Means 是最經典的分群演算法之一。一旦我們決定了 k 值,就可以用它來訓練模型並找出分群結果。

本案例應用重點:

  • from sklearn.cluster import KMeans: 從 scikit-learn 中匯入 K-Means 模型。
  • KMeans(n_clusters=5, ...): 初始化 K-Means 模型,其中最重要的參數 n_clusters 就是我們直接指定(本案例為5),會是透過手肘法 (elbow) 決定的最佳分群數。
  • .fit_predict(): 這個方法會一次性完成兩件事:首先,它會根據資料訓練模型,找出 5 個最佳的群體中心點 (fit);接著,它會為資料中的每一個顧客,分配一個所屬的群組編號(0, 1, 2, 3, 或 4),並回傳這個結果 (predict)。
決定分群數 (k值) ,除了主觀設定一個值之外,也可以利用 Elbow方法,請看本文最後說明。

5. Matplotlib – 分群結果的視覺化

核心概念: 視覺化是驗證與解讀分群結果最直觀的方式。

本案例應用重點:

  • 使用 seaborn 函式庫來繪製一個進階且美觀的彩色散點圖,目的是將分群結果視覺化。
    • sns.scatterplot()snsseaborn 函式庫的通用縮寫。scatterplot 是專門用來繪製散點圖的函式。
    • data=df:告訴函式「請從我們整理好的 df 這張資料表來抓取數據。」
    • x='Annual Income (k$)':設定圖表的 X 軸(水平軸)要對應到 df 資料表中,名為「Annual Income (k$)」的那個欄位。
    • y='Spending Score (1-100)':設定圖表的 Y 軸(垂直軸)要對應到名為「Spending Score (1-100)」的那個欄位。
    • hue='Cluster':這是繪製分群圖的最關鍵參數。hue 的意思是「色調」。這個參數會指示 seaborn 根據 Cluster 欄位中的數值(例如 0, 1, 2, 3, 4)來為每一個點上不同的顏色。這使得我們能夠一眼就看出不同群體的分佈位置。
    • palette='viridis'palette 是調色盤的意思。這個參數用來設定 hue 上色時的顏色主題。'viridis' 是一個專業且美觀的漸層色盤。
    • s=100s 代表 size,用來設定散點圖上每個點的大小。
    • alpha=0.8:設定每個點的透明度。0.8 代表 80% 的不透明度,當點重疊時,顏色會加深,有助於觀察資料的密度。
    • edgecolor='k':設定每個點的邊框顏色。'k' 是黑色 (black) 的縮寫,為點加上黑色邊框可以讓視覺效果更清晰。
  • kmeans.cluster_centers_: 這是 K-Means 模型訓練完後,用來儲存各個群體中心點座標的屬性。我們會再用一次 plt.scatter(),將這些中心點以不同的樣式(如紅色的星號)標示在地圖上。
    • plt.scatter():呼叫 scatter 函式,代表我們要在同一張圖上,再畫一層新的散佈圖。
    • centers[:, 0]centers[:, 1]centers 是一個儲存了所有群組中心點座標的陣列(Array)。centers[:, 0] 是一種陣列切片 (slicing) 的語法,意思是「取出所有列的第 0 個欄位」,也就是所有中心點的 X 座標。
    • 同理,centers[:, 1] 就是取出所有中心點的 Y 座標。
    • c='red'c 代表 color。將這些中心點的顏色設定為紅色,使其在圖上非常顯眼。
    • s=250s 代表 size。將中心點的大小設定為 250,比一般的資料點大很多,以凸顯其重要性。
    • alpha=0.9:設定點的透明度為 90%(接近不透明),讓中心點的顏色更飽和。
    • marker='X'marker 代表標記樣式。將中心點的形狀設定為 ‘X’,以區別於代表個別顧客的圓點。
    • label='群組中心':為這些紅色的 ‘X’ 標記設定一個圖例名稱,叫做「群組中心」。當我們稍後呼叫 plt.legend() 時,圖例上就會顯示「X 群組中心」的說明。

手肘法 (The Elbow Method)

核心概念: 在使用 K-Means 之前,我們需要先決定到底要將顧客分成幾群 (k 值)。「手肘法」是一種常用的、透過視覺化來輔助判斷最佳 k 值的方法。

重點說明:

  • for k in range(1, 11):: 使用一個 for 迴圈,來依序測試將顧客分為 1 群、2 群、… 直到 10 群的各種情況。
  • kmeans.inertia_: inertia 是 K-Means 模型的一個屬性,它代表了「群內點到其中心點的距離平方和」。這個值越小,代表分群效果越緊密。也稱為WCSS(within-cluster sum of squares)組內評分和。
  • wcss.append(kmeans.inertia_): 在迴圈中,將每種 k 值算出來的 inertia_ 存入一個名為 wcss 的列表中,以便後續繪製「手肘圖」。
  • 以下是一段程式碼範例:
# 假設 X_scaled 是已經標準化後的資料
# X_scaled = scaler.fit_transform(df[['Annual Income (k$)', 'Spending Score (1-100)']])

wcss = [] # Within-Cluster Sum of Squares, 用來存放每個 k 值的 inertia_

# 測試 k 從 1 到 10 的情況
for k in range(1, 11):
    kmeans = KMeans(n_clusters=k, init='k-means++', random_state=42)
    kmeans.fit(X_scaled)
    wcss.append(kmeans.inertia_)

# 繪製手肘圖
plt.figure(figsize=(10, 6))
plt.plot(range(1, 11), wcss, marker='o', linestyle='--')
plt.title('手肘法 (The Elbow Method)')
plt.xlabel('分群數量 (k)')
plt.ylabel('WCSS (群內平方和)')
plt.xticks(range(1, 11))
plt.grid(True)
plt.show()

總結來說,這個案例的學習路徑是:

資料標準化 -> 直接指定K值,或用手肘法找出最佳 K 值 -> 訓練 K-Means 模型 -> 視覺化分群結果