matplotlib多子图与复杂布局实战

matplotlib多子图与复杂布局实战

在实际业务分析中,经常需要在一个画布上展示多个图表进行对比分析。

Matplotlib 提供了多种子图布局方式:subplot(规则网格)、subplot2grid(网格合并)、GridSpec(精细控制)、subplot_mosaic(命名布局)。

开发思路:

  1. 使用四种不同的子图布局方法,对应四种业务场景。
  2. 每个场景都包含实际业务数据和完整的图表元素。
  3. 展示如何在复杂布局中统一处理中文和坐标轴对齐。
  4. 包含高级技巧:共享坐标轴、隐藏刻度、嵌套布局等。

准备

python 复制代码
"""
chapter_9_multi_subplots_layouts.py
开发思路:
1. 使用四种不同的子图布局方法,对应四种业务场景。
2. 每个场景都包含实际业务数据和完整的图表元素。
3. 展示如何在复杂布局中统一处理中文和坐标轴对齐。
4. 包含高级技巧:共享坐标轴、隐藏刻度、嵌套布局等。
"""

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from matplotlib.gridspec import GridSpec

# ================== 全局中文配置 ==================
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False

# ================== 准备通用数据 ==================
np.random.seed(2026)
# 时间序列数据
dates = pd.date_range('2026-01-01', '2026-03-31', freq='D')
sales_daily = 100 + np.cumsum(np.random.randn(len(dates)) * 2) + 20 * np.sin(np.linspace(0, 4*np.pi, len(dates)))
# 分类数据
products = ['智能手机', '笔记本电脑', '平板电脑', '智能手表']
quarters = ['Q1', 'Q2', 'Q3', 'Q4']
sales_matrix = np.random.randint(80, 200, size=(len(products), len(quarters)))
# 分布数据
customer_ages = np.random.normal(35, 12, 1000)
customer_spending = np.random.normal(500, 200, 1000)

案例1:subplot - 规则网格布局 =

python 复制代码
# ================== 案例1:subplot - 规则网格布局 ==================
# 业务场景:产品总监需要同时查看4款产品的季度销售趋势
print("="*60)
print("案例1:subplot - 2x2规则网格布局(产品季度销售分析)")
print("="*60)

fig, axes = plt.subplots(2, 2, figsize=(14, 10))
fig.suptitle('2026年各产品季度销售趋势分析', fontsize=16, fontweight='bold', y=0.98)

# 将2x2的axes数组展平,方便遍历
axes_flat = axes.flatten()

for i, (product, ax) in enumerate(zip(products, axes_flat)):
    # 为每个产品生成不同的销售趋势
    trend = sales_matrix[i] + np.random.randint(-10, 10, 4)
    
    # 绘制柱状图
    bars = ax.bar(quarters, trend, color=plt.cm.Set3(i), edgecolor='black', linewidth=0.5)
    ax.set_title(f'{product}季度销售额', fontsize=12, pad=10)
    ax.set_xlabel('季度', fontsize=10)
    ax.set_ylabel('销售额(万元)', fontsize=10)
    ax.grid(True, alpha=0.3, linestyle='--')
    
    # 在柱子上添加数值标签
    for bar, val in zip(bars, trend):
        ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 2,
                f'{val}万', ha='center', va='bottom', fontsize=9)

plt.tight_layout()
plt.show()
复制代码
============================================================
案例1:subplot - 2x2规则网格布局(产品季度销售分析)
============================================================

案例2:subplot2grid - 网格合并布局

python 复制代码
# ================== 案例2:subplot2grid - 网格合并布局 ==================
# 业务场景:市场部需要突出显示总销售额,同时展示各产品占比和趋势
print("\n" + "="*60)
print("案例2:subplot2grid - 合并单元格布局(市场综合分析看板)")
print("="*60)

fig = plt.figure(figsize=(15, 9))
fig.suptitle('2026年市场综合分析看板', fontsize=18, fontweight='bold', y=0.98)

# 创建3x3的网格,并分配不同大小的子图
# 左上角大图:总销售额趋势 (占2行2列)
ax1 = plt.subplot2grid((3, 3), (0, 0), rowspan=2, colspan=2)
# 右上角:产品占比饼图 (占1行1列)
ax2 = plt.subplot2grid((3, 3), (0, 2), rowspan=1, colspan=1)
# 中右:季度环比折线图 (占1行1列)
ax3 = plt.subplot2grid((3, 3), (1, 2), rowspan=1, colspan=1)
# 底部:三个小图并排 (各占1列)
ax4 = plt.subplot2grid((3, 3), (2, 0), rowspan=1, colspan=1)
ax5 = plt.subplot2grid((3, 3), (2, 1), rowspan=1, colspan=1)
ax6 = plt.subplot2grid((3, 3), (2, 2), rowspan=1, colspan=1)

# ===== 图1:总销售额趋势 =====
weeks = np.arange(1, 13)
total_sales = 500 + np.cumsum(np.random.randn(12) * 20) + 10 * np.sin(np.linspace(0, 3*np.pi, 12))
ax1.plot(weeks, total_sales, 'o-', color='darkblue', linewidth=2, markersize=6)
ax1.fill_between(weeks, total_sales, 450, alpha=0.3, color='skyblue')
ax1.set_title('2026年Q1 周度总销售额趋势', fontsize=12)
ax1.set_xlabel('周数', fontsize=10)
ax1.set_ylabel('销售额(万元)', fontsize=10)
ax1.grid(True, alpha=0.3)
ax1.axhline(y=total_sales.mean(), color='red', linestyle='--', label=f'均值: {total_sales.mean():.0f}万')
ax1.legend()

# ===== 图2:产品占比饼图 =====
product_shares = sales_matrix.sum(axis=1)
colors = plt.cm.Pastel1(np.linspace(0, 1, len(products)))
wedges, texts, autotexts = ax2.pie(product_shares, labels=products, autopct='%1.1f%%',
                                    colors=colors, startangle=90)
for autotext in autotexts:
    autotext.set_color('white')
    autotext.set_fontweight('bold')
ax2.set_title('产品销售额占比', fontsize=12)

# ===== 图3:季度环比折线图 =====
qoq_growth = np.diff(sales_matrix.sum(axis=0)) / sales_matrix.sum(axis=0)[:-1] * 100
ax3.plot(quarters[1:], qoq_growth, 'rs-', linewidth=2, markersize=8)
ax3.axhline(y=0, color='gray', linestyle='-', linewidth=0.5)
ax3.set_title('季度环比增长率', fontsize=12)
ax3.set_xlabel('季度', fontsize=10)
ax3.set_ylabel('增长率 (%)', fontsize=10)
ax3.grid(True, alpha=0.3)
for i, (q, g) in enumerate(zip(quarters[1:], qoq_growth)):
    ax3.annotate(f'{g:.1f}%', xy=(i, g), xytext=(i, g+2 if g>0 else g-4),
                ha='center', fontsize=9, color='darkred')

# ===== 图4-6:三个产品详细卡片 =====
product_details = [
    ('智能手机', 'black', [85, 120, 150, 180]),
    ('笔记本电脑', 'blue', [95, 140, 135, 170]),
    ('平板电脑', 'green', [70, 95, 130, 160])
]

for ax, (product, color, trends) in zip([ax4, ax5, ax6], product_details):
    ax.plot(quarters, trends, marker='o', color=color, linewidth=2)
    ax.fill_between(quarters, trends, min(trends)-10, alpha=0.2, color=color)
    ax.set_title(f'{product}趋势', fontsize=10)
    ax.set_xlabel('季度', fontsize=8)
    ax.set_ylabel('销售额', fontsize=8)
    ax.tick_params(labelsize=8)
    ax.grid(True, alpha=0.2)

plt.tight_layout()
plt.show()
复制代码
============================================================
案例2:subplot2grid - 合并单元格布局(市场综合分析看板)
============================================================

案例3:GridSpec - 精细化布局

python 复制代码
# ================== 案例3:GridSpec - 精细化布局 ==================
# 业务场景:数据分析师需要制作一份包含原始数据分布、箱线图和Q-Q图的统计报告
print("\n" + "="*60)
print("案例3:GridSpec - 精细化布局(数据分布统计报告)")
print("="*60)

fig = plt.figure(figsize=(16, 10))
fig.suptitle('客户消费行为深度统计分析报告', fontsize=18, fontweight='bold', y=0.98)

# 创建3x3的网格,但每行高度不同,列宽也不同
gs = GridSpec(3, 3, figure=fig, 
              height_ratios=[1.5, 1, 1.2],  # 行高比例
              width_ratios=[1.2, 1, 1],     # 列宽比例
              hspace=0.3, wspace=0.3)        # 间距

# 分配子图
ax_hist = fig.add_subplot(gs[0, :])      # 第一行,占全部3列
ax_box1 = fig.add_subplot(gs[1, 0])      # 第二行第一列
ax_box2 = fig.add_subplot(gs[1, 1])      # 第二行第二列
ax_box3 = fig.add_subplot(gs[1, 2])      # 第二行第三列
ax_scatter = fig.add_subplot(gs[2, :2])  # 第三行前两列
ax_qq = fig.add_subplot(gs[2, 2])        # 第三行第三列

# ===== 直方图+KDE:整体分布 =====
ax_hist.hist(customer_spending, bins=30, density=True, alpha=0.6, color='steelblue', edgecolor='black')
# 添加核密度估计
from scipy import stats
kde = stats.gaussian_kde(customer_spending)
x_range = np.linspace(customer_spending.min(), customer_spending.max(), 200)
ax_hist.plot(x_range, kde(x_range), 'r-', linewidth=2, label='核密度估计')
ax_hist.axvline(customer_spending.mean(), color='green', linestyle='--', 
                linewidth=2, label=f'均值: {customer_spending.mean():.0f}')
ax_hist.axvline(np.median(customer_spending), color='orange', linestyle=':', 
                linewidth=2, label=f'中位数: {np.median(customer_spending):.0f}')
ax_hist.set_title('客户消费金额分布(含KDE)', fontsize=14)
ax_hist.set_xlabel('消费金额(元)', fontsize=11)
ax_hist.set_ylabel('概率密度', fontsize=11)
ax_hist.legend()
ax_hist.grid(True, alpha=0.2)

# ===== 三个箱线图:不同年龄段的消费分布 =====
# 划分年龄段
young_mask = customer_ages < 30
middle_mask = (customer_ages >= 30) & (customer_ages < 45)
old_mask = customer_ages >= 45

age_groups = [
    (young_mask, '年轻客户 (<30岁)', ax_box1, 'lightcoral'),
    (middle_mask, '中年客户 (30-45岁)', ax_box2, 'lightgreen'),
    (old_mask, '成熟客户 (≥45岁)', ax_box3, 'lightskyblue')
]

for mask, title, ax, color in age_groups:
    if np.sum(mask) > 0:
        data = customer_spending[mask]
        bp = ax.boxplot(data, patch_artist=True, widths=0.6)
        bp['boxes'][0].set_facecolor(color)
        bp['boxes'][0].set_alpha(0.7)
        ax.set_title(title, fontsize=11)
        ax.set_ylabel('消费金额', fontsize=9)
        ax.set_xticks([])
        ax.grid(True, alpha=0.2, axis='y')
        
        # 添加统计信息
        ax.text(0.5, 0.05, f'n={len(data)}', transform=ax.transAxes,
                ha='center', fontsize=9, bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.8))

# ===== 散点图:年龄与消费的关系 =====
scatter = ax_scatter.scatter(customer_ages, customer_spending, c=customer_ages, 
                             cmap='viridis', alpha=0.5, s=30)
plt.colorbar(scatter, ax=ax_scatter, label='年龄')
ax_scatter.set_xlabel('年龄(岁)', fontsize=11)
ax_scatter.set_ylabel('消费金额(元)', fontsize=11)
ax_scatter.set_title('年龄与消费金额关系散点图', fontsize=12)
ax_scatter.grid(True, alpha=0.2)

# 添加线性回归线
z = np.polyfit(customer_ages, customer_spending, 1)
p = np.poly1d(z)
ax_scatter.plot(np.sort(customer_ages), p(np.sort(customer_ages)), 
               'r--', linewidth=2, label=f'趋势线 (斜率={z[0]:.2f})')
ax_scatter.legend()

# ===== Q-Q图:正态性检验 =====
# 计算理论分位数和样本分位数
from scipy import stats
quantiles = np.linspace(0.01, 0.99, 100)
theoretical = stats.norm.ppf(quantiles, loc=customer_spending.mean(), 
                             scale=customer_spending.std())
sample = np.percentile(customer_spending, quantiles * 100)

ax_qq.scatter(theoretical, sample, alpha=0.6, s=20)
# 添加y=x参考线
min_val = min(theoretical.min(), sample.min())
max_val = max(theoretical.max(), sample.max())
ax_qq.plot([min_val, max_val], [min_val, max_val], 'r--', linewidth=2)
ax_qq.set_xlabel('理论分位数', fontsize=10)
ax_qq.set_ylabel('样本分位数', fontsize=10)
ax_qq.set_title('Q-Q图(正态性检验)', fontsize=12)
ax_qq.grid(True, alpha=0.2)

# GridSpec 布局与 tight_layout 不完全兼容,直接显示
plt.show()
复制代码
============================================================
案例3:GridSpec - 精细化布局(数据分布统计报告)
============================================================

案例4:subplot_mosaic - 命名布局

python 复制代码
# ================== 案例4:subplot_mosaic - 命名布局(Python 3.12+ 推荐) ==================
# 业务场景:运营团队需要制作一个综合运营看板,包含核心指标
print("\n" + "="*60)
print("案例4:subplot_mosaic - 命名布局(运营核心指标看板)")
print("="*60)

# 使用mosaic定义布局,这是Matplotlib 3.3+引入的现代布局方式
# 特别适合Python 3.12环境,代码可读性极高
mosaic = [
    ['header', 'header', 'header'],      # 第一行:标题区
    ['kpi1', 'kpi2', 'kpi3'],            # 第二行:三个KPI指标
    ['trend', 'trend', 'pie'],            # 第三行:趋势图和饼图
    ['detail1', 'detail2', 'detail3']     # 第四行:三个细节图
]

fig, axes_dict = plt.subplot_mosaic(mosaic, figsize=(15, 12),
                                     constrained_layout=True,
                                     height_ratios=[0.4, 0.7, 1.2, 0.8])

fig.suptitle('运营核心指标看板 (2026年3月)', fontsize=20, fontweight='bold', y=1.02)

# ===== header:标题区域,不实际绘图,只放文字 =====
axes_dict['header'].axis('off')
header_text = """总体运营状况:本月活跃用户 128.5万 | 总交易额 3,842万 | 订单量 45.2万
同比增长 +15.3% | 环比增长 +3.8% | 目标完成率 92%"""
axes_dict['header'].text(0.5, 0.5, header_text, ha='center', va='center',
                         fontsize=12, bbox=dict(boxstyle='round', facecolor='lightblue', alpha=0.5))

# ===== KPI指标卡 =====
kpi_data = [
    ('kpi1', 'DAU (万)', '128.5', '+5.2%', '#ff6b6b'),
    ('kpi2', 'ARPU (元)', '84.3', '+2.1%', '#4ecdc4'),
    ('kpi3', '转化率', '3.85%', '-0.3%', '#ffe66d')
]

for kpi_key, title, value, change, color in kpi_data:
    ax = axes_dict[kpi_key]
    ax.axis('off')
    ax.set_facecolor(color)
    
    # 绘制KPI卡片
    ax.text(0.5, 0.7, title, ha='center', va='center', fontsize=12, fontweight='bold')
    ax.text(0.5, 0.4, value, ha='center', va='center', fontsize=24, fontweight='bold')
    ax.text(0.5, 0.15, change, ha='center', va='center', fontsize=11,
           color='green' if '+' in change else 'red')

# ===== 趋势图 =====
ax_trend = axes_dict['trend']
days = np.arange(1, 31)
# 模拟DAU趋势
dau = 120 + 8 * np.sin(np.linspace(0, 4*np.pi, 30)) + np.random.randn(30) * 2
ax_trend.plot(days, dau, 'o-', color='darkblue', linewidth=2, markersize=4)
ax_trend.fill_between(days, 110, dau, where=(dau>115), color='lightgreen', alpha=0.3, label='高于基准')
ax_trend.fill_between(days, dau, 110, where=(dau<=115), color='lightsalmon', alpha=0.3, label='低于基准')
ax_trend.axhline(y=115, color='gray', linestyle='--', linewidth=1, label='基准线')
ax_trend.set_title('3月DAU趋势', fontsize=14, fontweight='bold')
ax_trend.set_xlabel('日期', fontsize=11)
ax_trend.set_ylabel('DAU (万)', fontsize=11)
ax_trend.legend(loc='upper right')
ax_trend.grid(True, alpha=0.2)

# 标记峰值
max_day = np.argmax(dau) + 1
ax_trend.annotate(f'峰值: {dau[max_day-1]:.1f}万', 
                 xy=(max_day, dau[max_day-1]),
                 xytext=(max_day+2, dau[max_day-1]+2),
                 arrowprops=dict(arrowstyle='->', color='red'))

# ===== 饼图 =====
ax_pie = axes_dict['pie']
channels = ['自然流量', '社交媒体', '付费广告', '老客复购', '活动引流']
channel_data = [45, 28, 35, 42, 20]
wedges, texts, autotexts = ax_pie.pie(channel_data, labels=channels, autopct='%1.1f%%',
                                       startangle=90, colors=plt.cm.Set3(np.linspace(0, 1, 5)))
for autotext in autotexts:
    autotext.set_color('white')
    autotext.set_fontweight('bold')
ax_pie.set_title('用户来源渠道分析', fontsize=14, fontweight='bold')

# ===== 三个细节图 =====
detail_data = [
    ('detail1', '时段活跃分布', ['0-6点', '6-12点', '12-18点', '18-24点'], [12, 38, 45, 62]),
    ('detail2', '设备分布', ['iOS', 'Android', 'Web'], [850, 1200, 320]),
    ('detail3', '城市等级分布', ['一线', '新一线', '二线', '其他'], [42, 38, 28, 20])
]

for detail_key, title, labels, values in detail_data:
    ax = axes_dict[detail_key]
    bars = ax.bar(labels, values, color=plt.cm.Pastel2(np.linspace(0, 1, len(labels))))
    ax.set_title(title, fontsize=11, fontweight='bold')
    ax.tick_params(axis='x', rotation=15)
    ax.grid(True, alpha=0.2, axis='y')
    
    # 添加数值标签
    for bar in bars:
        height = bar.get_height()
        ax.text(bar.get_x() + bar.get_width()/2., height + 1,
                f'{height}', ha='center', va='bottom', fontsize=8)

# 调整布局并显示
plt.show()
复制代码
============================================================
案例4:subplot_mosaic - 命名布局(运营核心指标看板)
============================================================

案例5:嵌套布局与坐标轴共享

python 复制代码
# ================== 案例5:嵌套布局与坐标轴共享 ==================
# 业务场景:时间序列分析需要对比多个相关指标,同时查看细节
print("\n" + "="*60)
print("案例5:嵌套布局与坐标轴共享(多指标对比分析)")
print("="*60)

fig = plt.figure(figsize=(14, 10))
fig.suptitle('电商多指标联动分析', fontsize=16, fontweight='bold', y=0.98)

# 使用GridSpec创建主布局
gs_main = GridSpec(3, 2, figure=fig, height_ratios=[1, 1, 1.5])

# 左侧创建一个垂直布局,包含两个共享X轴的子图
# 右侧是一个大图,包含多个子图的嵌套

# ===== 左侧:共享X轴的时间序列 =====
# 第一个子图:流量
ax_traffic = fig.add_subplot(gs_main[0, 0])
# 第二个子图:转化,共享X轴
ax_conversion = fig.add_subplot(gs_main[1, 0], sharex=ax_traffic)
# 第三个子图:收入,共享X轴
ax_revenue = fig.add_subplot(gs_main[2, 0], sharex=ax_traffic)

# 生成共享的时间轴
time_hours = np.arange(24)
traffic = 500 + 300 * np.sin(np.linspace(0, 2*np.pi, 24)) + np.random.randn(24)*50
conversion = 0.03 + 0.01 * np.sin(np.linspace(0, 2*np.pi, 24) + np.pi/2) + np.random.randn(24)*0.005
revenue = traffic * conversion * 100 + np.random.randn(24)*200

# 绘制三个共享X轴的图
ax_traffic.plot(time_hours, traffic, 'b-', linewidth=2)
ax_traffic.set_ylabel('访问量', color='b')
ax_traffic.tick_params(axis='y', labelcolor='b')
ax_traffic.set_title('小时级流量趋势')
ax_traffic.grid(True, alpha=0.2)

ax_conversion.plot(time_hours, conversion*100, 'r-', linewidth=2)
ax_conversion.set_ylabel('转化率 (%)', color='r')
ax_conversion.tick_params(axis='y', labelcolor='r')
ax_conversion.set_title('小时级转化率趋势')
ax_conversion.grid(True, alpha=0.2)

ax_revenue.plot(time_hours, revenue, 'g-', linewidth=2)
ax_revenue.set_xlabel('小时', fontsize=11)
ax_revenue.set_ylabel('收入 (元)', color='g')
ax_revenue.tick_params(axis='y', labelcolor='g')
ax_revenue.set_title('小时级收入趋势')
ax_revenue.grid(True, alpha=0.2)

# 隐藏中间的x轴标签,只保留最后一个
plt.setp(ax_traffic.get_xticklabels(), visible=False)
plt.setp(ax_conversion.get_xticklabels(), visible=False)

# ===== 右侧:嵌套布局 =====
# 创建一个嵌套的GridSpec,占据右侧两列
gs_right = GridSpec(2, 2, figure=fig, left=0.55, right=0.95, top=0.9, bottom=0.1,
                    hspace=0.3, wspace=0.3)

# 右上:散点图矩阵的简化版(只画两个)
ax_scatter1 = fig.add_subplot(gs_right[0, 0])
ax_scatter2 = fig.add_subplot(gs_right[0, 1])
ax_hist2d = fig.add_subplot(gs_right[1, :])  # 底部合并单元格

# 生成一些相关数据
x = np.random.randn(500)
y = 0.7 * x + 0.3 * np.random.randn(500)
z = 0.5 * x + 0.5 * np.random.randn(500)

ax_scatter1.scatter(x, y, alpha=0.5, s=20, c='steelblue')
ax_scatter1.set_xlabel('指标X')
ax_scatter1.set_ylabel('指标Y')
ax_scatter1.set_title('X-Y相关性')
ax_scatter1.grid(True, alpha=0.2)

ax_scatter2.scatter(x, z, alpha=0.5, s=20, c='crimson')
ax_scatter2.set_xlabel('指标X')
ax_scatter2.set_ylabel('指标Z')
ax_scatter2.set_title('X-Z相关性')
ax_scatter2.grid(True, alpha=0.2)

# 二维直方图
hb = ax_hist2d.hexbin(x, y, gridsize=20, cmap='Blues', alpha=0.8)
plt.colorbar(hb, ax=ax_hist2d, label='频次')
ax_hist2d.set_xlabel('指标X')
ax_hist2d.set_ylabel('指标Y')
ax_hist2d.set_title('二维分布密度 (六边形分箱)')

# GridSpec 嵌套布局与 tight_layout 不完全兼容,直接显示
plt.show()
复制代码
============================================================
案例5:嵌套布局与坐标轴共享(多指标对比分析)
============================================================

案例6:极坐标系与组合布局

python 复制代码
# ================== 案例6:极坐标系与组合布局 ==================
# 业务场景:展示周期性数据(如一周各天、一天各小时的周期性规律)
print("\n" + "="*60)
print("案例6:极坐标系与组合布局(周期数据分析)")
print("="*60)

fig = plt.figure(figsize=(14, 8))
fig.suptitle('用户活跃周期分析(极坐标与组合视图)', fontsize=16, fontweight='bold')

# 创建2x2的布局,其中一个使用极坐标
gs = GridSpec(2, 2, figure=fig, width_ratios=[1, 1.2])

# 笛卡尔坐标系的子图
ax1 = fig.add_subplot(gs[0, 0])  # 周趋势
ax2 = fig.add_subplot(gs[1, 0])  # 日趋势
# 极坐标子图
ax3 = fig.add_subplot(gs[:, 1], projection='polar')  # 极坐标,合并两行

# ===== 周趋势 =====
days = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
weekly_activity = [850, 820, 890, 920, 1050, 1350, 1420]
bars1 = ax1.bar(days, weekly_activity, color=plt.cm.viridis(np.linspace(0.2, 0.8, 7)))
ax1.set_title('周度活跃趋势', fontsize=12)
ax1.set_ylabel('活跃用户数')
ax1.tick_params(axis='x', rotation=30)
for bar, val in zip(bars1, weekly_activity):
    ax1.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 20, str(val),
            ha='center', va='bottom', fontsize=8)

# ===== 日趋势(24小时)=====
hours = np.arange(24)
hourly_activity = 300 + 200 * np.sin(np.linspace(0, 2*np.pi, 24) - np.pi/2)
hourly_activity = np.clip(hourly_activity + np.random.randn(24)*30, 100, 600).astype(int)

ax2.plot(hours, hourly_activity, 'o-', color='darkorange', linewidth=2)
ax2.fill_between(hours, hourly_activity, 200, alpha=0.3, color='orange')
ax2.set_title('小时级活跃趋势', fontsize=12)
ax2.set_xlabel('小时')
ax2.set_ylabel('活跃用户数')
ax2.grid(True, alpha=0.2)
ax2.set_xticks([0, 6, 12, 18, 23])
ax2.set_xticklabels(['0点', '6点', '12点', '18点', '23点'])

# ===== 极坐标:双重周期 =====
# 生成一周每天24小时的数据(7x24矩阵)
weekly_hourly = np.zeros((7, 24))
for day in range(7):
    # 基础模式:周末高,工作日低
    day_factor = 1.2 if day >= 5 else 0.9
    for hour in range(24):
        hour_factor = 0.3 + 0.7 * np.sin(np.pi * hour / 12 - np.pi/2)
        weekly_hourly[day, hour] = day_factor * hour_factor * 1000 + np.random.randn()*50

# 在极坐标中绘制热力图
theta, r = np.meshgrid(np.linspace(0, 2*np.pi, 24, endpoint=False),  # 角度:小时
                       np.arange(7))  # 半径:星期

# 使用pcolormesh在极坐标中绘制
mesh = ax3.pcolormesh(theta, r, weekly_hourly, cmap='hot', shading='auto')
plt.colorbar(mesh, ax=ax3, label='活跃度', shrink=0.8)

# 设置极坐标标签
ax3.set_theta_zero_location('N')  # 0度指向北方
ax3.set_theta_direction(-1)  # 顺时针方向
ax3.set_rgrids([0, 1, 2, 3, 4, 5, 6], labels=['周一', '周二', '周三', '周四', '周五', '周六', '周日'])
ax3.set_title('周-小时 双重周期热力图 (极坐标)', fontsize=12, pad=20)

# 添加角度刻度标签(小时)
hour_labels = ['0点', '3点', '6点', '9点', '12点', '15点', '18点', '21点']
hour_angles = np.linspace(0, 2*np.pi, 8, endpoint=False)
ax3.set_xticks(hour_angles)
ax3.set_xticklabels(hour_labels)

plt.tight_layout()
plt.show()

print("\n" + "="*60)
print("✅ 多子图布局教程完成!共展示6种不同布局场景")
print("="*60)
复制代码
============================================================
案例6:极坐标系与组合布局(周期数据分析)
============================================================
复制代码
============================================================
✅ 多子图布局教程完成!共展示6种不同布局场景
============================================================
相关推荐
固定资产管理系统软件2 小时前
商务局RFID固定资产管理系统的应用价值与落地要点解析
大数据·python
TheBestRucy3 小时前
Python 九阳神功:从零筑基到线程飞升
服务器·开发语言·网络·人工智能·python
lancyu3 小时前
关于多轮对话机器人的上下文Token优化和解决方案
人工智能·python·深度学习·机器学习·chatgpt·机器人·prompt
草莓橙子碗4 小时前
Claude 使用指南
开发语言·python
工具分享4 小时前
陪跑跟品爆单AI选品助手,高效跟品会赔本吗?
人工智能·python
COOLMO研究AI4 小时前
Python 如何实现 AI API 的提示词(Prompt)版本管理与热更新
人工智能·python·prompt
老郑聊AI业财智造4 小时前
Transformer 技术架构与源码分析
人工智能·python·深度学习·语言模型·架构·transformer·软件工程
程序员三藏4 小时前
自动化测试用例编写详解
自动化测试·软件测试·python·功能测试·测试工具·职场和发展·测试用例
230万光年的思念5 小时前
分子动力学模拟参考文献
python
HongXu_CaiYi5 小时前
0103-python相关-函数、文件
python