Python 資料視覺化入門:Matplotlib 與 Plotly 實戰教學
Python 資料視覺化入門 - Matplotlib & Plotly
目錄
1. 資料視覺化的重要性
為什麼我們需要資料視覺化?
- 發現隱藏的模式和趨勢:人類大腦天生擅長處理視覺資訊,透過圖表可以快速識別出數據中的模式和趨勢
- 簡化複雜數據:將龐大的數據集轉換成視覺化圖表,能夠幫助我們理解其中的複雜關係
- 有效溝通:圖表是溝通分析結果的強大工具,能夠讓非專業人士也能理解複雜的數據故事
- 輔助決策:好的視覺化能夠提供直觀的證據支持,協助做出明智的決策
- 發現異常值:視覺化可以快速幫助我們找出數據中的異常點或離群值
案例:Anscombe’s Quartet
這四組數據集有幾乎相同的統計特性(平均值、標準差、相關係數等),但視覺化後呈現出完全不同的模式。
同學們,今天我們來認識一個經典的統計學與資料科學教材──Anscombe 四重奏,它清楚地提醒我們:光看數字摘要(平均值、標準差、相關係數等)是不夠的,資料視覺化是不可或缺的一步。
四重奏的共同統計特性
這四組 (x₁,y₁)、(x₂,y₂)、(x₃,y₃)、(x₄,y₄) 資料集雖然圖形看起來完全不同,卻在以下幾個統計量上幾乎相同:
- x 與 y 的平均值
- x 與 y 的標準差
- x 與 y 之間的線性相關係數
- 根據最小平方法所擬合的直線(y = a + b x)的截距 a 和斜率 b
若只看這些數字指標,你會認為它們的分布和關係都差不多;但實際上……
四張圖的不同特徵
圖片 | 特徵描述 |
---|---|
圖 1 (x₁, y₁) | 這是最「理想」的線性關係:點大致分布在一條直線附近,誤差(殘差)分布均勻。 |
圖 2 (x₂, y₂) | 真正的關係其實是二次函數:散點呈現明顯的曲線趨勢,但線性回歸線仍有不錯的統計指標。 |
圖 3 (x₃, y₃) | 幾乎所有點都在線性趨勢上,但有一個異常值 (outlier) 極度偏離,這個單點強烈影響了回歸線的位置。 |
圖 4 (x₄, y₄) | x 幾乎都一樣(集中在同一個值),只有一個點的 x 值極大,這個單一極端值決定了整個相關係數與回歸斜率。 |
我們學到什麼?
-
統計摘要有其限制 平均、標準差、相關係數等數值能快速概述資料,但可能掩蓋極端值或非線性趨勢。
-
視覺化不可或缺 透過散佈圖、殘差圖等視覺化工具,可以立刻看出非線性、群聚、離群點等特徵,避免誤用錯誤模型。
-
小心異常值與極端值 單一觀測值就有可能左右整體分析結果,需進一步檢查資料品質,決定是否應排除或另行處理。
建議
- 做線性回歸前,一定要先畫散布圖 (Scatter Plot)。
- 若發現曲線趨勢,可考慮多項式回歸或非線性模型。
- 遇到疑似離群點,先檢查資料來源,再決定如何處理(如剔除、修正或加權)。
- 養成以視覺化檢視資料的習慣,避免「黑箱式」的盲目套用統計模型。
- 使用視覺化工具(如 Matplotlib、Plotly)來輔助資料分析,讓數據故事更具說服力。
同學們,記住:「數字會說謊,眼睛不會」,讓我們下次見到資料時,都先動手畫圖,深刻理解它的特性!
2. 簡介 Matplotlib 和 Plotly
Matplotlib
- 特點:靜態、經典、高度客製化
- 適用場景:科學研究、學術論文、報告、需要精確控制的圖表
- 優勢:
- Python 生態系中最受歡迎的繪圖庫之一
- 與 NumPy 和 Pandas 完美整合
- 高度客製化,幾乎每個元素都可以調整
- 廣泛的社群支持和豐富的文檔
- 缺點:
- 預設風格較為基礎,需要較多代碼進行美化
- 互動性有限
- 學習曲線較陡峭
Plotly
- 特點:互動式、適用於 Web、美觀
- 適用場景:Web 應用、儀表板、需要互動性的資料展示、商業報告
- 優勢:
- 高度互動性(縮放、懸停資訊、選擇等)
- 美觀的預設樣式
- 可以輕鬆整合到 Web 應用中
- Plotly Express 提供簡潔的 API
- 缺點:
- 需要連接網路才能使用部分功能
- 對於某些複雜的客製化可能較為困難
- 在學術出版物中較少使用
3. Matplotlib 基礎
基本結構
Matplotlib 的核心是 Figure 和 Axes 對象:
- Figure:整個圖表容器,可以包含多個 Axes
- Axes:實際的繪圖區域,包含 x 軸、y 軸和繪圖數據
import matplotlib.pyplot as plt
# 創建 Figure 和 Axes 對象
fig, ax = plt.subplots()
# 或者使用 pyplot 接口(自動創建 Figure 和 Axes)
plt.plot([1, 2, 3, 4], [1, 4, 9, 16])
繪圖流程
最基本的 Matplotlib 繪圖流程如下:
import matplotlib.pyplot as plt
import numpy as np
# 準備數據
x = np.linspace(0, 10, 100) # 0 到 10 之間的 100 個等距點
y = np.sin(x) # 對應的 sine 值
# 創建圖表
plt.figure(figsize=(8, 4)) # 設置圖表大小(可選)
plt.plot(x, y) # 繪製線圖
# 顯示圖表
plt.show()
添加圖表元素
import matplotlib.pyplot as plt
import numpy as np
# 準備數據
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)
# 創建圖表
plt.figure(figsize=(10, 6))
plt.plot(x, y1, label='sin(x)') # 添加圖例標籤
plt.plot(x, y2, label='cos(x)')
# 添加標題和標籤
plt.title('Sin and Cos Functions', fontsize=15) # 標題
plt.xlabel('x', fontsize=12) # x 軸標籤
plt.ylabel('y', fontsize=12) # y 軸標籤
# 添加圖例
plt.legend(fontsize=12)
# 添加網格
plt.grid(True, linestyle='--', alpha=0.7)
# 自定義 x 和 y 軸範圍
plt.xlim(0, 10)
plt.ylim(-1.5, 1.5)
plt.show()
常見圖表類型
線圖 (Line Plot)
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 10, 100)
y = np.sin(x)
plt.figure(figsize=(8, 4))
plt.plot(x, y, linestyle='-', color='blue', linewidth=2, marker='o', markersize=5, markevery=10)
plt.title('Line Plot Example')
plt.show()
散點圖 (Scatter Plot)
import matplotlib.pyplot as plt
import numpy as np
# 隨機生成數據
np.random.seed(42)
x = np.random.rand(50)
y = np.random.rand(50)
colors = np.random.rand(50)
sizes = 1000 * np.random.rand(50)
plt.figure(figsize=(8, 6))
plt.scatter(x, y, c=colors, s=sizes, alpha=0.6, cmap='viridis')
plt.colorbar() # 添加顏色條
plt.title('Scatter Plot Example')
plt.show()
長條圖 (Bar Plot)
import matplotlib.pyplot as plt
categories = ['A', 'B', 'C', 'D', 'E']
values = [3, 7, 2, 5, 8]
plt.figure(figsize=(8, 6))
plt.bar(categories, values, color='skyblue', edgecolor='black')
plt.title('Bar Plot Example')
plt.xlabel('Categories')
plt.ylabel('Values')
plt.show()
子圖與布局 (Subplots and Layouts)
import matplotlib.pyplot as plt
import numpy as np
# 創建 2x2 的子圖
fig, axs = plt.subplots(2, 2, figsize=(12, 8))
# 子圖 1:線圖
x = np.linspace(0, 5, 100)
axs[0, 0].plot(x, np.sin(x))
axs[0, 0].set_title('Sine Function')
# 子圖 2:散點圖
np.random.seed(42)
x = np.random.rand(50)
y = np.random.rand(50)
axs[0, 1].scatter(x, y, c='red', alpha=0.6)
axs[0, 1].set_title('Scatter Plot')
# 子圖 3:長條圖
categories = ['A', 'B', 'C', 'D', 'E']
values = [3, 7, 2, 5, 8]
axs[1, 0].bar(categories, values)
axs[1, 0].set_title('Bar Plot')
# 子圖 4:直方圖
data = np.random.randn(1000)
axs[1, 1].hist(data, bins=30, alpha=0.7, color='green')
axs[1, 1].set_title('Histogram')
# 調整子圖間距
plt.tight_layout()
plt.show()
Matplotlib 參考資源
- Matplotlib Pyplot Tutorial - 官方教學,涵蓋主要概念
- Matplotlib Cheatsheets - 快速參考表
- Matplotlib Gallery - 各種圖表類型的範例
Matplotlib 動手練習範例
練習 1:基本線圖
繪製台灣近年平均氣溫趨勢圖。
import matplotlib.pyplot as plt
import numpy as np
# 假設數據:台灣 2015-2024 年的年平均氣溫 (攝氏度)
years = np.arange(2015, 2025)
temperatures = [24.2, 24.8, 24.5, 25.1, 25.3, 25.6, 24.9, 25.7, 25.4, 26.1]
plt.figure(figsize=(10, 6))
plt.plot(years, temperatures, marker='o', linestyle='-', color='red', linewidth=2)
plt.title('台灣年平均氣溫 (2015-2024)', fontsize=15)
plt.xlabel('年份', fontsize=12)
plt.ylabel('溫度 (°C)', fontsize=12)
plt.grid(True, linestyle='--', alpha=0.7)
# 標註最高點和最低點
max_temp_idx = np.argmax(temperatures)
min_temp_idx = np.argmin(temperatures)
plt.annotate(f'最高: {temperatures[max_temp_idx]}°C',
xy=(years[max_temp_idx], temperatures[max_temp_idx]),
xytext=(years[max_temp_idx]+0.5, temperatures[max_temp_idx]+0.3),
arrowprops=dict(facecolor='black', shrink=0.05, width=1.5))
plt.annotate(f'最低: {temperatures[min_temp_idx]}°C',
xy=(years[min_temp_idx], temperatures[min_temp_idx]),
xytext=(years[min_temp_idx]-1, temperatures[min_temp_idx]-0.5),
arrowprops=dict(facecolor='black', shrink=0.05, width=1.5))
plt.xticks(years)
plt.ylim(23.5, 26.5)
plt.savefig('taiwan_temperature_trend.png', dpi=300, bbox_inches='tight')
plt.show()
練習 2:比較長條圖
比較不同程式語言的受歡迎程度。
import matplotlib.pyplot as plt
import numpy as np
# 數據:2024 年不同程式語言的受歡迎程度評分
languages = ['Python', 'JavaScript', 'Java', 'C++', 'Go', 'Rust', 'TypeScript']
ratings_2023 = [9.8, 8.5, 7.2, 6.8, 6.5, 5.8, 7.9]
ratings_2024 = [9.9, 8.3, 6.9, 7.0, 7.2, 6.9, 8.5]
# 設置長條的寬度和位置
bar_width = 0.35
x = np.arange(len(languages))
fig, ax = plt.subplots(figsize=(12, 7))
# 繪製長條圖
bar1 = ax.bar(x - bar_width/2, ratings_2023, bar_width, label='2023', color='skyblue', edgecolor='black')
bar2 = ax.bar(x + bar_width/2, ratings_2024, bar_width, label='2024', color='lightcoral', edgecolor='black')
# 添加圖表元素
ax.set_title('程式語言受歡迎程度比較 (2023 vs 2024)', fontsize=16)
ax.set_xlabel('程式語言', fontsize=14)
ax.set_ylabel('受歡迎程度評分', fontsize=14)
ax.set_xticks(x)
ax.set_xticklabels(languages, fontsize=12, rotation=0)
ax.set_ylim(0, 12)
ax.legend(fontsize=12)
# 在長條上方顯示數值
def add_labels(bars):
for bar in bars:
height = bar.get_height()
ax.annotate(f'{height}',
xy=(bar.get_x() + bar.get_width() / 2, height),
xytext=(0, 3), # 3 點的垂直偏移
textcoords="offset points",
ha='center', va='bottom',
fontsize=10)
add_labels(bar1)
add_labels(bar2)
# 添加網格線 (只顯示 y 軸方向)
ax.grid(True, axis='y', linestyle='--', alpha=0.7)
plt.tight_layout()
plt.savefig('programming_languages_popularity.png', dpi=300, bbox_inches='tight')
plt.show()
練習 3:多圖表組合
分析某公司的月銷售額和客戶數量關係。
import matplotlib.pyplot as plt
import numpy as np
# 模擬數據:2023 年某公司的月銷售額和客戶數量
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
sales = [120, 135, 125, 150, 165, 180, 190, 200, 210, 190, 170, 230] # 單位:萬元
customers = [1500, 1600, 1550, 1750, 1900, 2100, 2200, 2350, 2450, 2300, 2150, 2600] # 客戶數
# 創建一個圖表,包含兩個 y 軸
fig, ax1 = plt.subplots(figsize=(12, 7))
# 設置第一個 y 軸 (銷售額)
color = 'tab:blue'
ax1.set_xlabel('月份', fontsize=12)
ax1.set_ylabel('銷售額 (萬元)', color=color, fontsize=12)
line1 = ax1.plot(months, sales, marker='o', color=color, linewidth=2, label='銷售額')
ax1.tick_params(axis='y', labelcolor=color)
ax1.grid(True, linestyle='--', alpha=0.3)
# 創建第二個 y 軸 (客戶數)
ax2 = ax1.twinx()
color = 'tab:red'
ax2.set_ylabel('客戶數', color=color, fontsize=12)
line2 = ax2.plot(months, customers, marker='s', color=color, linewidth=2, label='客戶數')
ax2.tick_params(axis='y', labelcolor=color)
# 添加標題
plt.title('2023 年月銷售額和客戶數量趨勢', fontsize=15)
# 合併兩個圖例
lines = line1 + line2
labels = [l.get_label() for l in lines]
ax1.legend(lines, labels, loc='upper left', fontsize=12)
# 在銷售額達到峰值的月份添加註釋
max_sales_idx = np.argmax(sales)
max_sales = sales[max_sales_idx]
max_sales_month = months[max_sales_idx]
ax1.annotate(f'銷售峰值: {max_sales}萬元',
xy=(max_sales_idx, max_sales),
xytext=(max_sales_idx-2, max_sales+15),
arrowprops=dict(facecolor='black', shrink=0.05, width=1),
fontsize=10)
plt.tight_layout()
plt.savefig('sales_customers_trend.png', dpi=300, bbox_inches='tight')
plt.show()
4. Plotly 基礎
核心優勢
Plotly 的主要優勢在於:
- 互動性:用戶可以縮放、懸停查看資訊、選擇數據點等
- 美觀性:默認設計精美,顏色方案專業
- Web 整合:適合嵌入網頁和儀表板
- 簡單語法:特別是 Plotly Express 的高級 API 非常簡潔
Plotly 基本結構
Plotly 的圖表由 Figure 對象組成,通常使用 Pandas DataFrame 作為數據輸入。
import plotly.express as px
import pandas as pd
# 創建一個簡單的 DataFrame
df = pd.DataFrame({
'x': [1, 2, 3, 4, 5],
'y': [1, 4, 9, 16, 25]
})
# 使用 Plotly Express 創建圖表
fig = px.line(df, x='x', y='y', title='簡單的線圖')
# 顯示圖表
fig.show()
Plotly 繪圖流程
使用 Plotly Express 繪圖的基本流程如下:
import plotly.express as px
import pandas as pd
import numpy as np
# 準備數據
x = np.linspace(0, 10, 100)
y = np.sin(x)
df = pd.DataFrame({'x': x, 'y': y})
# 創建圖表
fig = px.line(df, x='x', y='y', title='Sine Function')
# 更新圖表佈局(可選)
fig.update_layout(
xaxis_title='X Axis',
yaxis_title='Sin(x)',
template='plotly_white' # 使用白色主題
)
# 更新軌跡(可選)
fig.update_traces(line=dict(color='red', width=2))
# 顯示圖表
fig.show()
與 Matplotlib 的區別
- 互動性:Plotly 提供互動式圖表,Matplotlib 主要是靜態圖表
- 數據輸入:Plotly 通常使用 Pandas DataFrame,Matplotlib 更靈活,可使用各種數據結構
- 輸出格式:Plotly 主要針對 Web 和儀表板,Matplotlib 適合用於學術出版物和報告
- 語法風格:Plotly Express 有更簡潔的高級 API,Matplotlib 更詳細和可控
- 渲染方式:Plotly 在瀏覽器中渲染,Matplotlib 在 Python 環境中渲染
常見的 Plotly 圖表類型
線圖 (Line Plot)
import plotly.express as px
import pandas as pd
import numpy as np
# 準備數據
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)
df = pd.DataFrame({
'x': np.concatenate([x, x]),
'y': np.concatenate([y1, y2]),
'function': ['sin(x)'] * 100 + ['cos(x)'] * 100
})
# 創建圖表
fig = px.line(df, x='x', y='y', color='function', title='Sin and Cos Functions',
labels={'x': 'X Value', 'y': 'Y Value', 'function': 'Function Type'},
line_dash='function') # 使用不同的線型
# 更新佈局
fig.update_layout(
template='plotly_white',
xaxis_title='X',
yaxis_title='Y',
legend_title='Function Type'
)
fig.show()
散點圖 (Scatter Plot)
import plotly.express as px
import numpy as np
import pandas as pd
# 生成隨機數據
np.random.seed(42)
n_points = 100
x = np.random.randn(n_points)
y = 2 * x + np.random.randn(n_points) * 1.5
sizes = np.random.randint(5, 25, n_points)
categories = np.random.choice(['A', 'B', 'C', 'D'], n_points)
df = pd.DataFrame({
'x': x,
'y': y,
'size': sizes,
'category': categories,
'value': np.abs(x * y)
})
# 創建散點圖
fig = px.scatter(df, x='x', y='y',
color='category', # 按類別著色
size='size', # 按大小變化
hover_data=['value'], # 懸停時顯示的附加數據
title='Interactive Scatter Plot',
labels={'x': 'X Axis', 'y': 'Y Axis', 'category': 'Category'},
color_discrete_sequence=px.colors.qualitative.Plotly) # 使用 Plotly 顏色序列
# 添加趨勢線
fig.update_layout(
template='plotly_white',
legend_title='Category'
)
fig.show()
長條圖 (Bar Plot)
import plotly.express as px
import pandas as pd
# 準備數據
data = {
'Country': ['USA', 'China', 'Japan', 'Germany', 'India', 'UK', 'France', 'Italy'],
'GDP_2022': [25035, 18321, 4301, 4031, 3198, 3176, 2782, 2058], # 單位:十億美元
'GDP_2023': [26854, 17886, 4410, 4456, 3332, 3736, 2925, 2169]
}
df = pd.DataFrame(data)
# 長數據轉換
df_long = pd.melt(df, id_vars=['Country'],
value_vars=['GDP_2022', 'GDP_2023'],
var_name='Year', value_name='GDP')
# 提取年份
df_long['Year'] = df_long['Year'].str.replace('GDP_', '')
# 創建分組長條圖
fig = px.bar(df_long, x='Country', y='GDP', color='Year', barmode='group',
title='各國 GDP 比較 (2022-2023)',
labels={'GDP': 'GDP (十億美元)', 'Country': '國家', 'Year': '年份'},
hover_data=['Country', 'Year', 'GDP'],
color_discrete_sequence=['#1f77b4', '#ff7f0e'])
# 更新佈局
fig.update_layout(
template='plotly_white',
legend_title='年份',
xaxis_title='國家',
yaxis_title='GDP (十億美元)',
xaxis={'categoryorder':'total descending'} # 根據總 GDP 降序排列國家
)
fig.show()
Plotly 參考資源
- Plotly 官網 - 探索功能與範例
- Plotly Express - 高級 API 文檔
- Plotly 筆記 (GitHub) - 特定圖表類型和進階設定
Plotly 動手練習範例
練習 1:互動式股票價格趨勢圖
模擬股票歷史價格數據並創建互動式趨勢圖。
import plotly.express as px
import plotly.graph_objects as go
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
# 生成模擬股票數據
np.random.seed(42)
n_days = 252 # 約一年的交易日
# 開始日期:今年初
start_date = datetime(2023, 1, 1)
date_list = [start_date + timedelta(days=i) for i in range(n_days)]
# 模擬價格走勢
def generate_price_series(start_price, volatility):
prices = [start_price]
for i in range(n_days - 1):
change = np.random.normal(0, volatility)
# 添加一些趨勢和季節性
trend = 0.05 * np.sin(i / 40) # 長期趨勢
seasonal = 0.02 * np.sin(i / 10) # 短期波動
new_price = prices[-1] * (1 + change/100 + trend + seasonal)
prices.append(max(new_price, 0.1 * start_price)) # 確保價格不會太低
return prices
# 生成三支股票的價格數據
stocks = {
'TECH': generate_price_series(150, 2.0),
'BANK': generate_price_series(80, 1.2),
'RETAIL': generate_price_series(50, 1.8)
}
# 創建數據框
df_stocks = pd.DataFrame({
'Date': date_list * 3,
'Price': np.concatenate([stocks['TECH'], stocks['BANK'], stocks['RETAIL']]),
'Stock': ['TECH'] * n_days + ['BANK'] * n_days + ['RETAIL'] * n_days
})
# 計算每日價格變化
df_pivot = df_stocks.pivot(index='Date', columns='Stock', values='Price')
daily_returns = df_pivot.pct_change().dropna()
# 創建互動式圖表
fig = px.line(df_stocks, x='Date', y='Price', color='Stock',
title='股票價格趨勢 (2023年)',
labels={'Price': '股價 (USD)', 'Date': '日期', 'Stock': '股票'},
hover_data={'Price': ':.2f'}, # 格式化價格顯示
color_discrete_sequence=['#2ca02c', '#1f77b4', '#ff7f0e'])
# 增加移動平均線 (30日)
for stock in stocks.keys():
df_stock = df_stocks[df_stocks['Stock'] == stock]
ma30 = df_stock['Price'].rolling(window=30).mean()
fig.add_trace(
go.Scatter(
x=df_stock['Date'],
y=ma30,
mode='lines',
line=dict(width=1, dash='dash'),
name=f'{stock} 30日均線',
hoverinfo='skip'
)
)
# 更新佈局和互動選項
fig.update_layout(
template='plotly_white',
coloraxis_colorbar=dict(title='GDP (十億美元)'),
height=600,
margin={"r": 0, "t": 50, "l": 0, "b": 0}
)
# 添加範圍選擇器
fig.update_xaxes(
rangeslider_visible=True,
rangeselector=dict(
buttons=list([
dict(count=1, label="1月", step="month", stepmode="backward"),
dict(count=3, label="3月", step="month", stepmode="backward"),
dict(count=6, label="6月", step="month", stepmode="backward"),
dict(step="all", label="全部")
])
)
)
fig.show()
練習 2:互動式儀表板 - 銷售數據分析
創建一個包含多個圖表的銷售數據分析儀表板。
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import pandas as pd
import numpy as np
# 生成模擬銷售數據
np.random.seed(42)
# 產品類別和區域
categories = ['電子產品', '家居用品', '服裝', '食品', '書籍']
regions = ['北部', '中部', '南部', '東部']
months = ['1月', '2月', '3月', '4月', '5月', '6月',
'7月', '8月', '9月', '10月', '11月', '12月']
# 生成銷售數據
n_records = 1000
data = {
'日期': pd.date_range(start='2023-01-01', end='2023-12-31', periods=n_records),
'產品類別': np.random.choice(categories, n_records),
'區域': np.random.choice(regions, n_records),
'銷售額': np.random.randint(100, 10000, n_records),
'數量': np.random.randint(1, 50, n_records)
}
df = pd.DataFrame(data)
# 添加月份列
df['月份'] = df['日期'].dt.strftime('%m月')
df['月份數字'] = df['日期'].dt.month
# 1. 創建一個包含多個子圖的儀表板
fig = make_subplots(
rows=2, cols=2,
subplot_titles=('各產品類別銷售分布', '各區域銷售額比較', '月度銷售趨勢', '產品類別和區域銷售熱圖'),
specs=[[{"type": "pie"}, {"type": "bar"}],
[{"type": "scatter"}, {"type": "heatmap"}]],
vertical_spacing=0.1,
horizontal_spacing=0.1
)
# 2. 子圖1:產品類別銷售額分布 (圓餅圖)
category_sales = df.groupby('產品類別')['銷售額'].sum().reset_index()
fig.add_trace(
go.Pie(
labels=category_sales['產品類別'],
values=category_sales['銷售額'],
textinfo='percent+label',
hole=0.4,
marker=dict(colors=px.colors.qualitative.Plotly)
),
row=1, col=1
)
# 3. 子圖2:各區域銷售額比較 (長條圖)
region_sales = df.groupby('區域')['銷售額'].sum().reset_index()
fig.add_trace(
go.Bar(
x=region_sales['區域'],
y=region_sales['銷售額'],
text=region_sales['銷售額'],
textposition='auto',
marker_color='rgb(158,202,225)'
),
row=1, col=2
)
# 4. 子圖3:月度銷售趨勢 (線圖)
monthly_sales = df.groupby('月份數字')['銷售額'].sum().reset_index()
monthly_sales = monthly_sales.sort_values('月份數字')
monthly_sales['月份'] = [months[i-1] for i in monthly_sales['月份數字']]
fig.add_trace(
go.Scatter(
x=monthly_sales['月份'],
y=monthly_sales['銷售額'],
mode='lines+markers',
line=dict(width=3, color='rgb(0,128,0)'),
marker=dict(size=8, symbol='circle')
),
row=2, col=1
)
# 5. 子圖4:產品類別和區域銷售熱圖
heatmap_data = df.pivot_table(
values='銷售額',
index='產品類別',
columns='區域',
aggfunc='sum'
)
fig.add_trace(
go.Heatmap(
z=heatmap_data.values,
x=heatmap_data.columns,
y=heatmap_data.index,
colorscale='Viridis',
showscale=True
),
row=2, col=2
)
# 更新佈局
fig.update_layout(
title_text='2023年銷售數據分析儀表板',
height=800,
width=1000,
showlegend=False,
template='plotly_white'
)
# 更新子圖軸標題
fig.update_yaxes(title_text='銷售額', row=1, col=2)
fig.update_yaxes(title_text='銷售額', row=2, col=1)
fig.update_xaxes(title_text='月份', row=2, col=1)
fig.update_xaxes(title_text='區域', row=2, col=2)
fig.show()
練習 3:互動式地圖 - 全球數據視覺化
展示如何使用 Plotly 創建互動式地圖視覺化。
import plotly.express as px
import pandas as pd
import numpy as np
# 創建模擬全球 GDP 和人口數據
countries = ['USA', 'China', 'Japan', 'Germany', 'India', 'UK', 'France', 'Italy', 'Brazil',
'Canada', 'Russia', 'South Korea', 'Australia', 'Spain', 'Mexico', 'Indonesia',
'Netherlands', 'Switzerland', 'Turkey', 'Saudi Arabia']
# 模擬 GDP 數據 (單位:十億美元)
gdp_values = [25000, 18000, 4300, 4100, 3500, 3200, 2800, 2000, 1800, 1700,
1600, 1700, 1500, 1400, 1300, 1200, 900, 800, 700, 800]
# 模擬人口數據 (單位:百萬)
population_values = [330, 1400, 126, 83, 1380, 67, 65, 60, 212, 38,
144, 52, 25, 47, 128, 270, 17, 8.5, 84, 35]
# 模擬 GDP 成長率 (%)
growth_values = [2.3, 5.5, 1.0, 1.5, 7.0, 1.1, 1.4, 0.8, 3.2, 1.9,
1.3, 2.8, 2.2, 1.2, 2.0, 5.0, 2.3, 3.0, 4.8, 1.8]
# 模擬人均 GDP (USD)
gdp_per_capita = [np.round(g * 1000 / p, 2) for g, p in zip(gdp_values, population_values)]
# 創建 DataFrame
df_world = pd.DataFrame({
'country': countries,
'GDP (Billion USD)': gdp_values,
'Population (Million)': population_values,
'GDP Growth (%)': growth_values,
'GDP per Capita (USD)': gdp_per_capita
})
# 添加 ISO 國家代碼 (用於地圖)
iso_codes = ['USA', 'CHN', 'JPN', 'DEU', 'IND', 'GBR', 'FRA', 'ITA', 'BRA',
'CAN', 'RUS', 'KOR', 'AUS', 'ESP', 'MEX', 'IDN', 'NLD', 'CHE', 'TUR', 'SAU']
df_world['iso_alpha'] = iso_codes
# 創建互動式世界地圖
fig = px.choropleth(df_world,
locations='iso_alpha',
color='GDP (Billion USD)',
hover_name='country',
hover_data=['GDP (Billion USD)', 'Population (Million)',
'GDP Growth (%)', 'GDP per Capita (USD)'],
color_continuous_scale=px.colors.sequential.Plasma,
projection='natural earth',
title='2023年全球GDP分布')
# 更新佈局
fig.update_layout(
template='plotly_white',
coloraxis_colorbar=dict(title='GDP (十億美元)'),
height=600,
margin={"r": 0, "t": 50, "l": 0, "b": 0}
)
fig.show()
# 創建氣泡地圖 (顯示 GDP、人口和成長率)
fig_bubble = px.scatter_geo(df_world,
locations='iso_alpha',
color='GDP Growth (%)',
size='GDP (Billion USD)',
hover_name='country',
hover_data=['GDP (Billion USD)', 'Population (Million)',
'GDP Growth (%)', 'GDP per Capita (USD)'],
color_continuous_scale=px.colors.sequential.Viridis,
projection='natural earth',
title='全球經濟指標氣泡圖 (大小=GDP, 顏色=GDP成長率)')
# 更新佈局
fig_bubble.update_layout(
template='plotly_white',
height=600,
margin={"r": 0, "t": 50, "l": 0, "b": 0}
)
fig_bubble.show()
結合 Matplotlib 和 Plotly 的優勢
在實際應用中,可以根據需求靈活選擇使用 Matplotlib 或 Plotly:
何時使用 Matplotlib
- 生成靜態報告和學術論文圖表
- 需要高度精確控制圖表外觀
- 需要與 NumPy 生態系統緊密整合
- 處理大量數據時 (Matplotlib 通常比 Plotly 更高效)
- 需要將圖表保存為高質量圖像文件
# Matplotlib 範例:論文品質圖表
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.ticker import MultipleLocator
# 設置風格
plt.style.use('seaborn-whitegrid')
# 創建數據
x = np.linspace(0, 10, 100)
y1 = 4 + 2 * np.sin(2 * x)
y2 = 4 + 2 * np.sin(2 * x + np.pi/4)
y3 = 4 + 2 * np.sin(2 * x + np.pi/2)
# 創建圖表和軸
fig, ax = plt.subplots(figsize=(10, 6))
# 繪製三條線
ax.plot(x, y1, 'o-', color='#1f77b4', markersize=4, linewidth=1.5, markevery=10, label='Phase 0')
ax.plot(x, y2, 's-', color='#ff7f0e', markersize=4, linewidth=1.5, markevery=10, label='Phase π/4')
ax.plot(x, y3, '^-', color='#2ca02c', markersize=4, linewidth=1.5, markevery=10, label='Phase π/2')
# 添加網格
ax.grid(True, linestyle='--', alpha=0.7)
# 設置坐標軸
ax.set_xlim(0, 10)
ax.set_ylim(0, 8)
ax.xaxis.set_major_locator(MultipleLocator(1))
ax.yaxis.set_major_locator(MultipleLocator(1))
# 添加標題和標籤
ax.set_title('Shifted Sine Waves', fontsize=16, pad=15)
ax.set_xlabel('x', fontsize=14, labelpad=10)
ax.set_ylabel('y = 4 + 2·sin(2x + φ)', fontsize=14, labelpad=10)
ax.legend(fontsize=12, frameon=True)
# 添加註釋
ax.annotate('Amplitude = 2', xy=(2, 6), xytext=(3, 7),
arrowprops=dict(facecolor='black', shrink=0.05, width=1.5),
fontsize=12)
# 調整佈局並保存
plt.tight_layout()
plt.savefig('publication_quality_figure.png', dpi=300, bbox_inches='tight')
plt.savefig('publication_quality_figure.pdf', bbox_inches='tight')
plt.show()
何時使用 Plotly
- 創建互動式儀表板
- 需要用戶能夠探索數據
- 嵌入到網頁或 Jupyter Notebook 中
- 需要分享給不熟悉 Python 的使用者
- 需要展示多維數據並讓用戶自行發掘洞見
# Plotly 範例:互動式儀表板元素
import plotly.graph_objects as go
import plotly.express as px
import pandas as pd
import numpy as np
# 創建模擬股票數據
np.random.seed(42)
dates = pd.date_range('2023-01-01', '2023-12-31', freq='B') # 工作日
prices = 100 * (1 + np.cumsum(np.random.normal(0, 0.015, len(dates))))
volumes = np.random.randint(100000, 1000000, len(dates))
# 創建 DataFrame
df = pd.DataFrame({
'Date': dates,
'Price': prices,
'Volume': volumes
})
# 計算移動平均線
df['MA5'] = df['Price'].rolling(window=5).mean()
df['MA20'] = df['Price'].rolling(window=20).mean()
# 創建燭線圖
fig = go.Figure()
# 添加價格線
fig.add_trace(
go.Scatter(
x=df['Date'],
y=df['Price'],
mode='lines',
name='股價',
line=dict(color='rgba(0,0,0,0.5)')
)
)
# 添加移動平均線
fig.add_trace(
go.Scatter(
x=df['Date'],
y=df['MA5'],
mode='lines',
name='5日均線',
line=dict(color='rgba(255,0,0,0.8)', width=1)
)
)
fig.add_trace(
go.Scatter(
x=df['Date'],
y=df['MA20'],
mode='lines',
name='20日均線',
line=dict(color='rgba(0,0,255,0.8)', width=1.5)
)
)
# 添加成交量長條圖 (使用 subplots)
fig.add_trace(
go.Bar(
x=df['Date'],
y=df['Volume'],
name='成交量',
marker=dict(color='rgba(0,128,0,0.5)'),
opacity=0.8,
yaxis='y2'
)
)
# 更新佈局
fig.update_layout(
title='2023年股票價格和成交量分析',
yaxis=dict(
title='股價',
titlefont=dict(color='rgba(0,0,0,0.8)'),
tickfont=dict(color='rgba(0,0,0,0.8)')
),
yaxis2=dict(
title='成交量',
titlefont=dict(color='green'),
tickfont=dict(color='green'),
anchor='x',
overlaying='y',
side='right'
),
xaxis=dict(
title='日期',
rangeslider=dict(visible=True),
rangeselector=dict(
buttons=list([
dict(count=1, label='1月', step='month', stepmode='backward'),
dict(count=3, label='3月', step='month', stepmode='backward'),
dict(count=6, label='6月', step='month', stepmode='backward'),
dict(step='all', label='全部')
])
)
),
template='plotly_white',
hovermode='x unified',
legend=dict(orientation='h', y=1.02)
)
fig.show()
結論:選擇適合您需求的工具
沒有一個「最好」的可視化工具,而是要根據您的具體需求選擇合適的工具:
-
目標受眾是誰?
- 學術/研究人員 → Matplotlib
- 商業分析/網頁使用者 → Plotly
-
呈現方式是什麼?
- 報告/論文/出版物 → Matplotlib
- 網頁/儀表板/互動式演示 → Plotly
-
數據的複雜度如何?
- 需要精確控制的複雜圖表 → Matplotlib
- 多維數據的互動式探索 → Plotly
-
開發時間有多少?
- 需要快速生成標準圖表 → Plotly Express
- 有時間進行詳細客製化 → Matplotlib
-
誰會使用這些圖表?
- 僅供個人或團隊使用 → Matplotlib 或 Plotly 均可
- 需要分享給不同技術背景的人 → Plotly
最佳實踐建議
- 從問題開始思考:先確定您要回答什麼問題,再選擇適合的視覺化工具
- 保持簡單:不要過度裝飾您的圖表,讓數據說話
- 考慮色彩無障礙:選擇色盲友好的調色板
- 註釋關鍵點:突出顯示重要的數據點或趨勢
- 適當使用互動性:互動功能應該增強而不是分散對數據的理解
無論您選擇哪種工具,記住:好的資料視覺化不是關於炫技,而是關於有效地傳達數據中的見解和故事。
祝您在 Python 資料視覺化的旅程中取得成功!