1. 云雨图绘制实战:生物信息学数据可视化利器
在生物信息学数据分析中,我们经常需要同时展示数据的分布特征和统计指标。传统单一图表往往难以兼顾这两方面需求——箱线图虽能清晰呈现四分位数但丢失了分布细节,小提琴图虽能展示密度却弱化了关键统计值。云雨图(Raincloud Plot)的创新之处在于将三种图表元素有机整合:上方"云"部是小提琴图展示概率密度,中部"雨"部是箱线图标注统计量,底部"点"阵则是原始数据散点。这种组合图表由Allen等人于2019年在《Wellcome Open Research》期刊正式提出,现已成为生物医学论文中的标准可视化方案之一。
我在分析单细胞RNA测序数据时,发现云雨图特别适合展示不同细胞亚群的基因表达分布。比如比较免疫细胞中PD-1基因的表达情况时,传统的箱线图会掩盖表达量的双峰分布特征,而纯小提琴图又难以准确读取中位数等关键指标。云雨图完美解决了这个痛点,让审稿人一眼就能抓住数据的关键特征。下面我将分享基于Matplotlib的完整实现方案,这个方案已经过我们实验室多个项目的实战检验。
2. 环境准备与数据模拟
2.1 工具链配置建议
推荐使用Python 3.8+环境搭配以下库版本组合,这是经过我们团队验证的稳定配置:
bash复制matplotlib==3.7.1
numpy==1.24.3
seaborn==0.12.2 # 非必须但推荐用于颜色主题
重要提示:Matplotlib从3.6版本开始优化了violinplot的性能,建议至少使用3.6+版本以避免大数据集时的渲染问题。如果遇到中文显示异常,需要额外配置字体:
python复制plt.rcParams['font.sans-serif'] = ['Arial Unicode MS'] # Mac系统
# 或 plt.rcParams['font.sans-serif'] = ['SimHei'] # Windows系统
2.2 模拟生物数据生成
我们模拟三组不同处理条件下的基因表达量数据,这是生物信息学分析的典型场景:
python复制import numpy as np
np.random.seed(42) # 固定随机种子保证可重复性
# 模拟对照组(Control)、处理组A(TreatmentA)、处理组B(TreatmentB)
control = np.random.normal(loc=5, scale=1.2, size=200)
treat_a = np.random.gamma(shape=2, scale=1.5, size=180)
treat_b = np.concatenate([
np.random.normal(loc=3, scale=0.8, size=120),
np.random.normal(loc=7, scale=1, size=80)
])
data_groups = [control, treat_a, treat_b]
group_labels = ['Control', 'Treatment A', 'Treatment B']
这段数据模拟体现了生物数据常见特征:
- 对照组呈正态分布(许多生物学指标的自然状态)
- 处理组A呈现右偏态分布(类似药物剂量响应)
- 处理组B是双峰分布(可能暗示存在异质性亚群)
3. 核心绘图组件实现
3.1 小提琴图定制化开发
小提琴图是云雨图的上部组件,我们通过调整violinplot的关键参数来优化展示效果:
python复制fig, ax = plt.subplots(figsize=(10, 6))
# 绘制小提琴图主体
violins = ax.violinplot(
data_groups,
positions=range(len(data_groups)),
widths=0.7,
showmeans=False,
showmedians=False,
showextrema=False
)
# 自定义小提琴图颜色和透明度
for pc in violins['bodies']:
pc.set_facecolor('#1E88E5')
pc.set_edgecolor('black')
pc.set_alpha(0.7)
# 添加中位线标记
for i, data in enumerate(data_groups):
median = np.median(data)
ax.hlines(median, i-0.3, i+0.3, color='white', linewidth=2)
关键参数解析:
widths=0.7:控制小提琴的宽度,建议0.5-0.8之间showmeans=False:通常在中位数和均值间选择显示一种showextrema=False:关闭极值线避免与后续箱线图重复
3.2 箱线图精细化配置
箱线图位于小提琴图下方,采用半透明效果实现视觉分层:
python复制# 绘制箱线图
boxs = ax.boxplot(
data_groups,
positions=range(len(data_groups)),
widths=0.15,
patch_artist=True,
showfliers=False, # 不显示异常值点
medianprops={'color': 'red', 'linewidth': 2},
boxprops={'facecolor': 'white', 'alpha': 0.7, 'edgecolor': 'black'},
whiskerprops={'color': 'black', 'linestyle': '--'},
capprops={'color': 'black'}
)
# 添加均值标记
for i, data in enumerate(data_groups):
mean = np.mean(data)
ax.scatter(i, mean, color='green', s=100, marker='D', edgecolor='white')
科研绘图经验:
- 异常值点(
showfliers)通常关闭,避免与散点图重叠 - 中位数线(
medianprops)建议用醒目颜色突出 - 均值用菱形标记,与中位数形成视觉区分
3.3 散点图抖动(jitter)处理
原始数据点采用"雨滴"式分布,关键技术是jitter算法:
python复制for i, data in enumerate(data_groups):
# 在x轴方向添加随机扰动
x = np.random.normal(i, 0.05, size=len(data))
ax.scatter(
x, data,
color='black',
alpha=0.4,
s=30, # 点大小
edgecolors='white',
linewidths=0.5
)
专业技巧:调节
alpha参数(0.3-0.5)可优化重叠点的可视化效果。对于超过500个数据点的情况,建议减小点大小(s)到10-15。
4. 科研级图表美化
4.1 坐标轴与图例优化
发表级图表需要专业的坐标轴设置:
python复制ax.set_xticks(range(len(data_groups)))
ax.set_xticklabels(group_labels, fontsize=12)
ax.set_ylabel('Expression Level (log2CPM)', fontsize=12)
ax.set_title('Gene Expression Distribution Across Conditions', pad=20)
# 添加图例
from matplotlib.lines import Line2D
legend_elements = [
Line2D([0], [0], color='red', lw=2, label='Median'),
Line2D([0], [0], marker='D', color='w', label='Mean',
markerfacecolor='green', markersize=10)
]
ax.legend(handles=legend_elements, loc='upper right')
# 设置科学坐标轴
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.grid(axis='y', linestyle='--', alpha=0.3)
4.2 多组比较的统计学标注
添加统计学显著性标记是发表论文的刚需:
python复制from scipy import stats
# 计算组间p值(示例用Mann-Whitney U检验)
p_CA = stats.mannwhitneyu(control, treat_a).pvalue
p_CB = stats.mannwhitneyu(control, treat_b).pvalue
p_AB = stats.mannwhitneyu(treat_a, treat_b).pvalue
# 添加显著性标记
def add_significance_bar(x1, x2, y, p):
ax.plot([x1, x1, x2, x2], [y, y+0.5, y+0.5, y], lw=1, color='black')
star = '*' if p < 0.001 else '**' if p < 0.0001 else '***'
ax.text((x1+x2)*0.5, y+0.6, star, ha='center', va='bottom')
add_significance_bar(0, 1, 12, p_CA)
add_significance_bar(1, 2, 13, p_AB)
add_significance_bar(0, 2, 14, p_CB)
5. 实战问题排查指南
5.1 常见渲染问题解决
-
小提琴图显示异常:
- 现象:出现不规则多边形而非平滑曲线
- 排查:检查数据是否包含NaN值,使用
np.isnan(data).sum()验证 - 修复:添加
data = data[~np.isnan(data)]过滤
-
散点重叠严重:
- 现象:数据点堆积成黑块
- 解决方案A:调整抖动幅度
np.random.normal(i, 0.08) - 解决方案B:改用蜂群图
sns.swarmplot(需安装seaborn)
-
PDF输出模糊:
python复制plt.savefig('raincloud.pdf', dpi=300, bbox_inches='tight', facecolor='white', edgecolor='none')
5.2 大数据集优化技巧
当处理单细胞测序等大数据量时(>10,000点):
- 散点图改用
alpha=0.05和s=2 - 启用快速渲染模式:
python复制plt.rcParams['path.simplify'] = True plt.rcParams['path.simplify_threshold'] = 1.0 - 考虑抽样显示:
python复制display_data = [np.random.choice(d, 2000) for d in data_groups]
6. 高级定制扩展
6.1 横向云雨图实现
适用于类别较多的场景(如时间序列):
python复制fig, ax = plt.subplots(figsize=(12, 6))
# 将x/y轴调换,其他参数保持不变
violins = ax.violinplot(
data_groups,
positions=range(len(data_groups)),
vert=False, # 关键参数
widths=0.7
)
# 对应调整散点的jitter方向
for i, data in enumerate(data_groups):
y = np.random.normal(i, 0.05, size=len(data))
ax.scatter(data, y, alpha=0.4)
6.2 分面云雨图
比较多个基因的表达分布:
python复制genes = ['GeneA', 'GeneB', 'GeneC']
fig, axes = plt.subplots(1, 3, figsize=(18, 5))
for ax, gene in zip(axes, genes):
# 每组基因模拟不同数据
data = simulate_gene_expression(gene)
plot_raincloud(ax, data) # 封装前面的绘图逻辑
ax.set_title(gene)
我在实际使用中发现,云雨图特别适合展示CRISPR筛选结果或药物敏感性数据。通过调整颜色方案(如用渐变色表示处理浓度),可以让图表传达更多维度信息。一个实用的技巧是保存绘图配置为模板函数,这样后续分析只需调用plot_raincloud(ax, data)即可快速生成统一风格的图表。
