Matplotlib 基础:折线图与散点图
做差异表达分析的时候,我最早遇到的坑就是画图工具没选好。那时候还在用 Excel 出图,每次换数据都得重新调格式,烦得要命。后来狠狠心学了 Matplotlib,才发现——原来图表是可以「编程」的。
这一章咱们就把 Matplotlib 最核心的两个图讲透:折线图(line plot)和散点图(scatter plot)。同时把 figure、axes、plot、scatter 这几个概念彻底搞明白。这两个图别看简单,在差异表达分析里用处特别大——比如画 MA 图、火山图、表达趋势图,底层全是它们。
1. 核心概念:Figure 与 Axes
我第一次用 Matplotlib 的时候,就被 plt.figure 和 plt.subplot 搞晕了。后来发现,只要记住一句话就行:Figure 是画板,Axes 是画纸。
一张 Figure 可以放多张 Axes(也就是多个子图)。每个 Axes 有自己的坐标轴、标题、图例。而咱们平时用的 plt.plot() 其实是在「当前 Axes」上画图。
plt.figure()—— 创建一个空画板fig.add_subplot()或plt.subplots()—— 在画板上放一张或几张画纸ax.plot()、ax.scatter()—— 在画纸上画画
我个人的习惯是:明确用 fig, ax = plt.subplots() 拿到子图对象,而不是用 plt.gca()。这样后续操作清晰,不容易搞混。
| 概念 | 类比 | 常用创建方式 |
|---|---|---|
Figure | 画板(可包含多张画纸) | plt.figure() 或 plt.subplots() 返回的第一个对象 |
Axes | 画纸(真正绘图区域) | fig.add_subplot() 或 plt.subplots() 返回的第二个对象 |
Axis | 坐标轴(x、y) | Axes 内部自动管理,通常不需要手动创建 |
plt.plot() 这种「pyplot 接口」。但生产级代码、课程作业、论文图,我建议一律用「面向对象接口」——也就是显式拿到 fig 和 ax 再操作。后期调布局、加子图会方便得多。
2. 折线图:用 plot() 画趋势
折线图在差异表达分析里最常见的场景:基因表达量随时间/条件的变化趋势。核心函数就是 ax.plot(x, y)。
import matplotlib.pyplot as plt
import numpy as np
x = np.array([0, 1, 2, 3, 4])
y = np.array([2.1, 3.5, 4.2, 3.8, 5.0])
fig, ax = plt.subplots(figsize=(6, 4))
ax.plot(x, y, label='Gene A', color='#3498db', linestyle='-', marker='o')
ax.set_xlabel('Time (h)')
ax.set_ylabel('Expression Level')
ax.set_title('Gene A Expression Over Time')
ax.legend()
plt.show()
这里有几个参数我当年经常搞反:
linestyle:线型,'-'实线,'--'虚线,':'点线。我用'--'画对照组,'-'画处理组,一目了然。marker:数据点标记样式。'o'圆点,'s'方形,'^'三角形。我习惯用'o'表示实际测量值,用'.'表示预测值。color:可以直接写名字,但我更推荐用十六进制色号,Fine-control。比如'#3498db'是 Matplotlib 默认蓝。
plot() 不会报错!它会自动把长的截断匹配短的,最后画出来的曲线完全不对。所以我每次画图前都会加一句 assert len(x) == len(y) 做检查。
折线图还可以叠加多条线:
y2 = np.array([1.8, 3.0, 3.9, 4.5, 4.8])
ax.plot(x, y2, label='Gene B', color='#e74c3c', linestyle='--', marker='s')
ax.legend()
这样就能在一个 Axes 里对比两种基因的表达趋势了。注意 legend 的位置,我一般用 loc='best' 让 Matplotlib 自己选,或者直接用 loc='upper left' 固定。
3. 散点图:用 scatter() 看分布
散点图在差异表达分析里更是主角——火山图、MA 图全都是散点图。核心函数 ax.scatter(x, y, s=..., c=..., alpha=...)。
np.random.seed(42)
n = 200
logFC = np.random.randn(n) * 2
pval = -np.log10(np.random.uniform(0, 1, n))
fig, ax = plt.subplots(figsize=(6, 5))
ax.scatter(logFC, pval, s=20, c='#2ecc71', alpha=0.6)
ax.set_xlabel('log2 Fold Change')
ax.set_ylabel('-log10(p-value)')
ax.set_title('Volcano Plot (Demo)')
plt.show()
参数里 s 控制点的大小(面积),c 控制颜色,alpha 控制透明度。如果数据点很多,一定要加 alpha,否则点会堆在一起看不清。
你想想看,差异表达分析里经常要区分「显著上调 / 显著下调 / 不显著」。这时候可以用不同颜色映射:
colors = ['#e74c3c' if lfc > 1 and p > 1.3 else
'#3498db' if lfc < -1 and p > 1.3 else
'gray']
ax.scatter(logFC, pval, c=colors, s=15, alpha=0.7)
这里 p > 1.3 对应原始 p-value < 0.05。我习惯先做条件筛选,生成颜色列表,再一次性 scatter,这样代码好维护。
| 特性 | plot() | scatter() |
|---|---|---|
| 核心用途 | 画趋势(连线) | 画分布(每个点独立) |
| 数据量 | 适合几十到几百个点 | 适合几百到几十万个点 |
| 每个点的颜色/大小 | 所有点统一 | 可以不同(通过 c、s 传入数组) |
| 性能 | 较快 | 大量点时较慢,可以考虑用 plot(marker='o', linestyle='') 作为轻量散点图 |
| 典型差异表达图 | 表达趋势图 | 火山图、MA 图 |
ax.plot(x, y, 'o') 可以画出无连线的散点,而且当数据量很大(比如 > 10 万)时,plot 比 scatter 快很多。缺点是每个点只能统一颜色和大小。我一般数万点以内的散点图用 scatter,再大就用 plot 加 marker 代替。
4. Figure 与 Axes 的高级管理:多子图
在实际项目中,我们很少只画一张图。比如差异表达分析报告里,常常要同时展示「基因 A 表达趋势」和「火山图」——这就需要多子图布局。
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4))
# 左子图:折线图
ax1.plot(x, y, 'o-', label='Gene A')
ax1.set_xlabel('Time')
ax1.set_ylabel('Expression')
ax1.legend()
ax1.set_title('Expression Trend')
# 右子图:散点图(火山图)
ax2.scatter(logFC, pval, alpha=0.5, s=10)
ax2.set_xlabel('log2FC')
ax2.set_ylabel('-log10(pval)')
ax2.set_title('Volcano Plot')
plt.tight_layout() # 自动调整间距
plt.show()
这里 plt.subplots(1, 2) 创建了 1 行 2 列的子图网格,返回的 ax1 和 ax2 就是两个 Axes 对象。后续操作分别对着各自 ax 做就行。
- 每个 Axes 只放一种类型的图(折线图或散点图),避免混在一起。
- 所有子图共享相同的 x 轴范围(通过
ax1.set_xlim统一设置),便于比较。 - 如果多行多列,可以使用
plt.subplots(2, 3)返回二维数组,用ax[row][col]索引。
5. 知识体系总览
下面这张图总结了本章的核心脉络。你可以看到 Figure 如何容纳多个 Axes,每个 Axes 如何承载不同图类型,以及最终如何绘制出差异表达分析中的常见图表。
6. 避坑锦囊
- 颜色统一:我建议提前定义一个颜色字典,比如
colors = {'up':'#e74c3c', 'down':'#3498db', 'ns':'gray'},全篇都用它,不要让每张图独立写颜色。 - 字体大小:论文或报告图中的字不要用默认大小。我通常设
plt.rcParams.update({'font.size': 12})全局统一。 - 保存清晰图:用
fig.savefig('output.png', dpi=300, bbox_inches='tight'),bbox_inches='tight'能避免空白边。 - 先在 Jupyter Notebook 里试,再出最终图:我喜欢在 Notebook 里用
%matplotlib inline反复调参,满意了再保存。
掌握好 Figure、Axes、plot、scatter 这四大概念,你就拿到了 Matplotlib 绘图的钥匙。后面几章咱们会基于这一章的基础,去绘制差异表达分析里更专业的图——比如带标签的火山图、带误差线的折线图。别急,先把这两类图练熟。