Matplotlib 数据可视化
数据可视化是数据分析的重要组成部分,通过图表可以直观地理解数据分布和规律。Matplotlib 是 Python 最基础、最强大的可视化库。本章将详细介绍 Matplotlib 的使用方法。
Matplotlib 概述
为什么学习 Matplotlib?
- 基础:其他可视化库(如 Seaborn、Plotly)都是基于 Matplotlib
- 灵活:可以创建任何类型的图表
- 控制力强:可以精细控制图表的每个元素
快速入门
第一个图表
import matplotlib.pyplot as plt
import numpy as np
# 创建数据
x = np.linspace(0, 10, 100)
y = np.sin(x)
# 绘制图表
plt.figure(figsize=(10, 6))
plt.plot(x, y)
plt.title('正弦函数')
plt.xlabel('X轴')
plt.ylabel('Y轴')
plt.grid(True)
plt.show()
两种绘图方式
# 方式1:pyplot 方式(简单快捷)
plt.figure()
plt.plot([1, 2, 3], [1, 4, 9])
plt.show()
# 方式2:面向对象方式(更精确控制)
fig, ax = plt.subplots()
ax.plot([1, 2, 3], [1, 4, 9])
ax.set_title('标题')
plt.show()
基本图表类型
折线图
import matplotlib.pyplot as plt
import numpy as np
# 创建数据
x = np.arange(0, 10, 1)
y1 = x * 2
y2 = x ** 2
# 绘制多条线
plt.figure(figsize=(10, 6))
plt.plot(x, y1, 'b-o', label='y = 2x', linewidth=2, markersize=8)
plt.plot(x, y2, 'r--', label='y = x²', linewidth=2)
# 添加元素
plt.title('折线图示例', fontsize=16, fontweight='bold')
plt.xlabel('X轴', fontsize=12)
plt.ylabel('Y轴', fontsize=12)
plt.legend(fontsize=12) # 图例
plt.grid(True, alpha=0.3) # 网格
# 设置坐标轴范围
plt.xlim(-1, 11)
plt.ylim(-5, 100)
plt.show()
柱状图
import matplotlib.pyplot as plt
import numpy as np
# 创建数据
categories = ['A', 'B', 'C', 'D', 'E']
values = [23, 45, 67, 34, 56]
# 简单柱状图
plt.figure(figsize=(10, 6))
plt.bar(categories, values, color='skyblue', edgecolor='navy')
# 添加数值标签
for i, v in enumerate(values):
plt.text(i, v + 1, str(v), ha='center', fontsize=12)
plt.title('柱状图示例', fontsize=16)
plt.xlabel('类别', fontsize=12)
plt.ylabel('数值', fontsize=12)
plt.ylim(0, 80)
plt.show()
# 横向柱状图
plt.figure(figsize=(10, 6))
plt.barh(categories, values, color='lightgreen', edgecolor='darkgreen')
plt.title('横向柱状图', fontsize=16)
plt.xlabel('数值', fontsize=12)
plt.show()
# 分组柱状图
x = np.arange(3)
group1 = [10, 20, 15]
group2 = [15, 18, 12]
width = 0.35
plt.figure(figsize=(10, 6))
plt.bar(x - width/2, group1, width, label='组1')
plt.bar(x + width/2, group2, width, label='组2')
plt.xticks(x, ['A', 'B', 'C'])
plt.legend()
plt.show()
直方图
import matplotlib.pyplot as plt
import numpy as np
# 生成正态分布数据
np.random.seed(42)
data = np.random.normal(100, 15, 1000)
# 简单直方图
plt.figure(figsize=(10, 6))
plt.hist(data, bins=30, color='steelblue', edgecolor='white', alpha=0.7)
plt.title('直方图示例', fontsize=16)
plt.xlabel('值', fontsize=12)
plt.ylabel('频数', fontsize=12)
plt.show()
# 带核密度估计的直方图
plt.figure(figsize=(10, 6))
plt.hist(data, bins=30, density=True, color='steelblue', edgecolor='white', alpha=0.7)
plt.hist(data, bins=30, density=True, alpha=0.3, color='red') # 叠加
plt.title('直方图(密度归一化)', fontsize=16)
plt.xlabel('值', fontsize=12)
plt.ylabel('密度', fontsize=12)
plt.show()
散点图
import matplotlib.pyplot as plt
import numpy as np
# 创建数据
np.random.seed(42)
x = np.random.randn(100)
y = x * 2 + np.random.randn(100) # 与x有线性关系
colors = np.random.randn(100) # 用于着色
sizes = np.random.randint(50, 500, 100) # 用于点大小
# 简单散点图
plt.figure(figsize=(10, 6))
plt.scatter(x, y, c='steelblue', alpha=0.6, s=50)
plt.title('散点图示例', fontsize=16)
plt.xlabel('X', fontsize=12)
plt.ylabel('Y', fontsize=12)
plt.grid(True, alpha=0.3)
plt.show()
# 高级散点图(颜色和大小)
plt.figure(figsize=(10, 6))
scatter = plt.scatter(x, y, c=colors, s=sizes, cmap='viridis', alpha=0.7)
plt.colorbar(scatter, label='颜色值') # 颜色条
plt.title('高级散点图', fontsize=16)
plt.xlabel('X', fontsize=12)
plt.ylabel('Y', fontsize=12)
plt.show()
饼图
import matplotlib.pyplot as plt
# 创建数据
sizes = [15, 30, 45, 10]
labels = ['A', 'B', 'C', 'D']
colors = ['#ff9999', '#66b3ff', '#99ff99', '#ffcc99']
explode = (0, 0.1, 0, 0) # 突出显示B
# 简单饼图
plt.figure(figsize=(10, 8))
plt.pie(sizes, labels=labels, autopct='%1.1f%%', startangle=90,
colors=colors, explode=explode, shadow=True)
plt.title('饼图示例', fontsize=16)
plt.axis('equal') # 等比例
plt.show()
箱线图
import matplotlib.pyplot as plt
import numpy as np
# 创建数据
np.random.seed(42)
data = [np.random.normal(0, 1, 100),
np.random.normal(0, 2, 100),
np.random.normal(0, 1.5, 100)]
# 简单箱线图
plt.figure(figsize=(10, 6))
plt.boxplot(data, labels=['A组', 'B组', 'C组'], patch_artist=True,
boxprops=dict(facecolor='lightblue'))
plt.title('箱线图示例', fontsize=16)
plt.ylabel('值', fontsize=12)
plt.grid(True, alpha=0.3)
plt.show()
# 横向箱线图
plt.figure(figsize=(10, 6))
plt.boxplot(data, labels=['A组', 'B组', 'C组'], vert=False, patch_artist=True)
plt.title('横向箱线图', fontsize=16)
plt.xlabel('值', fontsize=12)
plt.show()
图表样式和美化
设置样式
import matplotlib.pyplot as plt
import numpy as np
# 查看可用样式
print(plt.style.available)
# 使用样式
plt.style.use('seaborn-v0_8-darkgrid')
# 或
plt.style.use('ggplot')
# 创建图表
x = np.arange(10)
plt.figure(figsize=(10, 6))
plt.plot(x, x*2, 'o-')
plt.title('使用样式的图表')
plt.show()
# 重置样式
plt.style.use('default')
颜色设置
# 使用颜色名称
plt.plot([1, 2, 3], [1, 2, 3], color='red')
# 使用十六进制
plt.plot([1, 2, 3], [1, 2, 3], color='#FF5733')
# 使用RGB元组(0-1)
plt.plot([1, 2, 3], [1, 2, 3], color=(0.5, 0.2, 0.8))
# 使用颜色映射
x = np.linspace(0, 10, 100)
y = np.sin(x)
plt.scatter(x, y, c=y, cmap='coolwarm')
# 内置颜色循环
plt.plot([1, 2], [1, 2], 'o-') # 蓝色
plt.plot([1, 2], [2, 3], 's-') # 橙色
plt.plot([1, 2], [3, 4], '^-') # 绿色
文本和字体
plt.figure(figsize=(10, 6))
# 添加文本
plt.text(0.5, 0.5, '居中文本', fontsize=20, ha='center', va='center')
# 添加箭头注释
plt.annotate('最高点', xy=(5, 10), xytext=(3, 8),
arrowprops=dict(arrowstyle='->', color='red'),
fontsize=12)
# 添加图例
plt.plot([1, 2, 3], [1, 2, 3], label='线1')
plt.plot([1, 2, 3], [3, 2, 1], label='线2')
plt.legend(loc='upper right', fontsize=12)
plt.show()
子图布局
subplots
import matplotlib.pyplot as plt
import numpy as np
# 创建 2x2 子图
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
# 子图1:折线图
x = np.linspace(0, 10, 100)
axes[0, 0].plot(x, np.sin(x))
axes[0, 0].set_title('正弦波')
# 子图2:散点图
axes[0, 1].scatter(np.random.randn(50), np.random.randn(50), alpha=0.6)
axes[0, 1].set_title('散点图')
# 子图3:柱状图
axes[1, 0].bar(['A', 'B', 'C'], [10, 20, 15])
axes[1, 0].set_title('柱状图')
# 子图4:饼图
axes[1, 1].pie([30, 40, 30], labels=['X', 'Y', 'Z'])
axes[1, 1].set_title('饼图')
plt.tight_layout()
plt.show()
GridSpec 布局
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
fig = plt.figure(figsize=(12, 8))
gs = GridSpec(3, 3, figure=fig)
# 创建不规则布局
ax1 = fig.add_subplot(gs[0, :]) # 顶部跨列
ax2 = fig.add_subplot(gs[1, :2]) # 中部跨两列
ax3 = fig.add_subplot(gs[1, 2]) # 中部右侧
ax4 = fig.add_subplot(gs[2, 0]) # 底部左侧
ax5 = fig.add_subplot(gs[2, 1]) # 底部中间
ax6 = fig.add_subplot(gs[2, 2]) # 底部右侧
plt.show()
保存和导出
import matplotlib.pyplot as plt
import numpy as np
# 创建图表
x = np.linspace(0, 10, 100)
plt.figure(figsize=(10, 6))
plt.plot(x, np.sin(x))
plt.title('正弦函数')
# 保存为不同格式
plt.savefig('figure.png', dpi=300, bbox_inches='tight') # PNG
plt.savefig('figure.pdf', bbox_inches='tight') # PDF
plt.savefig('figure.svg', bbox_inches='tight') # SVG
plt.savefig('figure.jpg', quality=95, bbox_inches='tight') # JPG
# 保存透明背景
plt.savefig('figure_transparent.png', transparent=True)
实战示例
示例:股票数据可视化
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
# 模拟股票数据
np.random.seed(42)
dates = pd.date_range('2024-01-01', periods=100)
price = 100 + np.cumsum(np.random.randn(100))
# 创建K线图(简化版)
fig, axes = plt.subplots(2, 1, figsize=(12, 8), sharex=True)
# 收盘价折线图
axes[0].plot(dates, price, 'b-', linewidth=1.5)
axes[0].fill_between(dates, price.min(), price, alpha=0.3)
axes[0].set_title('股票收盘价', fontsize=14)
axes[0].set_ylabel('价格 (元)')
axes[0].grid(True, alpha=0.3)
# 成交量柱状图
volume = np.random.randint(1000000, 5000000, 100)
colors = ['green' if price[i] > price[i-1] else 'red' for i in range(1, 100)]
colors = ['gray'] + colors
axes[1].bar(dates, volume, color=colors, alpha=0.7)
axes[1].set_title('成交量', fontsize=14)
axes[1].set_xlabel('日期')
axes[1].set_ylabel('成交量')
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
示例:多维度数据分析
import matplotlib.pyplot as plt
import numpy as np
# 创建学生成绩数据
np.random.seed(42)
students = ['学生' + str(i) for i in range(1, 21)]
chinese = np.random.randint(60, 100, 20)
math = np.random.randint(60, 100, 20)
english = np.random.randint(60, 100, 20)
# 计算总分
total = chinese + math + english
sorted_idx = np.argsort(total)[::-1]
# 创建综合图表
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
# 图1:各科成绩对比(分组柱状图)
x = np.arange(len(students))
width = 0.25
axes[0, 0].bar(x - width, chinese, width, label='语文')
axes[0, 0].bar(x, math, width, label='数学')
axes[0, 0].bar(x + width, english, width, label='英语')
axes[0, 0].set_xlabel('学生')
axes[0, 0].set_ylabel('分数')
axes[0, 0].set_title('各科成绩对比')
axes[0, 0].set_xticks(x[::2])
axes[0, 0].set_xticklabels([students[i] for i in range(0, 20, 2)], rotation=45)
axes[0, 0].legend()
axes[0, 0].set_ylim(50, 110)
# 图2:成绩分布(直方图)
axes[0, 1].hist(chinese, bins=10, alpha=0.6, label='语文')
axes[0, 1].hist(math, bins=10, alpha=0.6, label='数学')
axes[0, 1].hist(english, bins=10, alpha=0.6, label='英语')
axes[0, 1].set_xlabel('分数')
axes[0, 1].set_ylabel('人数')
axes[0, 1].set_title('成绩分布')
axes[0, 1].legend()
# 图3:语文vs数学(散点图)
scatter = axes[1, 0].scatter(chinese, math, c=total, cmap='RdYlGn', s=100, alpha=0.7)
axes[1, 0].plot([60, 100], [60, 100], 'k--', alpha=0.5) # 对角线
axes[1, 0].set_xlabel('语文成绩')
axes[1, 0].set_ylabel('数学成绩')
axes[1, 0].set_title('语文vs数学成绩')
plt.colorbar(scatter, ax=axes[1, 0], label='总分')
# 图4:总分排名(水平柱状图)
top_students = [students[i] for i in sorted_idx[:10]]
top_totals = total[sorted_idx[:10]]
colors = plt.cm.RdYlGn(np.linspace(0.3, 0.9, 10))
axes[1, 1].barh(range(10), top_totals[::-1], color=colors[::-1])
axes[1, 1].set_yticks(range(10))
axes[1, 1].set_yticklabels(top_students[::-1])
axes[1, 1].set_xlabel('总分')
axes[1, 1].set_title('总分Top10')
# 添加数值标签
for i, v in enumerate(top_totals[::-1]):
axes[1, 1].text(v + 1, i, str(v), va='center')
plt.tight_layout()
plt.show()
小结
本章我们学习了:
- 快速入门:pyplot 和面向对象两种方式
- 基本图表:折线图、柱状图、直方图、散点图、饼图、箱线图
- 样式和美化:颜色、样式、文本、字体
- 子图布局:subplots 和 GridSpec
- 保存导出:PNG、PDF、SVG 等格式
- 实战案例:股票可视化、多维度数据分析
练习
- 使用真实数据集创建一个完整的可视化报告
- 比较不同样式对图表可读性的影响
参考资源
下一步
Matplotlib 是基础的可视化库。掌握了这些技能后,你可以尝试创建更多类型的图表并应用到实际项目中!