本节课你将学到
- 理解数据可视化在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项目增添强大的展示能力!