在當今資料驅動的商業環境中,統計分析方法與資料視覺化技術已成為從海量資料中萃取價值的核心能力。統計方法提供了嚴謹的數學框架,協助我們理解資料中的模式、關聯與因果關係,從簡單的描述性統計到複雜的預測模型,每種方法都有其適用的場景與限制。而資料視覺化則扮演著將這些抽象的統計結果轉化為直觀圖表的關鍵角色,讓技術與非技術背景的利益關係人都能快速掌握資料洞察。本文將系統性地探討資料科學領域的核心統計方法,包括相關分析、迴歸模型與假設檢定等技術,並深入剖析資料視覺化的設計原則、工具選擇與實作技巧,協助讀者建立從資料探索到洞察呈現的完整分析流程。
相關分析:探索變數間的關聯性
相關分析是探索性資料分析中最基礎也最重要的統計方法之一,它幫助我們量化兩個或多個變數之間的關聯強度與方向。在進行任何複雜的建模工作之前,理解變數間的相關性可以為後續的分析提供重要的方向指引,避免在不相關的變數上浪費時間,同時也能發現潛在的共線性問題。
Pearson 相關係數是最常用的線性相關度量指標,其值域範圍從 -1 到 +1。當係數接近 +1 時,表示兩個變數呈現強烈的正相關關係,即一個變數增加時,另一個變數也傾向增加。相反地,係數接近 -1 則表示強烈的負相關,一個變數增加時另一個變數傾向減少。係數接近 0 則意味著兩個變數之間不存在線性關係。需要注意的是,Pearson 相關係數只能捕捉線性關係,對於非線性的關聯模式則需要使用其他方法。
Spearman 等級相關係數與 Kendall tau 係數是兩種基於排序的非參數相關度量方法。這些方法不假設資料服從特定的分佈,對異常值的敏感度較低,特別適合用於處理順序型資料或存在極端值的情況。在金融資料分析中,由於股價報酬率常出現極端波動,Spearman 相關係數往往比 Pearson 相關係數更能反映真實的關聯性。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from scipy import stats
from scipy.stats import pearsonr, spearmanr, kendalltau
import warnings
warnings.filterwarnings('ignore')
# 設定中文字型與視覺風格
plt.rcParams['font.sans-serif'] = ['Microsoft JhengHei']
plt.rcParams['axes.unicode_minus'] = False
sns.set_style('whitegrid')
sns.set_palette('husl')
class CorrelationAnalyzer:
"""
相關分析器提供多種相關係數計算與視覺化方法
支援 Pearson、Spearman、Kendall 等不同類型的相關分析
"""
def __init__(self, data: pd.DataFrame):
"""
初始化相關分析器
Parameters:
-----------
data : pd.DataFrame
待分析的資料框架
"""
self.data = data
self.numeric_columns = data.select_dtypes(include=[np.number]).columns
def calculate_correlations(self, method='pearson'):
"""
計算相關矩陣
Parameters:
-----------
method : str
相關係數類型,可選 'pearson'、'spearman'、'kendall'
Returns:
--------
pd.DataFrame
相關係數矩陣
"""
if method not in ['pearson', 'spearman', 'kendall']:
raise ValueError("方法必須是 'pearson'、'spearman' 或 'kendall'")
corr_matrix = self.data[self.numeric_columns].corr(method=method)
return corr_matrix
def test_significance(self, var1, var2, method='pearson', alpha=0.05):
"""
檢定兩個變數相關性的統計顯著性
Parameters:
-----------
var1, var2 : str
變數名稱
method : str
相關係數類型
alpha : float
顯著水準
Returns:
--------
dict
包含相關係數、p值與顯著性判斷的字典
"""
x = self.data[var1].dropna()
y = self.data[var2].dropna()
# 確保兩個序列長度相同
common_idx = x.index.intersection(y.index)
x = x.loc[common_idx]
y = y.loc[common_idx]
if method == 'pearson':
corr, pvalue = pearsonr(x, y)
elif method == 'spearman':
corr, pvalue = spearmanr(x, y)
elif method == 'kendall':
corr, pvalue = kendalltau(x, y)
else:
raise ValueError("不支援的相關方法")
return {
'correlation': corr,
'p_value': pvalue,
'significant': pvalue < alpha,
'alpha': alpha,
'method': method
}
def plot_correlation_matrix(self, method='pearson', figsize=(12, 10)):
"""
繪製相關係數矩陣熱力圖
Parameters:
-----------
method : str
相關係數類型
figsize : tuple
圖表尺寸
"""
corr_matrix = self.calculate_correlations(method=method)
plt.figure(figsize=figsize)
# 建立遮罩矩陣,只顯示下三角
mask = np.triu(np.ones_like(corr_matrix, dtype=bool))
sns.heatmap(
corr_matrix,
mask=mask,
annot=True,
fmt='.2f',
cmap='RdBu_r',
center=0,
square=True,
linewidths=1,
cbar_kws={'label': f'{method.capitalize()} 相關係數'}
)
plt.title(f'{method.capitalize()} 相關係數矩陣',
fontsize=16, fontweight='bold', pad=20)
plt.tight_layout()
plt.show()
def plot_scatter_with_regression(self, x_var, y_var, figsize=(10, 6)):
"""
繪製散佈圖並加上迴歸線
Parameters:
-----------
x_var, y_var : str
變數名稱
figsize : tuple
圖表尺寸
"""
fig, ax = plt.subplots(figsize=figsize)
# 計算相關係數
result = self.test_significance(x_var, y_var, method='pearson')
# 繪製散佈圖
sns.scatterplot(
data=self.data,
x=x_var,
y=y_var,
alpha=0.6,
s=100,
ax=ax
)
# 加上迴歸線
sns.regplot(
data=self.data,
x=x_var,
y=y_var,
scatter=False,
color='red',
ax=ax
)
# 添加統計資訊
textstr = f"Pearson r = {result['correlation']:.3f}\n"
textstr += f"p-value = {result['p_value']:.4f}\n"
textstr += f"顯著性: {'是' if result['significant'] else '否'}"
props = dict(boxstyle='round', facecolor='wheat', alpha=0.8)
ax.text(0.05, 0.95, textstr, transform=ax.transAxes,
fontsize=11, verticalalignment='top', bbox=props)
ax.set_xlabel(x_var, fontsize=12, fontweight='bold')
ax.set_ylabel(y_var, fontsize=12, fontweight='bold')
ax.set_title(f'{x_var} 與 {y_var} 的相關性分析',
fontsize=14, fontweight='bold', pad=15)
plt.tight_layout()
plt.show()
# 示範使用
np.random.seed(42)
# 生成範例資料
n_samples = 200
data = pd.DataFrame({
'銷售額': np.random.normal(100, 20, n_samples),
'廣告支出': np.random.normal(50, 10, n_samples),
'客戶滿意度': np.random.normal(4, 0.5, n_samples),
'市場佔有率': np.random.normal(15, 3, n_samples)
})
# 建立變數間的相關性
data['銷售額'] = data['銷售額'] + 2 * data['廣告支出'] + np.random.normal(0, 10, n_samples)
data['客戶滿意度'] = data['客戶滿意度'] + 0.05 * data['銷售額'] + np.random.normal(0, 0.3, n_samples)
# 初始化分析器
analyzer = CorrelationAnalyzer(data)
# 繪製相關矩陣
analyzer.plot_correlation_matrix(method='pearson')
# 分析特定變數對
result = analyzer.test_significance('廣告支出', '銷售額', method='pearson')
print(f"\n廣告支出與銷售額的相關分析:")
print(f"相關係數: {result['correlation']:.4f}")
print(f"p值: {result['p_value']:.6f}")
print(f"在 α={result['alpha']} 水準下顯著: {result['significant']}")
# 繪製散佈圖
analyzer.plot_scatter_with_regression('廣告支出', '銷售額')
這段程式碼展示了完整的相關分析流程。CorrelationAnalyzer 類別封裝了多種相關分析方法,不僅計算相關係數,還提供統計顯著性檢定。視覺化部分使用熱力圖展示變數間的整體相關結構,透過顏色編碼快速識別強相關與弱相關的變數對。散佈圖結合迴歸線則提供了更直觀的雙變數關係展示,統計資訊的標註讓分析結果一目了然。
@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 120
start
:載入資料集;
:選擇數值型變數;
:選擇相關係數類型;
note right
Pearson: 線性相關
Spearman: 單調相關
Kendall: 等級相關
end note
:計算相關矩陣;
fork
:視覺化熱力圖;
fork again
:識別強相關變數對;
if (相關係數絕對值 > 0.7?) then (是)
:標記為強相關;
else (否)
:標記為弱相關;
endif
end fork
:執行顯著性檢定;
if (p值 < 顯著水準?) then (是)
:接受顯著相關;
:繪製散佈圖與迴歸線;
else (否)
:相關性不顯著;
:記錄分析結果;
endif
:生成分析報告;
stop
@enduml迴歸模型:預測與因果推斷
迴歸分析是資料科學中最重要的預測建模技術之一,它不僅能夠量化自變數對依變數的影響程度,還能用於預測未來的結果。從簡單的線性迴歸到複雜的非線性模型,迴歸分析提供了豐富的工具箱來處理各種預測問題。
線性迴歸假設依變數與自變數之間存在線性關係,透過最小平方法找出最佳擬合直線。模型的品質可以透過多個指標來評估,包括判定係數 R²、調整後 R²、殘差分析等。R² 表示模型能夠解釋的變異比例,但它會隨著加入更多變數而自動增加,因此調整後 R² 提供了對模型複雜度的懲罰,更適合用於多元迴歸模型的比較。
from sklearn.linear_model import LinearRegression, Ridge, Lasso
from sklearn.preprocessing import StandardScaler, PolynomialFeatures
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.metrics import mean_squared_error, r2_score, mean_absolute_error
import statsmodels.api as sm
class RegressionAnalyzer:
"""
迴歸分析器提供線性與非線性迴歸建模功能
支援模型訓練、評估與視覺化
"""
def __init__(self):
self.model = None
self.scaler = StandardScaler()
self.feature_names = None
def fit_linear_regression(self, X, y, fit_intercept=True):
"""
訓練線性迴歸模型
Parameters:
-----------
X : array-like
特徵矩陣
y : array-like
目標變數
fit_intercept : bool
是否擬合截距項
Returns:
--------
dict
包含模型參數與評估指標的字典
"""
self.model = LinearRegression(fit_intercept=fit_intercept)
self.model.fit(X, y)
# 計算評估指標
y_pred = self.model.predict(X)
results = {
'coefficients': self.model.coef_,
'intercept': self.model.intercept_,
'r2_score': r2_score(y, y_pred),
'rmse': np.sqrt(mean_squared_error(y, y_pred)),
'mae': mean_absolute_error(y, y_pred)
}
return results
def fit_with_statsmodels(self, X, y):
"""
使用 statsmodels 進行詳細的統計推斷
提供係數的標準誤、t值、p值等統計資訊
Parameters:
-----------
X : array-like
特徵矩陣
y : array-like
目標變數
Returns:
--------
statsmodels.regression.linear_model.RegressionResultsWrapper
詳細的迴歸結果
"""
X_with_const = sm.add_constant(X)
model = sm.OLS(y, X_with_const)
results = model.fit()
return results
def fit_polynomial_regression(self, X, y, degree=2):
"""
訓練多項式迴歸模型
Parameters:
-----------
X : array-like
特徵矩陣
y : array-like
目標變數
degree : int
多項式次數
Returns:
--------
dict
包含模型與轉換器的字典
"""
poly_features = PolynomialFeatures(degree=degree, include_bias=False)
X_poly = poly_features.fit_transform(X)
model = LinearRegression()
model.fit(X_poly, y)
y_pred = model.predict(X_poly)
return {
'model': model,
'poly_features': poly_features,
'r2_score': r2_score(y, y_pred),
'rmse': np.sqrt(mean_squared_error(y, y_pred))
}
def fit_regularized_regression(self, X, y, alpha=1.0, method='ridge'):
"""
訓練正則化迴歸模型(Ridge 或 Lasso)
Parameters:
-----------
X : array-like
特徵矩陣
y : array-like
目標變數
alpha : float
正則化強度
method : str
正則化方法,'ridge' 或 'lasso'
Returns:
--------
dict
模型結果
"""
if method == 'ridge':
model = Ridge(alpha=alpha)
elif method == 'lasso':
model = Lasso(alpha=alpha)
else:
raise ValueError("方法必須是 'ridge' 或 'lasso'")
model.fit(X, y)
y_pred = model.predict(X)
return {
'model': model,
'coefficients': model.coef_,
'intercept': model.intercept_,
'r2_score': r2_score(y, y_pred),
'rmse': np.sqrt(mean_squared_error(y, y_pred)),
'non_zero_coefs': np.sum(model.coef_ != 0)
}
def cross_validate(self, X, y, cv=5):
"""
執行交叉驗證評估模型穩定性
Parameters:
-----------
X : array-like
特徵矩陣
y : array-like
目標變數
cv : int
折數
Returns:
--------
dict
交叉驗證結果
"""
model = LinearRegression()
scores = cross_val_score(
model, X, y,
cv=cv,
scoring='r2'
)
return {
'cv_scores': scores,
'mean_score': scores.mean(),
'std_score': scores.std()
}
def plot_residuals(self, X, y, figsize=(14, 5)):
"""
繪製殘差分析圖
Parameters:
-----------
X : array-like
特徵矩陣
y : array-like
目標變數
figsize : tuple
圖表尺寸
"""
if self.model is None:
raise ValueError("請先訓練模型")
y_pred = self.model.predict(X)
residuals = y - y_pred
fig, axes = plt.subplots(1, 3, figsize=figsize)
# 殘差與預測值散佈圖
axes[0].scatter(y_pred, residuals, alpha=0.5)
axes[0].axhline(y=0, color='r', linestyle='--')
axes[0].set_xlabel('預測值', fontweight='bold')
axes[0].set_ylabel('殘差', fontweight='bold')
axes[0].set_title('殘差圖', fontweight='bold')
axes[0].grid(True, alpha=0.3)
# 殘差分佈直方圖
axes[1].hist(residuals, bins=30, edgecolor='black', alpha=0.7)
axes[1].set_xlabel('殘差', fontweight='bold')
axes[1].set_ylabel('頻率', fontweight='bold')
axes[1].set_title('殘差分佈', fontweight='bold')
axes[1].grid(True, alpha=0.3)
# Q-Q 圖
stats.probplot(residuals, dist="norm", plot=axes[2])
axes[2].set_title('Q-Q 圖', fontweight='bold')
axes[2].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# 示範使用
np.random.seed(42)
# 生成範例資料
n_samples = 300
X = np.random.normal(0, 1, (n_samples, 3))
true_coef = np.array([2.5, -1.3, 0.8])
y = X @ true_coef + 5 + np.random.normal(0, 1, n_samples)
# 建立資料框架
feature_names = ['特徵1', '特徵2', '特徵3']
df = pd.DataFrame(X, columns=feature_names)
df['目標變數'] = y
# 分割訓練與測試集
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# 初始化分析器
regressor = RegressionAnalyzer()
# 訓練線性迴歸模型
results = regressor.fit_linear_regression(X_train, y_train)
print("線性迴歸模型結果:")
print(f"係數: {results['coefficients']}")
print(f"截距: {results['intercept']:.4f}")
print(f"R² 分數: {results['r2_score']:.4f}")
print(f"RMSE: {results['rmse']:.4f}")
print(f"MAE: {results['mae']:.4f}")
# 使用 statsmodels 進行詳細分析
sm_results = regressor.fit_with_statsmodels(X_train, y_train)
print("\n" + "="*60)
print("Statsmodels 詳細分析:")
print(sm_results.summary())
# 交叉驗證
cv_results = regressor.cross_validate(X_train, y_train, cv=5)
print(f"\n交叉驗證 R² 分數: {cv_results['mean_score']:.4f} (+/- {cv_results['std_score']:.4f})")
# 繪製殘差圖
regressor.plot_residuals(X_train, y_train)
正則化迴歸是處理多重共線性與過度擬合問題的有效方法。Ridge 迴歸透過在損失函數中加入 L2 懲罰項,將係數向零收縮但不會完全歸零。Lasso 迴歸則使用 L1 懲罰項,能夠將某些係數精確地縮減為零,實現特徵選擇的效果。在特徵數量遠大於樣本數的高維度問題中,正則化技術尤為重要。
殘差分析是驗證迴歸模型假設的重要工具。理想的迴歸模型應該產生隨機分佈的殘差,不存在明顯的模式。如果殘差圖顯示出曲線趨勢,可能表示模型遺漏了非線性關係。如果殘差的變異數隨預測值增加而增大,則違反了同質變異性假設,可能需要進行變數轉換或使用加權最小平方法。
@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 130
start
:準備特徵矩陣與目標變數;
:分割訓練集與測試集;
:選擇迴歸模型類型;
note right
線性迴歸
多項式迴歸
Ridge 迴歸
Lasso 迴歸
end note
:訓練模型;
fork
:計算訓練集指標;
note right
R² 分數
RMSE
MAE
end note
fork again
:執行交叉驗證;
:評估模型穩定性;
end fork
:在測試集上評估;
:執行殘差分析;
if (殘差呈隨機分佈?) then (是)
if (通過常態性檢定?) then (是)
if (變異數同質?) then (是)
:模型假設滿足;
else (否)
:考慮變數轉換;
endif
else (否)
:檢查異常值;
endif
else (否)
:考慮非線性模型;
endif
:生成預測結果;
:輸出模型報告;
stop
@enduml假設檢定:統計推斷的基礎
假設檢定是統計推斷的核心方法,用於基於樣本資料對母體參數進行推論。在資料科學實務中,我們經常需要回答諸如「新的行銷策略是否真的提升了銷售額」或「兩個使用者群體的行為是否存在顯著差異」等問題,假設檢定提供了嚴謹的統計框架來回答這些問題。
假設檢定的基本邏輯是建立虛無假設與對立假設,然後計算在虛無假設為真的情況下,觀察到當前資料或更極端資料的機率,即 p 值。如果 p 值小於預設的顯著水準,通常是 0.05,我們就拒絕虛無假設,接受對立假設。需要注意的是,拒絕虛無假設不代表證明了對立假設絕對正確,只是表示在當前資料支持下,對立假設更為可信。
from scipy.stats import ttest_ind, ttest_rel, chi2_contingency, f_oneway
from scipy.stats import shapiro, levene, mannwhitneyu, wilcoxon
from scipy.stats import kruskal
class HypothesisTest:
"""
假設檢定分析器提供多種統計檢定方法
支援參數檢定與非參數檢定
"""
def __init__(self, alpha=0.05):
"""
初始化假設檢定器
Parameters:
-----------
alpha : float
顯著水準
"""
self.alpha = alpha
def check_normality(self, data):
"""
檢查資料常態性(Shapiro-Wilk 檢定)
Parameters:
-----------
data : array-like
待檢定的資料
Returns:
--------
dict
檢定結果
"""
stat, pvalue = shapiro(data)
return {
'test': 'Shapiro-Wilk',
'statistic': stat,
'p_value': pvalue,
'is_normal': pvalue > self.alpha,
'alpha': self.alpha
}
def check_variance_equality(self, *groups):
"""
檢查變異數同質性(Levene 檢定)
Parameters:
-----------
*groups : array-like
多個群組的資料
Returns:
--------
dict
檢定結果
"""
stat, pvalue = levene(*groups)
return {
'test': 'Levene',
'statistic': stat,
'p_value': pvalue,
'equal_variance': pvalue > self.alpha,
'alpha': self.alpha
}
def independent_t_test(self, group1, group2):
"""
獨立樣本 t 檢定
用於比較兩個獨立群組的平均數差異
Parameters:
-----------
group1, group2 : array-like
兩個獨立群組的資料
Returns:
--------
dict
檢定結果
"""
# 檢查常態性
norm1 = self.check_normality(group1)
norm2 = self.check_normality(group2)
# 檢查變異數同質性
var_test = self.check_variance_equality(group1, group2)
# 執行 t 檢定
stat, pvalue = ttest_ind(
group1, group2,
equal_var=var_test['equal_variance']
)
return {
'test': 'Independent t-test',
'statistic': stat,
'p_value': pvalue,
'significant': pvalue < self.alpha,
'alpha': self.alpha,
'mean_diff': np.mean(group1) - np.mean(group2),
'assumptions': {
'group1_normal': norm1['is_normal'],
'group2_normal': norm2['is_normal'],
'equal_variance': var_test['equal_variance']
}
}
def paired_t_test(self, before, after):
"""
配對樣本 t 檢定
用於比較同一群組在不同時間或條件下的差異
Parameters:
-----------
before, after : array-like
配對的前測與後測資料
Returns:
--------
dict
檢定結果
"""
# 計算差異
diff = np.array(after) - np.array(before)
# 檢查差異的常態性
norm_test = self.check_normality(diff)
# 執行配對 t 檢定
stat, pvalue = ttest_rel(before, after)
return {
'test': 'Paired t-test',
'statistic': stat,
'p_value': pvalue,
'significant': pvalue < self.alpha,
'alpha': self.alpha,
'mean_diff': np.mean(diff),
'assumptions': {
'difference_normal': norm_test['is_normal']
}
}
def mann_whitney_test(self, group1, group2):
"""
Mann-Whitney U 檢定(非參數檢定)
用於比較兩個獨立群組的分佈差異
不要求資料服從常態分佈
Parameters:
-----------
group1, group2 : array-like
兩個獨立群組的資料
Returns:
--------
dict
檢定結果
"""
stat, pvalue = mannwhitneyu(group1, group2, alternative='two-sided')
return {
'test': 'Mann-Whitney U',
'statistic': stat,
'p_value': pvalue,
'significant': pvalue < self.alpha,
'alpha': self.alpha,
'median_diff': np.median(group1) - np.median(group2)
}
def anova_test(self, *groups):
"""
單因子變異數分析(ANOVA)
用於比較三個或更多群組的平均數差異
Parameters:
-----------
*groups : array-like
多個群組的資料
Returns:
--------
dict
檢定結果
"""
# 檢查各群組常態性
normality_tests = [self.check_normality(g) for g in groups]
# 檢查變異數同質性
var_test = self.check_variance_equality(*groups)
# 執行 ANOVA
stat, pvalue = f_oneway(*groups)
return {
'test': 'One-way ANOVA',
'statistic': stat,
'p_value': pvalue,
'significant': pvalue < self.alpha,
'alpha': self.alpha,
'group_means': [np.mean(g) for g in groups],
'assumptions': {
'all_normal': all(t['is_normal'] for t in normality_tests),
'equal_variance': var_test['equal_variance']
}
}
def kruskal_wallis_test(self, *groups):
"""
Kruskal-Wallis H 檢定(非參數 ANOVA)
用於比較三個或更多群組的分佈差異
不要求資料服從常態分佈
Parameters:
-----------
*groups : array-like
多個群組的資料
Returns:
--------
dict
檢定結果
"""
stat, pvalue = kruskal(*groups)
return {
'test': 'Kruskal-Wallis H',
'statistic': stat,
'p_value': pvalue,
'significant': pvalue < self.alpha,
'alpha': self.alpha,
'group_medians': [np.median(g) for g in groups]
}
def chi_square_test(self, contingency_table):
"""
卡方獨立性檢定
用於檢定兩個類別變數之間是否獨立
Parameters:
-----------
contingency_table : array-like
列聯表
Returns:
--------
dict
檢定結果
"""
chi2, pvalue, dof, expected = chi2_contingency(contingency_table)
return {
'test': 'Chi-square',
'statistic': chi2,
'p_value': pvalue,
'degrees_of_freedom': dof,
'significant': pvalue < self.alpha,
'alpha': self.alpha,
'expected_freq': expected
}
# 示範使用
np.random.seed(42)
# 生成範例資料
control_group = np.random.normal(100, 15, 50)
treatment_group = np.random.normal(110, 15, 50)
# 初始化檢定器
tester = HypothesisTest(alpha=0.05)
# 獨立樣本 t 檢定
t_test_result = tester.independent_t_test(control_group, treatment_group)
print("獨立樣本 t 檢定結果:")
print(f"檢定統計量: {t_test_result['statistic']:.4f}")
print(f"p 值: {t_test_result['p_value']:.6f}")
print(f"平均數差異: {t_test_result['mean_diff']:.2f}")
print(f"統計顯著: {t_test_result['significant']}")
print(f"\n假設檢定:")
print(f" 對照組常態: {t_test_result['assumptions']['group1_normal']}")
print(f" 實驗組常態: {t_test_result['assumptions']['group2_normal']}")
print(f" 變異數同質: {t_test_result['assumptions']['equal_variance']}")
# 如果不滿足常態性假設,使用非參數檢定
if not all([t_test_result['assumptions']['group1_normal'],
t_test_result['assumptions']['group2_normal']]):
print("\n資料不滿足常態性假設,改用 Mann-Whitney U 檢定:")
mw_result = tester.mann_whitney_test(control_group, treatment_group)
print(f"檢定統計量: {mw_result['statistic']:.4f}")
print(f"p 值: {mw_result['p_value']:.6f}")
print(f"中位數差異: {mw_result['median_diff']:.2f}")
print(f"統計顯著: {mw_result['significant']}")
# ANOVA 範例
group1 = np.random.normal(100, 10, 30)
group2 = np.random.normal(105, 10, 30)
group3 = np.random.normal(110, 10, 30)
anova_result = tester.anova_test(group1, group2, group3)
print("\n\n單因子變異數分析結果:")
print(f"F 統計量: {anova_result['statistic']:.4f}")
print(f"p 值: {anova_result['p_value']:.6f}")
print(f"各組平均數: {[f'{m:.2f}' for m in anova_result['group_means']]}")
print(f"統計顯著: {anova_result['significant']}")
型一錯誤與型二錯誤是假設檢定中需要權衡的兩種風險。型一錯誤是指虛無假設為真卻被錯誤拒絕,其機率等於顯著水準 α。型二錯誤是指虛無假設為假卻未被拒絕,其機率記為 β。檢定力定義為 1-β,表示當對立假設為真時正確拒絕虛無假設的機率。在設計實驗時,需要透過樣本數規劃來確保足夠的檢定力。
非參數檢定是當資料不滿足參數檢定的假設時的替代方案。Mann-Whitney U 檢定可以替代獨立樣本 t 檢定,Wilcoxon 符號秩檢定可以替代配對 t 檢定,Kruskal-Wallis H 檢定可以替代單因子 ANOVA。非參數檢定的優點是對資料分佈的假設較少,缺點是檢定力通常較參數檢定低。
@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 130
start
:建立研究問題;
:定義虛無假設與對立假設;
:選擇顯著水準 α;
:收集樣本資料;
:檢查資料假設;
note right
常態性檢定
變異數同質性檢定
獨立性檢定
end note
if (滿足參數檢定假設?) then (是)
:選擇適當的參數檢定;
note right
t 檢定
ANOVA
迴歸分析
end note
else (否)
:選擇適當的非參數檢定;
note right
Mann-Whitney U
Kruskal-Wallis H
Wilcoxon
end note
endif
:計算檢定統計量;
:計算 p 值;
if (p 值 < α?) then (是)
:拒絕虛無假設;
:接受對立假設;
else (否)
:無法拒絕虛無假設;
endif
:解釋結果並考慮實務意義;
:報告效果量;
stop
@enduml資料視覺化設計原則與實踐
資料視覺化不僅是將資料轉換為圖表的技術過程,更是一門結合美學、認知心理學與資訊設計的藝術。優秀的資料視覺化能夠快速傳達複雜的資訊,揭示隱藏的模式,並激發深入的洞察。相反地,設計不良的視覺化可能誤導讀者,造成資訊的曲解。
視覺化設計的首要原則是清晰與簡潔。圖表應該讓讀者一眼就能理解主要訊息,不需要費力解讀複雜的符號或過多的元素。這要求我們在設計時做出取捨,只保留最重要的資訊,移除所有不必要的裝飾。Edward Tufte 提出的資料墨水比概念強調,圖表中用於呈現資料的墨水應該佔主導地位,而非裝飾性元素。
準確性是資料視覺化的基本要求。圖表必須忠實地反映資料的真實狀況,不能透過操縱座標軸、選擇性展示資料或使用誤導性的視覺編碼來扭曲事實。常見的錯誤包括使用不從零開始的 Y 軸放大微小差異、使用三維效果造成視覺扭曲,以及在圓餅圖中使用過多分類導致難以比較。
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
import pandas as pd
class DataVisualizer:
"""
資料視覺化器提供多種圖表類型與設計模式
遵循最佳實踐原則建立清晰有效的視覺化
"""
def __init__(self, style='whitegrid', palette='husl'):
"""
初始化視覺化器
Parameters:
-----------
style : str
Seaborn 視覺風格
palette : str
色彩調色盤
"""
sns.set_style(style)
sns.set_palette(palette)
plt.rcParams['font.sans-serif'] = ['Microsoft JhengHei']
plt.rcParams['axes.unicode_minus'] = False
plt.rcParams['figure.dpi'] = 100
def create_comparison_chart(self, data, categories, values,
title, figsize=(12, 6)):
"""
建立比較圖表(長條圖)
適用於類別間的數值比較
Parameters:
-----------
data : pd.DataFrame
資料框架
categories : str
類別欄位名稱
values : str
數值欄位名稱
title : str
圖表標題
figsize : tuple
圖表尺寸
"""
fig, ax = plt.subplots(figsize=figsize)
# 建立長條圖
bars = ax.bar(data[categories], data[values],
edgecolor='black', linewidth=1.2)
# 為每個長條添加數值標籤
for bar in bars:
height = bar.get_height()
ax.text(bar.get_x() + bar.get_width()/2., height,
f'{height:.1f}',
ha='center', va='bottom', fontweight='bold')
ax.set_xlabel(categories, fontsize=12, fontweight='bold')
ax.set_ylabel(values, fontsize=12, fontweight='bold')
ax.set_title(title, fontsize=14, fontweight='bold', pad=20)
ax.grid(axis='y', alpha=0.3, linestyle='--')
plt.tight_layout()
plt.show()
def create_distribution_chart(self, data, variable,
title, bins=30, figsize=(12, 5)):
"""
建立分佈圖表(直方圖+密度圖)
適用於展示單一變數的分佈特性
Parameters:
-----------
data : array-like or pd.Series
資料
variable : str
變數名稱
title : str
圖表標題
bins : int
直方圖的柱數
figsize : tuple
圖表尺寸
"""
fig, axes = plt.subplots(1, 2, figsize=figsize)
# 直方圖
axes[0].hist(data, bins=bins, edgecolor='black',
alpha=0.7, density=True)
axes[0].set_xlabel(variable, fontsize=11, fontweight='bold')
axes[0].set_ylabel('密度', fontsize=11, fontweight='bold')
axes[0].set_title('直方圖', fontsize=12, fontweight='bold')
axes[0].grid(axis='y', alpha=0.3)
# 箱型圖
axes[1].boxplot(data, vert=True, patch_artist=True,
boxprops=dict(facecolor='lightblue', alpha=0.7),
medianprops=dict(color='red', linewidth=2))
axes[1].set_ylabel(variable, fontsize=11, fontweight='bold')
axes[1].set_title('箱型圖', fontsize=12, fontweight='bold')
axes[1].grid(axis='y', alpha=0.3)
fig.suptitle(title, fontsize=14, fontweight='bold', y=1.02)
plt.tight_layout()
plt.show()
def create_time_series_chart(self, data, time_col, value_col,
title, figsize=(14, 6)):
"""
建立時間序列圖表
適用於展示資料隨時間的變化趨勢
Parameters:
-----------
data : pd.DataFrame
資料框架
time_col : str
時間欄位名稱
value_col : str
數值欄位名稱
title : str
圖表標題
figsize : tuple
圖表尺寸
"""
fig, ax = plt.subplots(figsize=figsize)
ax.plot(data[time_col], data[value_col],
linewidth=2, marker='o', markersize=4)
# 添加移動平均線
if len(data) > 7:
ma = data[value_col].rolling(window=7).mean()
ax.plot(data[time_col], ma,
linewidth=2, linestyle='--',
label='7日移動平均', alpha=0.7)
ax.legend(loc='best', fontsize=10)
ax.set_xlabel(time_col, fontsize=12, fontweight='bold')
ax.set_ylabel(value_col, fontsize=12, fontweight='bold')
ax.set_title(title, fontsize=14, fontweight='bold', pad=20)
ax.grid(True, alpha=0.3, linestyle='--')
plt.xticks(rotation=45, ha='right')
plt.tight_layout()
plt.show()
def create_multivariate_chart(self, data, figsize=(14, 10)):
"""
建立多變數關係圖表(配對圖)
適用於同時探索多個變數間的關係
Parameters:
-----------
data : pd.DataFrame
資料框架
figsize : tuple
圖表尺寸
"""
numeric_cols = data.select_dtypes(include=[np.number]).columns
if len(numeric_cols) < 2:
print("需要至少兩個數值型變數")
return
pair_plot = sns.pairplot(
data[numeric_cols],
diag_kind='kde',
plot_kws={'alpha': 0.6, 's': 80},
diag_kws={'linewidth': 2}
)
pair_plot.fig.suptitle('多變數關係圖',
fontsize=16, fontweight='bold', y=1.01)
plt.show()
def create_dashboard(self, data, figsize=(16, 12)):
"""
建立儀表板式綜合視覺化
整合多種圖表類型提供全面的資料概覽
Parameters:
-----------
data : pd.DataFrame
資料框架
figsize : tuple
圖表尺寸
"""
fig = plt.figure(figsize=figsize)
gs = fig.add_gridspec(3, 3, hspace=0.3, wspace=0.3)
numeric_cols = data.select_dtypes(include=[np.number]).columns
if len(numeric_cols) < 2:
print("需要足夠的數值型變數")
return
# 左上:描述性統計
ax1 = fig.add_subplot(gs[0, :2])
stats_summary = data[numeric_cols].describe().T
ax1.axis('tight')
ax1.axis('off')
table = ax1.table(
cellText=stats_summary.round(2).values,
colLabels=stats_summary.columns,
rowLabels=stats_summary.index,
cellLoc='center',
loc='center'
)
table.auto_set_font_size(False)
table.set_fontsize(9)
table.scale(1, 2)
ax1.set_title('描述性統計摘要',
fontsize=12, fontweight='bold', pad=10)
# 右上:相關矩陣
ax2 = fig.add_subplot(gs[0, 2])
corr_matrix = data[numeric_cols].corr()
sns.heatmap(corr_matrix, annot=True, fmt='.2f',
cmap='RdBu_r', center=0, square=True,
cbar_kws={'shrink': 0.8}, ax=ax2)
ax2.set_title('相關矩陣', fontsize=12, fontweight='bold')
# 中間列:分佈圖
for idx, col in enumerate(numeric_cols[:3]):
ax = fig.add_subplot(gs[1, idx])
ax.hist(data[col].dropna(), bins=20,
edgecolor='black', alpha=0.7)
ax.set_xlabel(col, fontsize=10, fontweight='bold')
ax.set_ylabel('頻率', fontsize=10, fontweight='bold')
ax.grid(axis='y', alpha=0.3)
# 底部:散佈圖矩陣
if len(numeric_cols) >= 2:
for idx, (i, j) in enumerate([(0, 1), (0, 2), (1, 2)]):
if idx < 3 and i < len(numeric_cols) and j < len(numeric_cols):
ax = fig.add_subplot(gs[2, idx])
ax.scatter(data[numeric_cols[i]],
data[numeric_cols[j]],
alpha=0.6, s=50)
ax.set_xlabel(numeric_cols[i],
fontsize=10, fontweight='bold')
ax.set_ylabel(numeric_cols[j],
fontsize=10, fontweight='bold')
ax.grid(True, alpha=0.3)
fig.suptitle('資料分析儀表板',
fontsize=16, fontweight='bold')
plt.show()
# 示範使用
np.random.seed(42)
# 生成範例資料
dates = pd.date_range('2024-01-01', periods=100, freq='D')
data = pd.DataFrame({
'日期': dates,
'銷售額': np.random.normal(1000, 200, 100) + np.linspace(0, 500, 100),
'廣告支出': np.random.normal(200, 50, 100),
'網站流量': np.random.normal(5000, 1000, 100),
'轉換率': np.random.normal(0.03, 0.01, 100)
})
# 初始化視覺化器
viz = DataVisualizer()
# 建立各種圖表
categories_data = pd.DataFrame({
'產品': ['產品A', '產品B', '產品C', '產品D'],
'銷售量': [250, 380, 190, 420]
})
viz.create_comparison_chart(
categories_data, '產品', '銷售量',
'各產品銷售量比較'
)
viz.create_distribution_chart(
data['銷售額'], '銷售額',
'銷售額分佈分析'
)
viz.create_time_series_chart(
data, '日期', '銷售額',
'銷售額時間序列趨勢'
)
viz.create_dashboard(data.drop('日期', axis=1))
色彩的使用是視覺化設計中需要特別注意的要素。色彩不僅能夠增強視覺吸引力,更重要的是能夠編碼資訊與引導注意力。在選擇色彩時,需要考慮色盲友善性,避免使用紅綠配色。對於順序型資料,應使用單一色系的漸變;對於類別型資料,應使用對比鮮明的不同色調。同時要避免使用過多顏色造成視覺混亂。
互動性是現代資料視覺化的重要特徵。互動式圖表允許使用者根據自己的需求探索資料,透過縮放、篩選、懸停顯示詳細資訊等操作獲得個人化的洞察。Tableau、Plotly 等工具提供了豐富的互動功能,能夠建立動態儀表板,讓資料視覺化從靜態展示轉變為探索式分析的工具。
@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 140
start
:定義視覺化目標;
note right
探索性分析
說明性呈現
互動式儀表板
end note
:選擇適當的圖表類型;
note right
長條圖: 類別比較
折線圖: 趨勢變化
散佈圖: 相關關係
箱型圖: 分佈特性
熱力圖: 矩陣資料
end note
:設計視覺編碼;
fork
:選擇色彩方案;
note right
考慮色盲友善性
使用一致的色彩語意
end note
fork again
:設定座標軸範圍;
note right
Y軸是否從零開始
對數刻度的使用
end note
fork again
:添加標籤與註解;
note right
標題清晰簡潔
軸標籤完整
圖例易於理解
end note
end fork
:移除圖表垃圾;
note right
簡化網格線
移除不必要的裝飾
減少視覺雜訊
end note
:測試可讀性;
if (清晰易懂?) then (是)
if (準確呈現資料?) then (是)
:最終化視覺化;
else (否)
:調整視覺編碼;
endif
else (否)
:簡化設計;
endif
:輸出並整合到報告;
stop
@enduml在資料科學的實踐中,統計方法與視覺化技術形成了互補的關係。統計方法提供了量化分析的嚴謹框架,讓我們能夠從資料中抽取有統計意義的結論。相關分析揭示變數間的關聯模式,迴歸模型建立預測關係,假設檢定則提供了科學的推論工具。這些方法的正確應用需要對統計原理的深入理解,包括假設條件的檢驗、模型適用性的評估,以及結果的謹慎解釋。
視覺化技術則將這些抽象的統計結果轉化為直觀的圖形語言。一張設計良好的圖表能夠讓複雜的模式一目了然,促進跨部門的溝通與協作。然而,視覺化的力量也伴隨著責任,不當的設計可能誤導讀者,造成錯誤的決策。因此,掌握視覺化的設計原則,選擇適當的圖表類型,並遵循最佳實踐,是每位資料科學家必備的技能。
玄貓認為,未來資料科學的發展將更加注重自動化與智慧化。機器學習技術開始應用於自動圖表生成,根據資料特性與分析目標自動選擇最適合的視覺化方式。增強分析系統能夠自動發現資料中的異常模式並生成洞察報告。自然語言生成技術則可以將視覺化圖表轉化為文字描述,使分析結果更易於理解與傳播。
然而,無論技術如何進步,對統計原理的紮實理解與對視覺化設計的美學追求,始終是資料科學家的核心競爭力。只有深刻理解資料背後的統計意義,才能避免常見的分析陷阱。只有用心設計視覺化,才能有效傳達洞察,推動資料驅動的決策。對於致力於資料科學領域的專業人士而言,持續精進統計分析能力與視覺化技巧,是通往卓越的必經之路。