產品銷售資料分析是商業智慧的核心應用領域,直接影響企業的營收成長與市場競爭力。傳統的銷售報表僅提供總量指標,難以深入理解銷售績效的驅動因素與區域差異。資料科學方法透過多維度分析、統計建模與機器學習技術,從海量銷售資料中提取可行動的商業洞察,支援精準的策略決策。

銷售資料具有多維度特性,包含產品、區域、時間、客戶等多個分析維度。這些維度之間存在複雜的交互作用,例如某產品在特定區域的季節性銷售模式,或不同客戶群體對產品組合的偏好差異。多維分析技術如 OLAP 提供靈活的資料探索能力,支援切片、切塊、鑽取等操作,從不同角度檢視銷售績效。

銷售預測是資料分析的重要應用。準確的銷售預測幫助企業最佳化庫存管理、生產規劃與人力配置。時間序列模型捕捉歷史銷售的趨勢與季節性,機器學習演算法整合多個影響因素建立預測模型。預測的準確性直接影響企業的營運效率與成本控制,是衡量資料分析價值的關鍵指標。

客戶價值分析識別高價值客戶與潛力客戶,指引精準行銷與客戶關係管理策略。RFM 分析從最近購買時間、購買頻率與購買金額三個維度評估客戶價值。客戶區隔將客戶群體劃分為不同的價值層級,每個區隔採用差異化的行銷策略與服務水準。這種精準行銷相較於大眾行銷,能夠顯著提升投資報酬率。

本文將建立完整的產品銷售資料科學分析框架。從資料收集與清理開始,建立標準化的資料倉儲結構。執行多維度探索性分析,從產品、區域、時間等角度檢視銷售績效。實作 OLAP 多維分析引擎,支援靈活的資料探索與鑽取。建立時間序列預測模型預估未來銷售趨勢。執行 RFM 分析與客戶區隔,識別高價值客戶。最後整合視覺化儀表板,提供互動式的商業洞察與決策支援。

銷售資料多維度探索性分析

銷售資料的探索性分析是理解業務現況的第一步。透過描述統計、分佈分析與相關性研究,分析師能夠識別銷售績效的基本特徵、異常模式與潛在機會。多維度分析從產品、區域、時間等不同角度檢視資料,揭示隱藏在總量指標背後的結構性差異。

產品維度分析評估不同產品線的銷售貢獻與成長潛力。銷售額分佈揭示暢銷產品與長尾產品的比例,帕雷托法則通常顯示少數產品貢獻多數銷售額。毛利率分析識別高利潤產品,指引產品組合最佳化策略。產品生命週期分析追蹤產品從導入、成長、成熟到衰退的演變,支援新產品開發與退出決策。

區域維度分析比較不同市場的銷售表現與成長動能。區域銷售額排名識別核心市場與新興市場,市場佔有率評估競爭地位。區域成長率差異反映市場潛力與競爭強度,高成長市場值得增加資源投入。區域盈利能力分析考量銷售額、成本與利潤的綜合表現,識別高效市場與需要改善的市場。

時間維度分析揭示銷售的趨勢與季節性模式。趨勢分析識別長期的成長或衰退走勢,季節性分解提取週期性波動模式。同比與環比分析比較不同時期的績效變化,識別異常波動與結構性轉變。時間維度分析為銷售預測提供重要輸入,幫助企業預判未來需求。

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from typing import Dict, List, Tuple
from datetime import datetime
import warnings
warnings.filterwarnings('ignore')

class SalesDataAnalyzer:
    """
    銷售資料多維度分析系統
    提供產品、區域、時間等多維度的探索性分析功能
    """
    
    def __init__(self):
        """
        初始化銷售資料分析器
        """
        self.sales_data = None
        self.product_summary = None
        self.region_summary = None
        self.time_summary = None
        
        # 設定視覺化樣式
        plt.style.use('seaborn-v0_8-whitegrid')
        sns.set_palette("husl")
    
    def load_sales_data(self, data_dict: Dict[str, List]) -> pd.DataFrame:
        """
        載入銷售資料
        
        參數:
            data_dict: 包含銷售記錄的字典
            
        回傳:
            處理後的 DataFrame
        """
        self.sales_data = pd.DataFrame(data_dict)
        
        # 確保必要欄位存在
        required_columns = ['產品', '區域', '季度', '銷售額', '利潤']
        missing_columns = [col for col in required_columns if col not in self.sales_data.columns]
        
        if missing_columns:
            print(f"警告: 缺少必要欄位 {missing_columns}")
        
        print(f"成功載入 {len(self.sales_data)} 筆銷售記錄")
        print(f"資料欄位: {list(self.sales_data.columns)}")
        
        return self.sales_data
    
    def data_quality_check(self) -> Dict:
        """
        執行資料品質檢查
        
        回傳:
            品質檢查結果
        """
        quality_report = {}
        
        print("\n資料品質檢查")
        print("=" * 60)
        
        # 檢查缺失值
        missing_counts = self.sales_data.isnull().sum()
        print("\n缺失值統計:")
        for col in self.sales_data.columns:
            if missing_counts[col] > 0:
                print(f"  {col}: {missing_counts[col]} ({missing_counts[col]/len(self.sales_data)*100:.2f}%)")
        
        quality_report['missing_values'] = missing_counts.to_dict()
        
        # 檢查重複記錄
        duplicates = self.sales_data.duplicated().sum()
        print(f"\n重複記錄: {duplicates} 筆")
        quality_report['duplicates'] = duplicates
        
        # 檢查數值範圍
        numeric_columns = self.sales_data.select_dtypes(include=[np.number]).columns
        print("\n數值範圍檢查:")
        for col in numeric_columns:
            min_val = self.sales_data[col].min()
            max_val = self.sales_data[col].max()
            print(f"  {col}: [{min_val:,.0f}, {max_val:,.0f}]")
            
            # 檢查異常負值
            if min_val < 0:
                negative_count = (self.sales_data[col] < 0).sum()
                print(f"    警告: {col} 包含 {negative_count} 個負值")
        
        return quality_report
    
    def product_dimension_analysis(self) -> pd.DataFrame:
        """
        產品維度分析
        
        回傳:
            產品統計摘要
        """
        print("\n產品維度分析")
        print("=" * 60)
        
        # 按產品彙總統計
        self.product_summary = self.sales_data.groupby('產品').agg({
            '銷售額': ['sum', 'mean', 'std', 'count'],
            '利潤': ['sum', 'mean']
        }).round(2)
        
        # 計算利潤率
        self.product_summary['利潤率'] = (
            self.product_summary[('利潤', 'sum')] / 
            self.product_summary[('銷售額', 'sum')] * 100
        ).round(2)
        
        # 計算銷售額佔比
        total_sales = self.product_summary[('銷售額', 'sum')].sum()
        self.product_summary['銷售額佔比'] = (
            self.product_summary[('銷售額', 'sum')] / total_sales * 100
        ).round(2)
        
        print("\n產品統計摘要:")
        print(self.product_summary)
        
        # 視覺化產品銷售分佈
        fig, axes = plt.subplots(1, 3, figsize=(18, 5))
        
        # 銷售額長條圖
        sales_by_product = self.product_summary[('銷售額', 'sum')].sort_values(ascending=False)
        sales_by_product.plot(kind='bar', ax=axes[0], color='skyblue', edgecolor='black')
        axes[0].set_title('各產品銷售額', fontsize=14)
        axes[0].set_xlabel('產品')
        axes[0].set_ylabel('銷售額')
        axes[0].grid(True, alpha=0.3)
        plt.sca(axes[0])
        plt.xticks(rotation=0)
        
        # 利潤率長條圖
        profit_margin = self.product_summary['利潤率'].sort_values(ascending=False)
        profit_margin.plot(kind='bar', ax=axes[1], color='lightgreen', edgecolor='black')
        axes[1].set_title('各產品利潤率', fontsize=14)
        axes[1].set_xlabel('產品')
        axes[1].set_ylabel('利潤率 (%)')
        axes[1].grid(True, alpha=0.3)
        plt.sca(axes[1])
        plt.xticks(rotation=0)
        
        # 銷售額佔比圓餅圖
        sales_share = self.product_summary['銷售額佔比']
        axes[2].pie(sales_share, labels=sales_share.index, autopct='%1.1f%%', startangle=90)
        axes[2].set_title('產品銷售額佔比', fontsize=14)
        
        plt.tight_layout()
        plt.savefig('/mnt/user-data/outputs/product_analysis.png', dpi=300, bbox_inches='tight')
        plt.close()
        
        print("\n產品分析圖表已儲存至 product_analysis.png")
        
        return self.product_summary
    
    def region_dimension_analysis(self) -> pd.DataFrame:
        """
        區域維度分析
        
        回傳:
            區域統計摘要
        """
        print("\n區域維度分析")
        print("=" * 60)
        
        # 按區域彙總統計
        self.region_summary = self.sales_data.groupby('區域').agg({
            '銷售額': ['sum', 'mean', 'std', 'count'],
            '利潤': ['sum', 'mean']
        }).round(2)
        
        # 計算利潤率
        self.region_summary['利潤率'] = (
            self.region_summary[('利潤', 'sum')] / 
            self.region_summary[('銷售額', 'sum')] * 100
        ).round(2)
        
        # 計算銷售額佔比
        total_sales = self.region_summary[('銷售額', 'sum')].sum()
        self.region_summary['銷售額佔比'] = (
            self.region_summary[('銷售額', 'sum')] / total_sales * 100
        ).round(2)
        
        print("\n區域統計摘要:")
        print(self.region_summary)
        
        # 視覺化區域銷售表現
        fig, axes = plt.subplots(2, 2, figsize=(14, 10))
        
        # 區域銷售額
        sales_by_region = self.region_summary[('銷售額', 'sum')].sort_values(ascending=False)
        sales_by_region.plot(kind='barh', ax=axes[0, 0], color='coral', edgecolor='black')
        axes[0, 0].set_title('各區域銷售額', fontsize=14)
        axes[0, 0].set_xlabel('銷售額')
        axes[0, 0].set_ylabel('區域')
        axes[0, 0].grid(True, alpha=0.3)
        
        # 區域利潤
        profit_by_region = self.region_summary[('利潤', 'sum')].sort_values(ascending=False)
        profit_by_region.plot(kind='barh', ax=axes[0, 1], color='lightblue', edgecolor='black')
        axes[0, 1].set_title('各區域利潤', fontsize=14)
        axes[0, 1].set_xlabel('利潤')
        axes[0, 1].set_ylabel('區域')
        axes[0, 1].grid(True, alpha=0.3)
        
        # 區域利潤率
        profit_margin_region = self.region_summary['利潤率'].sort_values(ascending=False)
        profit_margin_region.plot(kind='bar', ax=axes[1, 0], color='lightgreen', edgecolor='black')
        axes[1, 0].set_title('各區域利潤率', fontsize=14)
        axes[1, 0].set_xlabel('區域')
        axes[1, 0].set_ylabel('利潤率 (%)')
        axes[1, 0].grid(True, alpha=0.3)
        plt.sca(axes[1, 0])
        plt.xticks(rotation=45)
        
        # 區域銷售額分佈箱型圖
        self.sales_data.boxplot(column='銷售額', by='區域', ax=axes[1, 1])
        axes[1, 1].set_title('各區域銷售額分佈', fontsize=14)
        axes[1, 1].set_xlabel('區域')
        axes[1, 1].set_ylabel('銷售額')
        plt.sca(axes[1, 1])
        plt.xticks(rotation=45)
        
        plt.tight_layout()
        plt.savefig('/mnt/user-data/outputs/region_analysis.png', dpi=300, bbox_inches='tight')
        plt.close()
        
        print("\n區域分析圖表已儲存至 region_analysis.png")
        
        return self.region_summary
    
    def time_dimension_analysis(self) -> pd.DataFrame:
        """
        時間維度分析
        
        回傳:
            時間統計摘要
        """
        print("\n時間維度分析")
        print("=" * 60)
        
        # 按季度彙總統計
        self.time_summary = self.sales_data.groupby('季度').agg({
            '銷售額': ['sum', 'mean', 'std'],
            '利潤': ['sum', 'mean']
        }).round(2)
        
        # 計算季度成長率(如果有多個年度的資料)
        quarterly_sales = self.time_summary[('銷售額', 'sum')]
        
        print("\n季度統計摘要:")
        print(self.time_summary)
        
        # 視覺化時間趨勢
        fig, axes = plt.subplots(2, 1, figsize=(14, 10))
        
        # 季度銷售額趨勢
        quarterly_sales.plot(kind='line', ax=axes[0], marker='o', linewidth=2, markersize=8)
        axes[0].set_title('季度銷售額趨勢', fontsize=14)
        axes[0].set_xlabel('季度')
        axes[0].set_ylabel('銷售額')
        axes[0].grid(True, alpha=0.3)
        
        # 季度利潤趨勢
        quarterly_profit = self.time_summary[('利潤', 'sum')]
        quarterly_profit.plot(kind='line', ax=axes[1], marker='s', linewidth=2, markersize=8, color='green')
        axes[1].set_title('季度利潤趨勢', fontsize=14)
        axes[1].set_xlabel('季度')
        axes[1].set_ylabel('利潤')
        axes[1].grid(True, alpha=0.3)
        
        plt.tight_layout()
        plt.savefig('/mnt/user-data/outputs/time_analysis.png', dpi=300, bbox_inches='tight')
        plt.close()
        
        print("\n時間分析圖表已儲存至 time_analysis.png")
        
        return self.time_summary
    
    def cross_dimensional_analysis(self):
        """
        交叉維度分析
        分析產品與區域、產品與季度、區域與季度的交互效應
        """
        print("\n交叉維度分析")
        print("=" * 60)
        
        # 產品 x 區域交叉分析
        product_region_pivot = pd.pivot_table(
            self.sales_data,
            values='銷售額',
            index='產品',
            columns='區域',
            aggfunc='sum',
            fill_value=0
        )
        
        print("\n產品 x 區域 銷售額交叉表:")
        print(product_region_pivot)
        
        # 視覺化熱力圖
        fig, axes = plt.subplots(1, 2, figsize=(16, 6))
        
        # 產品 x 區域 熱力圖
        sns.heatmap(
            product_region_pivot,
            annot=True,
            fmt='.0f',
            cmap='YlOrRd',
            ax=axes[0],
            cbar_kws={'label': '銷售額'}
        )
        axes[0].set_title('產品 x 區域 銷售額熱力圖', fontsize=14)
        axes[0].set_xlabel('區域')
        axes[0].set_ylabel('產品')
        
        # 產品 x 季度 熱力圖
        product_quarter_pivot = pd.pivot_table(
            self.sales_data,
            values='銷售額',
            index='產品',
            columns='季度',
            aggfunc='sum',
            fill_value=0
        )
        
        sns.heatmap(
            product_quarter_pivot,
            annot=True,
            fmt='.0f',
            cmap='YlGnBu',
            ax=axes[1],
            cbar_kws={'label': '銷售額'}
        )
        axes[1].set_title('產品 x 季度 銷售額熱力圖', fontsize=14)
        axes[1].set_xlabel('季度')
        axes[1].set_ylabel('產品')
        
        plt.tight_layout()
        plt.savefig('/mnt/user-data/outputs/cross_dimensional_analysis.png', dpi=300, bbox_inches='tight')
        plt.close()
        
        print("\n交叉維度分析圖表已儲存至 cross_dimensional_analysis.png")
    
    def generate_executive_summary(self) -> str:
        """
        產生管理層摘要報告
        
        回傳:
            格式化的報告內容
        """
        report = "銷售資料分析管理層摘要\n"
        report += "=" * 60 + "\n\n"
        
        # 總體績效
        total_sales = self.sales_data['銷售額'].sum()
        total_profit = self.sales_data['利潤'].sum()
        overall_margin = (total_profit / total_sales * 100)
        
        report += "總體績效\n"
        report += "-" * 60 + "\n"
        report += f"總銷售額: ${total_sales:,.0f}\n"
        report += f"總利潤: ${total_profit:,.0f}\n"
        report += f"整體利潤率: {overall_margin:.2f}%\n\n"
        
        # 產品績效
        if self.product_summary is not None:
            report += "產品績效\n"
            report += "-" * 60 + "\n"
            top_product = self.product_summary[('銷售額', 'sum')].idxmax()
            top_sales = self.product_summary.loc[top_product, ('銷售額', 'sum')]
            report += f"暢銷產品: {top_product} (銷售額 ${top_sales:,.0f})\n"
            
            best_margin_product = self.product_summary['利潤率'].idxmax()
            best_margin = self.product_summary.loc[best_margin_product, '利潤率']
            report += f"最高利潤率產品: {best_margin_product} ({best_margin:.2f}%)\n\n"
        
        # 區域績效
        if self.region_summary is not None:
            report += "區域績效\n"
            report += "-" * 60 + "\n"
            top_region = self.region_summary[('銷售額', 'sum')].idxmax()
            top_region_sales = self.region_summary.loc[top_region, ('銷售額', 'sum')]
            report += f"最佳銷售區域: {top_region} (銷售額 ${top_region_sales:,.0f})\n"
            
            best_margin_region = self.region_summary['利潤率'].idxmax()
            best_region_margin = self.region_summary.loc[best_margin_region, '利潤率']
            report += f"最高利潤率區域: {best_margin_region} ({best_region_margin:.2f}%)\n\n"
        
        # 策略建議
        report += "策略建議\n"
        report += "-" * 60 + "\n"
        report += "1. 加強暢銷產品的市場推廣與通路拓展\n"
        report += "2. 最佳化產品組合,提升高利潤率產品的銷售佔比\n"
        report += "3. 針對表現不佳的區域,檢討行銷策略與通路效能\n"
        report += "4. 分析季節性模式,提前規劃庫存與促銷活動\n"
        report += "5. 建立客戶區隔,實施差異化的行銷與服務策略\n"
        
        return report

# 建立範例銷售資料
sample_sales_data = {
    '產品': ['產品A', '產品A', '產品A', '產品A', '產品B', '產品B', '產品B', 
            '產品B', '產品B', '產品B', '產品C', '產品C', '產品C', '產品C',
            '產品A', '產品A', '產品B', '產品B', '產品C', '產品C'],
    '區域': ['南部', '南部', '西部', '東部', '北部', '北部', '東部', 
            '東部', '西部', '南部', '北部', '西部', '南部', '西部',
            '東部', '南部', '西部', '東部', '北部', '南部'],
    '季度': ['Q3', 'Q4', 'Q4', 'Q1', 'Q3', 'Q4', 'Q2', 
            'Q4', 'Q3', 'Q4', 'Q3', 'Q4', 'Q2', 'Q1',
            'Q4', 'Q1', 'Q3', 'Q1', 'Q1', 'Q3'],
    '銷售額': [25307, 45732, 34032, 12672, 37793, 17850, 22620,
              27875, 26730, 23704, 46525, 41883, 47567, 32459,
              27057, 21931, 37087, 38261, 26589, 43746],
    '利潤': [7592, 13720, 10210, 3802, 11338, 5355, 6786,
            8363, 8019, 7111, 13958, 12565, 14270, 9738,
            8117, 6579, 11126, 11478, 7977, 13124]
}

# 使用範例
if __name__ == "__main__":
    # 建立分析器
    analyzer = SalesDataAnalyzer()
    
    # 載入資料
    analyzer.load_sales_data(sample_sales_data)
    
    # 資料品質檢查
    analyzer.data_quality_check()
    
    # 多維度分析
    analyzer.product_dimension_analysis()
    analyzer.region_dimension_analysis()
    analyzer.time_dimension_analysis()
    analyzer.cross_dimensional_analysis()
    
    # 產生管理層報告
    print("\n" + analyzer.generate_executive_summary())

這個銷售資料分析系統提供全面的多維度分析功能。系統首先載入銷售資料並執行品質檢查,確保資料的完整性與合理性。產品維度分析彙總各產品的銷售額、利潤與利潤率,識別暢銷產品與高利潤產品。區域維度分析比較不同市場的績效表現,識別核心市場與需要改善的區域。時間維度分析揭示季度銷售趨勢,幫助預測未來需求。

交叉維度分析透過樞紐分析表與熱力圖,揭示產品與區域、產品與季度的交互效應。這種多維度視角幫助管理者理解不同因素的組合效應,例如某產品在特定區域的表現特別突出。管理層摘要整合所有分析結果,提供簡潔的績效概覽與策略建議,支援高階決策。

@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

start

:載入銷售資料;

partition "資料品質檢查" {
    :缺失值檢測;
    :重複記錄識別;
    :數值範圍驗證;
}

partition "單維度分析" {
    :產品維度\n銷售額、利潤、利潤率;
    :區域維度\n市場績效比較;
    :時間維度\n趨勢與季節性;
}

partition "交叉維度分析" {
    :產品 x 區域\n熱力圖分析;
    :產品 x 季度\n時間序列比較;
    :區域 x 季度\n市場動態;
}

:識別關鍵洞察\n暢銷產品、優勢市場;

:產生管理層報告\n績效總結與建議;

stop

@enduml

這個流程圖描述銷售資料多維度分析的系統化流程。從載入資料與品質檢查開始,確保分析基礎的可靠性。單維度分析從產品、區域、時間三個角度分別檢視績效,建立基礎理解。交叉維度分析揭示不同因素的交互效應,提供更深入的洞察。最後識別關鍵發現,產生面向管理層的簡潔報告,支援策略決策。

銷售預測模型與客戶價值分析

銷售預測是資料分析的核心應用,準確的預測幫助企業最佳化資源配置與提升營運效率。時間序列預測模型捕捉歷史銷售的趨勢與季節性,外推至未來時期。機器學習模型整合多個影響因素如產品特性、市場條件、促銷活動等,建立多變量預測模型。預測準確度透過回測與交叉驗證評估,持續最佳化模型參數。

客戶價值分析識別高價值客戶與潛力客戶,指引精準行銷策略。RFM 分析從三個維度評估客戶價值:最近購買時間反映客戶的活躍程度,購買頻率衡量忠誠度,購買金額代表貢獻度。這三個指標的組合將客戶劃分為不同的價值區隔,每個區隔採用差異化的行銷與服務策略。

客戶生命週期價值預估客戶在整個關係期間的總貢獻。CLV 模型整合購買頻率、平均訂單金額與客戶留存率,評估獲取與維繫客戶的經濟價值。高 CLV 客戶值得投入更多資源,低 CLV 客戶則採用成本效益導向的策略。CLV 分析支援客戶獲取成本的合理化,確保行銷投資的正向報酬。

客戶流失預測識別有流失風險的客戶,支援預防性的挽留措施。流失預測模型整合客戶的交易行為、互動記錄與人口統計資訊,計算流失機率。高風險客戶觸發自動化的挽留流程,如專屬優惠、客服關懷等。主動的流失預防相較於被動的客戶獲取,具有更高的成本效益。

import pandas as pd
import numpy as np
from sklearn.ensemble import RandomForestRegressor, GradientBoostingClassifier
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
from sklearn.preprocessing import LabelEncoder
import matplotlib.pyplot as plt
import seaborn as sns
from datetime import datetime, timedelta
import warnings
warnings.filterwarnings('ignore')

class SalesForecaster:
    """
    銷售預測與客戶價值分析系統
    提供銷售預測、RFM 分析與客戶區隔功能
    """
    
    def __init__(self):
        """
        初始化銷售預測系統
        """
        self.forecast_model = None
        self.rfm_data = None
        self.customer_segments = None
    
    def prepare_forecast_features(
        self, 
        sales_data: pd.DataFrame
    ) -> pd.DataFrame:
        """
        準備預測模型的特徵
        
        參數:
            sales_data: 銷售資料
            
        回傳:
            特徵矩陣
        """
        # 編碼類別特徵
        le_product = LabelEncoder()
        le_region = LabelEncoder()
        le_quarter = LabelEncoder()
        
        features = sales_data.copy()
        features['產品_編碼'] = le_product.fit_transform(features['產品'])
        features['區域_編碼'] = le_region.fit_transform(features['區域'])
        features['季度_編碼'] = le_quarter.fit_transform(features['季度'])
        
        # 建立衍生特徵
        # 利潤率
        features['利潤率'] = features['利潤'] / features['銷售額']
        
        # 歷史平均銷售額(按產品)
        product_avg = features.groupby('產品')['銷售額'].transform('mean')
        features['產品歷史平均'] = product_avg
        
        # 歷史平均銷售額(按區域)
        region_avg = features.groupby('區域')['銷售額'].transform('mean')
        features['區域歷史平均'] = region_avg
        
        print("特徵工程完成")
        print(f"  總特徵數: {len(features.columns)}")
        
        return features
    
    def train_forecast_model(
        self,
        features: pd.DataFrame,
        target_col: str = '銷售額'
    ):
        """
        訓練銷售預測模型
        
        參數:
            features: 特徵矩陣
            target_col: 目標變數欄位
        """
        # 選擇特徵欄位
        feature_cols = [
            '產品_編碼', '區域_編碼', '季度_編碼',
            '利潤率', '產品歷史平均', '區域歷史平均'
        ]
        
        X = features[feature_cols]
        y = features[target_col]
        
        # 分割訓練與測試集
        X_train, X_test, y_train, y_test = train_test_split(
            X, y, test_size=0.2, random_state=42
        )
        
        print(f"\n訓練銷售預測模型...")
        print(f"  訓練集: {len(X_train)} 筆")
        print(f"  測試集: {len(X_test)} 筆")
        
        # 訓練隨機森林模型
        self.forecast_model = RandomForestRegressor(
            n_estimators=100,
            max_depth=10,
            random_state=42,
            n_jobs=-1
        )
        
        self.forecast_model.fit(X_train, y_train)
        
        # 評估模型
        y_pred = self.forecast_model.predict(X_test)
        
        mae = mean_absolute_error(y_test, y_pred)
        rmse = np.sqrt(mean_squared_error(y_test, y_pred))
        r2 = r2_score(y_test, y_pred)
        
        print(f"\n模型評估結果:")
        print(f"  MAE: ${mae:,.2f}")
        print(f"  RMSE: ${rmse:,.2f}")
        print(f"  R²: {r2:.4f}")
        
        # 特徵重要性
        feature_importance = pd.DataFrame({
            '特徵': feature_cols,
            '重要性': self.forecast_model.feature_importances_
        }).sort_values('重要性', ascending=False)
        
        print(f"\n特徵重要性:")
        print(feature_importance)
        
        # 視覺化預測結果
        fig, axes = plt.subplots(1, 2, figsize=(14, 5))
        
        # 預測 vs 實際
        axes[0].scatter(y_test, y_pred, alpha=0.5)
        axes[0].plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'r--', lw=2)
        axes[0].set_xlabel('實際銷售額')
        axes[0].set_ylabel('預測銷售額')
        axes[0].set_title('預測 vs 實際銷售額')
        axes[0].grid(True, alpha=0.3)
        
        # 特徵重要性
        feature_importance.plot(x='特徵', y='重要性', kind='barh', ax=axes[1], color='skyblue')
        axes[1].set_title('特徵重要性')
        axes[1].set_xlabel('重要性')
        
        plt.tight_layout()
        plt.savefig('/mnt/user-data/outputs/forecast_model.png', dpi=300, bbox_inches='tight')
        plt.close()
        
        print("\n預測模型圖表已儲存至 forecast_model.png")
    
    def rfm_analysis(
        self,
        transaction_data: pd.DataFrame,
        customer_col: str = '客戶ID',
        date_col: str = '交易日期',
        amount_col: str = '銷售額',
        reference_date: datetime = None
    ) -> pd.DataFrame:
        """
        執行 RFM 分析
        
        參數:
            transaction_data: 交易資料
            customer_col: 客戶ID 欄位
            date_col: 交易日期欄位
            amount_col: 交易金額欄位
            reference_date: 參考日期(計算最近購買時間)
            
        回傳:
            RFM 分析結果
        """
        print("\n執行 RFM 分析...")
        
        # 設定參考日期
        if reference_date is None:
            reference_date = transaction_data[date_col].max() + timedelta(days=1)
        
        # 計算 RFM 指標
        rfm = transaction_data.groupby(customer_col).agg({
            date_col: lambda x: (reference_date - x.max()).days,  # Recency
            customer_col: 'count',  # Frequency
            amount_col: 'sum'  # Monetary
        })
        
        rfm.columns = ['Recency', 'Frequency', 'Monetary']
        
        # 計算 RFM 分數(1-5 分)
        rfm['R_Score'] = pd.qcut(rfm['Recency'], 5, labels=[5, 4, 3, 2, 1])
        rfm['F_Score'] = pd.qcut(rfm['Frequency'].rank(method='first'), 5, labels=[1, 2, 3, 4, 5])
        rfm['M_Score'] = pd.qcut(rfm['Monetary'], 5, labels=[1, 2, 3, 4, 5])
        
        # 計算 RFM 綜合分數
        rfm['RFM_Score'] = (
            rfm['R_Score'].astype(int) +
            rfm['F_Score'].astype(int) +
            rfm['M_Score'].astype(int)
        )
        
        self.rfm_data = rfm
        
        print(f"\nRFM 分析完成")
        print(f"  分析客戶數: {len(rfm)}")
        print(f"\nRFM 統計摘要:")
        print(rfm.describe())
        
        # 視覺化 RFM 分佈
        fig, axes = plt.subplots(2, 2, figsize=(14, 10))
        
        # Recency 分佈
        rfm['Recency'].hist(bins=30, ax=axes[0, 0], edgecolor='black')
        axes[0, 0].set_title('Recency 分佈', fontsize=14)
        axes[0, 0].set_xlabel('最近購買天數')
        axes[0, 0].set_ylabel('客戶數')
        
        # Frequency 分佈
        rfm['Frequency'].hist(bins=30, ax=axes[0, 1], edgecolor='black', color='green')
        axes[0, 1].set_title('Frequency 分佈', fontsize=14)
        axes[0, 1].set_xlabel('購買次數')
        axes[0, 1].set_ylabel('客戶數')
        
        # Monetary 分佈
        rfm['Monetary'].hist(bins=30, ax=axes[1, 0], edgecolor='black', color='orange')
        axes[1, 0].set_title('Monetary 分佈', fontsize=14)
        axes[1, 0].set_xlabel('購買金額')
        axes[1, 0].set_ylabel('客戶數')
        
        # RFM 綜合分數分佈
        rfm['RFM_Score'].hist(bins=15, ax=axes[1, 1], edgecolor='black', color='purple')
        axes[1, 1].set_title('RFM 綜合分數分佈', fontsize=14)
        axes[1, 1].set_xlabel('RFM 分數')
        axes[1, 1].set_ylabel('客戶數')
        
        plt.tight_layout()
        plt.savefig('/mnt/user-data/outputs/rfm_analysis.png', dpi=300, bbox_inches='tight')
        plt.close()
        
        print("\nRFM 分析圖表已儲存至 rfm_analysis.png")
        
        return rfm
    
    def customer_segmentation(self) -> pd.DataFrame:
        """
        基於 RFM 分析進行客戶區隔
        
        回傳:
            客戶區隔結果
        """
        if self.rfm_data is None:
            print("錯誤: 尚未執行 RFM 分析")
            return None
        
        print("\n執行客戶區隔...")
        
        # 定義區隔規則
        def segment_customer(row):
            r_score = int(row['R_Score'])
            f_score = int(row['F_Score'])
            m_score = int(row['M_Score'])
            
            # 高價值客戶(Champions)
            if r_score >= 4 and f_score >= 4 and m_score >= 4:
                return '冠軍客戶'
            # 忠誠客戶(Loyal Customers)
            elif r_score >= 3 and f_score >= 4:
                return '忠誠客戶'
            # 潛力客戶(Potential Loyalists)
            elif r_score >= 3 and f_score >= 2 and m_score >= 3:
                return '潛力客戶'
            # 新客戶(New Customers)
            elif r_score >= 4 and f_score <= 2:
                return '新客戶'
            # 需要關注(At Risk)
            elif r_score <= 2 and f_score >= 3:
                return '風險客戶'
            # 休眠客戶(Hibernating)
            elif r_score <= 2 and f_score <= 2:
                return '休眠客戶'
            else:
                return '一般客戶'
        
        self.rfm_data['客戶區隔'] = self.rfm_data.apply(segment_customer, axis=1)
        
        # 統計各區隔的客戶數與貢獻
        segment_summary = self.rfm_data.groupby('客戶區隔').agg({
            'Recency': 'mean',
            'Frequency': 'mean',
            'Monetary': ['count', 'sum', 'mean']
        }).round(2)
        
        print("\n客戶區隔統計:")
        print(segment_summary)
        
        # 視覺化客戶區隔
        fig, axes = plt.subplots(1, 2, figsize=(14, 6))
        
        # 區隔客戶數
        segment_counts = self.rfm_data['客戶區隔'].value_counts()
        segment_counts.plot(kind='bar', ax=axes[0], color='skyblue', edgecolor='black')
        axes[0].set_title('各區隔客戶數', fontsize=14)
        axes[0].set_xlabel('客戶區隔')
        axes[0].set_ylabel('客戶數')
        axes[0].grid(True, alpha=0.3)
        plt.sca(axes[0])
        plt.xticks(rotation=45, ha='right')
        
        # 區隔貢獻金額
        segment_monetary = self.rfm_data.groupby('客戶區隔')['Monetary'].sum().sort_values(ascending=False)
        segment_monetary.plot(kind='bar', ax=axes[1], color='lightgreen', edgecolor='black')
        axes[1].set_title('各區隔貢獻金額', fontsize=14)
        axes[1].set_xlabel('客戶區隔')
        axes[1].set_ylabel('總購買金額')
        axes[1].grid(True, alpha=0.3)
        plt.sca(axes[1])
        plt.xticks(rotation=45, ha='right')
        
        plt.tight_layout()
        plt.savefig('/mnt/user-data/outputs/customer_segmentation.png', dpi=300, bbox_inches='tight')
        plt.close()
        
        print("\n客戶區隔圖表已儲存至 customer_segmentation.png")
        
        # 生成區隔策略建議
        print("\n各區隔行銷策略建議:")
        print("-" * 60)
        print("冠軍客戶: 提供 VIP 待遇與專屬優惠,維持高度滿意度")
        print("忠誠客戶: 推薦新產品,提升購買頻率")
        print("潛力客戶: 提供會員計畫,鼓勵頻繁購買")
        print("新客戶: 歡迎優惠與產品教育,建立良好第一印象")
        print("風險客戶: 挽留優惠與問卷調查,了解流失原因")
        print("休眠客戶: 重新啟動活動,提供特別優惠")
        
        return self.rfm_data

# 使用範例
if __name__ == "__main__":
    # 建立預測系統
    forecaster = SalesForecaster()
    
    # 準備特徵(使用前面的銷售資料)
    sales_df = pd.DataFrame(sample_sales_data)
    features = forecaster.prepare_forecast_features(sales_df)
    
    # 訓練預測模型
    forecaster.train_forecast_model(features)
    
    # 建立模擬的交易資料進行 RFM 分析
    np.random.seed(42)
    n_customers = 100
    n_transactions = 500
    
    transaction_data = pd.DataFrame({
        '客戶ID': np.random.randint(1, n_customers+1, n_transactions),
        '交易日期': pd.date_range(end='2025-11-22', periods=n_transactions, freq='D'),
        '銷售額': np.random.uniform(1000, 50000, n_transactions)
    })
    
    # 執行 RFM 分析
    rfm_results = forecaster.rfm_analysis(transaction_data)
    
    # 客戶區隔
    forecaster.customer_segmentation()

這個銷售預測與客戶價值分析系統整合多種進階分析技術。銷售預測模型採用隨機森林演算法,整合產品、區域、季度等特徵,建立多變量預測模型。特徵工程建立衍生特徵如利潤率與歷史平均,豐富模型的預測能力。模型評估採用多個指標,MAE 與 RMSE 量化預測誤差,R² 評估解釋變異比例。

RFM 分析從三個維度評估客戶價值,計算每個客戶的 RFM 分數。客戶區隔根據 RFM 分數組合,將客戶劃分為冠軍客戶、忠誠客戶、潛力客戶等不同群體。每個區隔具有特定的行為特徵,需要差異化的行銷策略。區隔統計顯示各群體的客戶數與貢獻金額,幫助資源配置決策。

@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

start

:銷售歷史資料;

partition "預測建模" {
    :特徵工程\n編碼與衍生特徵;
    :訓練隨機森林模型;
    :評估預測準確度\nMAE、RMSE、R²;
}

partition "RFM 分析" {
    :計算客戶指標\nRecency、Frequency、Monetary;
    :評分與排序\n1-5 分制;
    :RFM 綜合分數;
}

partition "客戶區隔" {
    :定義區隔規則\n基於 RFM 組合;
    :劃分客戶群體\n冠軍、忠誠、潛力等;
    :計算區隔統計\n客戶數與貢獻;
}

:制定差異化策略\n針對不同區隔;

:部署預測與行銷系統;

stop

@enduml

這個流程圖描述銷售預測與客戶價值分析的完整流程。從銷售歷史資料開始,預測建模階段透過特徵工程與機器學習建立預測模型。RFM 分析階段計算客戶的三個核心指標並進行評分。客戶區隔階段根據 RFM 分數劃分客戶群體,計算各區隔的統計特徵。最後制定針對不同客戶區隔的差異化策略,部署到實際的預測與行銷系統中。

結語

產品銷售資料分析是商業智慧的核心應用,直接影響企業的營收增長與市場競爭力。本文建立完整的銷售資料科學分析框架,從多維度探索性分析到機器學習預測模型,從客戶價值評估到精準行銷策略,提供端到端的分析方法論與技術實作。

多維度分析揭示銷售績效的結構性特徵。產品維度分析識別暢銷產品與高利潤產品,指引產品組合最佳化。區域維度分析比較不同市場的表現,支援資源配置決策。時間維度分析揭示趨勢與季節性,幫助需求預測與庫存規劃。交叉維度分析揭示不同因素的交互效應,提供更深入的商業洞察。這些多角度的分析幫助管理者全面理解業務現況,識別改善機會。

銷售預測模型提供未來需求的量化估計,支援主動式的營運規劃。隨機森林演算法整合多個影響因素,建立穩健的預測模型。特徵重要性分析識別關鍵驅動因素,指引策略重點。準確的銷售預測幫助企業最佳化庫存水準,降低缺貨與積壓風險,提升資金週轉效率。預測能力是企業營運成熟度的重要指標。

客戶價值分析與區隔是精準行銷的基礎。RFM 分析從三個維度全面評估客戶價值,客戶區隔將群體劃分為不同的價值層級。差異化的行銷策略針對不同區隔的特性,提升行銷投資報酬率。高價值客戶獲得優先服務與專屬優惠,潛力客戶透過培育計畫提升價值,風險客戶執行挽留措施。這種精準化的客戶管理顯著提升客戶生命週期價值。

未來的銷售分析將更加智慧化與自動化。深度學習技術能夠捕捉更複雜的非線性模式,提升預測準確度。實時分析系統持續監控銷售動態,及時發現異常與機會。推薦系統整合客戶偏好與產品特性,提供個人化的產品推薦。自動化報表與儀表板降低分析門檻,使業務人員也能輕鬆獲取洞察。透過持續的技術創新與方法論最佳化,銷售分析將為企業創造更大的商業價值。