前言
資料視覺化是資料科學領域中不可或缺的核心技能,其重要性遠超過單純的圖表製作。透過適當的視覺化技術,複雜的數值資料能夠轉化為直觀易懂的視覺呈現,讓決策者快速掌握資料的核心洞察。在現代商業環境中,資料視覺化不僅是分析報告的組成部分,更是驅動業務決策、溝通分析發現以及說服利害關係人的關鍵工具。
Python 生態系統提供了豐富的視覺化函式庫,其中 Matplotlib 與 Seaborn 是最廣泛使用的兩個工具。Matplotlib 作為 Python 視覺化的基礎框架,提供完整的底層繪圖控制能力,讓開發者能夠精確調整圖表的每個細節。Seaborn 則建立在 Matplotlib 之上,專注於統計資料的視覺化,提供更簡潔的應用程式介面以及預設的美觀樣式配置。這兩個函式庫的結合使用,能夠滿足從基本圖表到複雜統計視覺化的各種需求。
有效的資料視覺化需要結合技術能力與設計思維。技術層面包括熟練運用各種圖表類型、理解資料結構與視覺化的對應關係,以及掌握色彩理論與版面配置原則。設計思維則強調從受眾角度出發,選擇最能傳達資訊的視覺形式,避免過度裝飾或誤導性的呈現方式。優秀的資料視覺化作品應該做到清晰、準確、美觀且具有洞察力。
本文將系統性地介紹 Python 資料視覺化的完整知識體系,從基礎概念到進階技巧,透過實際的商業案例展示如何建立專業級的視覺化作品。無論是商業分析、科學研究還是資料新聞,讀者都能從本文中獲得實用的技術指引與最佳實踐建議,提升資料視覺化的專業能力。
視覺化基礎架構
Matplotlib 是 Python 視覺化生態系統的核心基礎,其設計理念源自於 MATLAB 的繪圖介面,提供程序導向與物件導向兩種使用方式。程序導向的 pyplot 介面適合快速建立簡單圖表,而物件導向的架構則提供更精細的控制能力,適合建立複雜的多子圖佈局與客製化視覺效果。理解 Matplotlib 的架構層次是掌握進階視覺化技術的關鍵前提。
Matplotlib 的圖表結構採用階層式設計,最頂層是 Figure 物件,代表整個繪圖視窗或畫布。每個 Figure 可以包含一個或多個 Axes 物件,Axes 是實際繪製資料的區域,包含 X 軸、Y 軸以及繪圖區域。在 Axes 之下還有更細緻的元素層級,例如線條、標記、文字標籤以及圖例等。這種階層式結構讓使用者能夠針對圖表的特定部分進行精確調整,實現高度客製化的視覺效果。
圖表的建立流程通常遵循固定的模式。首先建立 Figure 與 Axes 物件,接著在 Axes 上繪製資料,然後設定各種視覺屬性如標題、座標軸標籤、圖例以及網格線等,最後儲存或顯示圖表。這個流程看似簡單,但每個步驟都包含豐富的參數選項與客製化可能性。掌握這些選項的使用方法,能夠讓視覺化作品更加專業且符合特定需求。
# 匯入必要的視覺化函式庫
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
# 配置 Matplotlib 的全域顯示參數
# 這些設定會影響所有後續建立的圖表
plt.rcParams['font.sans-serif'] = ['Microsoft JhengHei'] # 設定中文字型
plt.rcParams['axes.unicode_minus'] = False # 修正負號顯示問題
plt.rcParams['figure.figsize'] = (12, 8) # 設定預設圖表尺寸(寬x高,單位為英吋)
plt.rcParams['figure.dpi'] = 100 # 設定圖表解析度(每英吋像素數)
plt.rcParams['savefig.dpi'] = 300 # 設定儲存圖表時的解析度
plt.rcParams['font.size'] = 11 # 設定預設字型大小
plt.rcParams['axes.labelsize'] = 12 # 設定座標軸標籤字型大小
plt.rcParams['axes.titlesize'] = 14 # 設定圖表標題字型大小
plt.rcParams['xtick.labelsize'] = 10 # 設定 X 軸刻度標籤字型大小
plt.rcParams['ytick.labelsize'] = 10 # 設定 Y 軸刻度標籤字型大小
plt.rcParams['legend.fontsize'] = 11 # 設定圖例字型大小
plt.rcParams['figure.titlesize'] = 16 # 設定整體標題字型大小
# 建立基本圖表結構的範例
# 使用物件導向介面建立 Figure 與 Axes
# 方法一:建立單一子圖
fig, ax = plt.subplots(figsize=(10, 6))
# 在 Axes 上繪製簡單的折線圖
x = np.linspace(0, 10, 100) # 建立 0 到 10 之間的 100 個等間距數值
y = np.sin(x) # 計算正弦函數值
# 繪製折線圖,設定線條樣式與顏色
ax.plot(x, y,
color='#3498db', # 使用十六進位色碼定義顏色
linewidth=2, # 設定線條寬度
linestyle='-', # 設定線條樣式(實線)
marker='o', # 設定資料點標記形狀
markersize=4, # 設定標記大小
markevery=10, # 每隔 10 個點顯示一個標記
label='正弦函數') # 設定圖例標籤
# 設定圖表標題與座標軸標籤
ax.set_title('正弦函數視覺化',
fontsize=14,
fontweight='bold', # 設定粗體字
pad=20) # 設定標題與圖表之間的間距
ax.set_xlabel('X 軸數值', fontsize=12, fontweight='bold')
ax.set_ylabel('Y 軸數值', fontsize=12, fontweight='bold')
# 設定座標軸範圍
ax.set_xlim(0, 10) # X 軸顯示範圍
ax.set_ylim(-1.5, 1.5) # Y 軸顯示範圍
# 加入網格線以提升可讀性
ax.grid(True, # 啟用網格線
linestyle='--', # 設定虛線樣式
alpha=0.3, # 設定透明度
color='gray') # 設定顏色
# 顯示圖例
ax.legend(loc='upper right', # 設定圖例位置
framealpha=0.9, # 設定圖例框的透明度
edgecolor='black') # 設定圖例框的邊框顏色
# 調整版面配置,避免標籤被裁切
plt.tight_layout()
# 儲存圖表為高解析度圖檔
plt.savefig('sine_function_plot.png',
dpi=300, # 設定儲存解析度
bbox_inches='tight', # 自動裁切多餘的空白邊界
facecolor='white') # 設定背景顏色
# 顯示圖表
plt.show()
# 關閉圖表物件,釋放記憶體資源
plt.close()
# 方法二:建立多子圖佈局
# 2x2 的子圖陣列,用於同時展示多個相關圖表
fig, axes = plt.subplots(2, 2, figsize=(16, 12))
# 子圖 1:正弦函數
axes[0, 0].plot(x, np.sin(x), 'b-', linewidth=2)
axes[0, 0].set_title('正弦函數', fontweight='bold')
axes[0, 0].set_xlabel('X 軸')
axes[0, 0].set_ylabel('sin(x)')
axes[0, 0].grid(True, alpha=0.3)
# 子圖 2:餘弦函數
axes[0, 1].plot(x, np.cos(x), 'r-', linewidth=2)
axes[0, 1].set_title('餘弦函數', fontweight='bold')
axes[0, 1].set_xlabel('X 軸')
axes[0, 1].set_ylabel('cos(x)')
axes[0, 1].grid(True, alpha=0.3)
# 子圖 3:指數函數
axes[1, 0].plot(x, np.exp(x/5), 'g-', linewidth=2)
axes[1, 0].set_title('指數函數', fontweight='bold')
axes[1, 0].set_xlabel('X 軸')
axes[1, 0].set_ylabel('exp(x/5)')
axes[1, 0].grid(True, alpha=0.3)
# 子圖 4:對數函數
# 避免 log(0) 產生錯誤,從稍大於 0 的數值開始
x_log = np.linspace(0.1, 10, 100)
axes[1, 1].plot(x_log, np.log(x_log), 'm-', linewidth=2)
axes[1, 1].set_title('對數函數', fontweight='bold')
axes[1, 1].set_xlabel('X 軸')
axes[1, 1].set_ylabel('log(x)')
axes[1, 1].grid(True, alpha=0.3)
# 加入整體標題
fig.suptitle('數學函數視覺化集合',
fontsize=18,
fontweight='bold',
y=0.995) # 調整標題的垂直位置
# 調整子圖之間的間距
plt.tight_layout(rect=[0, 0, 1, 0.98]) # 為整體標題預留空間
plt.savefig('multiple_functions_plot.png', dpi=300, bbox_inches='tight')
plt.show()
plt.close()
@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
package "Matplotlib 視覺化架構" {
component "Figure 繪圖視窗" as figure {
component "Axes 座標軸系統" as axes1
component "Axes 座標軸系統" as axes2
}
package "Axes 組成元素" {
component "X 軸物件" as xaxis
component "Y 軸物件" as yaxis
component "繪圖區域" as plot_area
component "標題文字" as title
component "圖例物件" as legend
}
package "繪圖元素" {
component "線條物件" as line
component "標記物件" as marker
component "文字標籤" as text
component "網格線" as grid
}
}
figure --> axes1
figure --> axes2
axes1 --> xaxis
axes1 --> yaxis
axes1 --> plot_area
axes1 --> title
axes1 --> legend
plot_area --> line
plot_area --> marker
plot_area --> text
plot_area --> grid
note right of figure
最頂層容器
控制整體尺寸與佈局
end note
note right of axes1
實際繪圖區域
包含座標軸與資料
end note
note right of plot_area
資料視覺化呈現
包含各種圖形元素
end note
@enduml
Seaborn 統計視覺化
Seaborn 是建立在 Matplotlib 之上的高階視覺化函式庫,專門針對統計資料的視覺化需求而設計。相較於 Matplotlib 需要撰寫較多的程式碼來建立美觀的統計圖表,Seaborn 提供更簡潔的應用程式介面,並內建多種精心設計的色彩主題與樣式配置。這使得使用者能夠快速建立專業水準的統計視覺化作品,而不需要花費大量時間調整視覺細節。
Seaborn 的核心優勢在於其對 Pandas DataFrame 的原生支援。大部分的 Seaborn 繪圖函數可以直接接受 DataFrame 作為資料來源,並透過欄位名稱指定要視覺化的變數。這種設計大幅簡化了資料處理與視覺化之間的銜接,讓分析人員能夠專注於資料洞察的探索,而非處理資料格式轉換的技術細節。此外,Seaborn 自動處理許多統計計算,例如信賴區間、核密度估計以及迴歸擬合等。
圖表類型的選擇取決於資料的特性與分析目標。單變數分析通常使用直方圖、核密度圖或計數圖來展示分佈特性。雙變數分析則使用散佈圖、線圖或箱型圖來探索兩個變數之間的關係。多變數分析需要運用色彩、大小、形狀等視覺編碼來表達額外的維度,或者使用小倍數圖(facet grid)將資料分割成多個子圖。理解不同圖表類型的適用場景,是建立有效視覺化的重要能力。
# 匯入 Seaborn 視覺化函式庫
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
# 設定 Seaborn 的視覺樣式
# Seaborn 提供多種預設樣式主題
sns.set_style('whitegrid') # 設定白色背景與網格線樣式
# 其他可用樣式:'darkgrid', 'white', 'dark', 'ticks'
# 設定色彩調色盤
# Seaborn 提供豐富的色彩配置選項
sns.set_palette('deep') # 使用深色調色盤
# 其他可用調色盤:'pastel', 'bright', 'dark', 'colorblind', 'muted'
# 設定圖表內容的縮放比例
sns.set_context('notebook') # 適合筆記本環境的字型與線條大小
# 其他可用情境:'paper', 'talk', 'poster'
# 建立示範資料集
# 模擬商業銷售資料
np.random.seed(42) # 設定隨機種子以確保結果可重現
# 建立包含多個變數的銷售資料
n_records = 500
sales_data = pd.DataFrame({
'銷售額': np.random.gamma(2, 2, n_records) * 10000, # 使用 Gamma 分佈模擬銷售額
'成本': np.random.gamma(1.5, 2, n_records) * 5000, # 成本數據
'產品類別': np.random.choice(['電子產品', '服飾配件', '居家用品', '運動器材'], n_records),
'銷售區域': np.random.choice(['北區', '中區', '南區', '東區'], n_records),
'季度': np.random.choice(['Q1', 'Q2', 'Q3', 'Q4'], n_records),
'客戶滿意度': np.random.normal(4.0, 0.8, n_records).clip(1, 5) # 1-5 分的滿意度評分
})
# 計算利潤欄位
sales_data['利潤'] = sales_data['銷售額'] - sales_data['成本']
# 計算利潤率欄位(百分比)
sales_data['利潤率'] = (sales_data['利潤'] / sales_data['銷售額'] * 100).clip(0, 100)
print("銷售資料集概覽:")
print(sales_data.head(10))
print(f"\n資料集形狀: {sales_data.shape}")
print("\n基本統計摘要:")
print(sales_data.describe())
# 範例 1:分佈圖(Distribution Plot)
# 展示單一數值變數的分佈特性
fig, axes = plt.subplots(1, 2, figsize=(16, 6))
# 直方圖搭配核密度估計曲線
sns.histplot(data=sales_data,
x='銷售額',
kde=True, # 加入核密度估計曲線
bins=30, # 設定直方圖的區間數量
color='#3498db', # 設定顏色
ax=axes[0]) # 指定繪製的子圖
axes[0].set_title('銷售額分佈分析', fontsize=14, fontweight='bold', pad=15)
axes[0].set_xlabel('銷售額(元)', fontsize=12)
axes[0].set_ylabel('頻率', fontsize=12)
axes[0].axvline(sales_data['銷售額'].mean(),
color='red',
linestyle='--',
linewidth=2,
label=f"平均值: {sales_data['銷售額'].mean():.0f}")
axes[0].legend()
# 核密度圖(Kernel Density Estimate)
sns.kdeplot(data=sales_data,
x='利潤',
shade=True, # 填充曲線下方區域
color='#2ecc71', # 設定顏色
ax=axes[1]) # 指定繪製的子圖
axes[1].set_title('利潤分佈分析', fontsize=14, fontweight='bold', pad=15)
axes[1].set_xlabel('利潤(元)', fontsize=12)
axes[1].set_ylabel('密度', fontsize=12)
plt.tight_layout()
plt.savefig('distribution_analysis.png', dpi=300, bbox_inches='tight')
plt.show()
plt.close()
# 範例 2:類別資料視覺化
# 展示類別變數的計數與比較
fig, axes = plt.subplots(1, 2, figsize=(16, 6))
# 計數圖(Count Plot)
# 顯示每個類別的記錄數量
sns.countplot(data=sales_data,
x='產品類別',
palette='Set2', # 使用 Set2 調色盤
ax=axes[0])
axes[0].set_title('產品類別銷售記錄數量', fontsize=14, fontweight='bold', pad=15)
axes[0].set_xlabel('產品類別', fontsize=12)
axes[0].set_ylabel('記錄數量', fontsize=12)
axes[0].tick_params(axis='x', rotation=45) # 旋轉 X 軸標籤
# 在長條上方標註數值
for container in axes[0].containers:
axes[0].bar_label(container, fmt='%d', padding=3)
# 分組計數圖
# 依據兩個類別變數進行分組計數
sns.countplot(data=sales_data,
x='產品類別',
hue='銷售區域', # 按銷售區域分組
palette='Set3',
ax=axes[1])
axes[1].set_title('產品類別與銷售區域交叉分析', fontsize=14, fontweight='bold', pad=15)
axes[1].set_xlabel('產品類別', fontsize=12)
axes[1].set_ylabel('記錄數量', fontsize=12)
axes[1].tick_params(axis='x', rotation=45)
axes[1].legend(title='銷售區域', loc='upper right')
plt.tight_layout()
plt.savefig('category_analysis.png', dpi=300, bbox_inches='tight')
plt.show()
plt.close()
# 範例 3:長條圖(Bar Plot)
# 展示類別變數與數值變數之間的關係
fig, axes = plt.subplots(2, 1, figsize=(14, 12))
# 簡單長條圖
# 顯示每個產品類別的平均銷售額
sns.barplot(data=sales_data,
x='產品類別',
y='銷售額',
estimator=np.mean, # 使用平均值作為統計量
ci=95, # 顯示 95% 信賴區間
palette='viridis',
ax=axes[0])
axes[0].set_title('產品類別平均銷售額比較', fontsize=14, fontweight='bold', pad=15)
axes[0].set_xlabel('產品類別', fontsize=12)
axes[0].set_ylabel('平均銷售額(元)', fontsize=12)
axes[0].tick_params(axis='x', rotation=45)
# 分組長條圖
# 同時比較產品類別與銷售區域
sns.barplot(data=sales_data,
x='產品類別',
y='銷售額',
hue='銷售區域',
estimator=np.mean,
ci=95,
palette='Set2',
ax=axes[1])
axes[1].set_title('產品類別與銷售區域平均銷售額比較', fontsize=14, fontweight='bold', pad=15)
axes[1].set_xlabel('產品類別', fontsize=12)
axes[1].set_ylabel('平均銷售額(元)', fontsize=12)
axes[1].tick_params(axis='x', rotation=45)
axes[1].legend(title='銷售區域', loc='upper right')
plt.tight_layout()
plt.savefig('bar_chart_analysis.png', dpi=300, bbox_inches='tight')
plt.show()
plt.close()
# 範例 4:箱型圖(Box Plot)
# 展示資料的分佈特性與異常值
fig, axes = plt.subplots(2, 1, figsize=(14, 12))
# 基本箱型圖
# 比較不同產品類別的銷售額分佈
sns.boxplot(data=sales_data,
x='產品類別',
y='銷售額',
palette='Set3',
ax=axes[0])
axes[0].set_title('產品類別銷售額分佈分析', fontsize=14, fontweight='bold', pad=15)
axes[0].set_xlabel('產品類別', fontsize=12)
axes[0].set_ylabel('銷售額(元)', fontsize=12)
axes[0].tick_params(axis='x', rotation=45)
# 分組箱型圖
# 加入第二個分類維度
sns.boxplot(data=sales_data,
x='產品類別',
y='利潤',
hue='銷售區域',
palette='pastel',
ax=axes[1])
axes[1].set_title('產品類別與銷售區域利潤分佈分析', fontsize=14, fontweight='bold', pad=15)
axes[1].set_xlabel('產品類別', fontsize=12)
axes[1].set_ylabel('利潤(元)', fontsize=12)
axes[1].tick_params(axis='x', rotation=45)
axes[1].legend(title='銷售區域', loc='upper right')
plt.tight_layout()
plt.savefig('boxplot_analysis.png', dpi=300, bbox_inches='tight')
plt.show()
plt.close()
# 範例 5:小提琴圖(Violin Plot)
# 結合箱型圖與核密度圖的優點
fig, ax = plt.subplots(figsize=(14, 8))
sns.violinplot(data=sales_data,
x='產品類別',
y='客戶滿意度',
hue='銷售區域',
split=False, # 是否將小提琴圖分割顯示
palette='muted',
inner='box', # 內部顯示箱型圖
ax=ax)
ax.set_title('產品類別與銷售區域客戶滿意度分佈', fontsize=14, fontweight='bold', pad=15)
ax.set_xlabel('產品類別', fontsize=12)
ax.set_ylabel('客戶滿意度(1-5 分)', fontsize=12)
ax.tick_params(axis='x', rotation=45)
ax.legend(title='銷售區域', loc='lower right')
ax.set_ylim(0, 6) # 設定 Y 軸範圍
plt.tight_layout()
plt.savefig('violin_plot_analysis.png', dpi=300, bbox_inches='tight')
plt.show()
plt.close()
# 範例 6:散佈圖(Scatter Plot)
# 展示兩個數值變數之間的關係
fig, axes = plt.subplots(1, 2, figsize=(18, 7))
# 基本散佈圖
sns.scatterplot(data=sales_data,
x='銷售額',
y='利潤',
alpha=0.6, # 設定透明度
s=50, # 設定點的大小
color='#3498db',
ax=axes[0])
axes[0].set_title('銷售額與利潤關係分析', fontsize=14, fontweight='bold', pad=15)
axes[0].set_xlabel('銷售額(元)', fontsize=12)
axes[0].set_ylabel('利潤(元)', fontsize=12)
axes[0].grid(True, alpha=0.3)
# 多維度散佈圖
# 使用顏色與大小編碼額外資訊
sns.scatterplot(data=sales_data,
x='銷售額',
y='利潤',
hue='產品類別', # 使用顏色區分產品類別
size='客戶滿意度', # 使用大小表示滿意度
sizes=(50, 300), # 設定大小範圍
alpha=0.7,
palette='deep',
ax=axes[1])
axes[1].set_title('多維度銷售分析', fontsize=14, fontweight='bold', pad=15)
axes[1].set_xlabel('銷售額(元)', fontsize=12)
axes[1].set_ylabel('利潤(元)', fontsize=12)
axes[1].grid(True, alpha=0.3)
axes[1].legend(bbox_to_anchor=(1.05, 1), loc='upper left')
plt.tight_layout()
plt.savefig('scatter_plot_analysis.png', dpi=300, bbox_inches='tight')
plt.show()
plt.close()
# 範例 7:熱力圖(Heatmap)
# 展示數值型資料的矩陣結構
fig, axes = plt.subplots(1, 2, figsize=(18, 7))
# 相關係數矩陣熱力圖
# 計算數值變數之間的相關性
numeric_cols = ['銷售額', '成本', '利潤', '利潤率', '客戶滿意度']
correlation_matrix = sales_data[numeric_cols].corr()
sns.heatmap(correlation_matrix,
annot=True, # 在每個格子中顯示數值
fmt='.2f', # 數值格式化為兩位小數
cmap='coolwarm', # 設定色彩映射
center=0, # 將色彩中心設為 0
square=True, # 使格子為正方形
linewidths=1, # 設定格子之間的線條寬度
cbar_kws={'shrink': 0.8}, # 設定色彩條的大小
ax=axes[0])
axes[0].set_title('變數相關係數矩陣', fontsize=14, fontweight='bold', pad=15)
# 樞紐表熱力圖
# 建立產品類別與銷售區域的交叉統計表
pivot_table = sales_data.pivot_table(
values='銷售額',
index='產品類別',
columns='銷售區域',
aggfunc='mean'
)
sns.heatmap(pivot_table,
annot=True,
fmt='.0f',
cmap='YlOrRd', # 黃橙紅色系
linewidths=1,
cbar_kws={'label': '平均銷售額(元)'},
ax=axes[1])
axes[1].set_title('產品類別與銷售區域平均銷售額熱力圖', fontsize=14, fontweight='bold', pad=15)
axes[1].set_xlabel('銷售區域', fontsize=12)
axes[1].set_ylabel('產品類別', fontsize=12)
plt.tight_layout()
plt.savefig('heatmap_analysis.png', dpi=300, bbox_inches='tight')
plt.show()
plt.close()
print("\n所有視覺化範例已完成並儲存")
商業資料視覺化實務
商業資料視覺化的核心目標是支援決策制定與洞察發現。不同於學術研究或探索性分析,商業視覺化需要在有限的時間內清晰地傳達關鍵訊息,讓管理層能夠快速理解現況並採取行動。這要求視覺化設計必須以受眾需求為中心,選擇最直觀的圖表類型,並突出顯示最重要的資訊。
銷售分析是商業視覺化最常見的應用場景之一。銷售資料通常包含時間維度、地理區域、產品類別以及客戶區隔等多個面向。有效的銷售視覺化需要能夠展示趨勢變化、識別表現異常的區域或產品,以及比較不同時期或類別的績效差異。儀表板的設計應該讓使用者能夠快速掌握整體情況,並在需要時深入探索細節。
# 完整的商業銷售分析視覺化範例
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from datetime import datetime, timedelta
# 設定視覺化樣式
plt.rcParams['font.sans-serif'] = ['Microsoft JhengHei']
plt.rcParams['axes.unicode_minus'] = False
sns.set_style('whitegrid')
sns.set_palette('Set2')
# 建立模擬的銷售資料集
# 包含完整的商業分析所需維度
np.random.seed(42)
# 建立時間序列資料(過去 24 個月)
start_date = datetime(2023, 1, 1)
date_range = [start_date + timedelta(days=30*i) for i in range(24)]
# 建立包含多維度的銷售資料
regions = ['北區', '中區', '南區', '東區', '西區']
products = ['智慧型手機', '筆記型電腦', '平板電腦', '智慧手錶', '無線耳機']
channels = ['線上商城', '實體門市', '企業客戶']
# 生成詳細的交易記錄
n_records = 3000
sales_records = []
for _ in range(n_records):
# 隨機選擇日期
record_date = start_date + timedelta(days=np.random.randint(0, 720))
# 建立銷售記錄
record = {
'交易日期': record_date,
'年份': record_date.year,
'月份': record_date.month,
'季度': (record_date.month - 1) // 3 + 1,
'銷售區域': np.random.choice(regions),
'產品名稱': np.random.choice(products),
'銷售通路': np.random.choice(channels),
'銷售數量': np.random.randint(1, 50),
'單價': np.random.uniform(5000, 50000),
'折扣率': np.random.choice([0, 5, 10, 15, 20], p=[0.4, 0.2, 0.2, 0.15, 0.05])
}
# 計算衍生欄位
record['原始金額'] = record['銷售數量'] * record['單價']
record['折扣金額'] = record['原始金額'] * (record['折扣率'] / 100)
record['實際銷售額'] = record['原始金額'] - record['折扣金額']
record['成本'] = record['實際銷售額'] * np.random.uniform(0.4, 0.7)
record['利潤'] = record['實際銷售額'] - record['成本']
record['利潤率'] = (record['利潤'] / record['實際銷售額']) * 100
sales_records.append(record)
# 建立 DataFrame
sales_df = pd.DataFrame(sales_records)
# 資料排序
sales_df = sales_df.sort_values('交易日期').reset_index(drop=True)
print("=" * 70)
print("商業銷售資料分析")
print("=" * 70)
print(f"\n資料集概覽:")
print(f"記錄數量: {len(sales_df):,}")
print(f"時間範圍: {sales_df['交易日期'].min().strftime('%Y-%m-%d')} 至 {sales_df['交易日期'].max().strftime('%Y-%m-%d')}")
print(f"\n前 10 筆記錄:")
print(sales_df.head(10))
# 彙總統計分析
print(f"\n整體銷售績效:")
print(f"總銷售額: NT$ {sales_df['實際銷售額'].sum():,.0f}")
print(f"總利潤: NT$ {sales_df['利潤'].sum():,.0f}")
print(f"平均利潤率: {sales_df['利潤率'].mean():.2f}%")
print(f"平均交易金額: NT$ {sales_df['實際銷售額'].mean():,.0f}")
# 視覺化 1:時間序列趨勢分析
# 建立月度彙總資料
monthly_sales = sales_df.groupby(
pd.Grouper(key='交易日期', freq='M')
).agg({
'實際銷售額': 'sum',
'利潤': 'sum',
'銷售數量': 'sum'
}).reset_index()
# 計算移動平均(平滑趨勢)
monthly_sales['銷售額_移動平均'] = monthly_sales['實際銷售額'].rolling(window=3, min_periods=1).mean()
monthly_sales['利潤_移動平均'] = monthly_sales['利潤'].rolling(window=3, min_periods=1).mean()
fig, axes = plt.subplots(3, 1, figsize=(16, 14))
# 子圖 1:月度銷售額趨勢
axes[0].plot(monthly_sales['交易日期'],
monthly_sales['實際銷售額'] / 1000000,
marker='o',
linewidth=2,
markersize=6,
color='#3498db',
label='月度銷售額')
axes[0].plot(monthly_sales['交易日期'],
monthly_sales['銷售額_移動平均'] / 1000000,
linestyle='--',
linewidth=2.5,
color='#e74c3c',
label='3 個月移動平均')
axes[0].set_title('月度銷售額趨勢分析', fontsize=15, fontweight='bold', pad=20)
axes[0].set_xlabel('日期', fontsize=12)
axes[0].set_ylabel('銷售額(百萬元)', fontsize=12)
axes[0].legend(loc='upper left', fontsize=11)
axes[0].grid(True, alpha=0.3)
# 子圖 2:月度利潤趨勢
axes[1].plot(monthly_sales['交易日期'],
monthly_sales['利潤'] / 1000000,
marker='s',
linewidth=2,
markersize=6,
color='#2ecc71',
label='月度利潤')
axes[1].plot(monthly_sales['交易日期'],
monthly_sales['利潤_移動平均'] / 1000000,
linestyle='--',
linewidth=2.5,
color='#f39c12',
label='3 個月移動平均')
axes[1].set_title('月度利潤趨勢分析', fontsize=15, fontweight='bold', pad=20)
axes[1].set_xlabel('日期', fontsize=12)
axes[1].set_ylabel('利潤(百萬元)', fontsize=12)
axes[1].legend(loc='upper left', fontsize=11)
axes[1].grid(True, alpha=0.3)
# 子圖 3:月度銷售數量趨勢
axes[2].bar(monthly_sales['交易日期'],
monthly_sales['銷售數量'],
width=20,
color='#9b59b6',
alpha=0.7,
label='月度銷售數量')
axes[2].set_title('月度銷售數量趨勢分析', fontsize=15, fontweight='bold', pad=20)
axes[2].set_xlabel('日期', fontsize=12)
axes[2].set_ylabel('銷售數量', fontsize=12)
axes[2].legend(loc='upper left', fontsize=11)
axes[2].grid(True, alpha=0.3, axis='y')
plt.tight_layout()
plt.savefig('time_series_analysis.png', dpi=300, bbox_inches='tight')
plt.show()
plt.close()
# 視覺化 2:區域銷售績效比較
region_performance = sales_df.groupby('銷售區域').agg({
'實際銷售額': 'sum',
'利潤': 'sum',
'銷售數量': 'sum'
}).reset_index()
# 計算利潤率
region_performance['利潤率'] = (region_performance['利潤'] / region_performance['實際銷售額']) * 100
# 依銷售額排序
region_performance = region_performance.sort_values('實際銷售額', ascending=False)
fig, axes = plt.subplots(2, 2, figsize=(18, 14))
# 子圖 1:區域銷售額比較
axes[0, 0].barh(region_performance['銷售區域'],
region_performance['實際銷售額'] / 1000000,
color='#3498db',
edgecolor='black',
linewidth=1.5)
# 在長條末端標註數值
for i, (region, value) in enumerate(zip(region_performance['銷售區域'],
region_performance['實際銷售額'] / 1000000)):
axes[0, 0].text(value, i, f' {value:.1f}M',
va='center', fontsize=11, fontweight='bold')
axes[0, 0].set_title('銷售區域銷售額比較', fontsize=14, fontweight='bold', pad=15)
axes[0, 0].set_xlabel('銷售額(百萬元)', fontsize=12)
axes[0, 0].set_ylabel('銷售區域', fontsize=12)
axes[0, 0].grid(True, alpha=0.3, axis='x')
# 子圖 2:區域利潤比較
axes[0, 1].barh(region_performance['銷售區域'],
region_performance['利潤'] / 1000000,
color='#2ecc71',
edgecolor='black',
linewidth=1.5)
for i, (region, value) in enumerate(zip(region_performance['銷售區域'],
region_performance['利潤'] / 1000000)):
axes[0, 1].text(value, i, f' {value:.1f}M',
va='center', fontsize=11, fontweight='bold')
axes[0, 1].set_title('銷售區域利潤比較', fontsize=14, fontweight='bold', pad=15)
axes[0, 1].set_xlabel('利潤(百萬元)', fontsize=12)
axes[0, 1].set_ylabel('銷售區域', fontsize=12)
axes[0, 1].grid(True, alpha=0.3, axis='x')
# 子圖 3:區域利潤率比較
axes[1, 0].barh(region_performance['銷售區域'],
region_performance['利潤率'],
color='#f39c12',
edgecolor='black',
linewidth=1.5)
for i, (region, value) in enumerate(zip(region_performance['銷售區域'],
region_performance['利潤率'])):
axes[1, 0].text(value, i, f' {value:.1f}%',
va='center', fontsize=11, fontweight='bold')
axes[1, 0].set_title('銷售區域利潤率比較', fontsize=14, fontweight='bold', pad=15)
axes[1, 0].set_xlabel('利潤率(%)', fontsize=12)
axes[1, 0].set_ylabel('銷售區域', fontsize=12)
axes[1, 0].grid(True, alpha=0.3, axis='x')
# 子圖 4:區域銷售數量比較
axes[1, 1].barh(region_performance['銷售區域'],
region_performance['銷售數量'],
color='#9b59b6',
edgecolor='black',
linewidth=1.5)
for i, (region, value) in enumerate(zip(region_performance['銷售區域'],
region_performance['銷售數量'])):
axes[1, 1].text(value, i, f' {value:,.0f}',
va='center', fontsize=11, fontweight='bold')
axes[1, 1].set_title('銷售區域銷售數量比較', fontsize=14, fontweight='bold', pad=15)
axes[1, 1].set_xlabel('銷售數量', fontsize=12)
axes[1, 1].set_ylabel('銷售區域', fontsize=12)
axes[1, 1].grid(True, alpha=0.3, axis='x')
plt.tight_layout()
plt.savefig('regional_performance_comparison.png', dpi=300, bbox_inches='tight')
plt.show()
plt.close()
# 視覺化 3:產品績效分析
product_performance = sales_df.groupby('產品名稱').agg({
'實際銷售額': 'sum',
'利潤': 'sum',
'銷售數量': 'sum'
}).reset_index()
product_performance['利潤率'] = (product_performance['利潤'] / product_performance['實際銷售額']) * 100
product_performance = product_performance.sort_values('實際銷售額', ascending=True)
fig, axes = plt.subplots(1, 2, figsize=(18, 8))
# 子圖 1:產品銷售額與利潤比較
x_pos = np.arange(len(product_performance))
width = 0.35
axes[0].barh(x_pos - width/2,
product_performance['實際銷售額'] / 1000000,
width,
label='銷售額',
color='#3498db',
edgecolor='black')
axes[0].barh(x_pos + width/2,
product_performance['利潤'] / 1000000,
width,
label='利潤',
color='#2ecc71',
edgecolor='black')
axes[0].set_yticks(x_pos)
axes[0].set_yticklabels(product_performance['產品名稱'])
axes[0].set_title('產品銷售額與利潤比較', fontsize=14, fontweight='bold', pad=15)
axes[0].set_xlabel('金額(百萬元)', fontsize=12)
axes[0].legend(fontsize=11)
axes[0].grid(True, alpha=0.3, axis='x')
# 子圖 2:產品利潤率比較
colors = plt.cm.RdYlGn(np.linspace(0.3, 0.9, len(product_performance)))
axes[1].barh(product_performance['產品名稱'],
product_performance['利潤率'],
color=colors,
edgecolor='black',
linewidth=1.5)
for i, (product, value) in enumerate(zip(product_performance['產品名稱'],
product_performance['利潤率'])):
axes[1].text(value, i, f' {value:.1f}%',
va='center', fontsize=11, fontweight='bold')
axes[1].set_title('產品利潤率比較', fontsize=14, fontweight='bold', pad=15)
axes[1].set_xlabel('利潤率(%)', fontsize=12)
axes[1].grid(True, alpha=0.3, axis='x')
plt.tight_layout()
plt.savefig('product_performance_analysis.png', dpi=300, bbox_inches='tight')
plt.show()
plt.close()
# 視覺化 4:銷售通路分析
channel_performance = sales_df.groupby('銷售通路').agg({
'實際銷售額': 'sum',
'利潤': 'sum',
'銷售數量': 'sum'
}).reset_index()
channel_performance['市占率'] = (channel_performance['實際銷售額'] / channel_performance['實際銷售額'].sum()) * 100
fig, axes = plt.subplots(1, 2, figsize=(16, 7))
# 子圖 1:通路銷售額圓餅圖
colors = ['#3498db', '#2ecc71', '#f39c12']
explode = (0.05, 0.05, 0.05) # 將所有區塊稍微分離
axes[0].pie(channel_performance['實際銷售額'],
labels=channel_performance['銷售通路'],
autopct='%1.1f%%',
startangle=90,
colors=colors,
explode=explode,
shadow=True,
textprops={'fontsize': 12, 'fontweight': 'bold'})
axes[0].set_title('銷售通路銷售額分佈', fontsize=14, fontweight='bold', pad=15)
# 子圖 2:通路績效雷達圖準備
# 正規化各指標到 0-100 範圍
metrics = ['銷售額', '利潤', '銷售數量']
channel_radar_data = pd.DataFrame()
for metric, col in zip(metrics, ['實際銷售額', '利潤', '銷售數量']):
max_val = channel_performance[col].max()
channel_radar_data[metric] = (channel_performance[col] / max_val) * 100
channel_radar_data['通路'] = channel_performance['銷售通路'].values
# 使用長條圖展示各通路的綜合表現
x_pos = np.arange(len(channel_performance))
width = 0.25
axes[1].bar(x_pos - width,
channel_radar_data['銷售額'],
width,
label='銷售額(正規化)',
color='#3498db',
edgecolor='black')
axes[1].bar(x_pos,
channel_radar_data['利潤'],
width,
label='利潤(正規化)',
color='#2ecc71',
edgecolor='black')
axes[1].bar(x_pos + width,
channel_radar_data['銷售數量'],
width,
label='銷售數量(正規化)',
color='#f39c12',
edgecolor='black')
axes[1].set_xticks(x_pos)
axes[1].set_xticklabels(channel_performance['銷售通路'])
axes[1].set_title('銷售通路綜合績效比較', fontsize=14, fontweight='bold', pad=15)
axes[1].set_ylabel('正規化分數(0-100)', fontsize=12)
axes[1].legend(fontsize=10)
axes[1].grid(True, alpha=0.3, axis='y')
plt.tight_layout()
plt.savefig('channel_performance_analysis.png', dpi=300, bbox_inches='tight')
plt.show()
plt.close()
# 視覺化 5:季度績效對比
quarterly_performance = sales_df.groupby(['年份', '季度']).agg({
'實際銷售額': 'sum',
'利潤': 'sum'
}).reset_index()
quarterly_performance['年度_季度'] = quarterly_performance['年份'].astype(str) + ' Q' + quarterly_performance['季度'].astype(str)
fig, axes = plt.subplots(2, 1, figsize=(16, 12))
# 子圖 1:季度銷售額對比
axes[0].bar(quarterly_performance['年度_季度'],
quarterly_performance['實際銷售額'] / 1000000,
color='#3498db',
edgecolor='black',
linewidth=1.5,
alpha=0.8)
# 加入趨勢線
z = np.polyfit(range(len(quarterly_performance)),
quarterly_performance['實際銷售額'] / 1000000, 1)
p = np.poly1d(z)
axes[0].plot(range(len(quarterly_performance)),
p(range(len(quarterly_performance))),
"r--",
linewidth=2.5,
label=f'趨勢線 (斜率={z[0]:.2f})')
axes[0].set_title('季度銷售額趨勢', fontsize=14, fontweight='bold', pad=15)
axes[0].set_xlabel('季度', fontsize=12)
axes[0].set_ylabel('銷售額(百萬元)', fontsize=12)
axes[0].tick_params(axis='x', rotation=45)
axes[0].legend(fontsize=11)
axes[0].grid(True, alpha=0.3, axis='y')
# 子圖 2:季度利潤對比
axes[1].bar(quarterly_performance['年度_季度'],
quarterly_performance['利潤'] / 1000000,
color='#2ecc71',
edgecolor='black',
linewidth=1.5,
alpha=0.8)
# 加入趨勢線
z = np.polyfit(range(len(quarterly_performance)),
quarterly_performance['利潤'] / 1000000, 1)
p = np.poly1d(z)
axes[1].plot(range(len(quarterly_performance)),
p(range(len(quarterly_performance))),
"r--",
linewidth=2.5,
label=f'趨勢線 (斜率={z[0]:.2f})')
axes[1].set_title('季度利潤趨勢', fontsize=14, fontweight='bold', pad=15)
axes[1].set_xlabel('季度', fontsize=12)
axes[1].set_ylabel('利潤(百萬元)', fontsize=12)
axes[1].tick_params(axis='x', rotation=45)
axes[1].legend(fontsize=11)
axes[1].grid(True, alpha=0.3, axis='y')
plt.tight_layout()
plt.savefig('quarterly_performance_analysis.png', dpi=300, bbox_inches='tight')
plt.show()
plt.close()
print("\n" + "=" * 70)
print("商業銷售分析視覺化完成")
print("=" * 70)
print("\n已生成以下分析圖表:")
print("1. time_series_analysis.png - 時間序列趨勢分析")
print("2. regional_performance_comparison.png - 區域績效比較")
print("3. product_performance_analysis.png - 產品績效分析")
print("4. channel_performance_analysis.png - 銷售通路分析")
print("5. quarterly_performance_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
start
:接收原始銷售資料;
note right
資料來源
----
交易系統
客戶資料庫
庫存管理系統
end note
:資料清理與預處理;
note right
處理項目
----
移除重複記錄
填補缺失值
修正異常值
統一資料格式
end note
:計算衍生指標;
note right
新增欄位
----
利潤金額
利潤率百分比
成長率指標
市占率數據
end note
partition "多維度分析" {
:時間序列分析;
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
應用方向
----
策略規劃調整
資源配置最佳化
行銷活動設計
績效目標設定
end note
stop
@enduml
進階視覺化技術
隨著資料複雜度的提升,標準的統計圖表有時無法完整呈現資料的多維特性。進階視覺化技術透過整合多種視覺編碼方式,讓單一圖表能夠同時展示更多資訊維度。這些技術包括小倍數圖(facet grid)、配對圖(pair plot)、聯合分佈圖(joint plot)以及互動式視覺化等。掌握這些進階技術能夠顯著提升資料探索的深度與效率。
小倍數圖是將資料依據類別變數分割成多個子集,每個子集使用相同的圖表類型呈現。這種技術特別適合比較不同群組的資料模式,因為相同的座標軸尺度讓視覺比較變得容易。例如,比較不同地區或不同產品線的銷售趨勢時,小倍數圖能夠清楚展示每個子群組的獨特特性以及整體的共同模式。
配對圖提供資料集中所有數值變數之間的關係概覽。圖表的對角線通常顯示單一變數的分佈,而非對角線的位置則展示兩兩變數之間的散佈圖或相關性。這種全面性的視角能夠快速識別變數之間的線性關係、非線性關係或群聚模式,是探索性資料分析的強大工具。
# 進階視覺化技術應用範例
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib.gridspec import GridSpec
# 設定視覺化環境
plt.rcParams['font.sans-serif'] = ['Microsoft JhengHei']
plt.rcParams['axes.unicode_minus'] = False
sns.set_style('whitegrid')
# 建立綜合性的資料集
np.random.seed(42)
n_samples = 800
# 建立包含多個相關變數的資料集
advanced_data = pd.DataFrame({
'廣告支出': np.random.gamma(3, 2, n_samples) * 10000,
'社群媒體曝光': np.random.gamma(4, 2, n_samples) * 5000,
'網站流量': np.random.gamma(5, 2, n_samples) * 1000,
'轉換率': np.random.beta(3, 7, n_samples) * 100,
'客戶滿意度': np.random.normal(4.2, 0.6, n_samples).clip(1, 5),
'產品類別': np.random.choice(['科技產品', '家電產品', '時尚服飾', '健康保健'], n_samples),
'銷售通路': np.random.choice(['電商平台', '實體店面', '行動應用'], n_samples),
'顧客年齡層': np.random.choice(['18-25歲', '26-35歲', '36-45歲', '46-60歲'], n_samples)
})
# 建立目標變數(銷售額)
# 銷售額與多個自變數相關
advanced_data['銷售額'] = (
0.3 * advanced_data['廣告支出'] +
0.2 * advanced_data['社群媒體曝光'] +
0.4 * advanced_data['網站流量'] +
500 * advanced_data['轉換率'] +
10000 * advanced_data['客戶滿意度'] +
np.random.normal(0, 50000, n_samples)
).clip(0, None)
print("=" * 70)
print("進階視覺化資料集")
print("=" * 70)
print(f"\n資料集大小: {advanced_data.shape}")
print("\n前 10 筆記錄:")
print(advanced_data.head(10))
print("\n基本統計摘要:")
print(advanced_data.describe())
# 進階視覺化 1:小倍數圖(Facet Grid)
# 依據產品類別分別展示銷售額與廣告支出的關係
# 使用 FacetGrid 建立小倍數圖
g = sns.FacetGrid(advanced_data,
col='產品類別', # 依產品類別分欄
col_wrap=2, # 每列顯示 2 個子圖
height=5, # 每個子圖的高度
aspect=1.2) # 子圖的寬高比
# 在每個子圖上繪製散佈圖
g.map(sns.scatterplot,
'廣告支出',
'銷售額',
alpha=0.6,
s=50)
# 加入迴歸線
g.map(sns.regplot,
'廣告支出',
'銷售額',
scatter=False, # 不顯示散佈點
color='red',
line_kws={'linewidth': 2})
# 設定標題
g.fig.suptitle('不同產品類別的廣告支出與銷售額關係',
fontsize=16,
fontweight='bold',
y=1.02)
# 調整版面
plt.tight_layout()
plt.savefig('facet_grid_analysis.png', dpi=300, bbox_inches='tight')
plt.show()
plt.close()
# 進階視覺化 2:配對圖(Pair Plot)
# 展示所有數值變數之間的關係
# 選擇要分析的數值變數
numeric_vars = ['廣告支出', '社群媒體曝光', '網站流量', '轉換率', '銷售額']
pairplot_data = advanced_data[numeric_vars + ['產品類別']].copy()
# 為了提升可讀性,將數值縮放到合適範圍
pairplot_data['廣告支出'] = pairplot_data['廣告支出'] / 10000
pairplot_data['社群媒體曝光'] = pairplot_data['社群媒體曝光'] / 1000
pairplot_data['網站流量'] = pairplot_data['網站流量'] / 1000
pairplot_data['銷售額'] = pairplot_data['銷售額'] / 100000
# 建立配對圖
g = sns.pairplot(pairplot_data,
hue='產品類別', # 使用顏色區分產品類別
diag_kind='kde', # 對角線顯示核密度圖
plot_kws={'alpha': 0.6, 's': 30}, # 散佈圖設定
diag_kws={'shade': True}, # 核密度圖填充
palette='Set2',
height=2.5) # 每個子圖的大小
# 設定整體標題
g.fig.suptitle('行銷指標配對分析(數值已縮放)',
fontsize=16,
fontweight='bold',
y=1.01)
plt.tight_layout()
plt.savefig('pair_plot_analysis.png', dpi=300, bbox_inches='tight')
plt.show()
plt.close()
# 進階視覺化 3:聯合分佈圖(Joint Plot)
# 同時展示雙變數關係與各自的邊際分佈
# 建立聯合分佈圖
g = sns.jointplot(data=advanced_data,
x='廣告支出',
y='銷售額',
kind='reg', # 包含迴歸線的散佈圖
height=10,
space=0.2, # 邊際圖與主圖之間的間距
color='#3498db',
marginal_kws={'bins': 30, 'kde': True}) # 邊際圖設定
# 計算相關係數
correlation = advanced_data['廣告支出'].corr(advanced_data['銷售額'])
# 加入相關係數標註
g.ax_joint.text(0.05, 0.95,
f'相關係數: {correlation:.3f}',
transform=g.ax_joint.transAxes,
fontsize=13,
fontweight='bold',
verticalalignment='top',
bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))
# 設定標題
g.fig.suptitle('廣告支出與銷售額聯合分佈分析',
fontsize=14,
fontweight='bold',
y=0.995)
plt.tight_layout()
plt.savefig('joint_plot_analysis.png', dpi=300, bbox_inches='tight')
plt.show()
plt.close()
# 進階視覺化 4:多維度熱力圖
# 建立複雜的相關性與分類交叉分析
# 計算產品類別與銷售通路的交叉平均銷售額
pivot_complex = advanced_data.pivot_table(
values='銷售額',
index='產品類別',
columns='銷售通路',
aggfunc='mean'
)
# 建立圖表
fig, axes = plt.subplots(1, 2, figsize=(18, 7))
# 子圖 1:產品與通路交叉熱力圖
sns.heatmap(pivot_complex / 1000, # 轉換為千元
annot=True,
fmt='.1f',
cmap='YlGnBu',
linewidths=2,
linecolor='white',
cbar_kws={'label': '平均銷售額(千元)'},
ax=axes[0])
axes[0].set_title('產品類別與銷售通路平均銷售額',
fontsize=14,
fontweight='bold',
pad=15)
axes[0].set_xlabel('銷售通路', fontsize=12)
axes[0].set_ylabel('產品類別', fontsize=12)
# 子圖 2:數值變數相關係數熱力圖
correlation_matrix = advanced_data[numeric_vars].corr()
mask = np.triu(np.ones_like(correlation_matrix, dtype=bool)) # 建立上三角遮罩
sns.heatmap(correlation_matrix,
mask=mask, # 只顯示下三角
annot=True,
fmt='.3f',
cmap='coolwarm',
center=0,
square=True,
linewidths=2,
cbar_kws={'shrink': 0.8},
vmin=-1,
vmax=1,
ax=axes[1])
axes[1].set_title('行銷指標相關係數矩陣',
fontsize=14,
fontweight='bold',
pad=15)
plt.tight_layout()
plt.savefig('advanced_heatmap_analysis.png', dpi=300, bbox_inches='tight')
plt.show()
plt.close()
# 進階視覺化 5:複合圖表(使用 GridSpec)
# 在單一圖表中整合多種視覺化類型
fig = plt.figure(figsize=(18, 12))
gs = GridSpec(3, 3, figure=fig, hspace=0.3, wspace=0.3)
# 位置 1:主散佈圖(佔據左上 2x2 區域)
ax_main = fig.add_subplot(gs[0:2, 0:2])
scatter = ax_main.scatter(advanced_data['廣告支出'],
advanced_data['銷售額'],
c=advanced_data['客戶滿意度'],
s=advanced_data['轉換率'] * 10,
alpha=0.6,
cmap='viridis',
edgecolors='black',
linewidth=0.5)
ax_main.set_xlabel('廣告支出(元)', fontsize=12)
ax_main.set_ylabel('銷售額(元)', fontsize=12)
ax_main.set_title('多維度銷售分析(顏色=滿意度,大小=轉換率)',
fontsize=13,
fontweight='bold')
ax_main.grid(True, alpha=0.3)
# 加入色彩條
cbar = plt.colorbar(scatter, ax=ax_main)
cbar.set_label('客戶滿意度', fontsize=11)
# 位置 2:廣告支出分佈(右上)
ax_top = fig.add_subplot(gs[0:2, 2])
ax_top.hist(advanced_data['廣告支出'],
bins=30,
color='#3498db',
edgecolor='black',
alpha=0.7,
orientation='horizontal')
ax_top.set_xlabel('頻率', fontsize=11)
ax_top.set_title('廣告支出分佈', fontsize=12, fontweight='bold')
ax_top.grid(True, alpha=0.3, axis='x')
# 位置 3:銷售額分佈(左下)
ax_left = fig.add_subplot(gs[2, 0:2])
ax_left.hist(advanced_data['銷售額'],
bins=30,
color='#2ecc71',
edgecolor='black',
alpha=0.7)
ax_left.set_ylabel('頻率', fontsize=11)
ax_left.set_xlabel('銷售額(元)', fontsize=11)
ax_left.set_title('銷售額分佈', fontsize=12, fontweight='bold')
ax_left.grid(True, alpha=0.3, axis='y')
# 位置 4:產品類別統計(右下)
ax_right = fig.add_subplot(gs[2, 2])
category_counts = advanced_data['產品類別'].value_counts()
colors_pie = ['#3498db', '#2ecc71', '#f39c12', '#e74c3c']
ax_right.pie(category_counts.values,
labels=category_counts.index,
autopct='%1.1f%%',
colors=colors_pie,
startangle=90,
textprops={'fontsize': 9})
ax_right.set_title('產品類別分佈', fontsize=12, fontweight='bold')
# 整體標題
fig.suptitle('綜合銷售績效儀表板',
fontsize=16,
fontweight='bold',
y=0.995)
plt.savefig('composite_dashboard.png', dpi=300, bbox_inches='tight')
plt.show()
plt.close()
# 進階視覺化 6:分群散佈圖矩陣
# 使用不同子圖展示各通路的銷售表現
channels = advanced_data['銷售通路'].unique()
n_channels = len(channels)
fig, axes = plt.subplots(1, n_channels, figsize=(18, 6))
for idx, channel in enumerate(channels):
# 篩選特定通路的資料
channel_data = advanced_data[advanced_data['銷售通路'] == channel]
# 繪製散佈圖
axes[idx].scatter(channel_data['網站流量'],
channel_data['銷售額'],
c=channel_data['客戶滿意度'],
s=80,
alpha=0.6,
cmap='plasma',
edgecolors='black',
linewidth=0.5)
# 加入迴歸線
z = np.polyfit(channel_data['網站流量'],
channel_data['銷售額'], 1)
p = np.poly1d(z)
x_line = np.linspace(channel_data['網站流量'].min(),
channel_data['網站流量'].max(), 100)
axes[idx].plot(x_line, p(x_line),
'r--', linewidth=2,
label=f'迴歸線')
# 設定標題與標籤
axes[idx].set_title(f'{channel}\n(n={len(channel_data)})',
fontsize=12,
fontweight='bold')
axes[idx].set_xlabel('網站流量', fontsize=11)
if idx == 0:
axes[idx].set_ylabel('銷售額(元)', fontsize=11)
axes[idx].grid(True, alpha=0.3)
axes[idx].legend(fontsize=9)
# 整體標題
fig.suptitle('各銷售通路網站流量與銷售額關係',
fontsize=15,
fontweight='bold',
y=1.02)
plt.tight_layout()
plt.savefig('channel_scatter_matrix.png', dpi=300, bbox_inches='tight')
plt.show()
plt.close()
print("\n" + "=" * 70)
print("進階視覺化分析完成")
print("=" * 70)
print("\n已生成以下進階分析圖表:")
print("1. facet_grid_analysis.png - 小倍數圖分析")
print("2. pair_plot_analysis.png - 配對圖分析")
print("3. joint_plot_analysis.png - 聯合分佈圖")
print("4. advanced_heatmap_analysis.png - 多維度熱力圖")
print("5. composite_dashboard.png - 複合儀表板")
print("6. channel_scatter_matrix.png - 通路散佈圖矩陣")
視覺化設計原則
有效的資料視覺化不僅需要技術能力,更需要設計思維的支持。視覺化設計原則提供指引,協助我們建立清晰、準確且具有說服力的視覺作品。這些原則源自於認知心理學、資訊設計以及統計圖形學的研究成果,經過長期實踐驗證其有效性。
簡潔性是視覺化設計的首要原則。Edward Tufte 提出的資料墨水比(data-ink ratio)概念強調,圖表中的每個視覺元素都應該承載資訊,避免裝飾性的元素干擾訊息傳達。這意味著移除不必要的網格線、邊框、陰影以及 3D 效果,讓資料本身成為視覺焦點。然而,簡潔不等於簡陋,適當的視覺層次與細節仍然能夠提升圖表的專業度與可讀性。
色彩的選擇與使用需要謹慎考量多個因素。首先是色彩的意義聯想,例如紅色通常與警示或負面情況相關,綠色則代表正常或正面狀態。其次是色彩的辨識度,需要確保色盲人士也能夠理解圖表。色彩調色盤如 ColorBrewer 提供科學驗證的配色方案,針對不同資料類型(分類、順序、發散)提供適當的色彩組合。此外,色彩的數量也需要控制,過多的色彩會造成視覺混亂。
@startuml
!define DISABLE_LINK
!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
package "資料視覺化設計原則" {
package "核心設計原則" {
component "簡潔性原則" as simplicity
note right of simplicity
移除非必要元素
提高資料墨水比
避免過度裝飾
end note
component "準確性原則" as accuracy
note right of accuracy
正確的資料呈現
適當的尺度選擇
避免誤導性視覺化
end note
component "清晰性原則" as clarity
note right of clarity
明確的視覺層次
易讀的字型大小
適當的色彩對比
end note
}
package "色彩設計" {
component "色彩選擇" as color_choice
note right of color_choice
考量色盲友善
符合文化意涵
控制色彩數量
end note
component "色彩映射" as color_mapping
note right of color_mapping
分類資料:質性色彩
順序資料:漸層色彩
數值資料:連續色階
end note
}
package "版面配置" {
component "空間利用" as space
note right of space
適當的邊界留白
元素間距平衡
視覺焦點突出
end note
component "資訊層次" as hierarchy
note right of hierarchy
主要資訊優先
次要資訊輔助
細節資訊補充
end note
}
package "互動設計" {
component "使用者體驗" as ux
note right of ux
直覺的操作介面
即時的視覺回饋
流暢的互動體驗
end note
}
}
simplicity -[hidden]-> accuracy
accuracy -[hidden]-> clarity
color_choice -[hidden]-> color_mapping
space -[hidden]-> hierarchy
simplicity .> color_choice : 影響
accuracy .> color_mapping : 要求
clarity .> space : 需要
hierarchy .> ux : 支援
@enduml
結語
資料視覺化是連結資料分析與決策行動的關鍵橋樑。透過 Python 生態系統提供的強大工具,從 Matplotlib 的底層繪圖控制到 Seaborn 的高階統計視覺化,分析人員能夠將複雜的資料轉化為清晰直觀的視覺呈現。本文系統性地介紹了視覺化的核心概念、實務技術以及設計原則,提供完整的知識框架與實作指引。
掌握資料視覺化技術需要持續的學習與實踐。技術層面包括熟悉各種圖表類型的適用場景、理解視覺編碼的原理以及精通繪圖函式庫的應用程式介面。設計層面則需要培養對色彩、版面、資訊層次的敏感度,以及從受眾角度思考的能力。這兩個面向的結合,才能建立真正有效的視覺化作品。
隨著資料科學領域的發展,視覺化技術也在不斷演進。互動式視覺化讓使用者能夠動態探索資料,網頁技術的整合使得視覺化作品能夠輕鬆分享與嵌入。機器學習與視覺化的結合產生新的應用可能性,例如自動化的圖表選擇、智慧化的異常偵測視覺化以及解釋性人工智慧的視覺化呈現。
對於資料分析專業人員而言,投資時間提升視覺化能力將帶來顯著的回報。優秀的視覺化作品不僅能夠提升分析報告的品質,更能夠增強溝通說服力,讓資料洞察轉化為實際的商業價值。建議從基礎圖表類型開始練習,逐步嘗試更複雜的視覺化技術,並持續關注視覺化領域的最佳實踐與創新應用。透過不斷的實作與反思,每個人都能夠發展出屬於自己的視覺化風格,在資料科學的舞台上發揮更大的影響力。