Spaces:
Sleeping
Sleeping
| import pandas as pd | |
| import numpy as np | |
| from sklearn.preprocessing import StandardScaler, LabelEncoder | |
| from sklearn.cluster import KMeans | |
| from backend.config.setting import ( | |
| PRE_TELCO_CHURN_CSV, | |
| PRE_TEST_CUSTOMER_CSV, | |
| ) | |
| df1 = pd.read_csv(PRE_TELCO_CHURN_CSV) | |
| df2 = pd.read_csv(PRE_TEST_CUSTOMER_CSV) | |
| df = pd.concat([df1, df2], ignore_index=True) | |
| # Danh sách đầy đủ các feature bạn yêu cầu | |
| features_to_use = [ | |
| 'Customer ID', | |
| 'Age', | |
| 'Avg Monthly GB Download', | |
| 'Churn Score', | |
| 'Gender', | |
| 'Monthly Charge', | |
| 'CLTV', | |
| 'Contract', | |
| 'Streaming Movies', | |
| 'Streaming Music', | |
| 'Streaming TV', | |
| 'Satisfaction Score', | |
| 'Tenure in Months' | |
| ] | |
| # Chỉ giữ lại các cột có trong file | |
| existing_cols = [col for col in features_to_use if col in df.columns] | |
| df_final = df[existing_cols].copy() | |
| # Kiểm tra xem có đủ cột quan trọng không | |
| if 'Customer ID' not in df_final.columns: | |
| print(" Cảnh báo: Không tìm thấy cột Customer ID. Code vẫn chạy nhưng kết quả sẽ không có ID.") | |
| # ========================================== | |
| # 2. TIỀN XỬ LÝ (PRE-PROCESSING) | |
| # ========================================== | |
| # Tạo một bản sao để train model (không làm hỏng data gốc) | |
| X = df_final.copy() | |
| # Bỏ cột ID ra khỏi dữ liệu training (vì ID không có ý nghĩa phân cụm) | |
| if 'Customer ID' in X.columns: | |
| X = X.drop(columns=['Customer ID']) | |
| # --- 2.1 Xử lý dữ liệu dạng chữ (Encoding) --- | |
| # Xử lý Gender: Male/Female -> 0/1 | |
| le = LabelEncoder() | |
| if 'Gender' in X.columns: | |
| X['Gender'] = le.fit_transform(X['Gender'].astype(str)) | |
| # Xử lý Contract: Chuyển thành số theo thứ tự cam kết | |
| contract_map = {'Month-to-month': 0, 'One year': 1, 'Two year': 2} | |
| if 'Contract' in X.columns: | |
| # Nếu dữ liệu lạ không khớp map, gán là 0 | |
| X['Contract'] = X['Contract'].map(contract_map).fillna(0) | |
| # Xử lý các cột Streaming: Yes -> 1, No -> 0 | |
| streaming_cols = ['Streaming Movies', 'Streaming Music', 'Streaming TV'] | |
| for col in streaming_cols: | |
| if col in X.columns: | |
| # Xử lý cả trường hợp 'No internet service' -> coi là 0 | |
| X[col] = X[col].apply(lambda x: 1 if str(x).strip() == 'Yes' else 0) | |
| # --- 2.2 Xử lý dữ liệu trống (Missing Values) --- | |
| # Điền giá trị trung bình vào các ô trống (để tránh lỗi) | |
| X = X.fillna(X.mean()) | |
| # --- 2.3 Chuẩn hóa dữ liệu (Scaling) --- | |
| # Bước này CỰC KỲ QUAN TRỌNG khi dùng nhiều feature khác đơn vị (Tuổi, Tiền, GB...) | |
| scaler = StandardScaler() | |
| X_scaled = scaler.fit_transform(X) | |
| # ========================================== | |
| # 3. PHÂN CỤM (CLUSTERING) | |
| # ========================================== | |
| print(" Đang tiến hành phân cụm K-Means với 3 nhóm...") | |
| # Sử dụng K-Means với k=3 | |
| kmeans = KMeans(n_clusters=3, random_state=42, n_init=10) | |
| clusters = kmeans.fit_predict(X_scaled) | |
| # Gán nhãn cụm (0, 1, 2) tạm thời vào dataframe | |
| df_final['Cluster_Temp'] = clusters | |
| # ========================================== | |
| # 4. ĐỊNH DANH NHÓM A, B, C (RANKING) | |
| # ========================================== | |
| # Logic: Nhóm nào có CLTV (Giá trị vòng đời) cao nhất sẽ là A (VIP) | |
| # Nếu không có CLTV, dùng Monthly Charge | |
| ranking_metric = 'CLTV' if 'CLTV' in df_final.columns else 'Monthly Charge' | |
| # Tính giá trị trung bình của metric xếp hạng cho từng cụm | |
| cluster_stats = df_final.groupby('Cluster_Temp')[ranking_metric].mean().sort_values(ascending=False) | |
| print("\n Thống kê trung bình nhóm (Dựa trên CLTV):") | |
| print(cluster_stats) | |
| # Tạo map: Cao nhất -> A, Nhì -> B, Thấp -> C | |
| labels_map = {} | |
| rank_names = ['A', 'B', 'C'] # A là xịn nhất | |
| for i, cluster_idx in enumerate(cluster_stats.index): | |
| labels_map[cluster_idx] = rank_names[i] | |
| # Áp dụng map để tạo cột Segment cuối cùng | |
| df_final['Segment'] = df_final['Cluster_Temp'].map(labels_map) | |
| # ========================================== | |
| # 5. XUẤT FILE KẾT QUẢ | |
| # ========================================== | |
| # Bỏ cột Cluster_Temp thừa đi | |
| output_df = df_final.drop(columns=['Cluster_Temp']) | |
| filename = "telco_customer_segmented_full_features.csv" | |
| output_df.to_csv(filename, index=False) | |
| print(f"\n THÀNH CÔNG! File kết quả đã lưu tại: {filename}") | |
| print("-" * 50) | |
| print("Mẫu dữ liệu 5 dòng đầu tiên:") | |
| print(output_df[['Customer ID', 'Segment', 'CLTV', 'Churn Score', 'Monthly Charge']].head()) | |
| # In ra đặc điểm trung bình của từng nhóm để bạn dễ hiểu Insight | |
| print("\n ĐẶC ĐIỂM TRUNG BÌNH CỦA 3 NHÓM KHÁCH HÀNG:") | |
| summary = output_df.groupby('Segment')[['CLTV', 'Monthly Charge', 'Churn Score', 'Avg Monthly GB Download']].mean() | |
| print(summary) |