跳到主要内容

Matplotlib 基础

问题

Matplotlib 的 Figure/Axes 体系是怎样的?如何定制专业的图表?

答案

Figure 与 Axes 体系

两种 API
  • pyplot 风格plt.plot()):快速出图,适合简单场景
  • OO 风格fig, ax = plt.subplots()):推荐,更灵活可控

基础绘图

OO 风格基础绘图
import matplotlib.pyplot as plt
import numpy as np

# 创建画布和子图
fig, ax = plt.subplots(figsize=(8, 5))

# 绘制数据
x = np.arange(1, 13)
y1 = [120, 135, 140, 155, 170, 180, 175, 165, 150, 140, 130, 125]
y2 = [100, 110, 125, 140, 155, 165, 160, 150, 135, 120, 110, 105]

ax.plot(x, y1, marker='o', linewidth=2, label='2024', color='#2196F3')
ax.plot(x, y2, marker='s', linewidth=2, label='2023', color='#9E9E9E', linestyle='--')

# 定制外观
ax.set_xlabel('月份', fontsize=12)
ax.set_ylabel('收入(万元)', fontsize=12)
ax.set_title('月度收入对比', fontsize=14, fontweight='bold')
ax.set_xticks(x)
ax.set_xticklabels([f'{i}月' for i in x])
ax.legend(frameon=False)
ax.spines[['top', 'right']].set_visible(False) # 去掉上右边框
ax.grid(axis='y', alpha=0.3)

plt.tight_layout()
plt.show()

子图布局

多子图布局
import matplotlib.pyplot as plt
import numpy as np

fig, axes = plt.subplots(2, 2, figsize=(12, 8))

# 左上:折线图
axes[0, 0].plot(range(12), np.random.cumsum(np.random.randn(12)))
axes[0, 0].set_title('趋势')

# 右上:柱状图
axes[0, 1].bar(['A', 'B', 'C', 'D'], [25, 40, 30, 35])
axes[0, 1].set_title('对比')

# 左下:散点图
axes[1, 0].scatter(np.random.rand(50), np.random.rand(50), alpha=0.6)
axes[1, 0].set_title('关系')

# 右下:直方图
axes[1, 1].hist(np.random.normal(100, 15, 500), bins=25, edgecolor='white')
axes[1, 1].set_title('分布')

fig.suptitle('数据看板', fontsize=16, fontweight='bold')
plt.tight_layout()
plt.show()

高级定制

图表美化技巧
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
import numpy as np

fig, ax = plt.subplots(figsize=(10, 6))

categories = ['搜索', '社交', '直接', '邮件', '广告', '联盟']
values = [320, 280, 150, 90, 60, 40]
colors = ['#2196F3', '#4CAF50', '#FF9800', '#9C27B0', '#F44336', '#607D8B']

# 水平条形图,排序后绘制
order = np.argsort(values)
bars = ax.barh(
[categories[i] for i in order],
[values[i] for i in order],
color=[colors[i] for i in order],
height=0.6
)

# 在条形末端添加数值标签
for bar in bars:
width = bar.get_width()
ax.text(width + 5, bar.get_y() + bar.get_height()/2,
f'{width:,.0f}', va='center', fontsize=11)

# 美化
ax.set_xlabel('访客数', fontsize=12)
ax.set_title('各渠道访客数', fontsize=14, fontweight='bold', pad=15)
ax.spines[['top', 'right', 'bottom']].set_visible(False)
ax.xaxis.set_visible(False) # 已有数值标签,隐藏 X 轴

plt.tight_layout()
plt.show()

保存图表

高质量导出
# PNG(适合 Web、PPT)
fig.savefig('chart.png', dpi=150, bbox_inches='tight', facecolor='white')

# SVG(适合论文、无损缩放)
fig.savefig('chart.svg', format='svg', bbox_inches='tight')

# PDF(适合印刷)
fig.savefig('chart.pdf', format='pdf', bbox_inches='tight')

中文显示

解决中文显示问题
import matplotlib.pyplot as plt

# 方式一:全局设置(macOS)
plt.rcParams['font.sans-serif'] = ['PingFang SC', 'Heiti TC', 'SimHei']
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示

# 方式二:指定字体
from matplotlib.font_manager import FontProperties
font = FontProperties(fname='/System/Library/Fonts/PingFang.ttc', size=12)
ax.set_title('中文标题', fontproperties=font)

常见面试问题

Q1: plt.plot() 和 ax.plot() 有什么区别?

答案

  • plt.plot():pyplot 接口,操作"当前"子图,适合快速出图
  • ax.plot():面向对象接口,明确指定在哪个 Axes 上绑图
  • 实际项目推荐 OO 风格,多子图时不会混淆

Q2: Matplotlib 图表怎么美化?

答案

  1. 去掉上右边框:ax.spines[['top','right']].set_visible(False)
  2. 柔和网格线:ax.grid(alpha=0.3)
  3. 有意义的颜色(不要默认色)
  4. 添加数值标签
  5. 合适的 figsize 和 dpi

Q3: figsize 怎么选?

答案

  • 单图默认 (8, 5)(10, 6)
  • PPT 宽屏 (12, 6)
  • 多子图 (12, 8) 或更大
  • 保存时用 dpi=150(Web)或 dpi=300(印刷)

相关链接