统计分析:从数据中挖掘洞察
🎯 前言:数据侦探的推理游戏
想象一下,你是一位数据侦探,面前摆着一堆看似杂乱无章的数字。这些数字就像是犯罪现场的证据,看起来毫无头绪,但实际上却隐藏着惊人的秘密。而统计分析,就是你手中的放大镜,能够帮你从这些看似平凡的数据中,挖掘出令人惊叹的洞察!
还记得福尔摩斯是怎么从一个人的衣着推断出他的职业、婚姻状况甚至昨天的行程吗?统计分析就是数据科学中的福尔摩斯推理法——通过观察数据的模式、分布、相关性,我们能够揭示隐藏在数字背后的故事。
今天我们就来学习如何成为一名合格的"数据侦探",用Python的统计分析工具来破解数据密码!
📚 目录
- 统计分析基础概念
- 描述性统计:给数据画个素描
- 假设检验:让数据自己说话
- 相关性分析:找出数据间的暧昧关系
- 回归分析:预测未来的水晶球
- 方差分析:比较不同组的差异
- 实战项目:电商用户行为分析
- 进阶技巧与最佳实践
🧠 统计分析基础概念
什么是统计分析?
统计分析就像是数据的心理医生,它不仅要了解数据的表面现象,更要挖掘数据内心的真实想法。简单来说,统计分析就是通过数学方法来理解数据、发现规律、做出预测的过程。
统计分析的两大门派
- 描述性统计:就像给数据拍照,告诉你数据长什么样
- 推断性统计:就像给数据算命,根据样本推断总体
让我们先搭建好我们的"侦探工具箱":
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from scipy import stats
import warnings
warnings.filterwarnings('ignore')
# 设置中文显示
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 设置图表样式
sns.set_style("whitegrid")
plt.style.use('seaborn-v0_8')
print("🔍 数据侦探工具箱准备就绪!")
📊 描述性统计:给数据画个素描
描述性统计就像是给数据画素描,通过几个关键的数字来描述数据的整体特征。就好比你要给朋友介绍一个人,你会说他多高、多重、什么性格,而不是从头到脚描述每个细节。
集中趋势:数据的"重心"在哪里?
# 创建一个模拟的学生成绩数据
np.random.seed(42)
scores = np.random.normal(78, 12, 1000) # 均值78,标准差12的正态分布
scores = np.clip(scores, 0, 100) # 限制在0-100之间
# 计算集中趋势
mean_score = np.mean(scores) # 平均数:数据的"重心"
median_score = np.median(scores) # 中位数:数据的"中间值"
mode_score = stats.mode(scores.round()).mode[0] # 众数:最常见的值
print(f"🎯 学生成绩分析:")
print(f"平均分:{mean_score:.2f}分(班级整体水平)")
print(f"中位数:{median_score:.2f}分(中等水平分界线)")
print(f"众数:{mode_score}分(最常见的分数)")
离散程度:数据有多"散"?
# 计算离散程度
variance = np.var(scores) # 方差:数据的"散乱程度"
std_dev = np.std(scores) # 标准差:方差的平方根
range_score = np.max(scores) - np.min(scores) # 极差:最大值减最小值
print(f"\n📏 数据离散程度分析:")
print(f"方差:{variance:.2f}(数据散乱程度的平方)")
print(f"标准差:{std_dev:.2f}(平均偏离程度)")
print(f"极差:{range_score:.2f}(最高分与最低分的差距)")
# 四分位数:把数据分成四等份
q1 = np.percentile(scores, 25) # 第一四分位数
q3 = np.percentile(scores, 75) # 第三四分位数
iqr = q3 - q1 # 四分位数范围
print(f"第一四分位数(Q1):{q1:.2f}分(25%的学生在这个分数以下)")
print(f"第三四分位数(Q3):{q3:.2f}分(75%的学生在这个分数以下)")
print(f"四分位数范围(IQR):{iqr:.2f}分(中间50%学生的分数范围)")
分布形状:数据长什么样?
# 计算分布形状
skewness = stats.skew(scores) # 偏度:数据是否"歪脖子"
kurtosis = stats.kurtosis(scores) # 峰度:数据是否"尖头"
print(f"\n🎭 数据分布形状分析:")
print(f"偏度:{skewness:.3f}", end="")
if skewness > 0:
print("(右偏,低分同学较多)")
elif skewness < 0:
print("(左偏,高分同学较多)")
else:
print("(基本对称)")
print(f"峰度:{kurtosis:.3f}", end="")
if kurtosis > 0:
print("(尖峰,大部分同学成绩集中)")
elif kurtosis < 0:
print("(平峰,成绩分布较均匀)")
else:
print("(正态分布)")
可视化描述性统计
# 创建综合图表
fig, axes = plt.subplots(2, 2, figsize=(15, 10))
fig.suptitle('学生成绩统计分析大全', fontsize=16, fontweight='bold')
# 1. 直方图 + 密度曲线
axes[0, 0].hist(scores, bins=30, density=True, alpha=0.7, color='skyblue', edgecolor='black')
axes[0, 0].axvline(mean_score, color='red', linestyle='--', linewidth=2, label=f'平均分: {mean_score:.1f}')
axes[0, 0].axvline(median_score, color='green', linestyle='--', linewidth=2, label=f'中位数: {median_score:.1f}')
axes[0, 0].set_title('成绩分布直方图')
axes[0, 0].set_xlabel('分数')
axes[0, 0].set_ylabel('密度')
axes[0, 0].legend()
# 2. 箱线图
box_plot = axes[0, 1].boxplot(scores, patch_artist=True)
box_plot['boxes'][0].set_facecolor('lightblue')
axes[0, 1].set_title('成绩箱线图')
axes[0, 1].set_ylabel('分数')
axes[0, 1].grid(True, alpha=0.3)
# 3. Q-Q图(检验正态性)
stats.probplot(scores, dist="norm", plot=axes[1, 0])
axes[1, 0].set_title('Q-Q图(正态性检验)')
axes[1, 0].grid(True, alpha=0.3)
# 4. 累积分布函数
sorted_scores = np.sort(scores)
cumulative = np.arange(1, len(sorted_scores) + 1) / len(sorted_scores)
axes[1, 1].plot(sorted_scores, cumulative, linewidth=2, color='orange')
axes[1, 1].set_title('累积分布函数')
axes[1, 1].set_xlabel('分数')
axes[1, 1].set_ylabel('累积概率')
axes[1, 1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
🔍 假设检验:让数据自己说话
假设检验就像是法庭上的辩论,我们先提出一个假设(比如"这个药有效"),然后让数据来作证,看看证据是否足够推翻原假设。
单样本t检验:这批学生是否达到了平均水平?
# 假设全国平均分是80分,我们想知道这批学生是否达到了平均水平
national_average = 80
# H0(零假设):我们班平均分 = 80
# H1(备择假设):我们班平均分 ≠ 80
t_statistic, p_value = stats.ttest_1samp(scores, national_average)
print(f"🎯 单样本t检验结果:")
print(f"t统计量:{t_statistic:.3f}")
print(f"p值:{p_value:.6f}")
print(f"样本均值:{np.mean(scores):.2f}")
print(f"全国平均分:{national_average}")
alpha = 0.05 # 显著性水平
if p_value < alpha:
print(f"✅ 拒绝零假设(p < {alpha})")
if t_statistic < 0:
print("🔍 结论:我们班成绩显著低于全国平均水平")
else:
print("🔍 结论:我们班成绩显著高于全国平均水平")
else:
print(f"❌ 接受零假设(p >= {alpha})")
print("🔍 结论:我们班成绩与全国平均水平没有显著差异")
独立样本t检验:两个班级谁更厉害?
# 模拟两个班级的成绩
np.random.seed(42)
class_a = np.random.normal(78, 12, 500) # A班
class_b = np.random.normal(82, 10, 500) # B班
# H0:两个班级平均分相等
# H1:两个班级平均分不等
t_stat, p_val = stats.ttest_ind(class_a, class_b)
print(f"\n👥 独立样本t检验(A班 vs B班):")
print(f"A班平均分:{np.mean(class_a):.2f}分")
print(f"B班平均分:{np.mean(class_b):.2f}分")
print(f"t统计量:{t_stat:.3f}")
print(f"p值:{p_val:.6f}")
if p_val < 0.05:
print("✅ 两个班级成绩有显著差异")
if np.mean(class_a) > np.mean(class_b):
print("🏆 A班表现更好")
else:
print("🏆 B班表现更好")
else:
print("❌ 两个班级成绩没有显著差异")
配对样本t检验:培训前后有进步吗?
# 模拟培训前后的成绩
np.random.seed(42)
before_training = np.random.normal(75, 10, 100)
# 培训后成绩普遍提高3-8分
improvement = np.random.normal(5, 3, 100)
after_training = before_training + improvement
# H0:培训前后成绩没有差异
# H1:培训前后成绩有差异
t_stat, p_val = stats.ttest_rel(before_training, after_training)
print(f"\n📈 配对样本t检验(培训前 vs 培训后):")
print(f"培训前平均分:{np.mean(before_training):.2f}分")
print(f"培训后平均分:{np.mean(after_training):.2f}分")
print(f"平均提升:{np.mean(after_training - before_training):.2f}分")
print(f"t统计量:{t_stat:.3f}")
print(f"p值:{p_val:.6f}")
if p_val < 0.05:
print("✅ 培训效果显著")
if np.mean(after_training) > np.mean(before_training):
print("🎉 培训确实有效果,成绩显著提升!")
else:
print("😱 培训有负面效果,成绩显著下降!")
else:
print("❌ 培训效果不显著")
💕 相关性分析:找出数据间的暧昧关系
相关性分析就像是当媒人,专门找出数据之间的"暧昧关系"。有些变量之间亲密无间(高相关),有些则是陌生人(无相关),还有些是冤家(负相关)。
皮尔逊相关系数:线性关系的测量师
# 创建一个学生数据集
np.random.seed(42)
n_students = 1000
# 生成相关的变量
study_hours = np.random.normal(5, 2, n_students) # 学习时间
study_hours = np.clip(study_hours, 0, 12)
# 成绩与学习时间正相关
scores = 60 + 3 * study_hours + np.random.normal(0, 8, n_students)
scores = np.clip(scores, 0, 100)
# 睡眠时间与学习时间负相关
sleep_hours = 9 - 0.3 * study_hours + np.random.normal(0, 1, n_students)
sleep_hours = np.clip(sleep_hours, 4, 12)
# 游戏时间与学习时间强负相关
game_hours = 8 - 0.8 * study_hours + np.random.normal(0, 1.5, n_students)
game_hours = np.clip(game_hours, 0, 10)
# 创建数据框
df = pd.DataFrame({
'学习时间': study_hours,
'考试分数': scores,
'睡眠时间': sleep_hours,
'游戏时间': game_hours
})
print("📊 学生行为数据样本:")
print(df.head())
print(f"\n📏 数据集大小:{len(df)} 名学生")
计算和可视化相关系数
# 计算相关系数矩阵
correlation_matrix = df.corr()
print("\n💕 相关系数矩阵:")
print(correlation_matrix.round(3))
# 创建热力图
plt.figure(figsize=(10, 8))
mask = np.triu(np.ones_like(correlation_matrix, dtype=bool))
sns.heatmap(correlation_matrix,
annot=True,
cmap='RdBu_r',
center=0,
square=True,
mask=mask,
cbar_kws={'label': '相关系数'})
plt.title('学生行为相关性热力图', fontsize=14, fontweight='bold')
plt.tight_layout()
plt.show()
# 解释相关系数
print("\n🔍 相关性分析解读:")
for i in range(len(correlation_matrix.columns)):
for j in range(i+1, len(correlation_matrix.columns)):
var1, var2 = correlation_matrix.columns[i], correlation_matrix.columns[j]
corr = correlation_matrix.iloc[i, j]
if abs(corr) >= 0.7:
strength = "强"
elif abs(corr) >= 0.3:
strength = "中等"
else:
strength = "弱"
direction = "正" if corr > 0 else "负"
print(f"{var1} vs {var2}: {corr:.3f} ({strength}{direction}相关)")
相关性的显著性检验
# 对每对变量进行相关性显著性检验
print("\n📊 相关性显著性检验:")
columns = df.columns
for i in range(len(columns)):
for j in range(i+1, len(columns)):
var1, var2 = columns[i], columns[j]
corr, p_value = stats.pearsonr(df[var1], df[var2])
significance = "显著" if p_value < 0.05 else "不显著"
stars = "***" if p_value < 0.001 else "**" if p_value < 0.01 else "*" if p_value < 0.05 else ""
print(f"{var1} vs {var2}: r={corr:.3f}, p={p_value:.6f} ({significance}){stars}")
散点图矩阵:可视化所有关系
# 创建散点图矩阵
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
fig.suptitle('学生行为数据散点图矩阵', fontsize=16, fontweight='bold')
# 学习时间 vs 考试分数
axes[0, 0].scatter(df['学习时间'], df['考试分数'], alpha=0.6, color='blue')
axes[0, 0].set_xlabel('学习时间(小时)')
axes[0, 0].set_ylabel('考试分数')
axes[0, 0].set_title('学习时间 vs 考试分数')
# 添加回归线
z = np.polyfit(df['学习时间'], df['考试分数'], 1)
p = np.poly1d(z)
axes[0, 0].plot(df['学习时间'], p(df['学习时间']), "r--", alpha=0.8)
# 学习时间 vs 睡眠时间
axes[0, 1].scatter(df['学习时间'], df['睡眠时间'], alpha=0.6, color='green')
axes[0, 1].set_xlabel('学习时间(小时)')
axes[0, 1].set_ylabel('睡眠时间(小时)')
axes[0, 1].set_title('学习时间 vs 睡眠时间')
z = np.polyfit(df['学习时间'], df['睡眠时间'], 1)
p = np.poly1d(z)
axes[0, 1].plot(df['学习时间'], p(df['学习时间']), "r--", alpha=0.8)
# 学习时间 vs 游戏时间
axes[1, 0].scatter(df['学习时间'], df['游戏时间'], alpha=0.6, color='red')
axes[1, 0].set_xlabel('学习时间(小时)')
axes[1, 0].set_ylabel('游戏时间(小时)')
axes[1, 0].set_title('学习时间 vs 游戏时间')
z = np.polyfit(df['学习时间'], df['游戏时间'], 1)
p = np.poly1d(z)
axes[1, 0].plot(df['学习时间'], p(df['学习时间']), "r--", alpha=0.8)
# 睡眠时间 vs 游戏时间
axes[1, 1].scatter(df['睡眠时间'], df['游戏时间'], alpha=0.6, color='orange')
axes[1, 1].set_xlabel('睡眠时间(小时)')
axes[1, 1].set_ylabel('游戏时间(小时)')
axes[1, 1].set_title('睡眠时间 vs 游戏时间')
z = np.polyfit(df['睡眠时间'], df['游戏时间'], 1)
p = np.poly1d(z)
axes[1, 1].plot(df['睡眠时间'], p(df['游戏时间']), "r--", alpha=0.8)
plt.tight_layout()
plt.show()
🔮 回归分析:预测未来的水晶球
回归分析就像是一个预测未来的水晶球,它不仅能告诉你变量之间的关系,还能根据这些关系来预测未来的结果。
简单线性回归:一对一的关系
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score, mean_squared_error
# 使用学习时间预测考试分数
X = df[['学习时间']]
y = df['考试分数']
# 创建模型
model = LinearRegression()
model.fit(X, y)
# 预测
y_pred = model.predict(X)
# 计算模型评价指标
r2 = r2_score(y, y_pred)
mse = mean_squared_error(y, y_pred)
rmse = np.sqrt(mse)
print(f"📈 简单线性回归结果(学习时间 → 考试分数):")
print(f"回归方程:分数 = {model.intercept_:.2f} + {model.coef_[0]:.2f} × 学习时间")
print(f"R² (决定系数):{r2:.3f} ({r2*100:.1f}%的变异被解释)")
print(f"均方根误差(RMSE):{rmse:.2f}分")
# 解释回归系数
print(f"\n🔍 解释:")
print(f"• 截距 {model.intercept_:.2f}:不学习时的预期分数")
print(f"• 斜率 {model.coef_[0]:.2f}:每增加1小时学习时间,分数平均提高{model.coef_[0]:.2f}分")
多元线性回归:多个变量的合力
# 使用多个变量预测考试分数
X_multi = df[['学习时间', '睡眠时间', '游戏时间']]
y = df['考试分数']
# 创建多元回归模型
model_multi = LinearRegression()
model_multi.fit(X_multi, y)
# 预测
y_pred_multi = model_multi.predict(X_multi)
# 计算评价指标
r2_multi = r2_score(y, y_pred_multi)
rmse_multi = np.sqrt(mean_squared_error(y, y_pred_multi))
print(f"\n📊 多元线性回归结果:")
print(f"R² (决定系数):{r2_multi:.3f} ({r2_multi*100:.1f}%的变异被解释)")
print(f"均方根误差(RMSE):{rmse_multi:.2f}分")
# 回归系数解释
print(f"\n🔍 回归系数解释:")
print(f"截距:{model_multi.intercept_:.2f}")
for i, feature in enumerate(X_multi.columns):
coef = model_multi.coef_[i]
print(f"{feature}:{coef:.2f} (其他变量不变时,{feature}每增加1单位,分数变化{coef:.2f}分)")
回归诊断:检查模型是否靠谱
# 计算残差
residuals = y - y_pred_multi
# 创建回归诊断图
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
fig.suptitle('回归诊断图', fontsize=16, fontweight='bold')
# 1. 残差 vs 拟合值
axes[0, 0].scatter(y_pred_multi, residuals, alpha=0.6)
axes[0, 0].axhline(y=0, color='red', linestyle='--')
axes[0, 0].set_xlabel('拟合值')
axes[0, 0].set_ylabel('残差')
axes[0, 0].set_title('残差 vs 拟合值')
# 2. 残差的正态性检验
axes[0, 1].hist(residuals, bins=30, density=True, alpha=0.7, color='lightblue')
axes[0, 1].set_xlabel('残差')
axes[0, 1].set_ylabel('密度')
axes[0, 1].set_title('残差分布')
# 3. Q-Q图
stats.probplot(residuals, dist="norm", plot=axes[1, 0])
axes[1, 0].set_title('残差Q-Q图')
# 4. 实际值 vs 预测值
axes[1, 1].scatter(y, y_pred_multi, alpha=0.6)
axes[1, 1].plot([y.min(), y.max()], [y.min(), y.max()], 'r--', lw=2)
axes[1, 1].set_xlabel('实际值')
axes[1, 1].set_ylabel('预测值')
axes[1, 1].set_title('实际值 vs 预测值')
plt.tight_layout()
plt.show()
# 正态性检验
shapiro_stat, shapiro_p = stats.shapiro(residuals)
print(f"\n🔍 残差正态性检验 (Shapiro-Wilk):")
print(f"统计量:{shapiro_stat:.4f}")
print(f"p值:{shapiro_p:.6f}")
if shapiro_p > 0.05:
print("✅ 残差符合正态分布")
else:
print("❌ 残差不符合正态分布")
📊 方差分析:比较不同组的差异
方差分析(ANOVA)就像是组织一场"群雄争霸",看看不同组之间是否真的有显著差异,还是只是偶然的波动。
单因素方差分析:一个因素的影响
# 模拟不同学习方法的效果
np.random.seed(42)
# 传统学习法
traditional = np.random.normal(75, 10, 100)
# 在线学习法
online = np.random.normal(78, 12, 100)
# 混合学习法
blended = np.random.normal(82, 8, 100)
# 自主学习法
self_study = np.random.normal(79, 15, 100)
# 合并数据
methods = ['传统学习', '在线学习', '混合学习', '自主学习']
all_scores = [traditional, online, blended, self_study]
# 进行单因素方差分析
f_statistic, p_value = stats.f_oneway(*all_scores)
print(f"📊 单因素方差分析(学习方法对成绩的影响):")
print(f"F统计量:{f_statistic:.3f}")
print(f"p值:{p_value:.6f}")
alpha = 0.05
if p_value < alpha:
print(f"✅ 不同学习方法对成绩有显著影响 (p < {alpha})")
else:
print(f"❌ 不同学习方法对成绩没有显著影响 (p >= {alpha})")
# 描述性统计
print(f"\n📏 各组描述性统计:")
for i, method in enumerate(methods):
mean_score = np.mean(all_scores[i])
std_score = np.std(all_scores[i])
print(f"{method}:均值 = {mean_score:.2f}, 标准差 = {std_score:.2f}")
事后检验:找出具体的差异
from scipy.stats import tukey_hsd
# 准备数据进行事后检验
all_data = np.concatenate(all_scores)
group_labels = []
for i, method in enumerate(methods):
group_labels.extend([method] * len(all_scores[i]))
# Tukey HSD 事后检验
tukey_result = tukey_hsd(*all_scores)
print(f"\n🔍 Tukey HSD 事后检验结果:")
print("各组两两比较:")
for i in range(len(methods)):
for j in range(i+1, len(methods)):
p_adj = tukey_result.pvalue[i, j]
mean_diff = np.mean(all_scores[i]) - np.mean(all_scores[j])
significance = "显著" if p_adj < 0.05 else "不显著"
print(f"{methods[i]} vs {methods[j]}: 均值差 = {mean_diff:.2f}, p = {p_adj:.4f} ({significance})")
方差分析可视化
# 创建箱线图比较不同组
plt.figure(figsize=(12, 8))
# 准备数据
plot_data = pd.DataFrame({
'学习方法': group_labels,
'成绩': all_data
})
# 绘制箱线图
sns.boxplot(data=plot_data, x='学习方法', y='成绩', palette='Set2')
plt.title('不同学习方法的成绩分布', fontsize=14, fontweight='bold')
plt.ylabel('成绩')
plt.xlabel('学习方法')
# 添加均值点
for i, method in enumerate(methods):
mean_val = np.mean(all_scores[i])
plt.plot(i, mean_val, 'ro', markersize=8, label='均值' if i == 0 else "")
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# 创建小提琴图
plt.figure(figsize=(12, 8))
sns.violinplot(data=plot_data, x='学习方法', y='成绩', palette='Set3')
plt.title('不同学习方法的成绩分布(小提琴图)', fontsize=14, fontweight='bold')
plt.ylabel('成绩')
plt.xlabel('学习方法')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
💼 实战项目:电商用户行为分析
现在让我们用一个真实的项目来综合运用所学的统计分析技能。假设我们是一家电商平台的数据分析师,需要分析用户行为数据。
创建模拟数据
# 创建电商用户行为数据
np.random.seed(42)
n_users = 5000
# 基础用户信息
ages = np.random.normal(35, 12, n_users)
ages = np.clip(ages, 18, 70).astype(int)
# 会员等级(影响购买行为)
membership_levels = np.random.choice(['普通', '银卡', '金卡', '钻石'], n_users, p=[0.5, 0.3, 0.15, 0.05])
# 月收入(影响购买力)
incomes = np.random.lognormal(10, 0.5, n_users)
incomes = np.clip(incomes, 3000, 100000)
# 网站访问次数(基于年龄和会员等级)
membership_multiplier = {'普通': 1, '银卡': 1.5, '金卡': 2, '钻石': 3}
base_visits = np.random.poisson(10, n_users)
visits = []
for i in range(n_users):
multiplier = membership_multiplier[membership_levels[i]]
age_factor = 1 + (40 - ages[i]) * 0.01 # 年龄越接近40,访问越多
visit_count = int(base_visits[i] * multiplier * age_factor)
visits.append(max(1, visit_count))
visits = np.array(visits)
# 购买金额(基于收入、会员等级和访问次数)
base_spending = incomes * 0.02 # 基础消费为收入的2%
membership_bonus = np.array([membership_multiplier[level] for level in membership_levels])
visit_bonus = np.log(visits + 1) * 0.5
spending = base_spending * membership_bonus * visit_bonus + np.random.normal(0, 200, n_users)
spending = np.clip(spending, 0, 10000)
# 创建数据框
ecommerce_df = pd.DataFrame({
'年龄': ages,
'会员等级': membership_levels,
'月收入': incomes,
'月访问次数': visits,
'月消费金额': spending
})
print("🛒 电商用户行为数据集:")
print(ecommerce_df.head(10))
print(f"\n📊 数据集概览:")
print(ecommerce_df.describe())
描述性统计分析
# 各会员等级的统计描述
print("\n💎 按会员等级分组的统计描述:")
member_stats = ecommerce_df.groupby('会员等级').agg({
'年龄': ['mean', 'std'],
'月收入': ['mean', 'std'],
'月访问次数': ['mean', 'std'],
'月消费金额': ['mean', 'std']
}).round(2)
print(member_stats)
# 计算各变量的相关性
print("\n🔗 变量相关性分析:")
# 只选择数值型变量
numeric_cols = ['年龄', '月收入', '月访问次数', '月消费金额']
correlation_matrix = ecommerce_df[numeric_cols].corr()
print(correlation_matrix.round(3))
# 可视化相关性
plt.figure(figsize=(10, 8))
sns.heatmap(correlation_matrix, annot=True, cmap='coolwarm', center=0,
square=True, cbar_kws={'label': '相关系数'})
plt.title('电商用户行为相关性矩阵', fontsize=14, fontweight='bold')
plt.tight_layout()
plt.show()
方差分析:会员等级对消费的影响
# 按会员等级分组进行方差分析
groups = []
for level in ['普通', '银卡', '金卡', '钻石']:
group_data = ecommerce_df[ecommerce_df['会员等级'] == level]['月消费金额']
groups.append(group_data)
# 进行方差分析
f_stat, p_val = stats.f_oneway(*groups)
print(f"\n💳 会员等级对消费金额的方差分析:")
print(f"F统计量:{f_stat:.3f}")
print(f"p值:{p_val:.6f}")
if p_val < 0.05:
print("✅ 不同会员等级的消费金额有显著差异")
else:
print("❌ 不同会员等级的消费金额没有显著差异")
# 可视化不同会员等级的消费分布
plt.figure(figsize=(12, 8))
sns.boxplot(data=ecommerce_df, x='会员等级', y='月消费金额',
order=['普通', '银卡', '金卡', '钻石'])
plt.title('不同会员等级的月消费金额分布', fontsize=14, fontweight='bold')
plt.ylabel('月消费金额 (元)')
plt.xlabel('会员等级')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
回归分析:预测用户消费
# 准备回归分析数据
# 将会员等级转换为数值
level_mapping = {'普通': 1, '银卡': 2, '金卡': 3, '钻石': 4}
ecommerce_df['会员等级_数值'] = ecommerce_df['会员等级'].map(level_mapping)
# 建立多元回归模型
X = ecommerce_df[['年龄', '月收入', '月访问次数', '会员等级_数值']]
y = ecommerce_df['月消费金额']
# 拟合模型
model = LinearRegression()
model.fit(X, y)
# 预测
y_pred = model.predict(X)
# 评估模型
r2 = r2_score(y, y_pred)
rmse = np.sqrt(mean_squared_error(y, y_pred))
print(f"\n📈 多元回归分析结果:")
print(f"R² (决定系数):{r2:.3f} ({r2*100:.1f}%的变异被解释)")
print(f"均方根误差(RMSE):{rmse:.2f}元")
print(f"\n🔍 回归系数解释:")
print(f"截距:{model.intercept_:.2f}元")
features = ['年龄', '月收入', '月访问次数', '会员等级']
for i, feature in enumerate(features):
coef = model.coef_[i]
if feature == '月收入':
print(f"{feature}:{coef:.4f} (收入每增加1元,消费增加{coef:.4f}元)")
elif feature == '会员等级':
print(f"{feature}:{coef:.2f} (会员等级每提升1级,消费增加{coef:.2f}元)")
else:
print(f"{feature}:{coef:.2f} ({feature}每增加1单位,消费变化{coef:.2f}元)")
用户分群分析
# 基于消费金额和访问次数进行用户分群
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
# 准备聚类数据
cluster_data = ecommerce_df[['月访问次数', '月消费金额']].copy()
# 标准化数据
scaler = StandardScaler()
cluster_data_scaled = scaler.fit_transform(cluster_data)
# K-means聚类
kmeans = KMeans(n_clusters=4, random_state=42)
clusters = kmeans.fit_predict(cluster_data_scaled)
# 添加聚类标签
ecommerce_df['用户群体'] = clusters
# 分析各群体特征
print(f"\n👥 用户分群分析:")
cluster_analysis = ecommerce_df.groupby('用户群体').agg({
'年龄': 'mean',
'月收入': 'mean',
'月访问次数': 'mean',
'月消费金额': 'mean',
'会员等级': lambda x: x.value_counts().index[0] # 最常见的会员等级
}).round(2)
print(cluster_analysis)
# 为每个群体命名
cluster_names = {
0: '低活跃低消费',
1: '高活跃高消费',
2: '中活跃中消费',
3: '低活跃高消费'
}
# 可视化聚类结果
plt.figure(figsize=(12, 8))
colors = ['red', 'blue', 'green', 'orange']
for i in range(4):
cluster_points = ecommerce_df[ecommerce_df['用户群体'] == i]
plt.scatter(cluster_points['月访问次数'], cluster_points['月消费金额'],
c=colors[i], label=f'群体{i}: {cluster_names[i]}', alpha=0.6)
plt.xlabel('月访问次数')
plt.ylabel('月消费金额 (元)')
plt.title('用户群体分析', fontsize=14, fontweight='bold')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
🚀 进阶技巧与最佳实践
统计分析的注意事项
print("⚠️ 统计分析的重要注意事项:")
print("""
1. 🎯 相关性不等于因果性
- 发现相关性很容易,但确定因果关系需要更多证据
- 例:冰淇淋销量与溺水人数相关,但不是因果关系
2. 📊 样本代表性很重要
- 确保样本能代表总体
- 避免选择偏差
3. 🔍 检验假设的前提条件
- 正态性检验
- 方差齐性检验
- 独立性假设
4. 📈 多重比较问题
- 进行多次检验时要调整显著性水平
- 使用 Bonferroni 修正等方法
5. 📏 效应大小同样重要
- 不要只关注p值
- 考虑实际意义
6. 🧮 异常值的处理
- 识别并合理处理异常值
- 使用稳健的统计方法
""")
统计分析报告模板
def generate_analysis_report(data, target_variable, explanatory_variables):
"""
生成标准化的统计分析报告
"""
report = f"""
📊 统计分析报告
==================
🎯 分析目标:{target_variable}
📋 解释变量:{', '.join(explanatory_variables)}
📏 样本大小:{len(data)} 个观测值
📈 描述性统计:
{data[explanatory_variables + [target_variable]].describe().round(2)}
🔗 相关性分析:
{data[explanatory_variables + [target_variable]].corr().round(3)}
💡 主要发现:
1. [在这里填写主要发现]
2. [在这里填写主要发现]
3. [在这里填写主要发现]
🔍 建议:
1. [在这里填写建议]
2. [在这里填写建议]
3. [在这里填写建议]
⚠️ 局限性:
1. [在这里填写分析局限性]
2. [在这里填写分析局限性]
"""
return report
# 使用示例
explanatory_vars = ['年龄', '月收入', '月访问次数']
report = generate_analysis_report(ecommerce_df, '月消费金额', explanatory_vars)
print(report)
🔧 常见问题与解决方案
Q1: 如何处理非正态分布的数据?
print("❓ 问题:数据不符合正态分布怎么办?")
print("""
💡 解决方案:
1. 数据转换:
- 对数转换:适用于右偏数据
- 平方根转换:适用于泊松分布数据
- Box-Cox转换:自动寻找最佳转换
2. 使用非参数检验:
- Mann-Whitney U检验替代t检验
- Kruskal-Wallis检验替代方差分析
- Spearman相关系数替代Pearson相关系数
3. 使用稳健统计方法:
- 中位数替代均值
- 四分位数范围替代标准差
""")
# 示例:对数转换
plt.figure(figsize=(12, 5))
# 原始数据
plt.subplot(1, 2, 1)
plt.hist(ecommerce_df['月收入'], bins=50, alpha=0.7, color='skyblue')
plt.title('原始月收入分布')
plt.xlabel('月收入')
plt.ylabel('频数')
# 对数转换后
plt.subplot(1, 2, 2)
log_income = np.log(ecommerce_df['月收入'])
plt.hist(log_income, bins=50, alpha=0.7, color='lightcoral')
plt.title('对数转换后的月收入分布')
plt.xlabel('log(月收入)')
plt.ylabel('频数')
plt.tight_layout()
plt.show()
Q2: 如何处理异常值?
print("\n❓ 问题:如何识别和处理异常值?")
# 使用箱线图方法识别异常值
def detect_outliers_iqr(data, column):
"""使用IQR方法检测异常值"""
Q1 = data[column].quantile(0.25)
Q3 = data[column].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
outliers = data[(data[column] < lower_bound) | (data[column] > upper_bound)]
return outliers, lower_bound, upper_bound
# 检测月消费金额的异常值
outliers, lower, upper = detect_outliers_iqr(ecommerce_df, '月消费金额')
print(f"🔍 异常值检测结果:")
print(f"下界:{lower:.2f}")
print(f"上界:{upper:.2f}")
print(f"异常值数量:{len(outliers)}")
print(f"异常值比例:{len(outliers)/len(ecommerce_df)*100:.1f}%")
# 可视化异常值
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
plt.boxplot(ecommerce_df['月消费金额'])
plt.title('月消费金额箱线图')
plt.ylabel('月消费金额')
plt.subplot(1, 2, 2)
plt.scatter(range(len(ecommerce_df)), ecommerce_df['月消费金额'], alpha=0.6)
plt.axhline(y=upper, color='red', linestyle='--', label=f'上界: {upper:.0f}')
plt.axhline(y=lower, color='red', linestyle='--', label=f'下界: {lower:.0f}')
plt.title('月消费金额散点图')
plt.xlabel('样本索引')
plt.ylabel('月消费金额')
plt.legend()
plt.tight_layout()
plt.show()
Q3: 如何选择合适的统计检验?
print("\n❓ 问题:如何选择合适的统计检验?")
print("""
🎯 选择统计检验的决策树:
1. 比较两组均值:
✅ 数据正态 + 方差齐性 → 独立样本t检验
✅ 数据正态 + 方差不齐 → Welch t检验
✅ 数据非正态 → Mann-Whitney U检验
2. 比较多组均值:
✅ 数据正态 + 方差齐性 → 单因素方差分析
✅ 数据非正态 → Kruskal-Wallis检验
3. 相关性分析:
✅ 线性关系 + 正态分布 → Pearson相关
✅ 非线性关系或非正态 → Spearman相关
4. 分类变量关联:
✅ 卡方检验
5. 配对比较:
✅ 配对t检验或Wilcoxon符号秩检验
""")
# 创建统计检验选择器
def choose_statistical_test(data_type, groups, distribution):
"""
根据数据特征推荐合适的统计检验
"""
recommendations = []
if data_type == 'continuous':
if groups == 2:
if distribution == 'normal':
recommendations.append("独立样本t检验")
else:
recommendations.append("Mann-Whitney U检验")
elif groups > 2:
if distribution == 'normal':
recommendations.append("单因素方差分析")
else:
recommendations.append("Kruskal-Wallis检验")
elif data_type == 'categorical':
recommendations.append("卡方检验")
return recommendations
# 使用示例
test_recommendations = choose_statistical_test('continuous', 4, 'normal')
print(f"推荐的统计检验:{test_recommendations}")
📖 扩展阅读与资源推荐
print("📚 推荐学习资源:")
print("""
📖 经典教材:
• 《统计学习方法》- 李航
• 《Python统计学习》- 韦斯麦金尼
• 《Think Stats》- Allen B. Downey
🌐 在线资源:
• Khan Academy Statistics
• StatQuest YouTube频道
• Coursera统计学课程
🛠️ 实用工具:
• SciPy: 科学计算库
• Statsmodels: 统计建模
• Pingouin: 统计分析包
• Scikit-learn: 机器学习库
📊 数据集练习:
• UCI Machine Learning Repository
• Kaggle数据集
• 政府开放数据平台
""")
🎬 下集预告
恭喜你!你已经掌握了统计分析的核心技能,从描述性统计到推断性统计,从假设检验到回归分析,你现在已经是一名合格的"数据侦探"了!
下一篇文章《Web爬虫:让程序帮你收集数据》将带你学习如何自动化地收集网络数据。我们将学习:
- 🕷️ 爬虫基础:让程序自动浏览网页
- 🛠️ BeautifulSoup:解析HTML的利器
- 🚀 Scrapy框架:专业级爬虫工具
- 🎭 反爬虫对策:如何优雅地绕过限制
- 📊 数据清洗:把爬取的数据变成宝藏
记住,统计分析不仅仅是计算数字,更是一门发现真相的艺术。每一个统计检验都是一次推理,每一个相关系数都是一段故事。继续保持好奇心,用数据去探索这个世界的奥秘吧!
📝 总结与思考题
🎯 本文要点总结:
- 描述性统计:用数字描述数据的基本特征
- 假设检验:用科学方法验证我们的猜测
- 相关性分析:发现变量间的关系
- 回归分析:建立预测模型
- 方差分析:比较不同组间的差异
🤔 思考题:
- 为什么相关性不等于因果性?你能举出一个生活中的例子吗?
- 在什么情况下应该使用非参数检验而不是参数检验?
- 如何判断一个回归模型是否可靠?需要检查哪些指标?
- 在进行多重比较时,为什么需要调整显著性水平?
💻 实践作业:
- 初级任务:使用本文的代码分析一个你感兴趣的数据集
- 中级任务:完成一个完整的A/B测试统计分析
- 高级任务:建立一个多元回归模型,并进行完整的诊断
记住,统计分析的精髓在于从数据中发现洞察,而不是简单地计算数字。每一次分析都是一次探索之旅!🚀