當代商業環境中,資料科學已從輔助性工具演變為企業競爭力的核心要素。然而,資料科學的價值實現並非僅依賴先進的演算法或強大的運算能力,更關鍵的是建立從資料收集到洞察溝通的完整技術體系。這個體系涵蓋統計學的嚴謹基礎、機器學習的實用技術,以及視覺化設計的溝通藝術,每個環節都對最終成果產生決定性影響。

台灣企業在推動資料科學應用時常面臨多重挑戰。資料品質參差不齊影響分析結果的可信度,跨部門的資料整合困難阻礙全局性洞察的獲得,技術人才的缺乏限制進階應用的開展。更關鍵的是,即使建立了精確的預測模型,如何將技術結果轉化為管理階層能理解並採納的商業建議仍是一大難題。這些挑戰需要系統性的方法論來解決。

本文從資料科學的理論基礎出發,建構涵蓋統計推斷、模式識別、預測建模到結果視覺化的完整框架。透過製造業預測性維護的實際案例,展示從問題定義、資料準備、模型訓練到部署監測的完整工作流程。同時深入探討資料視覺化的設計原則與實作技巧,協助技術團隊建立有效的溝通機制。這些內容將為企業建立資料科學能力提供實務指引。

機率理論與統計推斷基礎

機率理論提供描述不確定性與隨機現象的嚴謹框架,是資料科學的數學基礎。當我們面對真實世界的資料時,很少能夠獲得完整的母體資訊,因此必須透過樣本資料來推斷母體的特性。這個從部分到整體的推理過程,正是統計推斷的本質。理解機率分配的特性能協助分析師選擇適當的統計方法,正確解讀分析結果並量化預測的不確定性。

常態分配是統計學中最重要的機率分配,其鐘型曲線描述許多自然現象與測量誤差的分布特性。根據中央極限定理,大量獨立隨機變數的總和趨近於常態分配,這使得常態分配在統計推斷中具有核心地位。許多統計檢定如 t 檢定與變異數分析都建立在常態假設之上。在實務應用中,製程參數、產品尺寸、反應時間等連續型變數常呈現近似常態的分布,可以使用平均數與標準差兩個參數完整描述其特性。

泊松分配描述單位時間或空間內隨機事件發生次數的機率,適用於罕見事件的建模。網站訪客到達、設備故障發生、電話客服來電等計數型資料常符合泊松分配。這種分配只需要一個參數λ表示平均發生率,當λ較小時分配呈現右偏,隨著λ增大逐漸趨近對稱。泊松分配在排隊理論、可靠度工程以及流行病學中有廣泛應用。

二項分配描述固定次數獨立試驗中成功次數的機率,每次試驗只有成功與失敗兩種結果且成功機率固定。產品檢驗的合格率、行銷活動的轉換率、醫療測試的陽性率等比例型資料可以使用二項分配建模。當試驗次數夠大且成功機率不太接近零或一時,二項分配可以用常態分配近似,簡化計算複雜度。

統計推斷實務應用

統計推斷透過樣本資料對母體特性進行推論,彌補無法觀察全體的限制。推斷方法分為估計與檢定兩大類,估計給出母體參數的數值範圍,檢定則判斷關於母體的假說是否成立。這些方法建立在機率理論之上,量化推論的不確定性並控制錯誤判斷的風險。

點估計使用單一數值估計母體參數,常見的估計方法包括最大概似估計、動差估計與貝氏估計。樣本平均數是母體平均數的不偏估計,樣本變異數在分母使用 n-1 校正偏誤。估計量的良好性質包括不偏性、有效性與一致性,在有限樣本下權衡這些性質是統計推斷的核心議題。

區間估計提供參數可能範圍與信心水準,較點估計提供更完整的資訊。信賴區間的解釋是若重複抽樣多次,該區間涵蓋真實參數的比例會接近信心水準。常見的 95% 信賴區間表示長期而言有 95% 的區間會包含真實參數。信賴區間的寬度反映估計的精確度,樣本數愈大區間愈窄,變異愈大區間愈寬。

假設檢定評估樣本資料是否支持關於母體的假說,廣泛應用於比較分析與因果推論。檢定程序從建立虛無假設與對立假設開始,計算檢定統計量並與臨界值比較,或直接計算 p 值判斷是否拒絕虛無假設。第一型錯誤是拒絕真實的虛無假設,其機率稱為顯著水準α通常設定為 0.05。第二型錯誤是未拒絕錯誤的虛無假設,其機率記為β,檢定力 1-β 表示正確拒絕錯誤假設的能力。

"""
統計推斷完整實作
展示描述統計、信賴區間估計、假設檢定與常態性檢定
"""

import numpy as np
import pandas as pd
from scipy import stats
import matplotlib.pyplot as plt
import seaborn as sns

# 設定中文字型支援
plt.rcParams['font.sans-serif'] = ['Microsoft JhengHei']
plt.rcParams['axes.unicode_minus'] = False

class StatisticalInference:
    """
    統計推斷工具類別
    提供完整的描述統計、假設檢定與信賴區間估計功能
    """
    
    def __init__(self, data, alpha=0.05):
        """
        初始化統計推斷物件
        
        參數:
            data: 樣本資料(numpy array或list)
            alpha: 顯著水準,預設為0.05(5%)
        """
        self.data = np.array(data)
        self.alpha = alpha
        self.n = len(data)
        
    def descriptive_statistics(self):
        """
        計算完整的描述性統計量
        提供資料分布的全面特徵摘要
        
        回傳:
            包含各種統計量的字典
        """
        stats_dict = {
            'mean': np.mean(self.data),              # 算術平均數
            'median': np.median(self.data),          # 中位數
            'mode': stats.mode(self.data, keepdims=True)[0][0],  # 眾數
            'std': np.std(self.data, ddof=1),        # 標準差(樣本)
            'variance': np.var(self.data, ddof=1),   # 變異數(樣本)
            'min': np.min(self.data),                # 最小值
            'max': np.max(self.data),                # 最大值
            'range': np.ptp(self.data),              # 全距
            'q1': np.percentile(self.data, 25),      # 第一四分位數
            'q3': np.percentile(self.data, 75),      # 第三四分位數
            'iqr': stats.iqr(self.data),             # 四分位距
            'skewness': stats.skew(self.data),       # 偏態係數
            'kurtosis': stats.kurtosis(self.data),   # 峰態係數
            'cv': np.std(self.data, ddof=1) / np.mean(self.data)  # 變異係數
        }
        return stats_dict
    
    def confidence_interval(self, confidence_level=0.95):
        """
        計算母體平均數的信賴區間
        使用 t 分配進行估計(適用於未知母體標準差的情況)
        
        參數:
            confidence_level: 信賴水準,預設為0.95(95%)
        
        回傳:
            信賴區間的下界與上界(tuple)
        """
        # 計算樣本平均數與標準誤
        sample_mean = np.mean(self.data)
        standard_error = stats.sem(self.data)  # 標準誤 = 標準差 / sqrt(n)
        
        # 計算 t 分配的臨界值
        # 自由度為 n-1
        df = self.n - 1
        t_critical = stats.t.ppf((1 + confidence_level) / 2, df)
        
        # 計算誤差範圍
        margin_of_error = t_critical * standard_error
        
        # 計算信賴區間
        lower_bound = sample_mean - margin_of_error
        upper_bound = sample_mean + margin_of_error
        
        return (lower_bound, upper_bound)
    
    def one_sample_t_test(self, population_mean):
        """
        單樣本 t 檢定
        檢定樣本平均數是否與假設的母體平均數有顯著差異
        
        虛無假設 H0: μ = μ0
        對立假設 H1: μ ≠ μ0
        
        參數:
            population_mean: 假設的母體平均數
        
        回傳:
            包含檢定結果的字典
        """
        # 執行雙尾 t 檢定
        t_statistic, p_value = stats.ttest_1samp(self.data, population_mean)
        
        # 判斷是否拒絕虛無假設
        reject_null = p_value < self.alpha
        
        # 計算效果量(Cohen's d)
        effect_size = (np.mean(self.data) - population_mean) / np.std(self.data, ddof=1)
        
        result = {
            't_statistic': t_statistic,
            'p_value': p_value,
            'alpha': self.alpha,
            'degrees_of_freedom': self.n - 1,
            'effect_size': effect_size,
            'reject_null': reject_null,
            'conclusion': '拒絕虛無假設,樣本平均數與母體平均數有顯著差異' if reject_null 
                         else '無法拒絕虛無假設,樣本平均數與母體平均數無顯著差異'
        }
        
        return result
    
    def normality_test(self):
        """
        常態性檢定
        使用 Shapiro-Wilk 檢定判斷資料是否符合常態分配
        
        虛無假設 H0: 資料符合常態分配
        對立假設 H1: 資料不符合常態分配
        
        回傳:
            包含檢定結果的字典
        """
        # 執行 Shapiro-Wilk 檢定
        statistic, p_value = stats.shapiro(self.data)
        
        # 判斷是否符合常態分配
        # p 值大於 alpha 表示無法拒絕常態分配的假設
        is_normal = p_value > self.alpha
        
        result = {
            'test': 'Shapiro-Wilk',
            'statistic': statistic,
            'p_value': p_value,
            'alpha': self.alpha,
            'is_normal': is_normal,
            'conclusion': '資料符合常態分配(無法拒絕虛無假設)' if is_normal 
                         else '資料不符合常態分配(拒絕虛無假設)'
        }
        
        return result
    
    def visualize_distribution(self, save_path=None):
        """
        視覺化資料分配
        繪製直方圖、常態分配曲線與Q-Q圖
        
        參數:
            save_path: 儲存圖表的路徑(可選)
        """
        fig, axes = plt.subplots(1, 3, figsize=(18, 5))
        
        # 子圖1: 直方圖與常態分配曲線
        axes[0].hist(self.data, bins=30, density=True, 
                     alpha=0.7, color='skyblue', edgecolor='black',
                     label='實際資料')
        
        # 疊加理論常態分配曲線
        mu = np.mean(self.data)
        sigma = np.std(self.data, ddof=1)
        x = np.linspace(self.data.min(), self.data.max(), 200)
        axes[0].plot(x, stats.norm.pdf(x, mu, sigma), 
                     'r-', linewidth=2.5, label='常態分配')
        
        axes[0].axvline(mu, color='green', linestyle='--', 
                       linewidth=2, label=f'平均數={mu:.2f}')
        axes[0].set_xlabel('數值', fontsize=12)
        axes[0].set_ylabel('機率密度', fontsize=12)
        axes[0].set_title('資料分配直方圖', fontsize=14, fontweight='bold')
        axes[0].legend(fontsize=10)
        axes[0].grid(True, alpha=0.3)
        
        # 子圖2: Q-Q圖(檢查常態性)
        stats.probplot(self.data, dist="norm", plot=axes[1])
        axes[1].set_title('常態Q-Q圖', fontsize=14, fontweight='bold')
        axes[1].grid(True, alpha=0.3)
        
        # 子圖3: 盒鬚圖
        bp = axes[2].boxplot(self.data, vert=True, patch_artist=True,
                            boxprops=dict(facecolor='lightblue'),
                            medianprops=dict(color='red', linewidth=2))
        axes[2].set_ylabel('數值', fontsize=12)
        axes[2].set_title('盒鬚圖', fontsize=14, fontweight='bold')
        axes[2].grid(True, alpha=0.3, axis='y')
        
        plt.tight_layout()
        
        if save_path:
            plt.savefig(save_path, dpi=300, bbox_inches='tight')
            print(f"圖表已儲存至: {save_path}")
        
        plt.show()

# ============================================================================
# 實際應用範例
# ============================================================================

# 生成模擬資料(接近常態分配)
np.random.seed(42)
sample_data = np.random.normal(loc=100, scale=15, size=200)

# 建立統計推斷物件
inference = StatisticalInference(sample_data, alpha=0.05)

# 計算描述性統計
print("="*80)
print("描述性統計分析")
print("="*80)
desc_stats = inference.descriptive_statistics()
for key, value in desc_stats.items():
    print(f"{key:15s}: {value:.4f}")

# 計算95%信賴區間
print("\n" + "="*80)
print("信賴區間估計")
print("="*80)
ci = inference.confidence_interval(confidence_level=0.95)
print(f"母體平均數的95%信賴區間: ({ci[0]:.2f}, {ci[1]:.2f})")
print(f"解釋: 我們有95%的信心,母體平均數落在此區間內")

# 執行單樣本t檢定
print("\n" + "="*80)
print("單樣本t檢定")
print("="*80)
print("檢定假設: 母體平均數是否等於100")
t_test_result = inference.one_sample_t_test(population_mean=100)
print(f"t統計量: {t_test_result['t_statistic']:.4f}")
print(f"p值: {t_test_result['p_value']:.4f}")
print(f"自由度: {t_test_result['degrees_of_freedom']}")
print(f"效果量(Cohen's d): {t_test_result['effect_size']:.4f}")
print(f"結論: {t_test_result['conclusion']}")

# 執行常態性檢定
print("\n" + "="*80)
print("常態性檢定")
print("="*80)
normality_result = inference.normality_test()
print(f"{normality_result['test']}統計量: {normality_result['statistic']:.4f}")
print(f"p值: {normality_result['p_value']:.4f}")
print(f"結論: {normality_result['conclusion']}")

# 視覺化資料分配
print("\n繪製視覺化圖表...")
inference.visualize_distribution(save_path='statistical_analysis.png')

這個完整的統計推斷工具展示了描述統計、信賴區間估計、假設檢定以及常態性檢定等核心功能。程式碼特別注意自由度的正確計算、效果量的評估,以及結果的清晰呈現。

@startuml
!define PLANTUML_FORMAT svg
!theme _none_

skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100

title 統計推斷方法架構

start

:定義研究問題;

:收集樣本資料;

:描述性統計分析;
note right
  計算集中趨勢
  計算變異程度
  評估分布形狀
end note

:常態性檢定;

if (資料符合常態分配?) then (是)
  :使用參數檢定;
  note right
    t檢定
    變異數分析
    相關分析
  end note
else (否)
  :考慮資料轉換或\n使用無母數檢定;
  note right
    Mann-Whitney U檢定
    Kruskal-Wallis檢定
    Spearman相關
  end note
endif

:建立假設\n虛無假設H0\n對立假設H1;

:選擇顯著水準α;

:計算檢定統計量;

:計算p值;

if (p值 < α?) then (是)
  :拒絕虛無假設;
  :支持對立假設;
else (否)
  :無法拒絕虛無假設;
  :證據不足;
endif

:計算信賴區間;

:解釋結果\n評估實務意義;

stop

@enduml

迴歸分析與因果推論

迴歸分析建立變數間的數量關係,既可用於預測未來值也能探索影響機制。從最基本的簡單線性迴歸到複雜的非線性模型,迴歸技術提供彈性的建模框架適用於各種資料型態與研究目的。理解迴歸模型的假設、估計方法與診斷程序是正確應用的關鍵。

簡單線性迴歸探討單一自變數與應變數的線性關係,其數學形式為 y = β₀ + β₁x + ε,其中β₀是截距項,β₁是斜率參數,ε是隨機誤差項。最小平方法透過最小化殘差平方和估計參數,具有明確的幾何意義且計算簡便。決定係數R²衡量模型解釋變異的比例,介於零到一之間,愈接近一表示模型配適度愈好。

多元迴歸將分析擴展到多個自變數,能同時控制多個因素的影響並評估各自的貢獻。矩陣形式的表示簡化理論推導與計算實作,普通最小平方法仍是主要的估計方法。多元共線性是多元迴歸常見的問題,當自變數間高度相關時參數估計的變異數會膨脹,導致估計不穩定。變異數膨脹因子VIF可以診斷共線性的嚴重程度,超過十通常認為需要處理。

邏輯迴歸適用於二元應變數的建模,透過邏輯函數將線性組合映射到零到一的機率區間。勝算比的概念協助解釋係數的實質意義,正係數表示該變數增加會提高事件發生的勝算。最大概似估計取代最小平方法成為參數估計的主要方法,反覆加權最小平方演算法求解最佳化問題。模型評估除了概似比檢定外,ROC曲線與AUC值提供分類能力的視覺化評估。

因果推論關注自變數對應變數的因果效應而非單純的相關性。觀察性研究中混淆因素的存在使得因果推論充滿挑戰,傾向分數方法透過平衡處理組與對照組的共變數分布來模擬隨機分派的效果。工具變數法利用外生變數的變異識別因果效應,當存在無法觀察的混淆因素時提供有效的估計策略。

資料品質保證與特徵工程

資料品質直接影響分析結果的可靠性與價值,建立系統性的品質保證機制是資料科學專案成功的前提。從資料收集、儲存、處理到應用的整個生命週期都需要品質管控,確保資料的準確性、完整性、一致性與時效性。

資料準確性確保資料正確反映真實狀況,錯誤的資料會導致分析結果產生偏誤並誤導決策。資料錯誤的來源包括人工輸入失誤、系統故障、感測器漂移以及資料傳輸損毀。驗證規則設定合理的取值範圍與格式要求,自動檢測異常值。例如年齡應介於零到一百二十之間,郵遞區號應符合三到五碼的數字格式,交易金額不應為負值。

資料完整性確保所需資料都已收集且無遺漏,缺失資料會降低分析的統計檢定力並可能引入偏誤。缺失機制的判定影響處理策略的選擇,完全隨機缺失時簡單刪除或平均數插補即可,非隨機缺失則需要更複雜的方法如多重插補或模型基礎插補。重要變數的缺失率若超過百分之三十可能需要捨棄該變數或收集額外資料。

資料一致性確保相同概念在不同來源或時間點的表達方式統一,不一致會造成整合困難與分析錯誤。計量單位的標準化避免公制與英制混用,溫度使用攝氏或華氏需要明確定義。編碼系統的對照如產品代碼、客戶編號、地區代碼需要建立主檔案。時間戳記的時區處理在跨國資料中特別重要,統一使用UTC或明確標註當地時間。

@startuml
!define PLANTUML_FORMAT svg
!theme _none_

skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100

title 資料品質保證工作流程

start

:資料收集;
note right
  來源驗證
  授權確認
  格式檢查
end note

:資料清洗;
note right
  缺失值處理
  異常值偵測
  重複記錄移除
end note

:資料轉換;
note right
  格式標準化
  單位統一
  編碼對照
end note

:資料驗證;
note right
  業務規則檢查
  範圍限制驗證
  一致性測試
end note

if (品質檢查通過?) then (是)
  :載入資料倉儲;
else (否)
  :錯誤通報與修正;
  -> 返回清洗階段;
endif

:資料監控;
note right
  品質指標追蹤
  異常預警
  定期稽核
end note

stop

@enduml

特徵工程透過轉換、組合與選擇原始特徵創造更有表達力的特徵集合。領域知識指導哪些特徵可能重要,如在信用風險評估中收入與負債的比率比單獨的絕對值更有意義。數學轉換如對數、平方根能改善資料的分布特性,使線性模型更適用。交互作用項捕捉變數間的協同效應,如藥物組合的療效可能大於單獨效果的總和。

時間特徵的提取從時間戳記中萃取如小時、星期、月份等週期性資訊,捕捉時間模式。零售業的週末效應、餐飲業的用餐時段、能源業的季節性都是重要的時間特徵。滯後變數將過去的觀察值作為當前的特徵,如前一季的銷售額預測當季的需求。滾動統計如移動平均、移動標準差提供區間內的聚合資訊。

預測性維護完整實戰

製造業的設備維護一直是成本控制與生產效率的關鍵議題。傳統的定期維護策略雖然能夠降低突發故障的風險,但往往導致過度維護與資源浪費。預測性維護透過分析設備感測器資料,預測設備何時可能發生故障,從而實現最佳的維護時機選擇。

"""
預測性維護完整實戰案例
展示從資料準備到模型部署的完整機器學習工作流程
"""

import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split, cross_val_score, GridSearchCV
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import (classification_report, confusion_matrix, 
                            roc_auc_score, roc_curve, precision_recall_curve)
import matplotlib.pyplot as plt
import seaborn as sns
from datetime import datetime

# 設定視覺化樣式
plt.rcParams['font.sans-serif'] = ['Microsoft JhengHei']
plt.rcParams['axes.unicode_minus'] = False
sns.set_style("whitegrid")
sns.set_palette("husl")

class PredictiveMaintenanceSystem:
    """
    預測性維護系統
    完整實作從資料處理到模型部署的工作流程
    """
    
    def __init__(self, random_state=42):
        """
        初始化預測性維護系統
        
        參數:
            random_state: 隨機種子,確保結果可重現
        """
        self.random_state = random_state
        self.scaler = StandardScaler()
        self.model = None
        self.feature_names = None
        self.training_history = {}
        
    def generate_synthetic_data(self, n_samples=2000):
        """
        生成模擬的設備監測資料
        模擬真實製造環境中的感測器讀數與故障模式
        
        參數:
            n_samples: 總樣本數
        
        回傳:
            包含感測器讀數與故障標籤的DataFrame
        """
        np.random.seed(self.random_state)
        
        # 設定正常與故障樣本的比例(模擬真實情況中故障較少)
        n_normal = int(n_samples * 0.9)
        n_fault = n_samples - n_normal
        
        # 正常狀態:感測器讀數較穩定
        normal_data = {
            '溫度': np.random.normal(75, 3, n_normal),
            '振動': np.random.normal(12, 0.5, n_normal),
            '壓力': np.random.normal(100, 5, n_normal),
            '轉速': np.random.normal(1500, 30, n_normal),
            '電流': np.random.normal(10, 1, n_normal),
            '運轉時數': np.random.uniform(0, 8000, n_normal)
        }
        
        # 故障狀態:感測器讀數異常
        fault_data = {
            '溫度': np.random.normal(85, 6, n_fault),
            '振動': np.random.normal(15, 2, n_fault),
            '壓力': np.random.normal(90, 10, n_fault),
            '轉速': np.random.normal(1450, 80, n_fault),
            '電流': np.random.normal(13, 2, n_fault),
            '運轉時數': np.random.uniform(6000, 10000, n_fault)
        }
        
        # 合併正常與故障資料
        df = pd.DataFrame({
            **{k: np.concatenate([normal_data[k], fault_data[k]]) 
               for k in normal_data.keys()},
            '故障': np.concatenate([np.zeros(n_normal), np.ones(n_fault)])
        })
        
        # 隨機打亂資料順序
        df = df.sample(frac=1, random_state=self.random_state).reset_index(drop=True)
        
        # 加入時間戳記
        df['時間'] = pd.date_range(
            start='2024-01-01', 
            periods=len(df), 
            freq='H'
        )
        
        return df
    
    def exploratory_data_analysis(self, df):
        """
        探索性資料分析
        視覺化資料分布與特徵關係
        
        參數:
            df: 原始資料DataFrame
        """
        print("="*80)
        print("探索性資料分析")
        print("="*80)
        
        # 基本統計資訊
        print("\n資料集資訊:")
        print(df.info())
        
        print("\n描述性統計:")
        print(df.describe())
        
        print(f"\n故障比例: {df['故障'].mean():.2%}")
        print(f"故障樣本數: {df['故障'].sum():.0f}")
        print(f"正常樣本數: {(~df['故障'].astype(bool)).sum():.0f}")
        
        # 視覺化感測器分布
        sensor_cols = ['溫度', '振動', '壓力', '轉速', '電流']
        fig, axes = plt.subplots(2, 3, figsize=(18, 10))
        fig.suptitle('感測器讀數在正常與故障狀態的分布比較', 
                    fontsize=16, fontweight='bold')
        
        for idx, col in enumerate(sensor_cols):
            ax = axes[idx // 3, idx % 3]
            
            # 繪製分布直方圖
            df[df['故障'] == 0][col].hist(
                ax=ax, bins=40, alpha=0.6, 
                label='正常', color='blue', density=True
            )
            df[df['故障'] == 1][col].hist(
                ax=ax, bins=40, alpha=0.6, 
                label='故障', color='red', density=True
            )
            
            ax.set_xlabel(col, fontsize=11)
            ax.set_ylabel('密度', fontsize=11)
            ax.set_title(f'{col}分布', fontsize=12)
            ax.legend(fontsize=10)
            ax.grid(True, alpha=0.3)
        
        # 移除多餘的子圖
        fig.delaxes(axes[1, 2])
        
        plt.tight_layout()
        plt.savefig('eda_sensor_distribution.png', dpi=300, bbox_inches='tight')
        plt.show()
        
        # 相關性熱力圖
        plt.figure(figsize=(10, 8))
        correlation_matrix = df[sensor_cols + ['故障']].corr()
        sns.heatmap(correlation_matrix, annot=True, fmt='.2f', 
                   cmap='coolwarm', center=0, square=True,
                   cbar_kws={'label': '相關係數'})
        plt.title('特徵相關性矩陣', fontsize=14, fontweight='bold')
        plt.tight_layout()
        plt.savefig('eda_correlation_matrix.png', dpi=300, bbox_inches='tight')
        plt.show()
    
    def feature_engineering(self, df):
        """
        特徵工程
        創造衍生特徵以提升模型預測能力
        
        參數:
            df: 原始資料DataFrame
        
        回傳:
            包含新特徵的DataFrame
        """
        df = df.copy()
        
        # 溫度與振動的交互作用
        # 高溫搭配高振動可能是故障的強烈訊號
        df['溫度振動交互'] = df['溫度'] * df['振動']
        
        # 壓力與轉速的比率
        # 異常的壓力轉速關係可能指示機械問題
        df['壓力轉速比'] = df['壓力'] / (df['轉速'] + 1)  # 加1避免除以零
        
        # 設備老化指標
        # 運轉時數越長,故障風險越高
        df['老化因子'] = df['運轉時數'] / df['運轉時數'].max()
        
        # 綜合異常分數
        # 標準化所有感測器讀數後求平均
        sensor_cols = ['溫度', '振動', '壓力', '轉速', '電流']
        for col in sensor_cols:
            df[f'{col}_標準化'] = (df[col] - df[col].mean()) / df[col].std()
        
        df['異常分數'] = df[[f'{col}_標準化' for col in sensor_cols]].mean(axis=1)
        
        # 移除臨時的標準化欄位
        df = df.drop([f'{col}_標準化' for col in sensor_cols], axis=1)
        
        print("\n特徵工程完成:")
        print(f"原始特徵數: {len(sensor_cols) + 1}")  # +1 for 運轉時數
        print(f"新增特徵數: 4")
        print(f"總特徵數: {len(sensor_cols) + 5}")
        
        return df
    
    def prepare_training_data(self, df):
        """
        準備訓練資料
        分割特徵與目標變數,並進行訓練測試分割
        
        參數:
            df: 包含所有特徵的DataFrame
        
        回傳:
            X_train, X_test, y_train, y_test
        """
        # 選擇特徵欄位
        feature_cols = [
            '溫度', '振動', '壓力', '轉速', '電流', '運轉時數',
            '溫度振動交互', '壓力轉速比', '老化因子', '異常分數'
        ]
        
        X = df[feature_cols]
        y = df['故障']
        
        self.feature_names = feature_cols
        
        # 分割訓練集與測試集(80/20分割,分層抽樣)
        X_train, X_test, y_train, y_test = train_test_split(
            X, y,
            test_size=0.2,
            random_state=self.random_state,
            stratify=y  # 維持類別比例
        )
        
        print("\n資料分割結果:")
        print(f"訓練集大小: {len(X_train)} ({len(X_train)/len(X)*100:.1f}%)")
        print(f"測試集大小: {len(X_test)} ({len(X_test)/len(X)*100:.1f}%)")
        print(f"訓練集故障比例: {y_train.mean():.2%}")
        print(f"測試集故障比例: {y_test.mean():.2%}")
        
        # 特徵標準化
        X_train_scaled = self.scaler.fit_transform(X_train)
        X_test_scaled = self.scaler.transform(X_test)
        
        # 轉回DataFrame保留欄位名稱
        X_train_scaled = pd.DataFrame(
            X_train_scaled, 
            columns=feature_cols,
            index=X_train.index
        )
        X_test_scaled = pd.DataFrame(
            X_test_scaled,
            columns=feature_cols,
            index=X_test.index
        )
        
        return X_train_scaled, X_test_scaled, y_train, y_test
    
    def train_model(self, X_train, y_train, tune_hyperparameters=False):
        """
        訓練隨機森林模型
        可選擇是否進行超參數調校
        
        參數:
            X_train: 訓練特徵
            y_train: 訓練標籤
            tune_hyperparameters: 是否進行網格搜尋調校
        """
        print("\n" + "="*80)
        print("模型訓練")
        print("="*80)
        
        if tune_hyperparameters:
            print("\n執行超參數調校...")
            
            # 定義超參數網格
            param_grid = {
                'n_estimators': [100, 200, 300],
                'max_depth': [8, 10, 12],
                'min_samples_split': [10, 20, 30],
                'min_samples_leaf': [5, 10, 15]
            }
            
            # 網格搜尋
            grid_search = GridSearchCV(
                RandomForestClassifier(
                    class_weight='balanced',
                    random_state=self.random_state,
                    n_jobs=-1
                ),
                param_grid,
                cv=5,
                scoring='roc_auc',
                verbose=1,
                n_jobs=-1
            )
            
            grid_search.fit(X_train, y_train)
            self.model = grid_search.best_estimator_
            
            print(f"\n最佳參數: {grid_search.best_params_}")
            print(f"最佳交叉驗證分數: {grid_search.best_score_:.4f}")
            
        else:
            # 使用預設或手動調整的參數
            self.model = RandomForestClassifier(
                n_estimators=200,
                max_depth=10,
                min_samples_split=10,
                min_samples_leaf=5,
                class_weight='balanced',
                random_state=self.random_state,
                n_jobs=-1,
                verbose=0
            )
            
            print("\n訓練隨機森林模型...")
            start_time = datetime.now()
            self.model.fit(X_train, y_train)
            training_time = (datetime.now() - start_time).total_seconds()
            
            print(f"訓練完成!耗時: {training_time:.2f}秒")
        
        # 交叉驗證評估
        print("\n執行5折交叉驗證...")
        cv_scores = cross_val_score(
            self.model, X_train, y_train,
            cv=5,
            scoring='roc_auc',
            n_jobs=-1
        )
        
        print(f"交叉驗證ROC-AUC分數:")
        print(f"  各折分數: {cv_scores}")
        print(f"  平均分數: {cv_scores.mean():.4f}{cv_scores.std()*2:.4f})")
        
        self.training_history['cv_scores'] = cv_scores
    
    def evaluate_model(self, X_test, y_test):
        """
        全面評估模型效能
        
        參數:
            X_test: 測試特徵
            y_test: 測試標籤
        
        回傳:
            評估結果字典
        """
        print("\n" + "="*80)
        print("模型評估")
        print("="*80)
        
        # 預測
        y_pred = self.model.predict(X_test)
        y_pred_proba = self.model.predict_proba(X_test)[:, 1]
        
        # 分類報告
        print("\n分類報告:")
        print(classification_report(
            y_test, y_pred,
            target_names=['正常', '故障'],
            digits=4
        ))
        
        # ROC AUC
        roc_auc = roc_auc_score(y_test, y_pred_proba)
        print(f"ROC AUC分數: {roc_auc:.4f}")
        
        # 混淆矩陣
        cm = confusion_matrix(y_test, y_pred)
        
        # 視覺化結果
        self._plot_evaluation_results(y_test, y_pred, y_pred_proba, cm)
        self._plot_feature_importance()
        
        return {
            'confusion_matrix': cm,
            'roc_auc': roc_auc,
            'predictions': y_pred,
            'probabilities': y_pred_proba
        }
    
    def _plot_evaluation_results(self, y_true, y_pred, y_pred_proba, cm):
        """繪製評估結果圖表"""
        fig, axes = plt.subplots(2, 2, figsize=(15, 12))
        
        # 1. 混淆矩陣
        sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
                   xticklabels=['預測正常', '預測故障'],
                   yticklabels=['實際正常', '實際故障'],
                   ax=axes[0, 0])
        axes[0, 0].set_title('混淆矩陣', fontsize=12, fontweight='bold')
        
        # 2. ROC曲線
        fpr, tpr, _ = roc_curve(y_true, y_pred_proba)
        roc_auc = roc_auc_score(y_true, y_pred_proba)
        
        axes[0, 1].plot(fpr, tpr, linewidth=2.5, 
                       label=f'ROC曲線 (AUC={roc_auc:.4f})')
        axes[0, 1].plot([0, 1], [0, 1], 'k--', linewidth=1.5,
                       label='隨機分類')
        axes[0, 1].set_xlabel('偽陽性率', fontsize=11)
        axes[0, 1].set_ylabel('真陽性率', fontsize=11)
        axes[0, 1].set_title('ROC曲線', fontsize=12, fontweight='bold')
        axes[0, 1].legend(fontsize=10)
        axes[0, 1].grid(True, alpha=0.3)
        
        # 3. Precision-Recall曲線
        precision, recall, _ = precision_recall_curve(y_true, y_pred_proba)
        
        axes[1, 0].plot(recall, precision, linewidth=2.5)
        axes[1, 0].set_xlabel('召回率', fontsize=11)
        axes[1, 0].set_ylabel('精確率', fontsize=11)
        axes[1, 0].set_title('Precision-Recall曲線', fontsize=12, fontweight='bold')
        axes[1, 0].grid(True, alpha=0.3)
        
        # 4. 預測機率分布
        axes[1, 1].hist(y_pred_proba[y_true == 0], bins=50, 
                       alpha=0.6, label='正常', color='blue')
        axes[1, 1].hist(y_pred_proba[y_true == 1], bins=50,
                       alpha=0.6, label='故障', color='red')
        axes[1, 1].set_xlabel('預測機率', fontsize=11)
        axes[1, 1].set_ylabel('樣本數', fontsize=11)
        axes[1, 1].set_title('預測機率分布', fontsize=12, fontweight='bold')
        axes[1, 1].legend(fontsize=10)
        axes[1, 1].grid(True, alpha=0.3)
        
        plt.tight_layout()
        plt.savefig('model_evaluation.png', dpi=300, bbox_inches='tight')
        plt.show()
    
    def _plot_feature_importance(self):
        """繪製特徵重要性圖表"""
        importances = self.model.feature_importances_
        indices = np.argsort(importances)[::-1]
        
        plt.figure(figsize=(12, 6))
        plt.bar(range(len(importances)), importances[indices])
        plt.xticks(range(len(importances)),
                  [self.feature_names[i] for i in indices],
                  rotation=45, ha='right')
        plt.xlabel('特徵', fontsize=12)
        plt.ylabel('重要性分數', fontsize=12)
        plt.title('隨機森林特徵重要性排名', fontsize=14, fontweight='bold')
        plt.grid(True, alpha=0.3, axis='y')
        plt.tight_layout()
        plt.savefig('feature_importance.png', dpi=300, bbox_inches='tight')
        plt.show()

# ============================================================================
# 執行完整的預測性維護專案
# ============================================================================

print("初始化預測性維護系統...")
pm_system = PredictiveMaintenanceSystem(random_state=42)

# 1. 生成模擬資料
print("\n生成模擬設備監測資料...")
data = pm_system.generate_synthetic_data(n_samples=2000)
print(f"資料集大小: {data.shape}")

# 2. 探索性資料分析
pm_system.exploratory_data_analysis(data)

# 3. 特徵工程
data = pm_system.feature_engineering(data)

# 4. 準備訓練資料
X_train, X_test, y_train, y_test = pm_system.prepare_training_data(data)

# 5. 訓練模型
pm_system.train_model(X_train, y_train, tune_hyperparameters=False)

# 6. 評估模型
results = pm_system.evaluate_model(X_test, y_test)

print("\n" + "="*80)
print("預測性維護系統建構完成!")
print("="*80)

這個完整的預測性維護系統展示了從資料準備、探索性分析、特徵工程、模型訓練到評估的完整工作流程。程式碼包含詳細註解,並提供豐富的視覺化結果。

@startuml
!define PLANTUML_FORMAT svg
!theme _none_

skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100

title 預測性維護機器學習完整流程

start

:問題定義;
note right
  預測時間窗口
  業務影響評估
  成功指標設定
end note

:資料收集;
note right
  感測器資料
  維護記錄
  設備規格
end note

:探索性資料分析;
note right
  資料品質檢查
  分布特性探索
  相關性分析
end note

:資料清洗;
note right
  缺失值處理
  異常值偵測
  資料驗證
end note

:特徵工程;
note right
  衍生特徵創造
  交互作用項
  領域知識融入
end note

:資料分割;
note right
  訓練測試分割
  交叉驗證準備
  分層抽樣
end note

:特徵標準化;
note right
  Z-score標準化
  避免資料洩漏
end note

:模型訓練;
note right
  演算法選擇
  超參數調校
  交叉驗證
end note

:模型評估;
note right
  混淆矩陣
  ROC AUC
  特徵重要性
end note

if (效能符合要求?) then (是)
  :模型部署;
  note right
    即時預測服務
    監控儀表板
    預警系統
  end note
  
  :持續監控;
  note right
    效能追蹤
    概念漂移偵測
    定期重訓
  end note
else (否)
  :改進策略;
  note right
    調整特徵
    嘗試其他演算法
    收集更多資料
  end note
  -> 返回特徵工程;
endif

stop

@enduml

資料視覺化與溝通策略

資料視覺化將數值資訊轉換為視覺形式,利用人類視覺系統的強大模式識別能力快速傳達洞察。優秀的視覺化設計不僅美觀,更重要的是準確、清晰且能引導觀眾聚焦關鍵資訊。

清晰簡潔是視覺化設計的首要原則。每個圖表應該有單一明確的訊息,避免在同一張圖中塞入過多資訊導致觀眾無所適從。移除不必要的視覺元素如過度裝飾的邊框、3D效果、冗餘的格線,遵循資料墨水比的原則最大化用於呈現資料的墨水比例。標題應該清楚說明圖表內容,座標軸標籤包含單位,圖例放置在不妨礙資料呈現的位置。

準確性與完整性確保視覺化忠實反映原始資料而不誤導觀眾。座標軸應該從零開始避免誇大差異,除非有正當理由且明確標註。比例尺的選擇影響視覺印象,對數尺度適合跨越多個數量級的資料,但需要觀眾具備相應的解讀能力。資料的不確定性如誤差範圍、信賴區間應該視覺化呈現,避免給予過度確定的印象。

顏色與視覺編碼的使用需要遵循感知原理與文化慣例。色彩應該有意義而非單純裝飾,如用紅色表示警示、綠色表示正常、藍色表示中性資訊。連續型資料使用循序色階,類別型資料使用區別色階。避免使用紅綠配色因為色盲者無法區分。保持一致的編碼邏輯,相同概念在不同圖表中使用相同顏色。

儀表板設計架構

儀表板整合多個視覺化元件提供資訊的全局觀,支援監控、分析與決策。有效的儀表板設計需要清晰的資訊階層、合理的版面配置以及一致的視覺風格。

資訊階層將內容依重要性與細節程度組織成多個層級。最高層提供關鍵績效指標的鳥瞰,如總營收、客戶滿意度、庫存週轉率。中間層展開趨勢分析與比較,如各產品線的銷售成長、區域別的市場佔有率。最底層提供明細資料與深入分析,支援問題診斷與根因分析。

@startuml
!define PLANTUML_FORMAT svg
!theme _none_

skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100

title 儀表板資訊架構

package "概覽層" {
  [關鍵績效指標卡]
  [異常警示面板]
  [整體趨勢圖]
}

package "分析層" {
  [維度分解分析]
  [時間序列比較]
  [群組對比圖]
}

package "明細層" {
  [交易明細表]
  [詳細報表]
  [資料匯出功能]
}

package "互動控制" {
  [時間篩選器]
  [維度篩選器]
  [鑽取功能]
}

[關鍵績效指標卡] --> [維度分解分析]
[異常警示面板] --> [詳細報表]
[整體趨勢圖] --> [時間序列比較]

[維度分解分析] --> [交易明細表]
[時間序列比較] --> [交易明細表]
[群組對比圖] --> [交易明細表]

[時間篩選器] ..> [概覽層]
[維度篩選器] ..> [分析層]
[鑽取功能] ..> [明細層]

note right of [關鍵績效指標卡]
  大字體數值
  趨勢指示
  目標達成率
end note

note right of [互動控制]
  使用者自訂視圖
  即時資料更新
  多維度探索
end note

@enduml

資料溝通技巧

將分析結果有效傳達給決策者是資料科學家的重要技能。資料說故事結合分析的嚴謹性與溝通的藝術性,是影響組織決策的關鍵能力。

理解觀眾是有效溝通的起點。高階主管關注策略方向與業務影響,需要高階摘要與明確建議。中階管理者需要理解執行細節與資源需求。技術團隊對方法論有興趣,需要完整的技術文件支援驗證與複製。

建構敘事結構提供清晰的邏輯流程引導觀眾理解。第一幕建立情境說明為何這個分析重要,提出需要回答的問題。第二幕展示分析過程與發現,透過資料與視覺化支撐論點。第三幕提出結論與建議,說明應該採取什麼行動以及預期的影響。

量化商業影響將技術結果轉譯為管理語言。預測模型的準確率提升百分之五聽起來不錯,但換算成減少多少損失或增加多少收益更有說服力。連結分析結果與組織的策略目標如營收成長、成本降低、風險管理,展現資料科學的商業價值。

實踐整合與未來展望

資料科學的實務應用需要統計理論、程式技能、領域知識以及溝通能力的綜合運用。本文建構從機率統計基礎、迴歸分析方法、資料品質管理、模式識別技術到視覺化溝通的完整知識體系,並透過預測性維護案例展示實際應用流程。

從技術架構來看,資料科學專案的成功高度依賴完善的資料基礎建設。資料倉儲整合來自不同來源的資料提供分析所需的全局視角,資料管道自動化收集、清洗與轉換流程確保時效性,元資料管理記錄資料的血緣與定義支援理解與使用。

模型開發只是資料科學專案的一部分,部署與維運同樣重要卻常被低估。將實驗性的模型轉換為可靠的生產系統需要軟體工程的最佳實踐,包括版本控制、自動化測試、持續整合與部署。監控系統追蹤模型效能、資料品質與系統健康狀況,及時發現與處理問題。

展望未來,自動化機器學習降低技術門檻讓更多人能運用資料科學工具。AutoML自動化特徵工程、模型選擇與超參數調校,但領域知識與問題定義仍然需要人類專家。可解釋AI技術持續進展提升模型的透明度與可信度。邊緣運算將智慧推向資料產生的源頭,支援即時決策與隱私保護。

台灣企業在數位轉型的過程中,資料科學能力的建立是關鍵投資。從建立資料基礎建設、培養技術人才、建立治理機制到塑造資料驅動文化,需要長期的承諾與投入。成功的案例展現資料科學在提升營運效率、優化決策品質、創造新商業模式的潛力。技術本身只是工具,真正的價值來自於將技術應用於解決實際問題並創造商業影響。