本节课你将学到
- 理解数据可视化在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'] = 100print("✅ 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 == gax2.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 == rax3.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 snsclass SalesVisualizationManager:"""销售数据可视化管理器"""def __init__(self):"""初始化可视化管理器"""# 设置中文字体和样式plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS']plt.rcParams['axes.unicode_minus'] = Falseplt.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_ydayseasonal_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_datadef 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 * 100ax1.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.columnsN = 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().weekweek_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项目增添强大的展示能力!