name: "data-mining-course" description: "Teaching-oriented data mining workflow: environment setup, data inspection, cleaning, EDA, visualization, and modeling. Invoke when student or teacher asks to analyze a dataset, perform data mining tasks, or learn data analysis step by step."
本 Skill 為《資料探勘與商務智慧》課程設計,每一步都標註了對應知識點,讓學生在操作中學習。
| 步驟 | 知識點 | 對應章節 |
|---|---|---|
| 1 | Python 環境管理 — pip、虛擬環境、清華源映象 | §1 |
| 2 | 資料載入 — CSV/Excel 讀取、編碼問題、BOM 頭 | §2 |
| 3 | 資料探查 — 形狀、資料型別、缺失值、描述性統計 | §3 |
| 4 | 資料清洗 — 缺失值/重複值/異常值處理、型別轉換 | §4 |
| 5 | 探索性分析(EDA) — 分佈、相關性、分組聚合 | §5 |
| 6 | 特徵工程 — 派生變數、編碼、標準化 | §6 |
| 7 | 視覺化 — matplotlib/seaborn 基礎圖表 | §7 |
| 8 | 建模入門 — 迴歸/分類/聚類基礎 | §8 |
| 9 | 結果解釋 — 如何讀懂輸出、撰寫結論 | §9 |
學生需要理解:跑程式碼之前先檢查環境,避免"程式碼沒問題但跑不起來"。
# 1. 檢查 Python 版本(≥ 3.11 即可)
python --version
# 2. 檢查核心包是否已安裝(;分隔,PowerShell 相容)
python -c "import pandas; print('pandas', pandas.__version__)"
python -c "import matplotlib; print('matplotlib', matplotlib.__version__)"
python -c "import seaborn; print('seaborn', seaborn.__version__)"
python -c "import scipy; print('scipy', scipy.__version__)"
python -c "import sklearn; print('sklearn', sklearn.__version__)"
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pandas matplotlib seaborn scipy scikit-learn
&&,所以用分步執行pip:Python 官方 包管理工具 ,專門用來安裝 / 解除安裝 / 更新第三方庫,是環境配置的核心命令python --version提示不是內部命令:Python 未新增系統環境變數,重新安裝 Python 時勾選「Add Python to PATH」理解編碼(UTF-8 vs GBK)對中文資料處理的影響,學會用 pathlib 管理路徑。
# -*- coding: utf-8 -*-
import sys
sys.stdout.reconfigure(encoding='utf-8')
from pathlib import Path
import pandas as pd
# 使用 pathlib 管理路徑(禁止硬編碼絕對路徑)
data_dir = Path('data') # 資料放在專案 data/ 目錄下
file_path = data_dir / 'your_file.csv'
# 嘗試不同編碼讀取
try:
df = pd.read_csv(file_path, encoding='utf-8')
except UnicodeDecodeError:
df = pd.read_csv(file_path, encoding='gbk')
print(f'資料載入完成: {df.shape[0]} 行 × {df.shape[1]} 列')
\ufeff,表現為 出現在第一列列名前"先看資料,再動手" — 這是資料探勘的第一鐵律。
# ===== 基本結構 =====
print('資料形狀:', df.shape)
print('列名列表:', df.columns.tolist())
# ===== 資料型別 =====
print('\n各列資料型別:')
print(df.dtypes)
# ===== 缺失值 =====
print('\n缺失值統計:')
missing = df.isnull().sum()
missing_pct = (missing / len(df) * 100).round(2)
missing_df = pd.DataFrame({'缺失數': missing, '缺失率%': missing_pct})
print(missing_df[missing_df['缺失數'] > 0])
# ===== 重複值 =====
print(f'\n重複行數: {df.duplicated().sum()}')
# ===== 數值列統計 =====
print('\n數值列描述性統計:')
print(df.describe())
# ===== 分類列統計 =====
for col in df.select_dtypes(include='object').columns:
print(f'\n{col} 唯一值數: {df[col].nunique()}, 示例: {df[col].dropna().unique()[:5]}')
理解 Garbage In, Garbage Out — 髒資料產出的分析結論不可信。
clean = df.copy() # 保留原始資料
# ---- 4.1 列名規範化 ----
clean.columns = clean.columns.str.strip() # 去除首尾空格
# ---- 4.2 型別轉換 ----
# 日期列
clean['日期列'] = pd.to_datetime(clean['日期列'], errors='coerce')
# 數值列(如果是字串格式,如 "1,234")
clean['數值列'] = clean['數值列'].str.replace(',', '').astype(float)
# 科學計數法 ID(如 1.1582E+14)
clean['id'] = clean['id'].astype(str)
# ---- 4.3 缺失值處理 ----
# 策略 A:刪除缺失率過高的行
clean = clean.dropna(subset=['關鍵列'])
# 策略 B:填充(均值/中位數/眾數/固定值)
clean['數字列'] = clean['數字列'].fillna(clean['數字列'].median())
clean['分類列'] = clean['分類列'].fillna('未知')
# 策略 C:前向/後向填充(時間序列)
clean['數值列'] = clean['數值列'].ffill()
# ---- 4.4 重複值 ----
clean = clean.drop_duplicates()
# ---- 4.5 異常值 ----
# IQR 方法:Q1 - 1.5*IQR ~ Q3 + 1.5*IQR 之外為異常
Q1 = clean['數值列'].quantile(0.25)
Q3 = clean['數值列'].quantile(0.75)
IQR = Q3 - Q1
outliers = clean[(clean['數值列'] < Q1 - 1.5*IQR) | (clean['數值列'] > Q3 + 1.5*IQR)]
print(f'檢測到 {len(outliers)} 個異常值')
print(f'清洗完成: {len(clean)} 行 (原始 {len(df)} 行)')
真實世界的資料永遠有瑕疵 —— 感測器故障、人工錄入錯誤、系統遷移丟失資料。跳過清洗直接建模,等同於用發黴的食材做菜:Garbage In, Garbage Out。清洗不是"可選的最佳化步驟",而是分析的前提條件。
| 缺失機制 | 定義 | 例子 | 推薦處理 |
|---|---|---|---|
| MCAR(完全隨機缺失) | 缺失與任何變數無關 | 問卷隨機漏填 | 可刪除,不會引入偏差 |
| MAR(隨機缺失) | 缺失與其他變數有關,但與被缺失變數本身無關 | 高收入者更不願填收入,但缺失只與收入高低無關 | 用均值/中位數/迴歸填充 |
| MNAR(非隨機缺失) | 缺失與被缺失變數本身有關 | 收入越高越不願填收入 | 需專門建模處理,單純刪除/填充會引入偏差 |
pd.to_datetime(..., errors='coerce') 中 coerce 的意思是"轉不了的變成 NaT(Not a Time)",不要報錯中斷程式。1.1582E+14 是浮點數,但在 Excel 中這是 ID(如 B站影片 ID),必須轉成字串 astype(str),否則後續匹配會失敗。學會用統計指標和分組聚合來發現資料中的規律。
EDA 第一準則:先識別 5 類商科常用資料型別,再匹配對應分析方法
先分型,再分析:掌握 5 類商科常用資料的分型規則,杜絕分析方法混用:
⚠️ 以下程式碼為一個完整指令碼,直接複製執行即可,無需手動改列名。
# -*- coding: utf-8 -*-
# ============================================================
# §5 探索性資料分析 EDA — 5 類商科資料分型分析
# ============================================================
# ---- 第一步:自動識別資料型別(零介入,適配所有資料) ----
print("=" * 60)
print("第一步:資料集欄位型別一覽")
print("=" * 60)
print(clean.dtypes)
print()
# 自動篩選各類欄位(學生無需手動修改列名)
numeric_cols = clean.select_dtypes(include=['int64', 'float64']).columns.tolist()
category_cols = clean.select_dtypes(include=['object', 'category']).columns.tolist()
time_cols = clean.select_dtypes(include=['datetime64']).columns.tolist()
# 文本型:從分類列中篩選唯一值多的列(>10種 → 更像文本而非分類)
text_cols = []
for col in category_cols:
try:
if clean[col].astype(str).nunique() > 10:
text_cols.append(col)
except Exception:
pass
print(f"✅ 數值型欄位({len(numeric_cols)} 個):{numeric_cols}")
print(f"✅ 分型別欄位({len(category_cols)} 個):{category_cols}")
print(f"✅ 文本型欄位({len(text_cols)} 個):{text_cols if text_cols else '無'}")
print(f"✅ 時間型欄位({len(time_cols)} 個):{time_cols if time_cols else '無'}")
print()
# ---- 一、數值型:描述性統計 + 彙總(統計年鑑核心) ----
# 知識點:均值、中位數、偏度、標準差、極值
print("=" * 60)
print("一、數值型分析 — 描述性統計(適配統計年鑑/面板數值)")
print("=" * 60)
if numeric_cols:
# 逐列展示關鍵指標(限前10列防止輸出過長)
for col in numeric_cols[:10]:
vals = clean[col].dropna()
if len(vals) > 0:
print(f" {col}: 均值={vals.mean():.2f}, 中位數={vals.median():.2f}, "
f"偏度={vals.skew():.2f}, 標準差={vals.std():.2f}, "
f"min={vals.min():.2f}, max={vals.max():.2f}")
# 彙總統計表(宏觀資料專屬:GDP/營收等多指標彙總)
print("\n 數值型指標彙總表(count/mean/sum/std):")
print(clean[numeric_cols].agg(['count', 'mean', 'sum', 'std']).round(2))
else:
print(" ⚠️ 未檢測到數值型欄位,跳過數值分析")
# ---- 二、數值型:相關性矩陣(統計年鑑常用) ----
# 知識點:皮爾遜相關係數、變數關聯關係
print()
print("=" * 60)
print("二、相關性分析 — 高相關變數篩選 (|r| > 0.5)")
print("=" * 60)
if len(numeric_cols) >= 2:
corr_matrix = clean[numeric_cols].corr()
high_corr = corr_matrix.where(abs(corr_matrix) > 0.5).stack().dropna()
found = False
for (c1, c2), val in high_corr.items():
if c1 < c2:
direction = "正" if val > 0 else "負"
print(f" {c1} ↔ {c2}: r = {val:.3f}({direction}相關)")
found = True
if not found:
print(" 未發現 |r| > 0.5 的變數對")
else:
print(" ⚠️ 數值型欄位不足 2 個,無法計算相關性")
# ---- 三、分型別:分組聚合(等價 SQL GROUP BY) ----
# 知識點:分組統計、多維指標彙總
print()
print("=" * 60)
print("三、分型別分析 — 分組聚合統計(適配面板分類/統計年鑑分類)")
print("=" * 60)
if category_cols and numeric_cols:
group_col = category_cols[0]
num1 = numeric_cols[0]
num2 = numeric_cols[1] if len(numeric_cols) >= 2 else numeric_cols[0]
group_stats = clean.groupby(group_col).agg(
計數=(num1, 'count'),
均值=(num1, 'mean'),
中位數=(num1, 'median'),
總和=(num2, 'sum'),
標準差=(num2, 'std')
).round(2)
print(f" 按「{group_col}」分組統計:")
print(group_stats)
else:
print(" ⚠️ 缺少分類列或數值列,無法分組")
# ---- 四、數值型:Top N 排名(統計年鑑/面板常用) ----
# 知識點:資料排序、極值分析
print()
print("=" * 60)
print("四、Top N 排名分析 — 極值發現")
print("=" * 60)
if numeric_cols:
top_col = numeric_cols[0]
key_col = category_cols[0] if category_cols else numeric_cols[0]
top10 = clean.nlargest(10, top_col)[[key_col, top_col]]
print(f" 「{top_col}」Top 10:")
print(top10.to_string(index=False))
else:
print(" ⚠️ 缺少數值列,無法生成 Top N")
# ---- 五、文本型:基礎頻數分析(統計年鑑文本/調研文本) ----
# 知識點:文本分類統計、關鍵詞頻次(零基礎入門)
print()
print("=" * 60)
print("五、文本資料分析 — 內容頻次統計(適配統計年鑑說明/調研文本)")
print("=" * 60)
if text_cols:
text_col = text_cols[0]
text_freq = clean[text_col].astype(str).value_counts().head(10)
print(f" 「{text_col}」文本內容 Top 10 頻次:")
print(text_freq)
else:
print(" ⚠️ 未檢測到文本型欄位,跳過文本分析")
# ---- 六、面板資料:時間趨勢分析 ----
# 知識點:時間序列基礎、多主體趨勢對比(面板資料核心)
print()
print("=" * 60)
print("六、面板資料 — 時間趨勢分析(多主體 × 多時間點)")
print("=" * 60)
if time_cols and numeric_cols and category_cols:
time_col = time_cols[0]
num_col = numeric_cols[0]
entity_col = category_cols[0]
panel_trend = clean.groupby([time_col, entity_col])[num_col].mean().unstack().round(2)
print(f" 「{entity_col}」在「{num_col}」上的時間趨勢:")
print(panel_trend)
else:
print(" ⚠️ 缺少時間列/數值列/分類列,無法進行面板資料趨勢分析")
print()
print("✅ §5 EDA 分析完成")
EDA 的目標不是"找到正確答案",而是發現值得深挖的方向。兩者有本質區別:
學生常犯的錯誤:把 EDA 中發現的模式直接當結論 —— 比如看到"大城市薪資高"就說"去大城市就能高薪",忽略了選擇偏差(大城市本身就吸引了更多高學歷人才)。EDA 是起點,不是終點。
| 資料型別 | 用錯方法的後果 |
|---|---|
| 把分類列當數值列 | df['城市'].mean() → 報錯,或算出無意義的"平均城市編碼" |
| 把數值列當分類列 | 每個不同的數值被當成一個類別,導致分組數 = 樣本數 |
| 把文本列當分類列 | 每行一個獨特值,分組無意義 |
| 把截面資料當面板資料 | 錯誤地強加"時間趨勢"解釋,得出虛假結論 |
這就是本 Skill 在 EDA 開頭強制執行 clean.dtypes 的原因 —— 先知道每一列是什麼型別,才能決定用什麼分析方法。
| 指標 | 告訴你的資訊 | 侷限 |
|---|---|---|
| 均值 | 整體水平 | 被極值嚴重拉偏 |
| 中位數 | "中間那個人"的水平 | 無法反映極端情況 |
| 偏度 | 資料往哪邊"甩尾" | 需要配合均值/中位數一起看 |
薪資資料幾乎總是正偏(右偏):大多數人拿中等薪資,少數人拿極高薪資。如果只彙報"平均薪資",學生會高估自己的收入預期。彙報中位數比彙報均值更接近"普通人能拿到的"。
相關係數把"這兩個變數看起來有關係"變成了一個可比較的數字。但要注意:
GROUP BY 在 pandas 中的等價操作 —— 本質是將資料按某一列的類別切分,然後對每個切塊做統計運算(計數、求和、均值等)| 資料型別 | 能做什麼 | 不能做什麼 |
|---|---|---|
| 數值型(統計年鑑核心) | 均值、相關、TopN、趨勢 | 不能用來分組分類 |
| 分型別(面板分類項) | 分組、計數 | 不能計算均值、相關係數 |
| 文本型(統計年鑑文本) | 頻數統計(入門階段) | 不做複雜文本挖掘 |
| 統計年鑑資料 | 彙總統計、地區/行業對比、相關性 | 照搬數值/分類程式碼 |
| 面板資料 | 時間趨勢 + 主體對比 | 忽略時間維度單獨分析 |
df.dtypes),再選擇對應程式碼理解"同樣的資料,不同的表達方式會直接影響模型效果"。
# ---- 6.1 時間特徵提取 ----
clean['年'] = clean['日期列'].dt.year
clean['月'] = clean['日期列'].dt.month
clean['星期'] = clean['日期列'].dt.dayofweek # 0=週一
clean['是否週末'] = (clean['星期'] >= 5).astype(int)
clean['季度'] = clean['日期列'].dt.quarter
# ---- 6.2 文本特徵 —— 提取關鍵詞/長度 ----
clean['文本長度'] = clean['文本列'].str.len()
clean['含關鍵詞'] = clean['文本列'].str.contains('關鍵詞', na=False).astype(int)
# ---- 6.3 比率特徵 —— 兩個數值列的比例 ----
clean['比率'] = (clean['分子列'] / clean['分母列']).replace([float('inf'), -float('inf')], 0)
# ⚠️ 除以零會產生 inf,必須處理
# ---- 6.4 分類變數編碼 ----
# One-Hot 編碼(適用於無序分類,如顏色:紅/藍/綠)
df_encoded = pd.get_dummies(clean, columns=['分類列'], prefix='cat')
# Label 編碼(適用於有序分類,如等級:低/中/高)
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
clean['等級_編碼'] = le.fit_transform(clean['等級列'])
模型的數學引擎只能處理數字矩陣。而原始資料往往是混雜的 —— 日期、文本、類別、缺失值。特徵工程就是把"雜亂的原始資料"翻譯成"模型能理解的數值特徵"。同樣的原始資料,不同的特徵表達方式,模型效果可以差 3~5 倍。
模型不認識"2022-03-15"這個字串,也不理解"12月之後是1月"的週期性。把日期拆成年/月/星期/季度這些獨立數字後,模型就能捕捉到季節性規律(如冬季招聘職位少)。
sin(月份/12*2π) 和 cos(月份/12*2π) 編碼月份,就能完美體現"12月和1月距離近"的週期關係。但課程入門階段用拆分即可。| 編碼方式 | 原理 | 什麼時候用 | 用錯的後果 |
|---|---|---|---|
| One-Hot | 每個類別變成一列 0/1 | 無序分類(城市、顏色、品牌) | 如果類別太多(>100種),特徵矩陣爆炸 |
| Label | 把類別替換成數字 1,2,3... | 有序分類(學歷、等級、星級) | 用在無序分類上,模型會以為"上海=3" > "北京=1" |
掌握資料探勘中最常用的 6 種圖表及各自適用場景。
import matplotlib.pyplot as plt
import matplotlib
matplotlib.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei']
matplotlib.rcParams['axes.unicode_minus'] = False # 解決負號顯示問題
import seaborn as sns
fig, axes = plt.subplots(2, 3, figsize=(18, 12))
# ① 直方圖 — 看分佈形態
axes[0, 0].hist(clean['數值列'], bins=30, color='#3498db', edgecolor='white')
axes[0, 0].set_title('數值列分佈直方圖')
axes[0, 0].set_xlabel('值')
axes[0, 0].set_ylabel('頻數')
# ② 箱線圖 — 看異常值與四分位數
sns.boxplot(data=clean, x='分類列', y='數值列', ax=axes[0, 1])
axes[0, 1].set_title('各類別的數值分佈(箱線圖)')
# ③ 柱狀圖 — 類別間比較
cat_counts = clean['分類列'].value_counts().head(10)
axes[0, 2].bar(range(len(cat_counts)), cat_counts.values, color='#e74c3c')
axes[0, 2].set_xticks(range(len(cat_counts)))
axes[0, 2].set_xticklabels(cat_counts.index, rotation=45, ha='right')
axes[0, 2].set_title('分類列 Top 10 頻數')
# ④ 散點圖 — 兩變數關係
axes[1, 0].scatter(clean['數值列1'], clean['數值列2'], alpha=0.5, s=20)
axes[1, 0].set_xlabel('數值列1')
axes[1, 0].set_ylabel('數值列2')
axes[1, 0].set_title('數值列1 vs 數值列2')
# ⑤ 折線圖 — 時間趨勢
time_data = clean.groupby('日期列')['數值列'].mean()
axes[1, 1].plot(time_data.index, time_data.values, color='#2ecc71')
axes[1, 1].set_title('時間趨勢')
axes[1, 1].tick_params(axis='x', rotation=45)
# ⑥ 熱力圖 — 相關性矩陣
num_cols = clean.select_dtypes(include='number').columns[:8]
sns.heatmap(clean[num_cols].corr(), annot=True, fmt='.2f', cmap='RdBu_r',
center=0, ax=axes[1, 2])
axes[1, 2].set_title('相關性熱力圖')
plt.tight_layout()
plt.savefig('eda_charts.png', dpi=150, bbox_inches='tight')
print('圖表已儲存至 eda_charts.png')
人眼是人類最強大的模式識別系統。肉眼掃一遍散點圖,能瞬間發現:線性/曲線趨勢、離群點、聚類結構、資料缺口。而直接丟進模型跑,這些資訊全部丟失。視覺化是建模前的"體檢報告"。
圖表本質上是用視覺屬性(位置、長度、顏色、形狀)來編碼資料特徵。選錯圖表 = 用錯視覺屬性,讀者無法正確解碼資訊:
| 想看什麼 | 用什麼圖 | 視覺編碼 | 為什麼選它 | 注意 |
|---|---|---|---|---|
| 分佈形態 | 直方圖 / KDE | 柱高/面積 = 頻數 | 單變數、看集中與分散 | bin 數量影響觀感 |
| 異常值 + 四分位 | 箱線圖 | 位置 = 四分位數,須外 = 異常 | 最適合多組比較,一張圖能看多組的分佈差異 | 點超出須線即異常 |
| 類別比較 | 柱狀圖 | 柱高 = 數量 | 分類 x 數值,簡單直接 | 類別太多時取 TopN |
| 兩變數關係 | 散點圖 | 點的 x/y 位置 | 發現相關性、聚類、離群、非線性 | 加透明度避免重疊 |
| 時間趨勢 | 折線圖 | 斜率 = 變化率 | 強調先後順序和變化速率 | 時間軸要排序 |
| 相關性 | 熱力圖 | 顏色深淺 = 相關強弱 | 同時展示幾十個變數的兩兩關係 | 顏色深淺 = 強弱 |
alpha=0.5 解決。理解監督學習(迴歸/分類)和無監督學習(聚類)的基本概念。
7w4.net小蔥技能站收錄全網優質技能,值得收藏。
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import r2_score, accuracy_score, classification_report
# ====== 準備特徵和標籤 ======
features = ['特徵1', '特徵2', '特徵3', '特徵4']
target = '目標列'
X = clean[features].copy()
y = clean[target].copy()
# 處理特徵中的缺失值
X = X.fillna(X.median())
# ====== 劃分訓練集/測試集 (7:3) ======
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
print(f'訓練集: {len(X_train)} 條, 測試集: {len(X_test)} 條')
# ====== 標準化 ======
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
# ⚠️ fit_transform 用在訓練集,transform 用在測試集
# ====== 場景 A:迴歸(預測連續值,如價格、播放量) ======
from sklearn.linear_model import LinearRegression
from sklearn.ensemble import RandomForestRegressor
model_reg = RandomForestRegressor(n_estimators=100, random_state=42)
model_reg.fit(X_train, y_train)
y_pred_reg = model_reg.predict(X_test)
print(f'迴歸 R² 分數: {r2_score(y_test, y_pred_reg):.3f}')
# 特徵重要性
importance = pd.DataFrame({'特徵': features, '重要性': model_reg.feature_importances_})
print(importance.sort_values('重要性', ascending=False))
# ====== 場景 B:分類(預測類別,如是/否、高/中/低)=======
from sklearn.ensemble import RandomForestClassifier
# 如果目標是連續值,需要先分箱
if y.nunique() > 10:
y = pd.cut(y, bins=3, labels=['低', '中', '高'])
X_train_c, X_test_c, y_train_c, y_test_c = train_test_split(
X, y, test_size=0.3, random_state=42
)
model_clf = RandomForestClassifier(n_estimators=100, random_state=42)
model_clf.fit(X_train_c, y_train_c)
y_pred_clf = model_clf.predict(X_test_c)
print(f'分類準確率: {accuracy_score(y_test_c, y_pred_clf):.3f}')
print(classification_report(y_test_c, y_pred_clf))
# ====== 場景 C:聚類(沒有標籤,發現自然分組)=======
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
# 肘部法則 —— 選最佳 K 值
inertias = []
K_range = range(1, 11)
for k in K_range:
km = KMeans(n_clusters=k, random_state=42, n_init=10)
km.fit(X_train_scaled)
inertias.append(km.inertia_)
print(f'肘部法則 Inertia: {dict(zip(K_range, [round(i, 2) for i in inertias]))}')
# 聚類
k_best = 3
kmeans = KMeans(n_clusters=k_best, random_state=42, n_init=10)
clean['聚類標籤'] = kmeans.fit_predict(X_train_scaled)
print(f'聚類輪廓係數: {silhouette_score(X_train_scaled, clean["聚類標籤"]):.3f}')
| 任務 | 問題形式 | 典型場景 | 常用演算法 | 評估指標 |
|---|---|---|---|---|
| 迴歸 | 預測一個連續數值 | 房價、薪資、銷量 | 線性迴歸、隨機森林迴歸 | R²(0~1,越高越好) |
| 分類 | 判斷屬於哪個類別 | 是否購買、信用評級 | 隨機森林分類、邏輯迴歸 | 準確率、精確率、召回率 |
| 聚類 | 沒有標籤,自動發現分組 | 使用者分群、崗位分層 | K-Means、DBSCAN | 輪廓係數、肘部法則 |
選擇原則:如果你有 y(目標列)→ 監督學習(迴歸或分類);如果沒有 y → 無監督學習(聚類)。
如果把所有資料都用來訓練模型,模型會把資料"背下來"(包括噪聲),但遇到新資料時表現極差 —— 這叫做過擬合。訓練集/測試集分離的本質是模擬"期末考試":用 70% 的資料當練習題,用 30% 的未見過資料當考試題。考試分數才代表真實水平。
random_state=42 不是魔法數字,而是隨機種子 —— 保證每次劃分結果相同,實驗可重複。K-Means 的核心是歐氏距離。如果"薪資(萬)"的量綱是 0.5~5,而"年齡"是 20~60,那麼薪資對距離的貢獻會被年齡完全淹沒。StandardScaler 把所有特徵拉到同一尺度上,讓每個特徵公平參與距離計算。
隨著 K 增大,Inertia(簇內平方和)一定會下降 —— 極端情況 K = 樣本數時 Inertia = 0。肘部法則找的是"下降速度突然放緩"的轉折點,即繼續增加 K 帶來的收益急劇減少的那個位置。
知道每個數字代表什麼含義,並能用通俗語言寫出來。
生成一份可列印的 HTML 分析報告,排版行文按學術期刊論文格式(含標題、摘要、分析方法、圖表、參考文獻樣式)。報告必須包含所用分析方法的簡要介紹及選擇理由。
# 生成 HTML 報告
html_content = f"""
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>資料分析報告</title>
<style>
body {{ font-family: "SimSun", "宋體", serif; max-width: 900px; margin: 0 auto; padding: 40px; }}
h1 {{ text-align: center; font-size: 18pt; }}
h2 {{ font-size: 14pt; border-bottom: 1px solid #333; padding-bottom: 4px; }}
table {{ border-collapse: collapse; width: 100%; margin: 10px 0; }}
th, td {{ border: 1px solid #666; padding: 6px 10px; font-size: 10pt; }}
th {{ background: #f0f0f0; }}
.abstract {{ background: #fafafa; padding: 12px 16px; border-left: 4px solid #3498db; }}
.method {{ background: #fff8e1; padding: 10px 14px; border-left: 4px solid #f39c12; margin: 10px 0; font-size: 10pt; }}
</style>
</head>
<body>
<h1>{title}</h1>
<div class="abstract"><b>摘要:</b>{abstract_text}</div>
<h2>0. 分析方法</h2>
<div class="method">
{method_text}
</div>
<h2>1. 資料概況</h2>
<p>{overview}</p>
<h2>2. 核心發現</h2>
<p>{findings}</p>
<h2>3. 相關性分析</h2>
<p>{correlation_text}</p>
<h2>4. 模型效果</h2>
<p>{model_text}</p>
<h2>5. 侷限與建議</h2>
<p>{limitations}</p>
</body>
</html>
"""
with open(output_dir / '分析報告.html', 'w', encoding='utf-8') as f:
f.write(html_content)
print(f'✅ HTML 報告已儲存至: {output_dir / "分析報告.html"}')
method_text)<b>本報告使用的分析方法及選擇理由:</b>
<ul>
<li><b>描述性統計分析</b>(均值、中位數、偏度):
用於刻畫資料集中趨勢與離散程度。選擇均值+中位數雙指標彙報,
因為薪資資料通常右偏,均值會被極端高薪拉高,中位數更能反映"普通人能拿到的"。</li>
<li><b>皮爾遜相關係數</b>(|r| > 0.5 為強相關):
用於量化變數之間的線性關聯程度。選擇皮爾遜而非斯皮爾曼,
因為需要測量的是線性關係而非單調關係(如"經驗增加→薪資穩步上升")。</li>
<li><b>分組聚合分析</b>(GROUP BY 等價操作):
用於按類別(城市/學歷/行業)比較薪資差異。選擇分組均值而非分組總數,
因為目標是橫向比較"哪個類別更高",而非"哪個類別崗位更多"。</li>
<li><b>K-Means 聚類</b>(輪廓係數驗證 + 肘部法則選 K):
用於無監督地發現崗位的"自然分層"。選擇 K-Means 而非 DBSCAN,
因為我們預期的崗位分層是球狀分佈(高/中/低三個梯隊),
而非密度差異分佈。標準化後再聚類是為了消除量綱差異。</li>
<li><b>視覺化方法</b>(直方圖/箱線圖/柱狀圖/散點圖/熱力圖):
每種圖的選擇基於視覺編碼理論——用最合適的視覺屬性(位置、長度、顏色)
編碼對應的資料特徵,詳見 §7 圖表選擇指南。</li>
</ul>
1. 資料概況:本分析使用 {len(clean)} 條資料,涵蓋 {時間範圍}
的 {資料來源}。
2. 核心發現:
- {最突出的發現1}(用數字支撐,如:A類佔比xx%,是B類的x倍)
- {最突出的發現2}(用數字支撐)
- {最突出的發現3}(用數字支撐)
3. 相關性分析:{變數A} 與 {變數B} 的相關係數為 {r},
表明 {強/中等/弱} {正/負}相關。
4. 模型效果:{模型名} 的 {指標名} 為 {值},
說明模型的 {預測/分類/聚類} 能力 {優秀/良好/一般}。
5. 侷限與建議:
- 資料侷限:{資料可能的問題(樣本量、覆蓋範圍、時效性)}
- 後續建議:{可以進一步分析的方向(加入新變數、擴充套件時間範圍等)}
| 錯誤 | 原因 | 解決方法 |
|---|---|---|
KeyError: '列名' |
列名寫錯了 | 列印 df.columns 確認 |
ValueError: could not convert |
型別不匹配 | 先 dtype 檢查,用 errors='coerce' |
| 中文亂碼 | 編碼不對 | 試 gbk 或 utf-8-sig |
| 圖表中文顯示方塊 | 字型缺失 | 設 rcParams['font.sans-serif'] |
| 相關係數 NaN | 列中有 NaN | 先 dropna() 再算相關 |
| 迴歸 R² 為負 | 模型比"猜均值"還差 | 檢查特徵選擇,可能需要更多特徵 |
SettingWithCopyWarning |
在切片上賦值 | 用 .copy() 建立獨立副本 |
這個Skill質量較好,內容非常全面,從環境配置到建模報告一氣呵成,每一步都有手把手教學,最適合完全沒有基礎的學生跟著學。優點是知識點講得清楚,不僅教怎麼操作還解釋為什麼這樣做,有配套的錯誤除錯指南。不足是內容太長讀起來有壓力,沒有配套的練習資料檔案,而且偏向入門級,進階內容偏少。