【零基础学AI】 第6讲:数据可视化基础

发布于:2025-06-30 ⋅ 阅读:(15) ⋅ 点赞:(0)

在这里插入图片描述

本节课你将学到

  • 理解数据可视化在AI中的重要作用
  • 掌握Matplotlib的基本架构和核心概念
  • 学会创建各种类型的图表(线图、柱状图、散点图、饼图等)
  • 掌握图表美化和自定义技巧
  • 完成销售趋势图表制作实战项目

开始之前

什么是数据可视化?

数据可视化就是用图形的方式展示数据,让复杂的数字变成直观的视觉信息。想象一下:

  • 纯数字表格:像密密麻麻的账本,信息都在,但很难快速理解
  • 可视化图表:像一张清晰的地图,一眼就能看出规律和趋势

数据可视化的核心价值:

  • 发现规律:肉眼很难从数字中发现的模式,在图表中一目了然
  • 传达洞察:复杂的分析结果通过图表变得容易理解
  • 辅助决策:直观的图表帮助管理者快速做出决策
  • 讲述故事:数据背后的故事通过图表生动地展现出来

为什么Matplotlib重要?

1. Python可视化的基石

  • Matplotlib:Python可视化的底层引擎,功能最全面
  • Seaborn:基于Matplotlib的高级统计图表库
  • Plotly:交互式图表库,底层也参考了Matplotlib设计
  • Pandas:内置的绘图功能就是调用Matplotlib

2. AI项目中的关键应用

  • 数据探索:在建模前理解数据分布和关系
  • 特征分析:可视化特征重要性和相关性
  • 模型评估:绘制ROC曲线、混淆矩阵等
  • 结果展示:将模型预测结果可视化

3. 业界标准

  • 科研论文:大多数机器学习论文的图表都用Matplotlib制作
  • 数据报告:企业级数据分析报告的标准工具
  • 教学演示:AI课程和教程的首选可视化工具

环境要求

  • 已完成前五讲的环境配置
  • Matplotlib已安装(在第1讲中已安装)
  • 建议同时安装Seaborn用于高级图表

Matplotlib基础架构

导入和基本设置

# 标准导入方式
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

# 查看Matplotlib版本
print(f"Matplotlib版本: {plt.matplotlib.__version__}")

# 设置中文字体支持
plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题

# 设置图表样式
plt.style.use('default')  # 可选:'seaborn', 'ggplot', 'classic'等

# 设置默认图表大小和分辨率
plt.rcParams['figure.figsize'] = (10, 6)
plt.rcParams['figure.dpi'] = 100

print("✅ Matplotlib环境配置完成")

Matplotlib的两种接口

# Matplotlib提供两种编程接口
print("🎨 Matplotlib两种接口演示")
print("=" * 35)

# 准备示例数据
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)

# 方式1:pyplot接口(类似MATLAB,简单直观)
print("📊 方式1: pyplot接口")
plt.figure(figsize=(12, 5))

plt.subplot(1, 2, 1)  # 1行2列的第1个子图
plt.plot(x, y1, label='sin(x)', color='blue', linewidth=2)
plt.plot(x, y2, label='cos(x)', color='red', linewidth=2)
plt.title('Pyplot接口示例')
plt.xlabel('x值')
plt.ylabel('y值')
plt.legend()
plt.grid(True, alpha=0.3)

# 方式2:面向对象接口(更灵活,适合复杂图表)
print("🔧 方式2: 面向对象接口")
fig, ax = plt.subplots(1, 1, figsize=(6, 5))  # 创建图形和轴对象

ax.plot(x, y1, label='sin(x)', color='blue', linewidth=2)
ax.plot(x, y2, label='cos(x)', color='red', linewidth=2)
ax.set_title('面向对象接口示例')
ax.set_xlabel('x值')
ax.set_ylabel('y值')
ax.legend()
ax.grid(True, alpha=0.3)

plt.tight_layout()  # 自动调整子图间距
plt.show()

print("💡 建议:简单图表用pyplot,复杂图表用面向对象接口")

Figure和Axes概念

# 理解Figure、Axes、Axis的层次结构
print(f"\n🏗️ Matplotlib架构解析")
print("=" * 30)

# Figure: 整个图形窗口
# Axes: 具体的绘图区域(可以有多个)
# Axis: 坐标轴(x轴、y轴)

# 创建复杂的图形布局
fig = plt.figure(figsize=(15, 10))
fig.suptitle('Matplotlib架构演示', fontsize=16, fontweight='bold')

# 添加多个Axes(子图)
ax1 = fig.add_subplot(2, 3, 1)  # 2行3列的第1个
ax2 = fig.add_subplot(2, 3, 2)  # 2行3列的第2个
ax3 = fig.add_subplot(2, 3, 3)  # 2行3列的第3个
ax4 = fig.add_subplot(2, 1, 2)  # 2行1列的第2个(占据下半部分)

# 在每个Axes中绘制不同类型的图表
# 子图1:线图
x = np.linspace(0, 10, 50)
ax1.plot(x, np.sin(x), 'b-', linewidth=2, label='sin(x)')
ax1.set_title('线图示例')
ax1.set_xlabel('x')
ax1.set_ylabel('sin(x)')
ax1.legend()
ax1.grid(True, alpha=0.3)

# 子图2:散点图
np.random.seed(42)
x_scatter = np.random.randn(50)
y_scatter = np.random.randn(50)
colors = np.random.rand(50)
ax2.scatter(x_scatter, y_scatter, c=colors, alpha=0.6, s=60)
ax2.set_title('散点图示例')
ax2.set_xlabel('X值')
ax2.set_ylabel('Y值')

# 子图3:柱状图
categories = ['A', 'B', 'C', 'D', 'E']
values = [23, 45, 56, 78, 32]
bars = ax3.bar(categories, values, color=['red', 'green', 'blue', 'orange', 'purple'])
ax3.set_title('柱状图示例')
ax3.set_xlabel('类别')
ax3.set_ylabel('数值')

# 在柱状图上添加数值标签
for bar, value in zip(bars, values):
    ax3.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 1,
             str(value), ha='center', va='bottom')

# 子图4:多系列折线图
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun']
sales_2023 = [120, 135, 148, 162, 151, 178]
sales_2024 = [125, 142, 156, 169, 163, 185]

ax4.plot(months, sales_2023, marker='o', linewidth=2, label='2023年销售')
ax4.plot(months, sales_2024, marker='s', linewidth=2, label='2024年销售')
ax4.set_title('月度销售对比')
ax4.set_xlabel('月份')
ax4.set_ylabel('销售额(万元)')
ax4.legend()
ax4.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

print("📋 架构总结:")
print("  - Figure: 整个图形画布")
print("  - Axes: 各个子图区域") 
print("  - Axis: x轴、y轴等坐标轴")
print("  - Artists: 所有可见元素(线条、文字、图例等)")

基本图表类型

线图(Line Plot)

# 线图详解和应用
print(f"\n📈 线图详解")
print("=" * 20)

# 创建示例数据
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
temperature = [2, 4, 8, 14, 20, 25, 28, 27, 22, 16, 9, 4]  # 某城市月平均气温
rainfall = [45, 38, 52, 61, 75, 89, 102, 95, 78, 67, 55, 48]  # 月降雨量

fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(18, 6))

# 基础线图
ax1.plot(months, temperature, color='red', linewidth=2, marker='o', markersize=6)
ax1.set_title('月平均气温变化', fontsize=14, fontweight='bold')
ax1.set_xlabel('月份')
ax1.set_ylabel('温度 (°C)')
ax1.grid(True, alpha=0.3)
ax1.tick_params(axis='x', rotation=45)

# 多系列线图
ax2.plot(months, temperature, color='red', linewidth=2, marker='o', 
         markersize=6, label='气温 (°C)')
ax2_twin = ax2.twinx()  # 创建共享x轴的第二个y轴
ax2_twin.plot(months, rainfall, color='blue', linewidth=2, marker='s', 
              markersize=6, label='降雨量 (mm)')

ax2.set_title('气温与降雨量变化', fontsize=14, fontweight='bold')
ax2.set_xlabel('月份')
ax2.set_ylabel('温度 (°C)', color='red')
ax2_twin.set_ylabel('降雨量 (mm)', color='blue')
ax2.tick_params(axis='x', rotation=45)
ax2.tick_params(axis='y', labelcolor='red')
ax2_twin.tick_params(axis='y', labelcolor='blue')

# 组合图例
lines1, labels1 = ax2.get_legend_handles_labels()
lines2, labels2 = ax2_twin.get_legend_handles_labels()
ax2.legend(lines1 + lines2, labels1 + labels2, loc='upper left')

# 线型样式展示
x = np.linspace(0, 10, 20)
ax3.plot(x, np.sin(x), '-', label='实线 solid', linewidth=2)
ax3.plot(x, np.sin(x + 0.5), '--', label='虚线 dashed', linewidth=2)
ax3.plot(x, np.sin(x + 1), '-.', label='点划线 dashdot', linewidth=2)
ax3.plot(x, np.sin(x + 1.5), ':', label='点线 dotted', linewidth=2)

ax3.set_title('线型样式展示', fontsize=14, fontweight='bold')
ax3.set_xlabel('x值')
ax3.set_ylabel('y值')
ax3.legend()
ax3.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

print("📝 线图使用场景:")
print("  - 时间序列数据(股价、气温、销售趋势等)")
print("  - 连续变量关系(函数图像、回归线等)")
print("  - 多组数据对比(不同年份、不同产品等)")

柱状图(Bar Plot)

# 柱状图详解
print(f"\n📊 柱状图详解")
print("=" * 20)

# 准备数据
products = ['iPhone', 'Samsung', 'Huawei', 'Xiaomi', 'OPPO']
q1_sales = [45, 38, 25, 22, 18]
q2_sales = [48, 35, 28, 25, 20]
q3_sales = [52, 40, 30, 28, 22]

fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(16, 12))

# 1. 基础柱状图
bars1 = ax1.bar(products, q1_sales, color=['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7'])
ax1.set_title('Q1销售额(单位:万台)', fontsize=14, fontweight='bold')
ax1.set_xlabel('品牌')
ax1.set_ylabel('销售额')

# 添加数值标签
for bar, value in zip(bars1, q1_sales):
    ax1.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.5,
             f'{value}万', ha='center', va='bottom', fontweight='bold')

# 2. 分组柱状图
x = np.arange(len(products))  # 标签位置
width = 0.25  # 柱子宽度

bars2_1 = ax2.bar(x - width, q1_sales, width, label='Q1', color='#FF6B6B', alpha=0.8)
bars2_2 = ax2.bar(x, q2_sales, width, label='Q2', color='#4ECDC4', alpha=0.8)
bars2_3 = ax2.bar(x + width, q3_sales, width, label='Q3', color='#45B7D1', alpha=0.8)

ax2.set_title('季度销售对比', fontsize=14, fontweight='bold')
ax2.set_xlabel('品牌')
ax2.set_ylabel('销售额(万台)')
ax2.set_xticks(x)
ax2.set_xticklabels(products)
ax2.legend()

# 3. 堆叠柱状图
ax3.bar(products, q1_sales, label='Q1', color='#FF6B6B', alpha=0.8)
ax3.bar(products, q2_sales, bottom=q1_sales, label='Q2', color='#4ECDC4', alpha=0.8)

# 计算Q3的底部位置
q3_bottom = [q1 + q2 for q1, q2 in zip(q1_sales, q2_sales)]
ax3.bar(products, q3_sales, bottom=q3_bottom, label='Q3', color='#45B7D1', alpha=0.8)

ax3.set_title('累计销售额', fontsize=14, fontweight='bold')
ax3.set_xlabel('品牌')
ax3.set_ylabel('累计销售额(万台)')
ax3.legend()

# 4. 水平柱状图
ax4.barh(products, q1_sales, color=['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7'])
ax4.set_title('Q1销售额(水平)', fontsize=14, fontweight='bold')
ax4.set_xlabel('销售额(万台)')
ax4.set_ylabel('品牌')

# 添加数值标签
for i, value in enumerate(q1_sales):
    ax4.text(value + 0.5, i, f'{value}万', va='center', fontweight='bold')

plt.tight_layout()
plt.show()

print("📝 柱状图使用场景:")
print("  - 分类数据比较(销售额、人口、得分等)")
print("  - 频次分布(直方图的离散版本)")
print("  - 多组数据对比(分组柱状图)")
print("  - 部分与整体关系(堆叠柱状图)")

散点图(Scatter Plot)

# 散点图详解
print(f"\n🔹 散点图详解")
print("=" * 20)

# 生成示例数据
np.random.seed(42)
n_points = 100

# 数据集1:身高体重关系
height = np.random.normal(170, 10, n_points)  # 身高(cm)
weight = 0.8 * height + np.random.normal(0, 5, n_points) - 80  # 体重(kg)
gender = np.random.choice(['男', '女'], n_points)

# 数据集2:广告投入与销售额
ad_spend = np.random.uniform(10, 100, n_points)  # 广告投入(万元)
sales = 2 * ad_spend + np.random.normal(0, 15, n_points) + 50  # 销售额(万元)
region = np.random.choice(['北区', '南区', '东区', '西区'], n_points)

fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(16, 12))

# 1. 基础散点图
ax1.scatter(height, weight, alpha=0.6, s=50, color='blue')
ax1.set_title('身高与体重关系', fontsize=14, fontweight='bold')
ax1.set_xlabel('身高 (cm)')
ax1.set_ylabel('体重 (kg)')
ax1.grid(True, alpha=0.3)

# 添加趋势线
z = np.polyfit(height, weight, 1)  # 一次线性拟合
p = np.poly1d(z)
ax1.plot(height, p(height), "r--", alpha=0.8, linewidth=2, label=f'趋势线: y={z[0]:.2f}x{z[1]:+.2f}')
ax1.legend()

# 2. 分类散点图(不同颜色表示不同类别)
colors = {'男': 'blue', '女': 'red'}
for g in ['男', '女']:
    mask = gender == g
    ax2.scatter(height[mask], weight[mask], 
               c=colors[g], label=g, alpha=0.6, s=50)

ax2.set_title('按性别分类的身高体重关系', fontsize=14, fontweight='bold')
ax2.set_xlabel('身高 (cm)')
ax2.set_ylabel('体重 (kg)')
ax2.legend()
ax2.grid(True, alpha=0.3)

# 3. 大小和颜色映射
# 点的大小表示销售额,颜色表示地区
region_colors = {'北区': 'red', '南区': 'green', '东区': 'blue', '西区': 'orange'}
for r in ['北区', '南区', '东区', '西区']:
    mask = region == r
    ax3.scatter(ad_spend[mask], sales[mask], 
               c=region_colors[r], label=r, alpha=0.6, 
               s=sales[mask]*2)  # 点的大小与销售额成正比

ax3.set_title('广告投入与销售额关系\n(点大小=销售额,颜色=地区)', fontsize=14, fontweight='bold')
ax3.set_xlabel('广告投入 (万元)')
ax3.set_ylabel('销售额 (万元)')
ax3.legend()
ax3.grid(True, alpha=0.3)

# 4. 气泡图(三维数据的二维展示)
# 第三个维度用颜色和大小表示
profit_margin = np.random.uniform(0.1, 0.3, n_points)  # 利润率

scatter = ax4.scatter(ad_spend, sales, 
                     c=profit_margin, s=profit_margin*1000, 
                     alpha=0.6, cmap='viridis')

ax4.set_title('广告投入、销售额与利润率关系\n(颜色和大小=利润率)', fontsize=14, fontweight='bold')
ax4.set_xlabel('广告投入 (万元)')
ax4.set_ylabel('销售额 (万元)')

# 添加颜色条
cbar = plt.colorbar(scatter, ax=ax4)
cbar.set_label('利润率', rotation=270, labelpad=20)

plt.tight_layout()
plt.show()

print("📝 散点图使用场景:")
print("  - 两个连续变量关系(相关性分析)")
print("  - 异常值检测(离群点识别)")
print("  - 聚类结果展示(不同颜色表示不同类别)")
print("  - 三维数据降维展示(颜色或大小表示第三维)")

饼图(Pie Chart)

# 饼图详解
print(f"\n🥧 饼图详解")
print("=" * 15)

# 准备数据
market_share = {
    'Android': 71.9,
    'iOS': 27.3,
    'Others': 0.8
}

sales_by_region = {
    '华东': 35,
    '华南': 28,
    '华北': 22,
    '华中': 10,
    '其他': 5
}

fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(16, 12))

# 1. 基础饼图
labels1 = list(market_share.keys())
sizes1 = list(market_share.values())
colors1 = ['#FF9999', '#66B2FF', '#99FF99']

wedges1, texts1, autotexts1 = ax1.pie(sizes1, labels=labels1, colors=colors1, 
                                       autopct='%1.1f%%', startangle=90)

ax1.set_title('全球移动操作系统市场份额', fontsize=14, fontweight='bold')

# 美化文字
for autotext in autotexts1:
    autotext.set_color('white')
    autotext.set_fontweight('bold')

# 2. 突出显示饼图
labels2 = list(sales_by_region.keys())
sizes2 = list(sales_by_region.values())
colors2 = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7']
explode2 = (0.1, 0, 0, 0, 0)  # 突出显示第一个扇形

wedges2, texts2, autotexts2 = ax2.pie(sizes2, labels=labels2, colors=colors2,
                                       autopct='%1.1f%%', startangle=90,
                                       explode=explode2, shadow=True)

ax2.set_title('各地区销售额分布', fontsize=14, fontweight='bold')

# 3. 环形图(甜甜圈图)
wedges3, texts3, autotexts3 = ax3.pie(sizes2, labels=labels2, colors=colors2,
                                       autopct='%1.1f%%', startangle=90,
                                       wedgeprops=dict(width=0.5))

ax3.set_title('各地区销售额分布(环形图)', fontsize=14, fontweight='bold')

# 在中心添加总计信息
total_sales = sum(sizes2)
ax3.text(0, 0, f'总销售额\n{total_sales}%', ha='center', va='center',
         fontsize=12, fontweight='bold')

# 4. 嵌套饼图
# 外环:大类别
outer_labels = ['移动设备', '电脑设备']
outer_sizes = [75, 25]
outer_colors = ['#FF6B6B', '#4ECDC4']

# 内环:细分类别
inner_labels = ['手机', '平板', '笔记本', '台式机']
inner_sizes = [50, 25, 15, 10]
inner_colors = ['#FF9999', '#FFB366', '#66B2FF', '#99CCFF']

# 绘制外环
ax4.pie(outer_sizes, labels=outer_labels, colors=outer_colors,
        radius=1, startangle=90, labeldistance=1.1)

# 绘制内环
ax4.pie(inner_sizes, labels=inner_labels, colors=inner_colors,
        radius=0.7, startangle=90, labeldistance=0.5)

ax4.set_title('设备销售额嵌套分布', fontsize=14, fontweight='bold')

plt.tight_layout()
plt.show()

print("📝 饼图使用场景:")
print("  - 部分与整体的比例关系")
print("  - 分类数据的占比展示")
print("  - 市场份额、预算分配等")
print("  ⚠️  注意:类别过多时不适用(建议不超过7个)")

直方图(Histogram)

# 直方图详解
print(f"\n📊 直方图详解")
print("=" * 20)

# 生成示例数据
np.random.seed(42)
normal_data = np.random.normal(100, 15, 1000)  # 正态分布数据
uniform_data = np.random.uniform(50, 150, 1000)  # 均匀分布数据
exponential_data = np.random.exponential(2, 1000)  # 指数分布数据

# 学生成绩数据(更真实的例子)
math_scores = np.random.normal(75, 12, 200)
english_scores = np.random.normal(78, 10, 200)

fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(16, 12))

# 1. 基础直方图
ax1.hist(normal_data, bins=30, color='skyblue', alpha=0.7, edgecolor='black')
ax1.set_title('正态分布数据直方图', fontsize=14, fontweight='bold')
ax1.set_xlabel('数值')
ax1.set_ylabel('频次')
ax1.axvline(normal_data.mean(), color='red', linestyle='--', 
            label=f'均值: {normal_data.mean():.1f}')
ax1.legend()
ax1.grid(True, alpha=0.3)

# 2. 多组数据对比
ax2.hist(math_scores, bins=20, alpha=0.7, label='数学成绩', color='blue')
ax2.hist(english_scores, bins=20, alpha=0.7, label='英语成绩', color='red')
ax2.set_title('数学与英语成绩分布对比', fontsize=14, fontweight='bold')
ax2.set_xlabel('成绩')
ax2.set_ylabel('人数')
ax2.legend()
ax2.grid(True, alpha=0.3)

# 3. 不同分布类型对比
ax3.hist(normal_data, bins=30, alpha=0.5, label='正态分布', density=True)
ax3.hist(uniform_data, bins=30, alpha=0.5, label='均匀分布', density=True)
ax3.set_title('不同分布类型对比(密度)', fontsize=14, fontweight='bold')
ax3.set_xlabel('数值')
ax3.set_ylabel('密度')
ax3.legend()
ax3.grid(True, alpha=0.3)

# 4. 累积直方图
counts, bins, patches = ax4.hist(math_scores, bins=20, cumulative=True, 
                                 alpha=0.7, color='green', edgecolor='black')
ax4.set_title('数学成绩累积分布', fontsize=14, fontweight='bold')
ax4.set_xlabel('成绩')
ax4.set_ylabel('累积人数')

# 添加百分位线
percentiles = [25, 50, 75]
for p in percentiles:
    value = np.percentile(math_scores, p)
    ax4.axvline(value, color='red', linestyle='--', alpha=0.7)
    ax4.text(value, ax4.get_ylim()[1]*0.9, f'{p}th', 
             ha='center', va='bottom', color='red', fontweight='bold')

ax4.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

print("📝 直方图使用场景:")
print("  - 数据分布形状分析(正态、偏态、双峰等)")
print("  - 异常值检测(分布的尾部)")
print("  - 质量控制(过程能力分析)")
print("  - A/B测试结果比较")

图表美化和自定义

颜色和样式设置

# 图表美化技巧
print(f"\n🎨 图表美化技巧")
print("=" * 25)

# 准备数据
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun']
revenue = [120, 135, 128, 142, 156, 168]
profit = [24, 28, 25, 30, 35, 38]

fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(16, 12))

# 1. 默认样式 vs 美化样式对比
# 默认样式
ax1.plot(months, revenue, label='收入')
ax1.plot(months, profit, label='利润')
ax1.set_title('默认样式')
ax1.legend()

# 美化样式
ax2.plot(months, revenue, color='#2E86AB', linewidth=3, marker='o', 
         markersize=8, markerfacecolor='white', markeredgewidth=2,
         markeredgecolor='#2E86AB', label='收入')
ax2.plot(months, profit, color='#A23B72', linewidth=3, marker='s', 
         markersize=8, markerfacecolor='white', markeredgewidth=2,
         markeredgecolor='#A23B72', label='利润')

ax2.set_title('美化样式', fontsize=16, fontweight='bold', pad=20)
ax2.set_xlabel('月份', fontsize=12, fontweight='bold')
ax2.set_ylabel('金额(万元)', fontsize=12, fontweight='bold')
ax2.legend(frameon=True, fancybox=True, shadow=True, fontsize=11)
ax2.grid(True, alpha=0.3, linestyle='--')
ax2.set_facecolor('#f8f9fa')  # 设置背景色

# 3. 颜色映射和渐变
x = np.linspace(0, 10, 100)
y = np.sin(x)
colors = plt.cm.viridis(np.linspace(0, 1, len(x)))  # 使用颜色映射

for i in range(len(x)-1):
    ax3.plot(x[i:i+2], y[i:i+2], color=colors[i], linewidth=3)

ax3.set_title('颜色渐变效果', fontsize=14, fontweight='bold')
ax3.set_xlabel('x值')
ax3.set_ylabel('sin(x)')
ax3.grid(True, alpha=0.3)

# 4. 高级样式设置
# 创建专业的财务图表
ax4.plot(months, revenue, color='#1f77b4', linewidth=4, marker='o', 
         markersize=10, label='月收入', zorder=3)

# 添加填充区域
ax4.fill_between(months, revenue, alpha=0.3, color='#1f77b4')

# 添加注释
max_revenue_idx = np.argmax(revenue)
ax4.annotate(f'最高收入\n{revenue[max_revenue_idx]}万元', 
             xy=(months[max_revenue_idx], revenue[max_revenue_idx]),
             xytext=(max_revenue_idx+0.5, revenue[max_revenue_idx]+10),
             arrowprops=dict(arrowstyle='->', color='red', lw=2),
             fontsize=12, fontweight='bold',
             bbox=dict(boxstyle="round,pad=0.3", facecolor='yellow', alpha=0.7))

# 设置坐标轴样式
ax4.spines['top'].set_visible(False)     # 隐藏上边框
ax4.spines['right'].set_visible(False)   # 隐藏右边框
ax4.spines['left'].set_linewidth(2)      # 加粗左边框
ax4.spines['bottom'].set_linewidth(2)    # 加粗下边框

ax4.set_title('专业财务图表样式', fontsize=14, fontweight='bold')
ax4.set_xlabel('月份', fontsize=12)
ax4.set_ylabel('收入(万元)', fontsize=12)
ax4.grid(True, alpha=0.3, axis='y')

plt.tight_layout()
plt.show()

print("🎨 美化技巧总结:")
print("  - 选择协调的颜色搭配")
print("  - 适当使用透明度和阴影")
print("  - 添加网格线但保持低调")
print("  - 使用注释突出重点信息")
print("  - 简化边框,突出数据本身")

子图和布局

# 复杂布局和子图管理
print(f"\n📐 复杂布局设计")
print("=" * 25)

# 创建综合分析仪表板
fig = plt.figure(figsize=(20, 12))

# 定义网格布局
gs = fig.add_gridspec(3, 4, hspace=0.3, wspace=0.3)

# 准备各种示例数据
np.random.seed(42)
time_data = pd.date_range('2024-01-01', periods=365, freq='D')
daily_sales = np.random.normal(1000, 200, 365) + 100 * np.sin(np.arange(365) * 2 * np.pi / 365)

categories = ['A', 'B', 'C', 'D', 'E']
category_values = [45, 38, 32, 28, 22]

regions = ['北区', '南区', '东区', '西区']
region_data = np.random.rand(4, 6) * 100

# 1. 主要趋势图(占据两行)
ax_main = fig.add_subplot(gs[0:2, 0:2])
ax_main.plot(time_data, daily_sales, color='#1f77b4', linewidth=1.5, alpha=0.8)

# 添加移动平均线
window = 30
rolling_mean = pd.Series(daily_sales).rolling(window=window).mean()
ax_main.plot(time_data, rolling_mean, color='red', linewidth=3, 
             label=f'{window}天移动平均')

ax_main.set_title('年度销售趋势分析', fontsize=16, fontweight='bold')
ax_main.set_xlabel('日期')
ax_main.set_ylabel('销售额')
ax_main.legend()
ax_main.grid(True, alpha=0.3)

# 2. 分类分析饼图
ax_pie = fig.add_subplot(gs[0, 2])
wedges, texts, autotexts = ax_pie.pie(category_values, labels=categories, 
                                       autopct='%1.1f%%', startangle=90)
ax_pie.set_title('产品类别分布', fontsize=12, fontweight='bold')

# 3. 地区对比柱状图
ax_bar = fig.add_subplot(gs[0, 3])
bars = ax_bar.bar(regions, [sum(region_data[i]) for i in range(4)], 
                  color=['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4'])
ax_bar.set_title('各地区销售总额', fontsize=12, fontweight='bold')
ax_bar.set_ylabel('销售额')

# 添加数值标签
for bar in bars:
    height = bar.get_height()
    ax_bar.text(bar.get_x() + bar.get_width()/2., height + 5,
                f'{height:.0f}', ha='center', va='bottom')

# 4. 热力图
ax_heatmap = fig.add_subplot(gs[1, 2:4])
im = ax_heatmap.imshow(region_data, cmap='YlOrRd', aspect='auto')

# 设置坐标轴标签
ax_heatmap.set_xticks(range(6))
ax_heatmap.set_xticklabels(['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'])
ax_heatmap.set_yticks(range(4))
ax_heatmap.set_yticklabels(regions)
ax_heatmap.set_title('地区月度销售热力图', fontsize=12, fontweight='bold')

# 添加数值标注
for i in range(4):
    for j in range(6):
        text = ax_heatmap.text(j, i, f'{region_data[i, j]:.0f}',
                              ha="center", va="center", color="black", fontsize=8)

# 添加颜色条
cbar = plt.colorbar(im, ax=ax_heatmap, shrink=0.8)
cbar.set_label('销售额', rotation=270, labelpad=15)

# 5. 分布分析直方图
ax_hist = fig.add_subplot(gs[2, 0])
ax_hist.hist(daily_sales, bins=30, alpha=0.7, color='skyblue', edgecolor='black')
ax_hist.set_title('销售额分布', fontsize=12, fontweight='bold')
ax_hist.set_xlabel('销售额')
ax_hist.set_ylabel('天数')
ax_hist.axvline(np.mean(daily_sales), color='red', linestyle='--', 
                label=f'均值: {np.mean(daily_sales):.0f}')
ax_hist.legend()

# 6. 相关性散点图
ax_scatter = fig.add_subplot(gs[2, 1])
x_corr = np.random.normal(50, 15, 100)
y_corr = 0.7 * x_corr + np.random.normal(0, 10, 100)
ax_scatter.scatter(x_corr, y_corr, alpha=0.6, s=50)

# 添加趋势线
z = np.polyfit(x_corr, y_corr, 1)
p = np.poly1d(z)
ax_scatter.plot(x_corr, p(x_corr), "r--", alpha=0.8)

ax_scatter.set_title('销售额与广告投入相关性', fontsize=12, fontweight='bold')
ax_scatter.set_xlabel('广告投入')
ax_scatter.set_ylabel('销售额')
ax_scatter.grid(True, alpha=0.3)

# 7. 箱线图
ax_box = fig.add_subplot(gs[2, 2])
box_data = [np.random.normal(0, std, 100) for std in range(1, 5)]
bp = ax_box.boxplot(box_data, labels=['Q1', 'Q2', 'Q3', 'Q4'], patch_artist=True)

colors = ['lightblue', 'lightgreen', 'lightyellow', 'lightcoral']
for patch, color in zip(bp['boxes'], colors):
    patch.set_facecolor(color)

ax_box.set_title('季度销售分布', fontsize=12, fontweight='bold')
ax_box.set_ylabel('销售额变化率')

# 8. 综合统计表格
ax_table = fig.add_subplot(gs[2, 3])
ax_table.axis('tight')
ax_table.axis('off')

# 创建统计表格
stats_data = [
    ['指标', '数值'],
    ['总销售额', f'{sum(daily_sales):.0f}万'],
    ['平均日销售', f'{np.mean(daily_sales):.0f}万'],
    ['最高日销售', f'{max(daily_sales):.0f}万'],
    ['销售天数', f'{len(daily_sales)}天'],
    ['增长率', '+15.3%']
]

table = ax_table.table(cellText=stats_data, cellLoc='center', loc='center',
                      colWidths=[0.4, 0.4])
table.auto_set_font_size(False)
table.set_fontsize(10)
table.scale(1, 2)

# 设置表格样式
for i in range(len(stats_data)):
    if i == 0:  # 标题行
        for j in range(2):
            table[(i, j)].set_facecolor('#4CAF50')
            table[(i, j)].set_text_props(weight='bold', color='white')
    else:
        for j in range(2):
            table[(i, j)].set_facecolor('#f0f0f0' if i % 2 == 0 else '#ffffff')

ax_table.set_title('关键指标汇总', fontsize=12, fontweight='bold')

# 添加总标题
fig.suptitle('销售数据综合分析仪表板', fontsize=20, fontweight='bold', y=0.98)

plt.show()

print("📊 复杂布局设计要点:")
print("  - 使用GridSpec进行精确布局控制")
print("  - 主要图表占据更大空间")
print("  - 保持视觉平衡和逻辑关系")
print("  - 统一色彩主题和字体样式")
print("  - 添加总标题增强整体性")

完整项目:销售趋势图表制作

让我们创建一个完整的销售趋势分析项目:

# sales_visualization.py - 销售趋势图表制作项目
"""
使用Matplotlib创建专业的销售趋势分析图表
演示数据可视化在业务分析中的应用
"""

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from datetime import datetime, timedelta
import seaborn as sns

class SalesVisualizationManager:
    """销售数据可视化管理器"""
    
    def __init__(self):
        """初始化可视化管理器"""
        # 设置中文字体和样式
        plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS']
        plt.rcParams['axes.unicode_minus'] = False
        plt.rcParams['figure.dpi'] = 100
        
        # 定义统一的颜色主题
        self.colors = {
            'primary': '#2E86AB',
            'secondary': '#A23B72', 
            'success': '#4CAF50',
            'warning': '#FF9800',
            'danger': '#F44336',
            'info': '#2196F3',
            'light': '#F5F5F5',
            'dark': '#333333'
        }
        
        # 设置默认样式
        plt.style.use('default')
        
        print("📊 销售数据可视化管理器初始化完成")
    
    def generate_sales_data(self, start_date='2023-01-01', days=365):
        """
        生成模拟销售数据
        
        参数:
            start_date: 开始日期
            days: 生成天数
        """
        print(f"🎲 生成 {days} 天的销售数据...")
        
        np.random.seed(42)
        
        # 生成日期序列
        date_range = pd.date_range(start=start_date, periods=days, freq='D')
        
        # 产品和地区信息
        products = ['iPhone', 'iPad', 'MacBook', 'AirPods', 'Apple Watch']
        regions = ['华东', '华南', '华北', '华中', '西部']
        
        records = []
        
        for date in date_range:
            for product in products:
                for region in regions:
                    # 基础销售额(不同产品有不同基准)
                    base_prices = {
                        'iPhone': 5000, 'iPad': 3000, 'MacBook': 8000,
                        'AirPods': 1500, 'Apple Watch': 2500
                    }
                    
                    # 季节性因子
                    day_of_year = date.timetuple().tm_yday
                    seasonal_factor = 1 + 0.3 * np.sin(2 * np.pi * day_of_year / 365)
                    
                    # 周末效应
                    weekend_factor = 0.8 if date.weekday() >= 5 else 1.0
                    
                    # 地区系数
                    region_factors = {
                        '华东': 1.3, '华南': 1.2, '华北': 1.1, 
                        '华中': 0.9, '西部': 0.8
                    }
                    
                    # 计算销售额
                    base_sales = base_prices[product]
                    daily_sales = (base_sales * seasonal_factor * weekend_factor * 
                                 region_factors[region] * np.random.normal(1, 0.2))
                    
                    # 确保为正数
                    daily_sales = max(daily_sales, base_sales * 0.3)
                    
                    # 计算销售数量
                    quantity = max(1, int(daily_sales / (base_sales * 0.8) + 
                                        np.random.normal(0, 0.5)))
                    
                    records.append({
                        'Date': date,
                        'Product': product,
                        'Region': region,
                        'Sales': round(daily_sales, 2),
                        'Quantity': quantity,
                        'Month': date.month,
                        'Quarter': (date.month - 1) // 3 + 1,
                        'Weekday': date.strftime('%A'),
                        'IsWeekend': date.weekday() >= 5
                    })
        
        self.sales_data = pd.DataFrame(records)
        print(f"✅ 销售数据生成完成,共 {len(self.sales_data)} 条记录")
        return self.sales_data
    
    def create_time_series_charts(self):
        """创建时间序列分析图表"""
        print("📈 创建时间序列分析图表...")
        
        # 准备时间序列数据
        daily_sales = self.sales_data.groupby('Date')['Sales'].sum().reset_index()
        monthly_sales = self.sales_data.groupby(self.sales_data['Date'].dt.to_period('M'))['Sales'].sum()
        weekly_sales = self.sales_data.groupby(self.sales_data['Date'].dt.to_period('W'))['Sales'].sum()
        
        fig, axes = plt.subplots(2, 2, figsize=(20, 12))
        fig.suptitle('销售趋势时间序列分析', fontsize=18, fontweight='bold')
        
        # 1. 日销售趋势
        ax1 = axes[0, 0]
        ax1.plot(daily_sales['Date'], daily_sales['Sales'], 
                color=self.colors['primary'], linewidth=1, alpha=0.7)
        
        # 添加7天移动平均线
        daily_sales['MA7'] = daily_sales['Sales'].rolling(window=7).mean()
        ax1.plot(daily_sales['Date'], daily_sales['MA7'], 
                color=self.colors['danger'], linewidth=3, label='7天移动平均')
        
        # 添加30天移动平均线
        daily_sales['MA30'] = daily_sales['Sales'].rolling(window=30).mean()
        ax1.plot(daily_sales['Date'], daily_sales['MA30'], 
                color=self.colors['success'], linewidth=3, label='30天移动平均')
        
        ax1.set_title('日销售额趋势', fontsize=14, fontweight='bold')
        ax1.set_xlabel('日期')
        ax1.set_ylabel('销售额(元)')
        ax1.legend()
        ax1.grid(True, alpha=0.3)
        
        # 2. 月度销售趋势
        ax2 = axes[0, 1]
        bars = ax2.bar(range(len(monthly_sales)), monthly_sales.values, 
                      color=self.colors['info'], alpha=0.8)
        
        # 添加趋势线
        x_trend = range(len(monthly_sales))
        z = np.polyfit(x_trend, monthly_sales.values, 1)
        p = np.poly1d(z)
        ax2.plot(x_trend, p(x_trend), color=self.colors['danger'], 
                linewidth=3, linestyle='--', label='趋势线')
        
        ax2.set_title('月度销售额趋势', fontsize=14, fontweight='bold')
        ax2.set_xlabel('月份')
        ax2.set_ylabel('销售额(元)')
        ax2.set_xticks(range(len(monthly_sales)))
        ax2.set_xticklabels([str(period) for period in monthly_sales.index], rotation=45)
        ax2.legend()
        
        # 添加数值标签
        for i, bar in enumerate(bars):
            height = bar.get_height()
            ax2.text(bar.get_x() + bar.get_width()/2., height + height*0.01,
                    f'{height/10000:.0f}万', ha='center', va='bottom', fontsize=9)
        
        # 3. 周度销售趋势
        ax3 = axes[1, 0]
        ax3.plot(range(len(weekly_sales)), weekly_sales.values, 
                marker='o', markersize=4, color=self.colors['secondary'], 
                linewidth=2, markerfacecolor='white', markeredgewidth=2)
        
        ax3.set_title('周销售额趋势', fontsize=14, fontweight='bold')
        ax3.set_xlabel('周数')
        ax3.set_ylabel('销售额(元)')
        ax3.grid(True, alpha=0.3)
        
        # 4. 季节性分析
        ax4 = axes[1, 1]
        
        # 按月份统计平均销售额
        monthly_avg = self.sales_data.groupby('Month')['Sales'].mean()
        
        # 创建极坐标图显示季节性
        theta = np.linspace(0, 2*np.pi, 12)
        r = monthly_avg.values
        
        # 闭合曲线
        theta = np.concatenate([theta, [theta[0]]])
        r = np.concatenate([r, [r[0]]])
        
        ax4.plot(theta, r, color=self.colors['warning'], linewidth=3, marker='o')
        ax4.fill(theta, r, alpha=0.3, color=self.colors['warning'])
        ax4.set_thetagrids(np.arange(0, 360, 30), 
                          ['1月', '2月', '3月', '4月', '5月', '6月',
                           '7月', '8月', '9月', '10月', '11月', '12月'])
        ax4.set_title('月度销售季节性分析(极坐标)', fontsize=14, fontweight='bold')
        
        plt.tight_layout()
        plt.show()
        
        print("✅ 时间序列图表创建完成")
    
    def create_product_analysis_charts(self):
        """创建产品分析图表"""
        print("📱 创建产品分析图表...")
        
        fig, axes = plt.subplots(2, 2, figsize=(18, 12))
        fig.suptitle('产品销售分析', fontsize=18, fontweight='bold')
        
        # 1. 产品销售额对比
        ax1 = axes[0, 0]
        product_sales = self.sales_data.groupby('Product')['Sales'].sum().sort_values(ascending=False)
        
        bars = ax1.bar(product_sales.index, product_sales.values, 
                      color=[self.colors['primary'], self.colors['secondary'], 
                            self.colors['success'], self.colors['warning'], 
                            self.colors['info']])
        
        ax1.set_title('各产品销售额对比', fontsize=14, fontweight='bold')
        ax1.set_xlabel('产品')
        ax1.set_ylabel('销售额(元)')
        ax1.tick_params(axis='x', rotation=45)
        
        # 添加数值标签和百分比
        total_sales = product_sales.sum()
        for i, (bar, value) in enumerate(zip(bars, product_sales.values)):
            percentage = value / total_sales * 100
            ax1.text(bar.get_x() + bar.get_width()/2., bar.get_height() + value*0.01,
                    f'{value/10000:.0f}万\n({percentage:.1f}%)', 
                    ha='center', va='bottom', fontweight='bold')
        
        # 2. 产品市场份额饼图
        ax2 = axes[0, 1]
        colors = [self.colors['primary'], self.colors['secondary'], 
                 self.colors['success'], self.colors['warning'], self.colors['info']]
        
        wedges, texts, autotexts = ax2.pie(product_sales.values, labels=product_sales.index,
                                          colors=colors, autopct='%1.1f%%', startangle=90,
                                          explode=(0.05, 0, 0, 0, 0))  # 突出最大份额
        
        ax2.set_title('产品市场份额分布', fontsize=14, fontweight='bold')
        
        # 美化饼图文字
        for autotext in autotexts:
            autotext.set_color('white')
            autotext.set_fontweight('bold')
            autotext.set_fontsize(10)
        
        # 3. 产品月度趋势对比
        ax3 = axes[1, 0]
        product_monthly = self.sales_data.groupby(['Month', 'Product'])['Sales'].sum().unstack()
        
        for i, product in enumerate(product_monthly.columns):
            ax3.plot(product_monthly.index, product_monthly[product], 
                    marker='o', linewidth=2, label=product, color=colors[i])
        
        ax3.set_title('各产品月度销售趋势', fontsize=14, fontweight='bold')
        ax3.set_xlabel('月份')
        ax3.set_ylabel('销售额(元)')
        ax3.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
        ax3.grid(True, alpha=0.3)
        
        # 4. 产品销售数量与金额关系
        ax4 = axes[1, 1]
        product_stats = self.sales_data.groupby('Product').agg({
            'Sales': 'sum',
            'Quantity': 'sum'
        }).reset_index()
        
        # 计算平均单价
        product_stats['AvgPrice'] = product_stats['Sales'] / product_stats['Quantity']
        
        # 气泡图:x=数量,y=销售额,大小=平均单价
        scatter = ax4.scatter(product_stats['Quantity'], product_stats['Sales'],
                             s=product_stats['AvgPrice']/10, alpha=0.6,
                             c=range(len(product_stats)), cmap='viridis')
        
        # 添加产品名称标注
        for i, row in product_stats.iterrows():
            ax4.annotate(row['Product'], 
                        (row['Quantity'], row['Sales']),
                        xytext=(5, 5), textcoords='offset points',
                        fontweight='bold')
        
        ax4.set_title('产品销售数量vs金额关系\n(气泡大小=平均单价)', fontsize=14, fontweight='bold')
        ax4.set_xlabel('销售数量')
        ax4.set_ylabel('销售额(元)')
        ax4.grid(True, alpha=0.3)
        
        plt.tight_layout()
        plt.show()
        
        print("✅ 产品分析图表创建完成")
    
    def create_regional_analysis_charts(self):
        """创建地区分析图表"""
        print("🗺️ 创建地区分析图表...")
        
        fig, axes = plt.subplots(2, 2, figsize=(18, 12))
        fig.suptitle('地区销售分析', fontsize=18, fontweight='bold')
        
        # 1. 地区销售额对比
        ax1 = axes[0, 0]
        region_sales = self.sales_data.groupby('Region')['Sales'].sum().sort_values(ascending=True)
        
        bars = ax1.barh(region_sales.index, region_sales.values,
                       color=self.colors['primary'])
        
        ax1.set_title('各地区销售额对比', fontsize=14, fontweight='bold')
        ax1.set_xlabel('销售额(元)')
        ax1.set_ylabel('地区')
        
        # 添加数值标签
        for i, (bar, value) in enumerate(zip(bars, region_sales.values)):
            ax1.text(bar.get_width() + value*0.01, bar.get_y() + bar.get_height()/2,
                    f'{value/10000:.0f}万', va='center', fontweight='bold')
        
        # 2. 地区产品组合热力图
        ax2 = axes[0, 1]
        region_product = self.sales_data.groupby(['Region', 'Product'])['Sales'].sum().unstack()
        
        # 标准化数据以便比较
        region_product_norm = region_product.div(region_product.sum(axis=1), axis=0)
        
        im = ax2.imshow(region_product_norm.values, cmap='YlOrRd', aspect='auto')
        
        # 设置坐标轴
        ax2.set_xticks(range(len(region_product_norm.columns)))
        ax2.set_xticklabels(region_product_norm.columns, rotation=45)
        ax2.set_yticks(range(len(region_product_norm.index)))
        ax2.set_yticklabels(region_product_norm.index)
        ax2.set_title('地区产品组合热力图(比例)', fontsize=14, fontweight='bold')
        
        # 添加数值标注
        for i in range(len(region_product_norm.index)):
            for j in range(len(region_product_norm.columns)):
                text = ax2.text(j, i, f'{region_product_norm.iloc[i, j]:.2f}',
                               ha="center", va="center", color="white", fontweight='bold')
        
        # 添加颜色条
        cbar = plt.colorbar(im, ax=ax2, shrink=0.8)
        cbar.set_label('产品占比', rotation=270, labelpad=15)
        
        # 3. 地区月度销售趋势
        ax3 = axes[1, 0]
        region_monthly = self.sales_data.groupby(['Month', 'Region'])['Sales'].sum().unstack()
        
        colors_region = [self.colors['primary'], self.colors['secondary'], 
                        self.colors['success'], self.colors['warning'], self.colors['info']]
        
        for i, region in enumerate(region_monthly.columns):
            ax3.plot(region_monthly.index, region_monthly[region], 
                    marker='o', linewidth=2, label=region, color=colors_region[i])
        
        ax3.set_title('各地区月度销售趋势', fontsize=14, fontweight='bold')
        ax3.set_xlabel('月份')
        ax3.set_ylabel('销售额(元)')
        ax3.legend()
        ax3.grid(True, alpha=0.3)
        
        # 4. 地区销售表现雷达图
        ax4 = axes[1, 1]
        
        # 计算各地区的多个指标
        region_metrics = self.sales_data.groupby('Region').agg({
            'Sales': ['sum', 'mean', 'count'],
            'Quantity': 'sum'
        })
        
        # 扁平化列名
        region_metrics.columns = ['总销售额', '平均订单额', '订单数', '总数量']
        
        # 标准化指标(0-1范围)
        region_metrics_norm = region_metrics.div(region_metrics.max())
        
        # 选择一个地区进行雷达图展示
        selected_region = region_metrics_norm.index[0]
        values = region_metrics_norm.loc[selected_region].values
        
        # 设置雷达图
        categories = region_metrics_norm.columns
        N = len(categories)
        
        # 计算角度
        angles = [n / float(N) * 2 * np.pi for n in range(N)]
        angles += angles[:1]  # 闭合图形
        
        # 闭合数据
        values = np.concatenate([values, [values[0]]])
        
        # 转换为极坐标
        ax4 = plt.subplot(2, 2, 4, projection='polar')
        ax4.plot(angles, values, 'o-', linewidth=2, color=self.colors['primary'])
        ax4.fill(angles, values, alpha=0.25, color=self.colors['primary'])
        
        # 设置标签
        ax4.set_xticks(angles[:-1])
        ax4.set_xticklabels(categories)
        ax4.set_ylim(0, 1)
        ax4.set_title(f'{selected_region}销售表现雷达图', fontsize=14, fontweight='bold', pad=20)
        
        plt.tight_layout()
        plt.show()
        
        print("✅ 地区分析图表创建完成")
    
    def create_comprehensive_dashboard(self):
        """创建综合仪表板"""
        print("📊 创建综合销售仪表板...")
        
        # 创建大型仪表板
        fig = plt.figure(figsize=(24, 16))
        gs = fig.add_gridspec(4, 6, hspace=0.3, wspace=0.3)
        
        # 计算关键指标
        total_sales = self.sales_data['Sales'].sum()
        total_quantity = self.sales_data['Quantity'].sum()
        avg_order_value = self.sales_data['Sales'].mean()
        num_orders = len(self.sales_data)
        
        # 1. 关键指标卡片(顶部)
        metrics = [
            ('总销售额', f'{total_sales/10000:.0f}万元', self.colors['primary']),
            ('总销量', f'{total_quantity:,}件', self.colors['success']),
            ('平均订单', f'{avg_order_value:.0f}元', self.colors['warning']),
            ('订单数量', f'{num_orders:,}笔', self.colors['info'])
        ]
        
        for i, (title, value, color) in enumerate(metrics):
            ax = fig.add_subplot(gs[0, i:i+1])
            ax.text(0.5, 0.7, value, ha='center', va='center', 
                   fontsize=24, fontweight='bold', color=color)
            ax.text(0.5, 0.3, title, ha='center', va='center', 
                   fontsize=14, color='gray')
            ax.set_xlim(0, 1)
            ax.set_ylim(0, 1)
            ax.axis('off')
            
            # 添加背景框
            ax.add_patch(plt.Rectangle((0.05, 0.1), 0.9, 0.8, 
                                     facecolor=color, alpha=0.1, linewidth=2))
        
        # 2. 主要销售趋势图(左上大图)
        ax_main = fig.add_subplot(gs[1:3, 0:3])
        daily_sales = self.sales_data.groupby('Date')['Sales'].sum()
        
        ax_main.plot(daily_sales.index, daily_sales.values, 
                    color=self.colors['primary'], linewidth=1.5, alpha=0.7)
        
        # 添加移动平均线
        ma7 = daily_sales.rolling(window=7).mean()
        ma30 = daily_sales.rolling(window=30).mean()
        
        ax_main.plot(daily_sales.index, ma7, color=self.colors['danger'], 
                    linewidth=2, label='7天移动平均')
        ax_main.plot(daily_sales.index, ma30, color=self.colors['success'], 
                    linewidth=2, label='30天移动平均')
        
        ax_main.set_title('日销售额趋势分析', fontsize=16, fontweight='bold')
        ax_main.set_xlabel('日期')
        ax_main.set_ylabel('销售额(元)')
        ax_main.legend()
        ax_main.grid(True, alpha=0.3)
        
        # 3. 产品销售分布(右上)
        ax_product = fig.add_subplot(gs[1, 3:5])
        product_sales = self.sales_data.groupby('Product')['Sales'].sum()
        
        bars = ax_product.bar(range(len(product_sales)), product_sales.values,
                             color=[self.colors['primary'], self.colors['secondary'], 
                                   self.colors['success'], self.colors['warning'], 
                                   self.colors['info']])
        
        ax_product.set_title('产品销售额分布', fontsize=14, fontweight='bold')
        ax_product.set_xticks(range(len(product_sales)))
        ax_product.set_xticklabels(product_sales.index, rotation=45)
        ax_product.set_ylabel('销售额(元)')
        
        # 4. 地区销售饼图(右中)
        ax_region = fig.add_subplot(gs[2, 3:5])
        region_sales = self.sales_data.groupby('Region')['Sales'].sum()
        
        wedges, texts, autotexts = ax_region.pie(region_sales.values, 
                                                 labels=region_sales.index,
                                                 autopct='%1.1f%%', startangle=90)
        ax_region.set_title('地区销售分布', fontsize=14, fontweight='bold')
        
        # 5. 月度销售对比(右上角)
        ax_monthly = fig.add_subplot(gs[1:3, 5])
        monthly_sales = self.sales_data.groupby('Month')['Sales'].sum()
        
        bars = ax_monthly.bar(monthly_sales.index, monthly_sales.values,
                             color=self.colors['info'], alpha=0.8)
        ax_monthly.set_title('月度销售', fontsize=12, fontweight='bold')
        ax_monthly.set_xlabel('月份')
        ax_monthly.set_ylabel('销售额')
        
        # 6. 销售热力图(底部左)
        ax_heatmap = fig.add_subplot(gs[3, 0:2])
        
        # 创建周-月热力图
        self.sales_data['Week'] = self.sales_data['Date'].dt.isocalendar().week
        week_month_sales = self.sales_data.groupby(['Month', 'Week'])['Sales'].sum().unstack(fill_value=0)
        
        # 只选择前12周
        week_month_sales = week_month_sales.iloc[:, :12]
        
        im = ax_heatmap.imshow(week_month_sales.values, cmap='YlOrRd', aspect='auto')
        ax_heatmap.set_title('月度-周度销售热力图', fontsize=12, fontweight='bold')
        ax_heatmap.set_xlabel('周')
        ax_heatmap.set_ylabel('月份')
        ax_heatmap.set_yticks(range(len(week_month_sales.index)))
        ax_heatmap.set_yticklabels(week_month_sales.index)
        
        # 7. 销售分布直方图(底部中)
        ax_dist = fig.add_subplot(gs[3, 2:4])
        ax_dist.hist(self.sales_data['Sales'], bins=30, alpha=0.7, 
                    color=self.colors['secondary'], edgecolor='black')
        ax_dist.set_title('单笔销售额分布', fontsize=12, fontweight='bold')
        ax_dist.set_xlabel('销售额(元)')
        ax_dist.set_ylabel('频次')
        ax_dist.axvline(self.sales_data['Sales'].mean(), color='red', 
                       linestyle='--', label=f'均值: {self.sales_data["Sales"].mean():.0f}')
        ax_dist.legend()
        
        # 8. 关键统计表格(底部右)
        ax_table = fig.add_subplot(gs[3, 4:6])
        ax_table.axis('tight')
        ax_table.axis('off')
        
        # 计算统计数据
        stats_data = [
            ['统计指标', '数值'],
            ['最高日销售', f'{daily_sales.max()/10000:.1f}万元'],
            ['最低日销售', f'{daily_sales.min()/10000:.1f}万元'],
            ['销售标准差', f'{daily_sales.std()/10000:.1f}万元'],
            ['最佳产品', product_sales.idxmax()],
            ['最佳地区', region_sales.idxmax()],
            ['数据天数', f'{len(daily_sales)}天']
        ]
        
        table = ax_table.table(cellText=stats_data, cellLoc='center', loc='center',
                              colWidths=[0.5, 0.5])
        table.auto_set_font_size(False)
        table.set_fontsize(10)
        table.scale(1, 1.5)
        
        # 设置表格样式
        for i in range(len(stats_data)):
            if i == 0:
                for j in range(2):
                    table[(i, j)].set_facecolor(self.colors['primary'])
                    table[(i, j)].set_text_props(weight='bold', color='white')
            else:
                for j in range(2):
                    table[(i, j)].set_facecolor('#f8f9fa' if i % 2 == 0 else '#ffffff')
        
        ax_table.set_title('关键统计指标', fontsize=12, fontweight='bold')
        
        # 添加总标题和时间戳
        fig.suptitle('销售数据综合分析仪表板', fontsize=24, fontweight='bold', y=0.98)
        
        # 添加生成时间
        current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        fig.text(0.99, 0.01, f'生成时间: {current_time}', ha='right', va='bottom', 
                fontsize=10, color='gray')
        
        plt.show()
        
        print("✅ 综合仪表板创建完成")
    
    def export_charts(self, format='png', dpi=300):
        """导出图表"""
        print(f"💾 导出图表为 {format} 格式...")
        
        # 重新创建所有图表并保存
        charts_info = [
            ('时间序列分析', self.create_time_series_charts),
            ('产品分析', self.create_product_analysis_charts),
            ('地区分析', self.create_regional_analysis_charts),
            ('综合仪表板', self.create_comprehensive_dashboard)
        ]
        
        for chart_name, chart_func in charts_info:
            plt.figure()
            chart_func()
            filename = f'sales_{chart_name}.{format}'
            plt.savefig(filename, dpi=dpi, bbox_inches='tight', 
                       facecolor='white', edgecolor='none')
            plt.close()
            print(f"  ✅ {filename} 已保存")
        
        print("📁 所有图表已导出完成")

def demo_sales_visualization():
    """销售可视化完整演示"""
    print("🚀 销售趋势图表制作项目演示")
    print("=" * 50)
    
    # 创建可视化管理器
    viz_manager = SalesVisualizationManager()
    
    # 生成示例数据
    viz_manager.generate_sales_data(days=365)
    
    # 数据概览
    print(f"\n📋 数据概览:")
    print(f"数据形状: {viz_manager.sales_data.shape}")
    print(f"日期范围: {viz_manager.sales_data['Date'].min()}{viz_manager.sales_data['Date'].max()}")
    print(f"产品种类: {viz_manager.sales_data['Product'].nunique()}种")
    print(f"销售地区: {viz_manager.sales_data['Region'].nunique()}个")
    
    # 创建各类分析图表
    viz_manager.create_time_series_charts()
    viz_manager.create_product_analysis_charts()
    viz_manager.create_regional_analysis_charts()
    viz_manager.create_comprehensive_dashboard()
    
    print(f"\n🎉 销售趋势图表制作项目演示完成!")
    print("📊 你已经学会了创建专业的数据可视化图表")

if __name__ == "__main__":
    demo_sales_visualization()

可视化最佳实践

图表选择指南

# 可视化最佳实践指南
print("📚 可视化最佳实践")
print("=" * 30)

def choose_chart_type(data_type, purpose):
    """
    根据数据类型和目的选择合适的图表类型
    
    参数:
        data_type: 数据类型
        purpose: 分析目的
    """
    
    chart_guide = {
        ('时间序列', '趋势分析'): '线图',
        ('分类数据', '比较大小'): '柱状图',
        ('分类数据', '部分整体'): '饼图',
        ('两个连续变量', '相关关系'): '散点图',
        ('单个连续变量', '分布形状'): '直方图',
        ('多个数值变量', '综合比较'): '雷达图',
        ('二维数据', '模式识别'): '热力图',
        ('分类统计', '分布比较'): '箱线图'
    }
    
    return chart_guide.get((data_type, purpose), '根据具体情况选择')

# 图表选择示例
scenarios = [
    ('时间序列', '趋势分析', '股价变化、销售趋势'),
    ('分类数据', '比较大小', '各产品销量对比'),
    ('分类数据', '部分整体', '市场份额分析'),
    ('两个连续变量', '相关关系', '身高体重关系'),
    ('单个连续变量', '分布形状', '考试成绩分布'),
    ('多个数值变量', '综合比较', '员工能力评估'),
    ('二维数据', '模式识别', '地区产品销售'),
    ('分类统计', '分布比较', '不同组别的数据分布')
]

print("📊 图表类型选择指南:")
for data_type, purpose, example in scenarios:
    chart_type = choose_chart_type(data_type, purpose)
    print(f"  {data_type} + {purpose}{chart_type}")
    print(f"    示例: {example}")
    print()

设计原则

# 可视化设计原则
print("🎨 可视化设计原则")
print("=" * 25)

principles = [
    {
        'principle': '简洁性原则',
        'description': '去除不必要的装饰,突出数据本身',
        'good_practice': ['使用简洁的配色', '避免3D效果', '减少图表垃圾'],
        'bad_practice': ['过多的颜色', '复杂的背景', '无关的装饰元素']
    },
    {
        'principle': '准确性原则', 
        'description': '确保图表准确反映数据',
        'good_practice': ['从0开始的y轴', '合适的比例尺', '清晰的标签'],
        'bad_practice': ['截断的y轴', '误导的比例', '模糊的标签']
    },
    {
        'principle': '可读性原则',
        'description': '确保观众能够轻松理解图表',
        'good_practice': ['清晰的字体', '合适的大小', '有效的颜色对比'],
        'bad_practice': ['太小的字体', '低对比度', '难以区分的颜色']
    },
    {
        'principle': '一致性原则',
        'description': '在同一报告中保持视觉风格统一',
        'good_practice': ['统一的配色方案', '一致的字体', '相同的图例位置'],
        'bad_practice': ['混乱的颜色使用', '不同的字体样式', '随意的布局']
    }
]

for principle in principles:
    print(f"🔵 {principle['principle']}")
    print(f"   {principle['description']}")
    print(f"   ✅ 好的做法: {', '.join(principle['good_practice'])}")
    print(f"   ❌ 避免: {', '.join(principle['bad_practice'])}")
    print()

学习总结

通过本节课的学习,你已经掌握了:

✅ Matplotlib基础概念

  • 理解了数据可视化在AI中的重要作用
  • 掌握了Figure、Axes、Axis的层次结构
  • 学会了pyplot和面向对象两种编程接口

✅ 基本图表类型

  • 熟练创建线图、柱状图、散点图、饼图、直方图
  • 掌握了各种图表的适用场景和最佳实践
  • 学会了图表的美化和自定义技巧

✅ 高级可视化技能

  • 掌握了复杂布局和子图管理
  • 学会了创建综合性的数据分析仪表板
  • 理解了颜色、样式、注释等美化要素

✅ 实际项目经验

  • 完成了完整的销售趋势分析项目
  • 体验了从数据到洞察的可视化流程
  • 学会了创建专业级的数据分析报告

✅ 最佳实践认知

  • 了解了图表类型的选择原则
  • 掌握了可视化设计的核心要素
  • 学会了避免常见的可视化陷阱

下节课预告

第7讲我们将学习OpenAI API使用,这是当前最热门的AI应用开发技能:

  • OpenAI API的基本概念和注册流程
  • GPT模型的API调用和参数设置
  • 提示词工程的技巧和最佳实践
  • 构建智能问答系统项目
  • 处理API限制和错误处理

掌握OpenAI API将让你能够构建各种智能应用!


🎉 恭喜完成第6讲!
你已经掌握了数据可视化这项重要技能。好的可视化能让数据说话,让复杂的分析结果变得直观易懂。现在你可以创建专业的图表和仪表板,为数据分析和AI项目增添强大的展示能力!


网站公告

今日签到

点亮在社区的每一天
去签到