Python数据可视化:Matplotlib实战

Matplotlib 是 Python 最基础的绑图库,几乎所有可视化库都建立在它之上。本文通过实际例子覆盖常用图表类型和样式配置。

核心概念:Figure 和 Axes

import matplotlib.pyplot as plt
import numpy as np

# Figure 是整个画布,Axes 是画布上的一个子图
fig, ax = plt.subplots()  # 创建一个 Figure 和一个 Axes
ax.plot([1, 2, 3, 4], [1, 4, 2, 3])
ax.set_title('Simple Plot')
plt.show()

plt.plot() 是快捷方式,内部自动创建 Figure 和 Axes。但显式使用 fig, ax 的方式更灵活,推荐养成这个习惯。

折线图

x = np.linspace(0, 2 * np.pi, 100)

fig, ax = plt.subplots(figsize=(10, 6))
ax.plot(x, np.sin(x), label='sin(x)', linewidth=2)
ax.plot(x, np.cos(x), label='cos(x)', linewidth=2, linestyle='--')
ax.plot(x, np.sin(x) + np.cos(x), label='sin+cos', linewidth=1.5, alpha=0.7)

ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_title('Trigonometric Functions')
ax.legend()
ax.grid(True, alpha=0.3)

plt.tight_layout()
plt.savefig('line_chart.png', dpi=150)

常用参数:linewidth(线宽)、linestyle-/--/-./:)、markero/s/^)、alpha(透明度)。

柱状图

categories = ['Python', 'Java', 'Go', 'Rust', 'C++']
values_2020 = [30, 25, 15, 8, 22]
values_2021 = [35, 22, 20, 12, 18]

x = np.arange(len(categories))
width = 0.35

fig, ax = plt.subplots(figsize=(10, 6))
bars1 = ax.bar(x - width/2, values_2020, width, label='2020', color='steelblue')
bars2 = ax.bar(x + width/2, values_2021, width, label='2021', color='coral')

# 在柱子上方标注数值
for bar in bars1:
    ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.5,
            str(int(bar.get_height())), ha='center', fontsize=9)
for bar in bars2:
    ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.5,
            str(int(bar.get_height())), ha='center', fontsize=9)

ax.set_xticks(x)
ax.set_xticklabels(categories)
ax.set_ylabel('Popularity Score')
ax.set_title('Language Popularity Comparison')
ax.legend()
plt.tight_layout()
plt.savefig('bar_chart.png', dpi=150)

散点图

np.random.seed(42)
n = 200
x = np.random.randn(n)
y = 0.5 * x + np.random.randn(n) * 0.5
colors = np.random.rand(n)
sizes = np.abs(np.random.randn(n)) * 100

fig, ax = plt.subplots(figsize=(10, 8))
scatter = ax.scatter(x, y, c=colors, s=sizes, alpha=0.6, cmap='viridis')
plt.colorbar(scatter, ax=ax, label='Color Value')

# 添加趋势线
z = np.polyfit(x, y, 1)
p = np.poly1d(z)
ax.plot(sorted(x), p(sorted(x)), 'r--', linewidth=2, label=f'y={z[0]:.2f}x+{z[1]:.2f}')

ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_title('Scatter Plot with Trend Line')
ax.legend()
plt.tight_layout()
plt.savefig('scatter.png', dpi=150)

饼图

labels = ['工作', '睡觉', '学习', '娱乐', '运动', '其他']
sizes = [35, 30, 15, 10, 5, 5]
explode = (0.05, 0, 0.05, 0, 0, 0)  # 突出显示
colors = ['#ff9999', '#66b3ff', '#99ff99', '#ffcc99', '#ff66b3', '#c2c2f0']

fig, ax = plt.subplots(figsize=(8, 8))
wedges, texts, autotexts = ax.pie(
    sizes, explode=explode, labels=labels, colors=colors,
    autopct='%1.1f%%', startangle=90, pctdistance=0.85
)

# 设置字体
for text in texts + autotexts:
    text.set_fontsize(11)

ax.set_title('Daily Time Distribution')
plt.tight_layout()
plt.savefig('pie_chart.png', dpi=150)

子图布局

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

# 左上 - 折线图
axes[0, 0].plot(np.random.randn(50).cumsum())
axes[0, 0].set_title('Random Walk')

# 右上 - 直方图
axes[0, 1].hist(np.random.randn(1000), bins=30, color='steelblue', edgecolor='white')
axes[0, 1].set_title('Histogram')

# 左下 - 柱状图
axes[1, 0].barh(['A', 'B', 'C', 'D'], [4, 7, 1, 8], color='coral')
axes[1, 0].set_title('Horizontal Bar')

# 右下 - 箱线图
data = [np.random.randn(100) * s + m for s, m in [(1, 0), (1.5, 2), (0.5, -1)]]
axes[1, 1].boxplot(data, labels=['Group 1', 'Group 2', 'Group 3'])
axes[1, 1].set_title('Box Plot')

plt.suptitle('Multiple Chart Types', fontsize=14, fontweight='bold')
plt.tight_layout()
plt.savefig('subplots.png', dpi=150)

中文显示设置

import matplotlib
# 方案1:指定字体
matplotlib.rcParams['font.sans-serif'] = ['SimHei', 'DejaVu Sans']
matplotlib.rcParams['axes.unicode_minus'] = False

# 方案2:使用 FontProperties(更可靠)
from matplotlib.font_manager import FontProperties
font = FontProperties(fname='/usr/share/fonts/truetype/wqy/wqy-microhei.ttc')
ax.set_title('中文标题', fontproperties=font)

Linux 服务器如果缺少中文字体:

# Ubuntu/Debian
sudo apt install fonts-wqy-microhei
# 清除缓存
rm -rf ~/.cache/matplotlib

样式主题

# 查看可用样式
print(plt.style.available)

# 使用样式
plt.style.use('seaborn-v0_8')      # 清爽的 seaborn 风格
# plt.style.use('ggplot')           # R 语言 ggplot2 风格
# plt.style.use('dark_background')  # 暗色背景

也可以用 with 临时切换样式:

with plt.style.context('bmh'):
    fig, ax = plt.subplots()
    ax.plot([1, 2, 3], [1, 4, 2])
    plt.show()

小结

Matplotlib 的 API 设计虽然有些老旧,但胜在生态完善、文档丰富。掌握 Figure/Axes 模型后,复杂图表都是在这个基础上叠加元素。日常使用如果觉得 Matplotlib 太底层,可以用 Seaborn(统计图表)或 Plotly(交互式图表)作为更高层的封装。