跳到主要内容

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()

小结

本章我们学习了:

  1. 快速入门:pyplot 和面向对象两种方式
  2. 基本图表:折线图、柱状图、直方图、散点图、饼图、箱线图
  3. 样式和美化:颜色、样式、文本、字体
  4. 子图布局:subplots 和 GridSpec
  5. 保存导出:PNG、PDF、SVG 等格式
  6. 实战案例:股票可视化、多维度数据分析

练习

  1. 使用真实数据集创建一个完整的可视化报告
  2. 比较不同样式对图表可读性的影响

参考资源

下一步

Matplotlib 是基础的可视化库。掌握了这些技能后,你可以尝试创建更多类型的图表并应用到实际项目中!