一、库的简介

Seaborn是基于Matplotlib构建的高级数据可视化库,专为统计图形设计,提供了一系列精美的图表样式和颜色主题,让数据科学家能够用更少的代码创建更具洞察力的可视化结果。在实际生活中,Seaborn的应用场景非常广泛:市场分析师用它快速探索消费者行为模式,生物学家用它可视化实验数据的分布特征,金融分析师用它呈现复杂的相关性分析。当你在学术期刊中看到配色协调、信息丰富的统计图表,在商业报告中看到直观的多变量关系可视化,或是在数据科学竞赛中看到优雅的探索性数据分析时,Seaborn往往都在其中扮演着重要角色。它不仅简化了复杂统计图表的创建过程,更通过精心设计的默认美学,让数据分析结果自然而然地呈现出专业和优雅的气质。

二、安装库

安装Seaborn及其依赖非常简单:

python

# 基础安装
pip install seaborn

# 安装完整版本(包含所有可选依赖)
pip install seaborn[all]

# 使用conda安装
conda install seaborn

# 验证安装
import seaborn as sns
print(f"Seaborn版本: {sns.__version__}")

# 检查重要依赖版本
import matplotlib as mpl
import pandas as pd
import numpy as np
print(f"Matplotlib版本: {mpl.__version__}")
print(f"Pandas版本: {pd.__version__}")
print(f"NumPy版本: {np.__version__}")

# 安装可选依赖以支持更多功能
pip install statsmodels  # 支持更复杂的统计功能
pip install scipy  # 支持统计检验和分布拟合

对于需要生成出版质量图表的用户,建议安装完整依赖:

bash

pip install seaborn[all] matplotlib pandas numpy scipy statsmodels

三、基本用法

1. 样式和颜色主题设置

python

import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

# 设置Seaborn默认样式
sns.set_theme()  # 这是Seaborn 0.11+推荐的方式
# 或者使用传统方式设置样式
sns.set_style("whitegrid")  # 设置样式为白色网格
sns.set_palette("husl")  # 设置颜色调色板

# 查看可用的样式
print("可用样式:", sns.style.available)
# ['darkgrid', 'whitegrid', 'dark', 'white', 'ticks']

# 查看可用的调色板
print("定性调色板:", sns.color_palette("qualitative").as_hex()[:5])
print("连续调色板:", sns.color_palette("Blues").as_hex()[:5])
print("发散调色板:", sns.color_palette("RdBu").as_hex()[:5])

# 自定义样式
custom_style = {
    "axes.facecolor": "#f5f5f5",  # 背景色
    "grid.color": "white",        # 网格颜色
    "grid.linewidth": 1.5,        # 网格线宽
    "axes.grid": True,            # 显示网格
    "font.family": "DejaVu Sans", # 字体
    "axes.labelsize": 12,         # 坐标轴标签大小
    "axes.titlesize": 14,         # 标题大小
}
sns.set_theme(style=custom_style)

# 创建示例数据
np.random.seed(42)
data = pd.DataFrame({
    '类别': np.repeat(['A', 'B', 'C', 'D'], 25),
    '数值': np.random.randn(100).cumsum() + np.random.randint(0, 50, 100),
    '分组': np.tile(['X', 'Y'], 50)
})

# 应用不同样式进行对比
fig, axes = plt.subplots(2, 3, figsize=(15, 10))

# 不同样式展示
styles = ['darkgrid', 'whitegrid', 'dark', 'white', 'ticks']
for idx, style in enumerate(styles):
    sns.set_style(style)
    row, col = divmod(idx, 3)
    ax = axes[row, col]
    sns.histplot(data=data, x='数值', ax=ax, kde=True)
    ax.set_title(f'样式: {style}')
    ax.set_xlabel('')
    ax.set_ylabel('')

# 恢复默认样式
sns.set_theme()

plt.suptitle('Seaborn不同样式对比', fontsize=16, fontweight='bold')
plt.tight_layout()
plt.show()

2. 单变量分布可视化

python

import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from scipy import stats

# 创建多种分布的示例数据
np.random.seed(42)
n_samples = 1000

# 正态分布
normal_data = np.random.normal(loc=0, scale=1, size=n_samples)

# 偏态分布(右偏)
skewed_data = np.random.exponential(scale=2, size=n_samples)

# 双峰分布
bimodal_data = np.concatenate([
    np.random.normal(loc=-2, scale=0.5, size=n_samples//2),
    np.random.normal(loc=2, scale=0.8, size=n_samples//2)
])

# 创建DataFrame
dist_df = pd.DataFrame({
    '正态分布': normal_data,
    '右偏分布': skewed_data,
    '双峰分布': bimodal_data
}).melt(var_name='分布类型', value_name='数值')

# 创建子图
fig, axes = plt.subplots(2, 3, figsize=(15, 10))

# 1. 直方图 + 核密度估计
ax1 = axes[0, 0]
sns.histplot(data=normal_data, kde=True, ax=ax1, 
             color='skyblue', edgecolor='black', linewidth=1)
ax1.set_title('直方图 + KDE', fontsize=12, fontweight='bold')
ax1.set_xlabel('数值')
ax1.set_ylabel('频数')

# 添加理论正态分布曲线
x = np.linspace(normal_data.min(), normal_data.max(), 100)
pdf = stats.norm.pdf(x, loc=0, scale=1)
ax1.plot(x, pdf * n_samples * 0.1, 'r--', linewidth=2, label='理论分布')
ax1.legend()

# 2. 核密度估计图
ax2 = axes[0, 1]
sns.kdeplot(data=skewed_data, ax=ax2, color='lightgreen', 
            fill=True, alpha=0.5, linewidth=2)
ax2.set_title('核密度估计图', fontsize=12, fontweight='bold')
ax2.set_xlabel('数值')
ax2.set_ylabel('密度')

# 添加中位数和均值线
median = np.median(skewed_data)
mean = np.mean(skewed_data)
ax2.axvline(median, color='blue', linestyle='--', linewidth=2, label=f'中位数: {median:.2f}')
ax2.axvline(mean, color='red', linestyle='--', linewidth=2, label=f'均值: {mean:.2f}')
ax2.legend()

# 3. 箱线图
ax3 = axes[0, 2]
sns.boxplot(x=normal_data, ax=ax3, color='lightcoral')
ax3.set_title('箱线图', fontsize=12, fontweight='bold')
ax3.set_xlabel('数值')

# 添加统计信息
stats_text = f"""
Q1: {np.percentile(normal_data, 25):.2f}
中位数: {np.median(normal_data):.2f}
Q3: {np.percentile(normal_data, 75):.2f}
IQR: {np.percentile(normal_data, 75) - np.percentile(normal_data, 25):.2f}
"""
ax3.text(0.05, 0.95, stats_text, transform=ax3.transAxes, fontsize=10,
         verticalalignment='top', bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.8))

# 4. 小提琴图
ax4 = axes[1, 0]
sns.violinplot(data=dist_df, x='分布类型', y='数值', ax=ax4,
               palette='Set2', inner='quartile')
ax4.set_title('小提琴图(多分布比较)', fontsize=12, fontweight='bold')
ax4.set_xlabel('分布类型')
ax4.set_ylabel('数值')

# 5. 经验累积分布函数图
ax5 = axes[1, 1]
for dist_type in dist_df['分布类型'].unique():
    subset = dist_df[dist_df['分布类型'] == dist_type]
    sns.ecdfplot(data=subset, x='数值', ax=ax5, label=dist_type)
ax5.set_title('经验累积分布函数', fontsize=12, fontweight='bold')
ax5.set_xlabel('数值')
ax5.set_ylabel('累积概率')
ax5.legend()

# 6. 带状图 + 蜂群图
ax6 = axes[1, 2]
# 创建小型数据用于演示
sample_data = pd.DataFrame({
    '组别': np.repeat(['A', 'B', 'C'], 20),
    '数值': np.random.randn(60) + np.repeat([0, 2, -1], 20)
})

# 绘制带状图
sns.stripplot(data=sample_data, x='组别', y='数值', ax=ax6,
              palette='tab10', alpha=0.7, size=8, jitter=True)
# 添加蜂群图
sns.swarmplot(data=sample_data, x='组别', y='数值', ax=ax6,
              palette='dark:black', size=5, alpha=0.8)
ax6.set_title('带状图 + 蜂群图', fontsize=12, fontweight='bold')
ax6.set_xlabel('组别')
ax6.set_ylabel('数值')

plt.suptitle('单变量分布可视化方法大全', fontsize=16, fontweight='bold')
plt.tight_layout()
plt.show()

# 高级统计信息展示
print("分布统计摘要:")
for dist_name in ['正态分布', '右偏分布', '双峰分布']:
    dist_data = dist_df[dist_df['分布类型'] == dist_name]['数值']
    print(f"\n{dist_name}:")
    print(f"  样本数: {len(dist_data)}")
    print(f"  均值: {dist_data.mean():.4f}")
    print(f"  标准差: {dist_data.std():.4f}")
    print(f"  偏度: {stats.skew(dist_data):.4f}")
    print(f"  峰度: {stats.kurtosis(dist_data):.4f}")
    print(f"  Shapiro-Wilk正态性检验p值: {stats.shapiro(dist_data)[1]:.4e}")

3. 双变量关系分析

python

import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from scipy import stats

# 创建包含多种关系的示例数据
np.random.seed(42)
n_samples = 200

# 线性正相关
x1 = np.random.uniform(0, 10, n_samples)
y1 = 2 * x1 + np.random.normal(0, 2, n_samples)

# 线性负相关
x2 = np.random.uniform(0, 10, n_samples)
y2 = -1.5 * x2 + np.random.normal(0, 3, n_samples)

# 非线性关系(二次)
x3 = np.random.uniform(-5, 5, n_samples)
y3 = 0.5 * x3**2 + np.random.normal(0, 2, n_samples)

# 无关系
x4 = np.random.uniform(0, 10, n_samples)
y4 = np.random.uniform(0, 10, n_samples)

# 创建分组数据
group_data = pd.DataFrame({
    'X': np.concatenate([x1, x2, x3, x4]),
    'Y': np.concatenate([y1, y2, y3, y4]),
    '关系类型': (['线性正相关'] * n_samples + 
                ['线性负相关'] * n_samples + 
                ['非线性关系'] * n_samples + 
                ['无关系'] * n_samples)
})

# 计算相关系数
print("各关系类型的相关系数:")
for rel_type in group_data['关系类型'].unique():
    subset = group_data[group_data['关系类型'] == rel_type]
    corr = subset['X'].corr(subset['Y'])
    print(f"  {rel_type}: Pearson r = {corr:.4f}")

# 创建可视化
fig, axes = plt.subplots(2, 3, figsize=(15, 10))

# 1. 散点图
ax1 = axes[0, 0]
sns.scatterplot(data=group_data, x='X', y='Y', hue='关系类型', 
                palette='Set2', s=50, alpha=0.7, ax=ax1)
ax1.set_title('散点图(按关系类型着色)', fontsize=12, fontweight='bold')
ax1.set_xlabel('X')
ax1.set_ylabel('Y')
ax1.legend(title='关系类型')

# 2. 回归图
ax2 = axes[0, 1]
sns.regplot(data=group_data[group_data['关系类型'] == '线性正相关'], 
            x='X', y='Y', ax=ax2, color='blue',
            scatter_kws={'s': 50, 'alpha': 0.6},
            line_kws={'color': 'red', 'linewidth': 2})
ax2.set_title('线性回归图(带置信区间)', fontsize=12, fontweight='bold')
ax2.set_xlabel('X')
ax2.set_ylabel('Y')

# 添加回归方程
from sklearn.linear_model import LinearRegression
subset = group_data[group_data['关系类型'] == '线性正相关']
X = subset[['X']]
y = subset['Y']
model = LinearRegression()
model.fit(X, y)
equation = f'y = {model.coef_[0]:.2f}x + {model.intercept_:.2f}'
ax2.text(0.05, 0.95, equation, transform=ax2.transAxes, fontsize=11,
         verticalalignment='top', bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))

# 3. 残差图
ax3 = axes[0, 2]
sns.residplot(data=group_data[group_data['关系类型'] == '线性正相关'], 
              x='X', y='Y', ax=ax3, color='green',
              scatter_kws={'s': 50, 'alpha': 0.6},
              line_kws={'color': 'red', 'linewidth': 1})
ax3.axhline(y=0, color='black', linestyle='--', linewidth=1)
ax3.set_title('回归残差图', fontsize=12, fontweight='bold')
ax3.set_xlabel('X')
ax3.set_ylabel('残差')

# 4. 联合分布图
ax4 = axes[1, 0]
g = sns.jointplot(data=group_data[group_data['关系类型'] == '线性正相关'], 
                  x='X', y='Y', kind='reg', 
                  color='purple', height=5, ratio=3,
                  marginal_kws={'bins': 20, 'kde': True})
g.ax_joint.set_title('联合分布图', fontsize=12, fontweight='bold')
g.ax_joint.set_xlabel('X')
g.ax_joint.set_ylabel('Y')

# 5. 热图(相关性矩阵)
ax5 = axes[1, 1]
# 创建相关性矩阵
corr_data = group_data.pivot_table(index=None, columns='关系类型', 
                                   values=['X', 'Y'], aggfunc='mean')
corr_matrix = corr_data.corr()

sns.heatmap(corr_matrix, annot=True, fmt='.2f', cmap='coolwarm', 
            center=0, square=True, linewidths=1, ax=ax5,
            cbar_kws={'label': '相关系数'})
ax5.set_title('相关性热图', fontsize=12, fontweight='bold')

# 6. 成对关系图
# 创建新数据用于成对关系图
pair_data = pd.DataFrame({
    '特征1': np.random.randn(100),
    '特征2': np.random.randn(100) * 0.5 + 1,
    '特征3': np.random.randn(100) * 1.5 - 1,
    '类别': np.random.choice(['A', 'B', 'C'], 100)
})

ax6 = axes[1, 2]
# 使用pairplot可能需要单独的子图,这里简化处理
sns.scatterplot(data=pair_data, x='特征1', y='特征2', 
                hue='类别', style='类别', s=100, ax=ax6)
ax6.set_title('多特征散点图示例', fontsize=12, fontweight='bold')
ax6.set_xlabel('特征1')
ax6.set_ylabel('特征2')
ax6.legend(title='类别')

plt.suptitle('双变量关系分析方法大全', fontsize=16, fontweight='bold')
plt.tight_layout()
plt.show()

# 高级分析:成对关系网格
print("\n生成成对关系网格图...")
pair_grid = sns.pairplot(data=pair_data, hue='类别', 
                         palette='Set2', diag_kind='kde',
                         plot_kws={'alpha': 0.7, 's': 50},
                         diag_kws={'fill': True})
pair_grid.fig.suptitle('成对关系网格图', fontsize=16, fontweight='bold', y=1.02)
plt.show()

4. 分类数据可视化

python

import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from scipy import stats

# 创建复杂的分类数据
np.random.seed(42)
n_categories = 4
n_subcategories = 3
n_samples = 300

# 生成数据
data = []
for cat in ['电子产品', '服装', '家居', '食品']:
    for subcat in ['低端', '中端', '高端']:
        base_price = {'电子产品': [100, 500, 1500],
                      '服装': [50, 200, 800],
                      '家居': [80, 400, 1200],
                      '食品': [10, 50, 200]}[cat][['低端', '中端', '高端'].index(subcat)]
        
        price_variation = np.random.uniform(0.7, 1.3, n_samples // (n_categories * n_subcategories))
        sales_variation = np.random.uniform(0.5, 2.0, n_samples // (n_categories * n_subcategories))
        
        for i in range(n_samples // (n_categories * n_subcategories)):
            data.append({
                '产品类别': cat,
                '价格档次': subcat,
                '价格': base_price * price_variation[i],
                '销量': 1000 * sales_variation[i] / (base_price * price_variation[i]) ** 0.5,
                '评分': np.random.beta(a=2, b=2) * 4 + 1,  # 1-5分
                '退货率': np.random.beta(a=1, b=9) * 10,   # 0-10%
                '月份': np.random.choice(['1月', '2月', '3月', '4月', '5月', '6月'])
            })

df = pd.DataFrame(data)

# 添加衍生特征
df['收入'] = df['价格'] * df['销量']
df['利润'] = df['收入'] * np.random.uniform(0.1, 0.3, len(df))

print("数据概览:")
print(f"总记录数: {len(df)}")
print(f"产品类别: {df['产品类别'].unique().tolist()}")
print(f"价格档次: {df['价格档次'].unique().tolist()}")
print(f"数值特征: {df.select_dtypes(include=[np.number]).columns.tolist()}")

# 创建分类数据可视化
fig, axes = plt.subplots(2, 3, figsize=(18, 12))

# 1. 分类柱状图(带误差条)
ax1 = axes[0, 0]
sns.barplot(data=df, x='产品类别', y='收入', hue='价格档次', 
            palette='Blues', ax=ax1, ci='sd', capsize=0.1)
ax1.set_title('各品类收入分布(按价格档次)', fontsize=12, fontweight='bold')
ax1.set_xlabel('产品类别')
ax1.set_ylabel('平均收入')
ax1.tick_params(axis='x', rotation=45)
ax1.legend(title='价格档次')

# 添加数值标签
for container in ax1.containers:
    ax1.bar_label(container, fmt='%.0f', padding=3, fontsize=9)

# 2. 箱线图 + 带状图
ax2 = axes[0, 1]
sns.boxplot(data=df, x='产品类别', y='价格', hue='价格档次',
            palette='Set2', ax=ax2, showfliers=False)
sns.stripplot(data=df, x='产品类别', y='价格', hue='价格档次',
              palette='dark:black', ax=ax2, dodge=True, 
              size=3, alpha=0.5, jitter=0.2)
ax2.set_title('价格分布箱线图(带原始数据点)', fontsize=12, fontweight='bold')
ax2.set_xlabel('产品类别')
ax2.set_ylabel('价格')
ax2.tick_params(axis='x', rotation=45)
# 处理图例(避免重复)
handles, labels = ax2.get_legend_handles_labels()
ax2.legend(handles[:3], labels[:3], title='价格档次')

# 3. 小提琴图
ax3 = axes[0, 2]
sns.violinplot(data=df, x='产品类别', y='评分', hue='价格档次',
               palette='viridis', ax=ax3, split=True, inner='quartile')
ax3.set_title('评分分布小提琴图', fontsize=12, fontweight='bold')
ax3.set_xlabel('产品类别')
ax3.set_ylabel('评分')
ax3.tick_params(axis='x', rotation=45)
ax3.legend(title='价格档次')

# 4. 点图(Point Plot)
ax4 = axes[1, 0]
sns.pointplot(data=df, x='月份', y='销量', hue='产品类别',
              palette='tab10', ax=ax4, ci='sd', dodge=0.3,
              markers=['o', 's', '^', 'D'], linestyles=['-', '--', '-.', ':'])
ax4.set_title('月度销量趋势(按产品类别)', fontsize=12, fontweight='bold')
ax4.set_xlabel('月份')
ax4.set_ylabel('平均销量')
ax4.legend(title='产品类别', bbox_to_anchor=(1.05, 1), loc='upper left')

# 5. 热图(分类数据聚合)
ax5 = axes[1, 1]
# 创建透视表
heatmap_data = df.pivot_table(values='退货率', 
                              index='产品类别', 
                              columns='价格档次', 
                              aggfunc='mean')
sns.heatmap(heatmap_data, annot=True, fmt='.1f', cmap='YlOrRd',
            square=True, linewidths=1, ax=ax5, cbar_kws={'label': '平均退货率 (%)'})
ax5.set_title('各品类退货率热图', fontsize=12, fontweight='bold')
ax5.set_xlabel('价格档次')
ax5.set_ylabel('产品类别')

# 6. 分类散点图
ax6 = axes[1, 2]
sns.scatterplot(data=df, x='价格', y='销量', hue='产品类别',
                style='价格档次', size='评分', sizes=(50, 200),
                palette='Set2', alpha=0.7, ax=ax6)
ax6.set_title('价格 vs 销量(多维度编码)', fontsize=12, fontweight='bold')
ax6.set_xlabel('价格')
ax6.set_ylabel('销量')
ax6.legend(bbox_to_anchor=(1.05, 1), loc='upper left')

plt.suptitle('分类数据可视化方法大全', fontsize=16, fontweight='bold')
plt.tight_layout()
plt.show()

# 高级可视化:分类数据关系网格
print("\n生成分类数据关系网格...")
g = sns.FacetGrid(df, col='产品类别', row='价格档次', 
                  margin_titles=True, height=3, aspect=1.2)
g.map_dataframe(sns.scatterplot, x='价格', y='销量', 
                hue='月份', palette='tab10', alpha=0.7)
g.add_legend()
g.set_titles(row_template='{row_name}档次', col_template='{col_name}')
g.fig.suptitle('多维度分类数据关系网格', fontsize=16, fontweight='bold', y=1.02)
plt.show()

四、高级用法

1. 多面板复杂图形

python

import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from scipy import stats

# 设置专业样式
sns.set_theme(style="whitegrid", font_scale=1.2)
plt.rcParams['figure.dpi'] = 150

# 创建综合数据集
np.random.seed(42)
n_samples = 500

# 生成复杂数据
data = pd.DataFrame({
    '基因表达量_A': np.random.exponential(2, n_samples),
    '基因表达量_B': np.random.normal(5, 1.5, n_samples),
    '基因表达量_C': np.random.lognormal(1, 0.5, n_samples),
    '细胞直径': np.random.uniform(10, 30, n_samples),
    '细胞复杂度': np.random.beta(2, 5, n_samples) * 100,
    '处理组': np.random.choice(['对照组', '药物A', '药物B', '药物C'], n_samples),
    '细胞类型': np.random.choice(['神经元', '胶质细胞', '干细胞'], n_samples),
    '时间点': np.random.choice(['0h', '6h', '12h', '24h'], n_samples),
    '重复实验': np.random.choice(['实验1', '实验2', '实验3'], n_samples)
})

# 添加相关性(创建一些特征之间的关系)
data['基因表达量_B'] = data['基因表达量_B'] + 0.3 * data['基因表达量_A'] + np.random.normal(0, 1, n_samples)
data['细胞直径'] = data['细胞直径'] + 0.5 * data['基因表达量_C'] + np.random.normal(0, 3, n_samples)
data['细胞复杂度'] = 50 + 10 * data['基因表达量_A'] - 5 * data['基因表达量_B'] + np.random.normal(0, 15, n_samples)

# 创建多面板图形
fig = plt.figure(figsize=(20, 16))

# 使用GridSpec创建复杂布局
import matplotlib.gridspec as gridspec
gs = gridspec.GridSpec(3, 3, figure=fig, 
                       width_ratios=[1, 1, 0.8],
                       height_ratios=[1, 1, 1.2],
                       wspace=0.3, hspace=0.4)

# 1. 主散点图(左上角,占两行两列)
ax1 = fig.add_subplot(gs[:2, :2])
scatter = sns.scatterplot(data=data, x='基因表达量_A', y='基因表达量_B',
                          hue='处理组', style='细胞类型', size='细胞直径',
                          sizes=(30, 300), alpha=0.7, palette='Set2', ax=ax1)

# 添加回归线
for treatment in data['处理组'].unique():
    subset = data[data['处理组'] == treatment]
    sns.regplot(data=subset, x='基因表达量_A', y='基因表达量_B',
                scatter=False, ci=None, 
                line_kws={'alpha': 0.5, 'linewidth': 2}, ax=ax1)

ax1.set_title('基因表达关系网络(多维度编码)', fontsize=14, fontweight='bold')
ax1.set_xlabel('基因A表达量', fontsize=12)
ax1.set_ylabel('基因B表达量', fontsize=12)
ax1.legend(bbox_to_anchor=(1.05, 1), loc='upper left')

# 2. 小提琴图(右上角)
ax2 = fig.add_subplot(gs[0, 2])
sns.violinplot(data=data, x='处理组', y='基因表达量_C', 
               hue='时间点', split=True, inner='quartile',
               palette='husl', ax=ax2)
ax2.set_title('基因C表达分布', fontsize=12, fontweight='bold')
ax2.set_xlabel('处理组')
ax2.set_ylabel('基因C表达量')
ax2.tick_params(axis='x', rotation=45)
ax2.legend(title='时间点', bbox_to_anchor=(1.05, 1), loc='upper left')

# 3. 热图(中右)
ax3 = fig.add_subplot(gs[1, 2])
# 计算相关性矩阵
corr_cols = ['基因表达量_A', '基因表达量_B', '基因表达量_C', 
             '细胞直径', '细胞复杂度']
corr_matrix = data[corr_cols].corr()

# 创建带星号注释的热图
def annotate_heatmap(data, fmt, **kwargs):
    """自定义热图注释函数,添加统计显著性"""
    annotations = []
    p_values = np.zeros_like(data)
    
    # 计算p值
    n = len(data)
    for i in range(n):
        for j in range(n):
            if i != j:
                corr, p_value = stats.pearsonr(data[corr_cols[i]], data[corr_cols[j]])
                p_values[i, j] = p_value
    
    # 创建带星号的注释
    for i in range(n):
        row = []
        for j in range(n):
            if i == j:
                row.append(f'{data.iloc[i, j]:.2f}')
            else:
                p = p_values[i, j]
                if p < 0.001:
                    star = '***'
                elif p < 0.01:
                    star = '**'
                elif p < 0.05:
                    star = '*'
                else:
                    star = ''
                row.append(f'{data.iloc[i, j]:.2f}{star}')
        annotations.append(row)
    
    return annotations

annotations = annotate_heatmap(data[corr_cols], '.2f')
sns.heatmap(corr_matrix, annot=annotations, fmt='', cmap='coolwarm',
            center=0, square=True, linewidths=1, ax=ax3,
            cbar_kws={'label': '相关系数'}, annot_kws={'size': 9})
ax3.set_title('特征相关性热图\n(*p<0.05, **p<0.01, ***p<0.001)', 
              fontsize=12, fontweight='bold')

# 4. 箱线图网格(左下角)
ax4 = fig.add_subplot(gs[2, 0])
# 重塑数据用于箱线图
melted_data = data.melt(id_vars=['处理组', '细胞类型'], 
                        value_vars=['基因表达量_A', '基因表达量_B', '基因表达量_C'],
                        var_name='基因', value_name='表达量')

sns.boxplot(data=melted_data, x='基因', y='表达量', hue='处理组',
            palette='pastel', ax=ax4, showfliers=False)
sns.stripplot(data=melted_data, x='基因', y='表达量', hue='处理组',
              palette='dark:black', ax=ax4, dodge=True,
              size=2, alpha=0.5, jitter=0.2)
ax4.set_title('多基因表达箱线图', fontsize=12, fontweight='bold')
ax4.set_xlabel('基因')
ax4.set_ylabel('表达量')
ax4.tick_params(axis='x', rotation=45)
# 简化图例
handles, labels = ax4.get_legend_handles_labels()
ax4.legend(handles[:4], labels[:4], title='处理组')

# 5. 分布图网格(中下)
ax5 = fig.add_subplot(gs[2, 1])
# 绘制多变量分布
variables = ['细胞直径', '细胞复杂度']
treatments = data['处理组'].unique()

for i, var in enumerate(variables):
    for j, treatment in enumerate(treatments):
        subset = data[data['处理组'] == treatment]
        sns.kdeplot(data=subset, x=var, ax=ax5, 
                    label=f'{treatment} - {var}', 
                    fill=True, alpha=0.3)

ax5.set_title('细胞特征分布(按处理组)', fontsize=12, fontweight='bold')
ax5.set_xlabel('特征值')
ax5.set_ylabel('密度')
ax5.legend(bbox_to_anchor=(1.05, 1), loc='upper left')

# 6. 点图(右下角)
ax6 = fig.add_subplot(gs[2, 2])
# 计算每个组的统计量
summary_data = data.groupby(['处理组', '时间点']).agg({
    '基因表达量_A': 'mean',
    '基因表达量_B': 'mean',
    '细胞复杂度': 'mean'
}).reset_index()

# 绘制点图
sns.pointplot(data=summary_data, x='时间点', y='基因表达量_A', 
              hue='处理组', palette='Set2', ax=ax6,
              markers=['o', 's', '^', 'D'], dodge=0.3,
              errwidth=1.5, capsize=0.1)
ax6.set_title('时间序列基因表达趋势', fontsize=12, fontweight='bold')
ax6.set_xlabel('时间点')
ax6.set_ylabel('基因A平均表达量')
ax6.legend(title='处理组', bbox_to_anchor=(1.05, 1), loc='upper left')

# 添加全局标题
plt.suptitle('生物医学研究数据综合可视化面板', 
             fontsize=18, fontweight='bold', y=0.98)

# 调整布局
plt.tight_layout()
plt.show()

# 统计摘要
print("数据集统计摘要:")
print(f"总样本数: {len(data)}")
print(f"处理组分布:")
print(data['处理组'].value_counts())
print(f"\n细胞类型分布:")
print(data['细胞类型'].value_counts())
print(f"\n数值特征统计:")
print(data[['基因表达量_A', '基因表达量_B', '基因表达量_C', 
            '细胞直径', '细胞复杂度']].describe().round(2))

2. 时间序列可视化

python

import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from scipy import stats, signal

# 设置专业样式
sns.set_theme(style="darkgrid", palette="husl", font_scale=1.1)

# 创建复杂的时间序列数据
np.random.seed(42)
n_days = 365 * 3  # 3年数据
dates = pd.date_range(start='2021-01-01', periods=n_days, freq='D')

# 生成基础时间序列(带趋势、季节性和噪声)
def generate_time_series(base_value=100, trend=0.02, seasonal_amplitude=20, 
                         noise_level=5, freq='D', n_periods=n_days):
    """生成模拟时间序列"""
    # 时间索引
    t = np.arange(n_periods)
    
    # 趋势成分(线性趋势 + 轻微非线性)
    trend_component = base_value + trend * t + 0.001 * t**2
    
    # 季节性成分(多重季节性)
    # 年季节性
    seasonal_yearly = seasonal_amplitude * np.sin(2 * np.pi * t / 365)
    # 月季节性
    seasonal_monthly = 0.3 * seasonal_amplitude * np.sin(2 * np.pi * t / 30.5)
    # 周季节性
    seasonal_weekly = 0.1 * seasonal_amplitude * np.sin(2 * np.pi * t / 7)
    
    seasonal_component = seasonal_yearly + seasonal_monthly + seasonal_weekly
    
    # 噪声成分(异方差噪声)
    noise_component = np.random.normal(0, noise_level, n_periods) * (1 + 0.01 * t)
    
    # 异常值
    outlier_prob = 0.005  # 0.5%的概率出现异常值
    outliers = np.random.choice([0, 1], size=n_periods, p=[1-outlier_prob, outlier_prob])
    outlier_component = outliers * np.random.choice([-1, 1], n_periods) * seasonal_amplitude * 3
    
    # 合成时间序列
    ts = trend_component + seasonal_component + noise_component + outlier_component
    
    return ts

# 生成多个相关的时间序列
metrics = {
    '销售额': generate_time_series(100, 0.03, 25, 8),
    '用户数': generate_time_series(1000, 0.01, 200, 50),
    '转化率': generate_time_series(0.05, 0.0001, 0.01, 0.005),
    '客单价': generate_time_series(200, 0.1, 30, 10),
    '营销费用': generate_time_series(50, 0.02, 15, 5)
}

# 添加序列间的相关性
metrics['用户数'] = metrics['用户数'] + 0.5 * metrics['销售额'] + np.random.normal(0, 20, n_days)
metrics['转化率'] = 0.05 + 0.0002 * metrics['营销费用'] + np.random.normal(0, 0.003, n_days)
metrics['客单价'] = 200 + 0.1 * metrics['销售额'] - 0.05 * metrics['用户数'] + np.random.normal(0, 8, n_days)

# 创建DataFrame
ts_data = pd.DataFrame(metrics, index=dates)
ts_data.index.name = '日期'

# 添加衍生特征
ts_data['收入'] = ts_data['销售额'] * ts_data['客单价']
ts_data['获客成本'] = ts_data['营销费用'] / ts_data['用户数']
ts_data['月'] = ts_data.index.month
ts_data['季度'] = ts_data.index.quarter
ts_data['年份'] = ts_data.index.year
ts_data['星期'] = ts_data.index.dayofweek
ts_data['是否周末'] = ts_data['星期'].isin([5, 6]).astype(int)

print("时间序列数据概览:")
print(f"时间范围: {ts_data.index.min()} 到 {ts_data.index.max()}")
print(f"总天数: {len(ts_data)}")
print(f"指标数量: {len(ts_data.columns)}")

# 创建时间序列可视化面板
fig = plt.figure(figsize=(18, 14))

# 使用GridSpec创建布局
import matplotlib.gridspec as gridspec
gs = gridspec.GridSpec(3, 3, figure=fig, 
                       width_ratios=[1.5, 1, 1],
                       height_ratios=[1, 1, 1.2],
                       wspace=0.3, hspace=0.4)

# 1. 多时间序列趋势图(左上,占两行一列)
ax1 = fig.add_subplot(gs[:2, 0])
# 标准化以便在同一图中比较
normalized_data = (ts_data[['销售额', '用户数', '营销费用']] - 
                   ts_data[['销售额', '用户数', '营销费用']].mean()) / \
                  ts_data[['销售额', '用户数', '营销费用']].std()

for column in normalized_data.columns:
    sns.lineplot(data=normalized_data, x=normalized_data.index, y=column, 
                 label=column, ax=ax1, linewidth=1.5, alpha=0.8)

# 添加移动平均
window = 30
for column in normalized_data.columns:
    moving_avg = normalized_data[column].rolling(window=window).mean()
    ax1.plot(normalized_data.index, moving_avg, linewidth=2.5, 
             label=f'{column} ({window}天移动平均)')

ax1.set_title('核心指标时间序列(标准化)', fontsize=14, fontweight='bold')
ax1.set_xlabel('日期')
ax1.set_ylabel('标准化值')
ax1.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
ax1.tick_params(axis='x', rotation=45)

# 2. 季节分解(右上)
ax2 = fig.add_subplot(gs[0, 1])
# 使用Statsmodels进行季节分解
from statsmodels.tsa.seasonal import seasonal_decompose

# 选择销售额进行分析
sales_series = ts_data['销售额']
decomposition = seasonal_decompose(sales_series.resample('M').mean(), 
                                   model='additive', period=12)

# 绘制趋势成分
ax2.plot(decomposition.trend.index, decomposition.trend, 
         color='red', linewidth=2, label='趋势')
ax2.fill_between(decomposition.trend.index, 
                 decomposition.trend - decomposition.trend.std(),
                 decomposition.trend + decomposition.trend.std(),
                 alpha=0.2, color='red')
ax2.set_title('销售额趋势分解', fontsize=12, fontweight='bold')
ax2.set_xlabel('日期')
ax2.set_ylabel('销售额')
ax2.legend()
ax2.tick_params(axis='x', rotation=45)

# 3. 季节性分析(右中)
ax3 = fig.add_subplot(gs[1, 1])
# 计算月度平均值
monthly_avg = ts_data.groupby('月')['销售额'].agg(['mean', 'std']).reset_index()

# 绘制月度模式
sns.barplot(data=monthly_avg, x='月', y='mean', 
            palette='viridis', ax=ax3, alpha=0.7)
# 添加误差条
x_positions = np.arange(len(monthly_avg))
ax3.errorbar(x_positions, monthly_avg['mean'], yerr=monthly_avg['std'],
             fmt='none', color='black', capsize=3, capthick=1)

ax3.set_title('销售额月度季节性模式', fontsize=12, fontweight='bold')
ax3.set_xlabel('月份')
ax3.set_ylabel('平均销售额')
ax3.set_xticklabels(['1月', '2月', '3月', '4月', '5月', '6月', 
                     '7月', '8月', '9月', '10月', '11月', '12月'])

# 4. 自相关和偏自相关图(左中下)
ax4 = fig.add_subplot(gs[2, 0])
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf

# 绘制自相关函数
plot_acf(sales_series.diff().dropna(), lags=50, ax=ax4, alpha=0.05)
ax4.set_title('销售额一阶差分自相关函数', fontsize=12, fontweight='bold')
ax4.set_xlabel('滞后')
ax4.set_ylabel('自相关系数')

# 5. 热力图:星期vs月份(中下)
ax5 = fig.add_subplot(gs[2, 1])
# 创建透视表:星期 vs 月份 的平均销售额
heatmap_data = ts_data.pivot_table(values='销售额', 
                                   index='星期', 
                                   columns='月', 
                                   aggfunc='mean')

# 重新索引使星期从周一开始
heatmap_data = heatmap_data.reindex([1, 2, 3, 4, 5, 6, 0])

sns.heatmap(heatmap_data, annot=True, fmt='.0f', cmap='YlOrRd',
            linewidths=0.5, ax=ax5, cbar_kws={'label': '平均销售额'})

ax5.set_title('销售额:星期 vs 月份热图', fontsize=12, fontweight='bold')
ax5.set_xlabel('月份')
ax5.set_ylabel('星期')
ax5.set_yticklabels(['周一', '周二', '周三', '周四', '周五', '周六', '周日'])
ax5.set_xticklabels(['1月', '2月', '3月', '4月', '5月', '6月', 
                     '7月', '8月', '9月', '10月', '11月', '12月'])

# 6. 多指标关系散点图(右下)
ax6 = fig.add_subplot(gs[:2, 2])
# 按季度着色
scatter = sns.scatterplot(data=ts_data, x='用户数', y='销售额',
                          hue='季度', size='客单价', style='年份',
                          palette='Set2', sizes=(30, 200), alpha=0.7, ax=ax6)

# 添加回归线
sns.regplot(data=ts_data, x='用户数', y='销售额',
            scatter=False, ci=95, 
            line_kws={'color': 'red', 'linewidth': 2, 'alpha': 0.5}, ax=ax6)

ax6.set_title('用户数 vs 销售额(多维度编码)', fontsize=12, fontweight='bold')
ax6.set_xlabel('用户数')
ax6.set_ylabel('销售额')
ax6.legend(bbox_to_anchor=(1.05, 1), loc='upper left')

# 7. 滚动统计图(左下角)
ax7 = fig.add_subplot(gs[2, 2])
# 计算滚动统计量
window_sizes = [7, 30, 90]
colors = ['blue', 'green', 'red']

for window, color in zip(window_sizes, colors):
    rolling_mean = ts_data['销售额'].rolling(window=window).mean()
    rolling_std = ts_data['销售额'].rolling(window=window).std()
    
    # 绘制滚动平均值
    ax7.plot(ts_data.index, rolling_mean, color=color, linewidth=1.5,
             label=f'{window}天移动平均', alpha=0.8)
    
    # 填充滚动标准差区域
    ax7.fill_between(ts_data.index,
                     rolling_mean - rolling_std,
                     rolling_mean + rolling_std,
                     color=color, alpha=0.1)

ax7.set_title('销售额滚动统计量', fontsize=12, fontweight='bold')
ax7.set_xlabel('日期')
ax7.set_ylabel('销售额')
ax7.legend()
ax7.tick_params(axis='x', rotation=45)

# 添加全局标题
plt.suptitle('业务时间序列数据综合分析面板', 
             fontsize=18, fontweight='bold', y=0.98)

# 调整布局
plt.tight_layout()
plt.show()

# 高级分析:时间序列统计检验
print("\n时间序列统计检验:")
print("=" * 50)

# ADF单位根检验(检验平稳性)
from statsmodels.tsa.stattools import adfuller
adf_result = adfuller(sales_series.dropna())
print(f"ADF单位根检验(销售额):")
print(f"  ADF统计量: {adf_result[0]:.4f}")
print(f"  p值: {adf_result[1]:.4e}")
print(f"  是否平稳: {'是' if adf_result[1] < 0.05 else '否'}")

# 季节性检验
print(f"\n季节性分析(销售额):")
monthly_effects = ts_data.groupby('月')['销售额'].mean()
print(f"  月度变异系数: {monthly_effects.std() / monthly_effects.mean():.3f}")
print(f"  最强月份: {monthly_effects.idxmax()}月 ({monthly_effects.max():.1f})")
print(f"  最弱月份: {monthly_effects.idxmin()}月 ({monthly_effects.min():.1f})")

# 趋势分析
print(f"\n趋势分析(销售额):")
# 计算年度增长
yearly_growth = ts_data.groupby('年份')['销售额'].mean()
growth_rates = yearly_growth.pct_change().dropna()
print(f"  年均增长率: {growth_rates.mean():.2%}")
print(f"  增长稳定性(变异系数): {growth_rates.std() / growth_rates.mean():.3f}")

# 预测区间
print(f"\n预测分析:")
last_value = sales_series.iloc[-1]
trend_growth = 0.03  # 假设3%的日趋势增长
forecast_days = 30
forecast = last_value * (1 + trend_growth) ** forecast_days
print(f"  {forecast_days}天预测值: {forecast:.1f}")
print(f"  95%预测区间: [{forecast * 0.9:.1f}, {forecast * 1.1:.1f}]")

五、实际应用场景

1. 用户行为分析系统

python

class UserBehaviorAnalyzer:
    def __init__(self):
        self.data = None
        self.fig = None
        
    def generate_sample_data(self, n_users=1000, n_days=90):
        """生成模拟用户行为数据"""
        np.random.seed(42)
        
        # 生成用户基本属性
        user_ids = np.arange(1, n_users + 1)
        ages = np.random.randint(18, 65, n_users)
        genders = np.random.choice(['男', '女'], n_users, p=[0.55, 0.45])
        cities = np.random.choice(['北京', '上海', '广州', '深圳', '杭州', '成都', 
                                  '武汉', '南京', '西安', '重庆'], n_users)
        registration_dates = pd.date_range(
            start='2024-01-01', 
            end='2024-03-31', 
            periods=n_users
        )
        
        # 用户行为数据
        user_data = []
        for user_id, age, gender, city, reg_date in zip(user_ids, ages, genders, cities, registration_dates):
            # 每个用户生成多个行为记录
            n_sessions = np.random.poisson(30)  # 平均30次会话
            for _ in range(n_sessions):
                # 会话时间(注册后随机时间)
                days_after_reg = np.random.randint(0, (pd.Timestamp('2024-06-30') - reg_date).days)
                session_date = reg_date + pd.Timedelta(days=days_after_reg)
                
                # 会话特征
                session_duration = np.random.exponential(300)  # 平均5分钟
                page_views = np.random.randint(1, 50)
                clicks = np.random.binomial(page_views, 0.3)  # 30%点击率
                purchases = 1 if np.random.random() < 0.05 else 0  # 5%购买率
                purchase_amount = purchases * np.random.uniform(10, 1000) if purchases else 0
                
                # 设备类型
                device = np.random.choice(['手机', '电脑', '平板'], 
                                          p=[0.6, 0.3, 0.1])
                
                # 流量来源
                traffic_source = np.random.choice(['直接访问', '搜索引擎', '社交媒体', '广告'],
                                                  p=[0.2, 0.4, 0.2, 0.2])
                
                user_data.append({
                    '用户ID': user_id,
                    '年龄': age,
                    '性别': gender,
                    '城市': city,
                    '注册日期': reg_date,
                    '会话日期': session_date,
                    '会话时长': session_duration,
                    '页面浏览数': page_views,
                    '点击次数': clicks,
                    '是否购买': purchases,
                    '购买金额': purchase_amount,
                    '设备类型': device,
                    '流量来源': traffic_source
                })
        
        self.data = pd.DataFrame(user_data)
        
        # 添加衍生特征
        self.data['会话日期'] = pd.to_datetime(self.data['会话日期'])
        self.data['注册月份'] = self.data['注册日期'].dt.to_period('M')
        self.data['会话月份'] = self.data['会话日期'].dt.to_period('M')
        self.data['注册天数'] = (self.data['会话日期'] - self.data['注册日期']).dt.days
        self.data['点击率'] = self.data['点击次数'] / self.data['页面浏览数']
        self.data['会话时长分钟'] = self.data['会话时长'] / 60
        
        # 用户级别聚合
        user_agg = self.data.groupby('用户ID').agg({
            '会话日期': 'count',
            '是否购买': 'sum',
            '购买金额': 'sum',
            '会话时长': 'mean',
            '页面浏览数': 'mean'
        }).rename(columns={
            '会话日期': '总会话数',
            '是否购买': '购买次数',
            '购买金额': '总消费金额',
            '会话时长': '平均会话时长',
            '页面浏览数': '平均页面浏览数'
        })
        
        self.user_summary = user_agg.reset_index()
        
        print(f"数据生成完成:")
        print(f"  总用户数: {n_users}")
        print(f"  总会话数: {len(self.data)}")
        print(f"  总购买次数: {self.data['是否购买'].sum()}")
        print(f"  总消费金额: ${self.data['购买金额'].sum():,.2f}")
        
        return self.data
    
    def create_user_segmentation_dashboard(self):
        """创建用户分群分析仪表板"""
        if self.data is None:
            print("请先生成数据")
            return
        
        # 设置样式
        sns.set_theme(style="whitegrid", palette="husl", font_scale=1.1)
        
        # 创建图形
        fig = plt.figure(figsize=(20, 16))
        
        # 使用GridSpec创建布局
        import matplotlib.gridspec as gridspec
        gs = gridspec.GridSpec(3, 3, figure=fig, 
                              width_ratios=[1, 1, 1],
                              height_ratios=[1, 1, 1.2],
                              wspace=0.3, hspace=0.4)
        
        # 1. RFM分析(左上)
        ax1 = fig.add_subplot(gs[0, 0])
        
        # 计算RFM值
        current_date = self.data['会话日期'].max()
        
        rfm_data = self.data.groupby('用户ID').agg({
            '会话日期': lambda x: (current_date - x.max()).days,  # 最近访问
            '用户ID': 'count',  # 访问频率
            '购买金额': 'sum'  # 消费金额
        }).rename(columns={
            '会话日期': 'Recency',
            '用户ID': 'Frequency',
            '购买金额': 'Monetary'
        })
        
        # RFM分箱
        rfm_data['R_Score'] = pd.qcut(rfm_data['Recency'], 4, labels=[4, 3, 2, 1])
        rfm_data['F_Score'] = pd.qcut(rfm_data['Frequency'], 4, labels=[1, 2, 3, 4])
        rfm_data['M_Score'] = pd.qcut(rfm_data['Monetary'], 4, labels=[1, 2, 3, 4])
        
        rfm_data['RFM_Score'] = rfm_data['R_Score'].astype(str) + \
                                rfm_data['F_Score'].astype(str) + \
                                rfm_data['M_Score'].astype(str)
        
        # 用户分群
        def segment_user(row):
            if row['R_Score'] >= 3 and row['F_Score'] >= 3 and row['M_Score'] >= 3:
                return '高价值用户'
            elif row['R_Score'] >= 3:
                return '活跃用户'
            elif row['R_Score'] < 2:
                return '流失用户'
            else:
                return '一般用户'
        
        rfm_data['Segment'] = rfm_data.apply(segment_user, axis=1)
        
        # 绘制分群分布
        segment_counts = rfm_data['Segment'].value_counts()
        colors = sns.color_palette('Set2', len(segment_counts))
        
        wedges, texts, autotexts = ax1.pie(segment_counts.values, 
                                          labels=segment_counts.index,
                                          autopct='%1.1f%%',
                                          colors=colors,
                                          startangle=90,
                                          wedgeprops=dict(width=0.3, edgecolor='w'))
        
        # 美化饼图
        for autotext in autotexts:
            autotext.set_color('white')
            autotext.set_fontweight('bold')
        
        ax1.set_title('用户RFM分群分布', fontsize=12, fontweight='bold')
        
        # 2. 用户生命周期价值分布(右上)
        ax2 = fig.add_subplot(gs[0, 1])
        
        # 计算用户LTV(简化版)
        ltv_data = self.user_summary.copy()
        ltv_data['LTV'] = ltv_data['总消费金额']
        
        # 绘制LTV分布
        sns.histplot(data=ltv_data, x='LTV', kde=True, ax=ax2,
                    bins=30, color='skyblue', edgecolor='black')
        
        # 添加统计线
        mean_ltv = ltv_data['LTV'].mean()
        median_ltv = ltv_data['LTV'].median()
        
        ax2.axvline(mean_ltv, color='red', linestyle='--', 
                   linewidth=2, label=f'均值: ${mean_ltv:.2f}')
        ax2.axvline(median_ltv, color='green', linestyle='--', 
                   linewidth=2, label=f'中位数: ${median_ltv:.2f}')
        
        ax2.set_title('用户生命周期价值分布', fontsize=12, fontweight='bold')
        ax2.set_xlabel('LTV (美元)')
        ax2.set_ylabel('用户数')
        ax2.legend()
        ax2.set_xlim([0, ltv_data['LTV'].quantile(0.95)])  # 排除极端值
        
        # 3. 转化漏斗分析(左中)
        ax3 = fig.add_subplot(gs[1, 0])
        
        # 定义漏斗阶段
        funnel_stages = ['访问', '浏览', '点击', '购买']
        funnel_counts = [
            len(self.data),
            self.data['页面浏览数'].sum(),
            self.data['点击次数'].sum(),
            self.data['是否购买'].sum()
        ]
        
        # 计算转化率
        conversion_rates = []
        for i in range(1, len(funnel_counts)):
            rate = funnel_counts[i] / funnel_counts[i-1] * 100
            conversion_rates.append(f'{rate:.1f}%')
        
        # 绘制漏斗图
        y_positions = np.arange(len(funnel_stages))
        bar_width = 0.6
        
        # 归一化数据用于绘图
        max_count = max(funnel_counts)
        normalized_counts = [count / max_count for count in funnel_counts]
        
        bars = ax3.barh(y_positions, normalized_counts, 
                       height=bar_width, color=sns.color_palette('Blues', len(funnel_stages)))
        
        # 添加数值标签
        for i, (bar, count, rate) in enumerate(zip(bars, funnel_counts, [None] + conversion_rates)):
            ax3.text(bar.get_width() + 0.02, bar.get_y() + bar.get_height()/2,
                    f'{count:,}\n{rate if rate else ""}', 
                    va='center', fontsize=10)
        
        ax3.set_yticks(y_positions)
        ax3.set_yticklabels(funnel_stages)
        ax3.set_xlabel('归一化用户数')
        ax3.set_title('用户转化漏斗分析', fontsize=12, fontweight='bold')
        ax3.set_xlim([0, 1.1])
        
        # 4. 用户行为热力图(中中)
        ax4 = fig.add_subplot(gs[1, 1])
        
        # 创建用户行为矩阵(用户 vs 行为指标)
        behavior_matrix = self.user_summary[['总会话数', '购买次数', '总消费金额', 
                                           '平均会话时长', '平均页面浏览数']]
        
        # 标准化
        behavior_matrix_normalized = (behavior_matrix - behavior_matrix.mean()) / behavior_matrix.std()
        
        # 绘制热力图
        sns.heatmap(behavior_matrix_normalized.corr(), annot=True, fmt='.2f',
                   cmap='coolwarm', center=0, square=True, ax=ax4,
                   cbar_kws={'label': '相关系数'})
        
        ax4.set_title('用户行为指标相关性热图', fontsize=12, fontweight='bold')
        
        # 5. 用户留存分析(右下)
        ax5 = fig.add_subplot(gs[0, 2])
        
        # 计算用户留存率
        # 简化计算:以月为单位
        user_cohorts = self.data.groupby(['注册月份', '会话月份'])['用户ID'].nunique().unstack()
        
        # 计算留存率(每行除以其第一个非空值)
        retention_matrix = user_cohorts.div(user_cohorts.iloc[:, 0], axis=0) * 100
        
        # 绘制留存曲线
        for cohort in retention_matrix.index[:5]:  # 只显示前5个同期群
            ax5.plot(retention_matrix.columns.astype(str), 
                    retention_matrix.loc[cohort].values,
                    marker='o', label=str(cohort))
        
        ax5.set_title('用户月留存率曲线', fontsize=12, fontweight='bold')
        ax5.set_xlabel('注册后月份')
        ax5.set_ylabel('留存率 (%)')
        ax5.legend(title='注册月份')
        ax5.grid(True, alpha=0.3)
        
        # 6. 用户特征多维度分析(右中)
        ax6 = fig.add_subplot(gs[1, 2])
        
        # 选择关键特征
        features = ['年龄', '总会话数', '平均会话时长', '购买次数']
        
        # 创建平行坐标图数据
        parallel_data = self.user_summary[features].copy()
        
        # 标准化
        parallel_data_normalized = (parallel_data - parallel_data.mean()) / parallel_data.std()
        
        # 添加分群信息
        parallel_data_normalized['Segment'] = rfm_data['Segment'].values
        
        # 绘制平行坐标图(简化版)
        # 由于Seaborn没有原生平行坐标图,我们用多子图替代
        segment_colors = {'高价值用户': 'red', '活跃用户': 'green', 
                         '一般用户': 'blue', '流失用户': 'gray'}
        
        for segment, color in segment_colors.items():
            subset = parallel_data_normalized[parallel_data_normalized['Segment'] == segment]
            if len(subset) > 0:
                for i in range(len(subset)):
                    ax6.plot(features, subset[features].iloc[i].values, 
                            color=color, alpha=0.1, linewidth=0.5)
        
        # 添加每个特征的均值线
        for feature in features:
            mean_val = parallel_data_normalized[feature].mean()
            ax6.axhline(y=mean_val, color='black', linestyle='--', 
                       linewidth=1, alpha=0.5)
        
        ax6.set_title('用户特征多维度分析(按分群)', fontsize=12, fontweight='bold')
        ax6.set_xlabel('用户特征')
        ax6.set_ylabel('标准化值')
        ax6.set_xticks(range(len(features)))
        ax6.set_xticklabels(features, rotation=45)
        ax6.grid(True, alpha=0.3)
        
        # 7. 设备与流量来源分析(左下)
        ax7 = fig.add_subplot(gs[2, 0])
        
        # 创建分组数据
        device_source_data = self.data.groupby(['设备类型', '流量来源']).agg({
            '用户ID': 'count',
            '是否购买': 'mean',
            '购买金额': 'mean'
        }).reset_index()
        
        # 绘制分组柱状图
        sns.barplot(data=device_source_data, x='设备类型', y='是否购买',
                   hue='流量来源', palette='Set3', ax=ax7)
        
        ax7.set_title('设备类型与流量来源的购买转化率', fontsize=12, fontweight='bold')
        ax7.set_xlabel('设备类型')
        ax7.set_ylabel('购买转化率')
        ax7.legend(title='流量来源', bbox_to_anchor=(1.05, 1), loc='upper left')
        
        # 8. 时间序列行为分析(中下)
        ax8 = fig.add_subplot(gs[2, 1])
        
        # 按日期聚合
        daily_data = self.data.groupby('会话日期').agg({
            '用户ID': 'nunique',
            '是否购买': 'sum',
            '会话时长': 'mean'
        }).rename(columns={
            '用户ID': '日活跃用户数',
            '是否购买': '日购买次数',
            '会话时长': '平均会话时长'
        })
        
        # 绘制双轴图
        color1 = 'tab:blue'
        ax8.plot(daily_data.index, daily_data['日活跃用户数'], 
                color=color1, linewidth=2, label='日活跃用户')
        ax8.set_xlabel('日期')
        ax8.set_ylabel('日活跃用户数', color=color1)
        ax8.tick_params(axis='y', labelcolor=color1)
        ax8.tick_params(axis='x', rotation=45)
        
        # 第二个y轴
        ax8_2 = ax8.twinx()
        color2 = 'tab:red'
        ax8_2.plot(daily_data.index, daily_data['日购买次数'], 
                  color=color2, linewidth=2, label='日购买次数', linestyle='--')
        ax8_2.set_ylabel('日购买次数', color=color2)
        ax8_2.tick_params(axis='y', labelcolor=color2)
        
        ax8.set_title('用户行为时间序列趋势', fontsize=12, fontweight='bold')
        
        # 合并图例
        lines1, labels1 = ax8.get_legend_handles_labels()
        lines2, labels2 = ax8_2.get_legend_handles_labels()
        ax8.legend(lines1 + lines2, labels1 + labels2, loc='upper left')
        
        # 9. 用户聚类分析(右下)
        ax9 = fig.add_subplot(gs[2, 2])
        
        # 使用PCA进行降维
        from sklearn.decomposition import PCA
        from sklearn.preprocessing import StandardScaler
        
        # 准备数据
        cluster_features = ['总会话数', '购买次数', '总消费金额', 
                          '平均会话时长', '平均页面浏览数']
        cluster_data = self.user_summary[cluster_features].fillna(0)
        
        # 标准化
        scaler = StandardScaler()
        scaled_data = scaler.fit_transform(cluster_data)
        
        # PCA降维到2维
        pca = PCA(n_components=2)
        pca_result = pca.fit_transform(scaled_data)
        
        # 添加到数据框
        self.user_summary['PC1'] = pca_result[:, 0]
        self.user_summary['PC2'] = pca_result[:, 1]
        
        # 按分群着色
        scatter = sns.scatterplot(data=self.user_summary, x='PC1', y='PC2',
                                 hue=rfm_data['Segment'], palette='Set2',
                                 alpha=0.6, s=50, ax=ax9)
        
        # 添加聚类中心(按分群)
        for segment in rfm_data['Segment'].unique():
            subset = self.user_summary[rfm_data['Segment'] == segment]
            if len(subset) > 0:
                center_x = subset['PC1'].mean()
                center_y = subset['PC2'].mean()
                ax9.scatter(center_x, center_y, color='black', 
                           s=200, marker='X', edgecolors='white', linewidth=2)
                ax9.text(center_x, center_y, segment, fontsize=10,
                        fontweight='bold', ha='center', va='center',
                        bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))
        
        ax9.set_title('用户行为PCA降维与聚类分析', fontsize=12, fontweight='bold')
        ax9.set_xlabel(f'PC1 ({pca.explained_variance_ratio_[0]:.1%}方差)')
        ax9.set_ylabel(f'PC2 ({pca.explained_variance_ratio_[1]:.1%}方差)')
        ax9.legend(title='用户分群', bbox_to_anchor=(1.05, 1), loc='upper left')
        
        # 添加全局标题
        plt.suptitle('用户行为分析综合仪表板', 
                    fontsize=18, fontweight='bold', y=0.98)
        
        # 调整布局
        plt.tight_layout()
        
        self.fig = fig
        return fig
    
    def calculate_key_metrics(self):
        """计算关键业务指标"""
        if self.data is None:
            print("请先生成数据")
            return
        
        metrics = {}
        
        # 用户指标
        total_users = self.user_summary['用户ID'].nunique()
        active_users = self.user_summary[self.user_summary['总会话数'] > 0]['用户ID'].nunique()
        paying_users = self.user_summary[self.user_summary['购买次数'] > 0]['用户ID'].nunique()
        
        metrics['用户指标'] = {
            '总用户数': total_users,
            '活跃用户数': active_users,
            '付费用户数': paying_users,
            '活跃用户占比': f'{active_users/total_users*100:.1f}%',
            '付费用户占比': f'{paying_users/total_users*100:.1f}%'
        }
        
        # 行为指标
        total_sessions = len(self.data)
        avg_session_duration = self.data['会话时长分钟'].mean()
        avg_pageviews = self.data['页面浏览数'].mean()
        avg_ctr = self.data['点击率'].mean() * 100
        
        metrics['行为指标'] = {
            '总会话数': total_sessions,
            '平均会话时长': f'{avg_session_duration:.1f}分钟',
            '平均页面浏览数': f'{avg_pageviews:.1f}',
            '平均点击率': f'{avg_ctr:.1f}%'
        }
        
        # 转化指标
        total_purchases = self.data['是否购买'].sum()
        conversion_rate = total_purchases / total_sessions * 100
        avg_purchase_value = self.data[self.data['是否购买'] == 1]['购买金额'].mean()
        total_revenue = self.data['购买金额'].sum()
        
        metrics['转化指标'] = {
            '总购买次数': total_purchases,
            '转化率': f'{conversion_rate:.2f}%',
            '平均订单价值': f'${avg_purchase_value:.2f}',
            '总收入': f'${total_revenue:,.2f}'
        }
        
        # RFM分群指标
        if hasattr(self, 'user_summary'):
            # 计算每个分群的关键指标
            segment_metrics = {}
            for segment in ['高价值用户', '活跃用户', '一般用户', '流失用户']:
                segment_users = self.user_summary[self.user_summary['用户ID'].isin(
                    self.user_summary['用户ID']  # 这里需要实际的RFM分群数据
                )]
                if len(segment_users) > 0:
                    segment_metrics[segment] = {
                        '用户数': len(segment_users),
                        '占比': f'{len(segment_users)/total_users*100:.1f}%',
                        '平均LTV': f'${segment_users["总消费金额"].mean():.2f}'
                    }
            
            metrics['分群指标'] = segment_metrics
        
        return metrics

# 使用示例
analyzer = UserBehaviorAnalyzer()
data = analyzer.generate_sample_data(n_users=1000, n_days=90)
dashboard = analyzer.create_user_segmentation_dashboard()

# 计算关键指标
metrics = analyzer.calculate_key_metrics()
print("\n关键业务指标:")
print("=" * 50)
for category, category_metrics in metrics.items():
    print(f"\n{category}:")
    for metric_name, metric_value in category_metrics.items():
        print(f"  {metric_name}: {metric_value}")

plt.show()

Seaborn通过其优雅的设计哲学和精心考虑的默认设置,真正实现了"美就是生产力"的理念。在数据科学工作中,我们常常发现:一个清晰、美观的可视化不仅能更好地传达信息,还能激发更深层次的洞察。Seaborn的成功不仅在于它提供了丰富的统计图表类型,更在于它建立了一套完整的美学体系,让数据分析师能够专注于数据本身,而不是图表的格式调整。

随着数据可视化需求的不断增长,Seaborn也在持续进化。新的版本提供了更好的颜色主题、更强的定制能力,以及与现代数据科学工作流的深度集成。同时,Seaborn与Matplotlib的完美兼容性意味着你既可以享受Seaborn的简洁优雅,又可以在需要时使用Matplotlib的强大功能进行深度定制。

你在使用Seaborn进行数据可视化时有什么特别的技巧或遇到过什么有趣的挑战?或者你有关于统计可视化的独特应用场景想要分享吗?欢迎在评论区交流你的经验和想法!

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐