data-mining-course

👤 user_072425e5 📦 v1.0.0 ⭐ 4.4 ⬇️ 326 下載
🎓 教育學習 免費

📖 技能介紹


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(NUFE)

本 Skill 為《資料探勘與商務智慧》課程設計,每一步都標註了對應知識點,讓學生在操作中學習。


⚠️ 核心執行規範(教師/Agent 必須遵守)

  1. 逐節執行,不可跳躍:每節(§1→§2→…→§9)必須獨立執行,執行完一節後暫停,先講解該節的「💡 教學要點」,再進入下一節。禁止把多節合併成一個指令碼。
  2. 知識點先於程式碼結果:每節的輸出中,先呈現「教學要點」(概念、原理、方法選擇原因),再呈現資料結果。資料特徵是素材,不是知識本身。
  3. 每步講清楚「為什麼」:學生不僅要看到「怎麼跑程式碼」,更要理解「為什麼用這個方法」「為什麼不選那個方法」。
  4. 資料分析方法須寫入報告:§9 生成的 HTML 報告中,必須包含所用分析方法的簡要介紹及選擇理由。

教學知識點地圖

步驟 知識點 對應章節
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 環境管理)

教學目標

學生需要理解:跑程式碼之前先檢查環境,避免"程式碼沒問題但跑不起來"。

執行流程

# 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

💡 教學要點

  • 為什麼用清華源? 預設 PyPI 在國外,下載慢且容易斷
  • 為什麼分步檢查? 只有缺的才裝,節約時間
  • PowerShell 不支援 &&,所以用分步執行
  • pip:Python 官方 包管理工具 ,專門用來安裝 / 解除安裝 / 更新第三方庫,是環境配置的核心命令
  • 執行 python --version提示不是內部命令:Python 未新增系統環境變數,重新安裝 Python 時勾選「Add Python to PATH」
  • 庫pandas 資料處理核心工具:負責讀取 Excel/CSV 資料、處理缺失值、篩選資料(資料預處理模組)
  • matplotlib 基礎繪圖工具:繪製柱狀圖、折線圖、散點圖,做資料視覺化展示
  • seaborn 高階視覺化工具:比 matplotlib 更美觀,用於繪製聚類熱力圖、相關性圖等
  • scipy 科學計算工具:支撐演算法執行,處理數值計算、統計分析任務
  • scikit-learn(sklearn) 機器學習核心庫:實現分類、聚類、關聯規則等所有資料探勘演算法

§2 資料載入(知識點:檔案 I/O 與編碼)

教學目標

理解編碼(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]} 列')

💡 教學要點

  • UTF-8 vs GBK:UTF-8 是國際標準,GBK 是中文 Windows 預設編碼
  • BOM 頭:某些 Windows 軟體匯出的 CSV 帶 \ufeff,表現為  出現在第一列列名前
  • pathlib.Path:比字串拼接更安全,跨平臺相容

§3 資料探查(知識點:認識你的資料)

教學目標

"先看資料,再動手" — 這是資料探勘的第一鐵律。

標準探查清單

# ===== 基本結構 =====
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]}')

💡 教學要點

  • describe():count(非空數)、mean(均值)、std(標準差)、min、25%、50%(中位數)、75%、max
  • 缺失率 > 50% 的列一般直接刪除
  • 重複行 可能是資料採集 bug,需要溯源

§4 資料清洗(知識點:資料質量是分析的基石)

教學目標

理解 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(非隨機缺失) 缺失與被缺失變數本身有關 收入越高越不願填收入 需專門建模處理,單純刪除/填充會引入偏差
  • 為什麼有異常值時用中位數不用均值? 假設 10 個員工月薪 [5k, 6k, 6k, 6.5k, 7k, 7k, 7k, 8k, 8k, 50k(老闆)],均值=11.2k,中位數=7k。用均值填充會把所有人的薪資"拉高",這就是異常值的破壞力。中位數對極值不敏感,是更穩健的選擇。
  • IQR 為什麼比 "均值±3σ" 好? "均值±3σ" 假設資料是正態分佈 —— 而真實資料幾乎從不正態。IQR 基於四分位數排序,不做分佈假設,對偏態資料、長尾資料都能用。

型別轉換的坑

  • 日期列:Excel 中的日期底層是數字(從 1900-01-01 開始的天數),pd.to_datetime(..., errors='coerce')coerce 的意思是"轉不了的變成 NaT(Not a Time)",不要報錯中斷程式。
  • 科學計數法1.1582E+14 是浮點數,但在 Excel 中這是 ID(如 B站影片 ID),必須轉成字串 astype(str),否則後續匹配會失敗。

§5 探索性資料分析 EDA(知識點:從資料中發現故事)

教學目標

學會用統計指標和分組聚合來發現資料中的規律。

EDA 第一準則:先識別 5 類商科常用資料型別,再匹配對應分析方法

先分型,再分析:掌握 5 類商科常用資料的分型規則,杜絕分析方法混用:

  • 學會針對數值、分類、文本、統計年鑑、面板資料,匹配對應的 EDA 分析方法
  • 結合商科/統計場景,將統計結果轉化為可理解的業務/宏觀結論(如統計年鑑的地區差異、面板資料的時間趨勢)
  • 掌握基礎文本資料、面板資料的 EDA 實操,適配課程後續實驗與實訓

⚠️ 以下程式碼為一個完整指令碼,直接複製執行即可,無需手動改列名。

# -*- 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 的目標不是"找到正確答案",而是發現值得深挖的方向。兩者有本質區別:

  • 驗證性分析(Confirmatory):先有假設("學歷高的薪資一定高"),再用資料檢驗 → 統計學檢驗(t 檢驗、ANOVA)
  • 探索性分析(Exploratory):先看資料、再形成假設("咦,城市之間薪資差異好大,是什麼原因?") → EDA

學生常犯的錯誤:把 EDA 中發現的模式直接當結論 —— 比如看到"大城市薪資高"就說"去大城市就能高薪",忽略了選擇偏差(大城市本身就吸引了更多高學歷人才)。EDA 是起點,不是終點

為什麼必須先分資料型別再分析?

資料型別 用錯方法的後果
把分類列當數值列 df['城市'].mean() → 報錯,或算出無意義的"平均城市編碼"
把數值列當分類列 每個不同的數值被當成一個類別,導致分組數 = 樣本數
把文本列當分類列 每行一個獨特值,分組無意義
把截面資料當面板資料 錯誤地強加"時間趨勢"解釋,得出虛假結論

這就是本 Skill 在 EDA 開頭強制執行 clean.dtypes 的原因 —— 先知道每一列是什麼型別,才能決定用什麼分析方法。

為什麼用偏度而不是隻看均值和中位數?

指標 告訴你的資訊 侷限
均值 整體水平 被極值嚴重拉偏
中位數 "中間那個人"的水平 無法反映極端情況
偏度 資料往哪邊"甩尾" 需要配合均值/中位數一起看

薪資資料幾乎總是正偏(右偏):大多數人拿中等薪資,少數人拿極高薪資。如果只彙報"平均薪資",學生會高估自己的收入預期。彙報中位數比彙報均值更接近"普通人能拿到的"

為什麼用皮爾遜相關係數而不是隨便"看圖說話"?

相關係數把"這兩個變數看起來有關係"變成了一個可比較的數字。但要注意:

  • |r| > 0.7 → 強相關,但不等於因果關係。人均巧克力消費量與諾獎得主數量 r≈0.8,但吃巧克力不會讓你得諾獎 —— 混淆變數是國民富裕度。
  • r 只能衡量線性關係,如果你的資料是 U 型曲線(如年齡-消費能力),r 可能接近 0 但實際關係很強。
  • 零相關不等於沒關係

分組聚合與 5 類資料分型規則(核心口訣)

  • 分組聚合:SQL 的 GROUP BY 在 pandas 中的等價操作 —— 本質是將資料按某一列的類別切分,然後對每個切塊做統計運算(計數、求和、均值等)
資料型別 能做什麼 不能做什麼
數值型(統計年鑑核心) 均值、相關、TopN、趨勢 不能用來分組分類
分型別(面板分類項) 分組、計數 不能計算均值、相關係數
文本型(統計年鑑文本) 頻數統計(入門階段) 不做複雜文本挖掘
統計年鑑資料 彙總統計、地區/行業對比、相關性 照搬數值/分類程式碼
面板資料 時間趨勢 + 主體對比 忽略時間維度單獨分析
  • 關鍵準則:所有 EDA 操作必須先看資料型別(df.dtypes),再選擇對應程式碼

§6 特徵工程(知識點:讓模型更好地理解資料)

教學目標

理解"同樣的資料,不同的表達方式會直接影響模型效果"。

# ---- 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 編碼 vs Label 編碼:選錯會讓模型"學錯"

編碼方式 原理 什麼時候用 用錯的後果
One-Hot 每個類別變成一列 0/1 無序分類(城市、顏色、品牌) 如果類別太多(>100種),特徵矩陣爆炸
Label 把類別替換成數字 1,2,3... 有序分類(學歷、等級、星級) 用在無序分類上,模型會以為"上海=3" > "北京=1"

標準化 vs 歸一化:不是一回事

  • 標準化(StandardScaler):使資料均值為 0、標準差為 1。保留原始分佈形狀。
  • 歸一化(MinMaxScaler):將資料縮放到 [0, 1]。對異常值敏感。
  • 樹模型(隨機森林、XGBoost)不需要標準化/歸一化 —— 它們只關心分裂點的相對大小,不關心絕對數值。
  • 距離類模型(KNN、K-Means、SVM、神經網路)必須標準化 —— 否則"薪資(單位萬)"和"年齡(單位年)"的巨大量綱差異會讓距離計算完全被薪資主導。

§7 視覺化(知識點:一張好圖勝過千言萬語)

教學目標

掌握資料探勘中最常用的 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')

💡 教學要點 — 圖表選擇指南

為什麼必須先視覺化再做建模?

人眼是人類最強大的模式識別系統。肉眼掃一遍散點圖,能瞬間發現:線性/曲線趨勢、離群點、聚類結構、資料缺口。而直接丟進模型跑,這些資訊全部丟失。視覺化是建模前的"體檢報告"

6 種圖表的底層選擇邏輯(視覺編碼理論)

圖表本質上是用視覺屬性(位置、長度、顏色、形狀)來編碼資料特徵。選錯圖表 = 用錯視覺屬性,讀者無法正確解碼資訊:

想看什麼 用什麼圖 視覺編碼 為什麼選它 注意
分佈形態 直方圖 / KDE 柱高/面積 = 頻數 單變數、看集中與分散 bin 數量影響觀感
異常值 + 四分位 箱線圖 位置 = 四分位數,須外 = 異常 最適合多組比較,一張圖能看多組的分佈差異 點超出須線即異常
類別比較 柱狀圖 柱高 = 數量 分類 x 數值,簡單直接 類別太多時取 TopN
兩變數關係 散點圖 點的 x/y 位置 發現相關性、聚類、離群、非線性 加透明度避免重疊
時間趨勢 折線圖 斜率 = 變化率 強調先後順序和變化速率 時間軸要排序
相關性 熱力圖 顏色深淺 = 相關強弱 同時展示幾十個變數的兩兩關係 顏色深淺 = 強弱

常見錯誤

  • 用餅圖展示 10+ 個類別:餅圖人眼只能區分 3-5 個扇區,多了完全看不清。用柱狀圖替代。
  • 用 3D 圖展示 2D 資料:3D 透視變形會嚴重歪曲數值,學術論文中禁止。
  • 散點圖不加透明度:大量重疊點會被隱藏(overplotting),加 alpha=0.5 解決。
  • 顏色用紅綠配:紅綠色盲人群佔 8%(男性 1/12),用藍橙/藍紅替代。

§8 建模入門(知識點:從描述走向預測)

教學目標

理解監督學習(迴歸/分類)和無監督學習(聚類)的基本概念。

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 不是魔法數字,而是隨機種子 —— 保證每次劃分結果相同,實驗可重複。
  • 為什麼是 7:3? 經驗值。資料量小時(<1000)可改用 8:2;資料量大時(>10000)可用 5:5 或交叉驗證。

為什麼 K-Means 聚類前需要標準化?

K-Means 的核心是歐氏距離。如果"薪資(萬)"的量綱是 0.5~5,而"年齡"是 20~60,那麼薪資對距離的貢獻會被年齡完全淹沒。StandardScaler 把所有特徵拉到同一尺度上,讓每個特徵公平參與距離計算。

肘部法則的原理

隨著 K 增大,Inertia(簇內平方和)一定會下降 —— 極端情況 K = 樣本數時 Inertia = 0。肘部法則找的是"下降速度突然放緩"的轉折點,即繼續增加 K 帶來的收益急劇減少的那個位置。

  • 輪廓係數(silhouette_score)是肘部法則的補充驗證:> 0.3 尚可,> 0.5 良好,> 0.7 優秀。

§9 結果解釋(知識點:得出可信結論)

教學目標

知道每個數字代表什麼含義,並能用通俗語言寫出來。

結論撰寫模板

文件輸出格式

生成一份可列印的 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>

結論內容模板(填入 HTML 報告)

1. 資料概況:本分析使用 {len(clean)} 條資料,涵蓋 {時間範圍}
   的 {資料來源}。

2. 核心發現:
   - {最突出的發現1}(用數字支撐,如:A類佔比xx%,是B類的x倍)
   - {最突出的發現2}(用數字支撐)
   - {最突出的發現3}(用數字支撐)

3. 相關性分析:{變數A} 與 {變數B} 的相關係數為 {r},
   表明 {強/中等/弱} {正/負}相關。

4. 模型效果:{模型名} 的 {指標名} 為 {值},
   說明模型的 {預測/分類/聚類} 能力 {優秀/良好/一般}。

5. 侷限與建議:
   - 資料侷限:{資料可能的問題(樣本量、覆蓋範圍、時效性)}
   - 後續建議:{可以進一步分析的方向(加入新變數、擴充套件時間範圍等)}

💡 教學要點

  • 結論要有數字,不能只說"A 和 B 有關係"
  • 要說明係數的大小和方向(正/負)
  • 要承認侷限(樣本量、資料質量、未考慮的因素)
  • HTML 報告用宋體排版,符合中文學術論文規範
  • 分析方法介紹是報告的必要組成部分 —— 讀者需要知道"為什麼用這個方法",才能判斷結論的可信度

完整執行 Checklist(學生自查用)

  • [ ] □ Python 版本 ≥ 3.11
  • [ ] □ 必要包已安裝(pandas, matplotlib, seaborn, scipy, sklearn)
  • [ ] □ 資料檔案存在且可讀
  • [ ] □ 已探查:形狀、型別、缺失值、描述統計
  • [ ] □ 已清洗:缺失值、重複值、異常值、型別轉換
  • [ ] □ 已做 EDA:分佈、相關性、分組統計、Top N
  • [ ] □ 已視覺化:至少包含 3 種圖表
  • [ ] □ 已建模(如適用):劃分訓練/測試、訓練、評估
  • [ ] □ 已撰寫結論:有數字、有解釋、有侷限說明

常見錯誤與除錯(FAQ)

錯誤 原因 解決方法
KeyError: '列名' 列名寫錯了 列印 df.columns 確認
ValueError: could not convert 型別不匹配 dtype 檢查,用 errors='coerce'
中文亂碼 編碼不對 gbkutf-8-sig
圖表中文顯示方塊 字型缺失 rcParams['font.sans-serif']
相關係數 NaN 列中有 NaN dropna() 再算相關
迴歸 R² 為負 模型比"猜均值"還差 檢查特徵選擇,可能需要更多特徵
SettingWithCopyWarning 在切片上賦值 .copy() 建立獨立副本

🤖 AI 評測

這個Skill質量較好,內容非常全面,從環境配置到建模報告一氣呵成,每一步都有手把手教學,最適合完全沒有基礎的學生跟著學。優點是知識點講得清楚,不僅教怎麼操作還解釋為什麼這樣做,有配套的錯誤除錯指南。不足是內容太長讀起來有壓力,沒有配套的練習資料檔案,而且偏向入門級,進階內容偏少。

📊 多維度評分

適應性3.8
規範性4.4
有效性4.5
可靠性4.2
可信度5

📁 包含檔案 (1 個)

📄 SKILL.md 39.9 KB