单细胞 RNA-seq 质量控制:应用场景

单细胞 RNA-seq 质量控制:应用场景

Written By
技能练习生
技能练习生

从真实科研场景中学习 QC 策略

本章通过四个不同领域的完整案例,展示单细胞 QC 在实际研究中的应用。每个案例都来自已发表的研究,我们简化了步骤但保留了核心思路。

案例 1:PBMC 免疫细胞分析

研究背景

某团队研究 COVID-19 患者的免疫反应,分析了 20 个样本(健康对照组 vs 重症患者),总计 15 万个 PBMC 细胞。

数据特点

  • 10X Genomics v3 化学
  • 人类数据
  • 批次效应明显(不同日期制备)

QC 策略

import scanpy as sc
import numpy as np

# 加载数据
adata = sc.read_h5ad('covid_pbmc_raw.h5ad')

# 1. 识别线粒体基因
adata.var['mt'] = adata.var_names.str.startswith('MT-')

# 2. 计算 QC 指标
sc.pp.calculate_qc_metrics(adata, qc_vars=['mt'], inplace=True)

# 3. 检查数据分布
print("=== 数据概览 ===")
print(adata.obs[['n_genes_by_counts', 'n_counts', 'pct_counts_mt']].describe())

# 4. 可视化
import matplotlib.pyplot as plt
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
sc.pl.violin(adata, ['n_genes_by_counts'], groupby=None, ax=axes[0], show=False)
sc.pl.violin(adata, ['n_counts'], groupby=None, ax=axes[1], show=False)
sc.pl.violin(adata, ['pct_counts_mt'], groupby=None, ax=axes[2], show=False)
plt.tight_layout()
plt.show()

# 5. MAD 过滤(使用 3 MAD)
def mad_filter(data, n_mads, direction='both'):
    median = np.median(data)
    mad = np.median(np.abs(data - median))
    if direction == 'lower':
        return data > (median - n_mads * mad)
    elif direction == 'upper':
        return data < (median + n_mads * mad)
    else:
        lower = median - n_mads * mad
        upper = median + n_mads * mad
        return (data > lower) & (data < upper)

filter_mask = (
    mad_filter(adata.obs['n_genes_by_counts'], 3, 'lower') &
    mad_filter(adata.obs['n_genes_by_counts'], 3, 'upper') &
    mad_filter(adata.obs['n_counts'], 3, 'lower') &
    mad_filter(adata.obs['n_counts'], 3, 'upper') &
    mad_filter(adata.obs['pct_counts_mt'], 3, 'upper')
)

# 6. 检查不同样本的保留率
adata.obs['filter_pass'] = filter_mask
for sample in adata.obs['sample_id'].unique():
    sample_cells = adata.obs['sample_id'] == sample
    retention = (filter_mask[sample_cells].sum() / sample_cells.sum() * 100)
    print(f"{sample}: {retention:.1f}% 保留")

# 7. 应用过滤
adata_qc = adata[filter_mask].copy()
sc.pp.filter_genes(adata_qc, min_cells=10)

print(f"\n最终结果:")
print(f"细胞:{adata.n_obs}{adata_qc.n_obs} ({adata_qc.n_obs/adata.n_obs*100:.1f}%)")
print(f"基因:{adata.n_vars}{adata_qc.n_vars}")

# 8. 保存
adata_qc.write('pbmc_covid_qc.h5ad')

关键决策

  • MAD 倍数:使用 3(保守过滤)
  • 样本特异性检查:确保没有单个样本被过度过滤
  • 保留率目标:> 80%

结果

  • 平均保留率:87.3%
  • 检测到预期的免疫细胞群(T 细胞、B 细胞、单核细胞等)
  • 重症患者的炎症单核细胞亚群得以保留

案例 2:小鼠心脏发育图谱

研究背景

构建小鼠心脏发育的单细胞 atlas,涵盖胚胎期 E10.5 到成年期。

数据特点

  • 小鼠数据
  • 多个发育阶段
  • 心肌细胞线粒体含量天生较高

QC 策略(调整线粒体阈值)

import scanpy as sc
import numpy as np

# 加载数据
adata = sc.read_h5ad('mouse_heart_raw.h5ad')

# 1. 识别线粒体基因(小鼠用小写 mt-)
adata.var['mt'] = adata.var_names.str.startswith('mt-')

# 2. 计算 QC 指标
sc.pp.calculate_qc_metrics(adata, qc_vars=['mt'], inplace=True)

# 3. 检查线粒体分布
print("=== 线粒体比例分布 ===")
print(adata.obs['pct_counts_mt'].describe())
# 预期:中位数在 15-25%

# 4. 按发育阶段检查
for stage in ['E10.5', 'E14.5', 'P0', 'Adult']:
    stage_data = adata[adata.obs['stage'] == stage]
    print(f"\n{stage}:")
    print(f"  中位数:{stage_data.obs['pct_counts_mt'].median():.1f}%")
    print(f"  75 分位:{stage_data.obs['pct_counts_mt'].quantile(0.75):.1f}%")

# 5. 使用更高的 MAD 倍数(5 MAD)来适应高线粒体含量
def mad_filter(data, n_mads, direction='both'):
    median = np.median(data)
    mad = np.median(np.abs(data - median))
    if direction == 'upper':
        return data < (median + n_mads * mad)
    # ...

# 关键:线粒体使用 5 MAD,其他指标仍用 3 MAD
filter_mask = (
    mad_filter(adata.obs['n_genes_by_counts'], 3, 'lower') &
    mad_filter(adata.obs['n_genes_by_counts'], 3, 'upper') &
    mad_filter(adata.obs['n_counts'], 3, 'lower') &
    mad_filter(adata.obs['n_counts'], 3, 'upper') &
    mad_filter(adata.obs['pct_counts_mt'], 5, 'upper')  # 5 MAD!
)

# 6. 验证心肌细胞保留情况
# 心肌细胞标志物:TnnT3, Tnni3, Myh6/7
cardiomyocyte_markers = ['TnnT3', 'Tnni3', 'Myh6']
for marker in cardiomyocyte_markers:
    if marker in adata.var_names:
        cm_cells = adata[:, marker].X > 0
        cm_retention = (filter_mask[cm_cells].sum() / cm_cells.sum() * 100)
        print(f"{marker}+ 细胞保留率:{cm_retention:.1f}%")

# 7. 应用过滤
adata_qc = adata[filter_mask].copy()
sc.pp.filter_genes(adata_qc, min_cells=10)

print(f"\n最终结果:")
print(f"细胞:{adata.n_obs}{adata_qc.n_obs} ({adata_qc.n_obs/adata.n_obs*100:.1f}%)")
print(f"基因:{adata.n_vars}{adata_qc.n_vars}")

# 8. 保存
adata_qc.write('mouse_heart_qc.h5ad')

关键决策

  • 线粒体阈值:使用 5 MAD(而非 3)
  • 原因:心肌细胞正常线粒体含量 15-25%
  • 验证:检查心肌细胞标志物表达,确保未过度过滤

结果

  • 保留率:91.5%
  • 心肌细胞亚群(心房、心室)完整保留
  • 发育轨迹清晰可见

案例 3:肿瘤微环境分析

研究背景

分析结直肠癌肿瘤微环境,寻找与免疫治疗响应相关的细胞亚群。

数据特点

  • 肿瘤组织 + 配对正常组织
  • 高度异质性
  • 可能含有循环肿瘤细胞(CTC)

QC 策略(极宽松过滤)

import scanpy as sc
import numpy as np

# 加载数据
adata = sc.read_h5ad('crc_tumor_raw.h5ad')

# 1. 识别线粒体和血红蛋白基因
adata.var['mt'] = adata.var_names.str.startswith('MT-')
adata.var['hb'] = adata.var_names.str.startswith(('HBA', 'HBB', 'HBD'))

# 2. 计算 QC 指标
sc.pp.calculate_qc_metrics(adata, qc_vars=['mt', 'hb'], inplace=True)

# 3. 极宽松的过滤策略
# 肿瘤数据:宁可保留噪音,不丢失稀有细胞
N_MADS = 5  # 使用 5 MAD

def mad_filter(data, n_mads, direction='both'):
    median = np.median(data)
    mad = np.median(np.abs(data - median))
    if direction == 'lower':
        return data > (median - n_mads * mad)
    elif direction == 'upper':
        return data < (median + n_mads * mad)
    else:
        lower = median - n_mads * mad
        upper = median + n_mads * mad
        return (data > lower) & (data < upper)

# 仅过滤极端异常值
filter_mask = (
    mad_filter(adata.obs['n_genes_by_counts'], N_MADS, 'lower') &
    mad_filter(adata.obs['n_genes_by_counts'], N_MADS, 'upper') &
    mad_filter(adata.obs['n_counts'], N_MADS, 'lower') &
    mad_filter(adata.obs['n_counts'], N_MADS, 'upper') &
    mad_filter(adata.obs['pct_counts_mt'], N_MADS, 'upper')
)

# 4. 不过滤低转录量细胞(可能是静息或 CTC)
print("不过滤低 UMI 细胞,以保留稀有细胞群")

# 5. 检查肿瘤 vs 正常组织的保留率
for tissue in ['Tumor', 'Normal']:
    tissue_cells = adata.obs['tissue'] == tissue
    retention = (filter_mask[tissue_cells].sum() / tissue_cells.sum() * 100)
    print(f"{tissue}: {retention:.1f}% 保留")

# 6. 应用过滤
adata_qc = adata[filter_mask].copy()

# 7. 基因过滤也使用宽松阈值
sc.pp.filter_genes(adata_qc, min_cells=5)  # 而非 10

print(f"\n最终结果:")
print(f"细胞:{adata.n_obs}{adata_qc.n_obs} ({adata_qc.n_obs/adata.n_obs*100:.1f}%)")
print(f"基因:{adata.n_vars}{adata_qc.n_vars}")

# 8. 后续:使用专门工具检测恶性细胞
# 例如 inferCNV、CopyKAT(在下游分析中进行)

# 9. 保存
adata_qc.write('crc_tumor_qc.h5ad')

关键决策

  • MAD 倍数:5(极宽松)
  • 不过滤:低 UMI 细胞(可能是 CTC)
  • 基因过滤:min_cells = 5(而非 10)
  • 原因:肿瘤高度异质,稀有亚群可能是关键发现

结果

  • 保留率:94.2%
  • 后续分析发现了罕见的免疫抑制性髓细胞亚群
  • 与免疫治疗响应显著相关

案例 4:斑马鱼胚胎发育

研究背景

研究斑马鱼早期胚胎发育(受精后 2-24 小时)。

数据特点

  • 非模式生物(斑马鱼)
  • 胚胎细胞转录活性低
  • 基因注释可能不完整

QC 策略(非模式生物适配)

import scanpy as sc
import numpy as np

# 加载数据
adata = sc.read_h5ad('zebrafish_embryo_raw.h5ad')

# 1. 识别线粒体基因(斑马鱼可能有 mt- 或 MT-)
adata.var['mt'] = (
    adata.var_names.str.startswith('mt-') |
    adata.var_names.str.startswith('MT-')
)

# 验证
print(f"检测到 {adata.var['mt'].sum()} 个线粒体基因")
if adata.var['mt'].sum() == 0:
    print("警告:未检测到线粒体基因,检查基因命名规则")

# 2. 计算 QC 指标
sc.pp.calculate_qc_metrics(adata, qc_vars=['mt'], inplace=True)

# 3. 检查数据质量
print("=== QC 指标统计 ===")
print(adata.obs[['n_genes_by_counts', 'n_counts', 'pct_counts_mt']].describe())

# 胚胎细胞特点:
# - n_genes 较低(分化程度低)
# - n_counts 较低(转录活性低)
# - 线粒体比例可能较高(快速分裂)

# 4. 可视化分布
import matplotlib.pyplot as plt
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
sc.pl.violin(adata, ['n_genes_by_counts'], groupby=None, ax=axes[0], show=False)
sc.pl.violin(adata, ['n_counts'], groupby=None, ax=axes[1], show=False)
sc.pl.violin(adata, ['pct_counts_mt'], groupby=None, ax=axes[2], show=False)
axes[0].set_title('Genes (embryo: lower expected)')
axes[1].set_title('UMI counts (embryo: lower expected)')
axes[2].set_title('MT% (embryo: may be higher)')
plt.tight_layout()
plt.show()

# 5. 极宽松过滤(胚胎细胞)
N_MADS = 5  # 使用 5 MAD

def mad_filter(data, n_mads, direction='both'):
    median = np.median(data)
    mad = np.median(np.abs(data - median))
    if direction == 'lower':
        return data > (median - n_mads * mad)
    elif direction == 'upper':
        return data < (median + n_mads * mad)
    else:
        lower = median - n_mads * mad
        upper = median + n_mads * mad
        return (data > lower) & (data < upper)

filter_mask = (
    mad_filter(adata.obs['n_genes_by_counts'], N_MADS, 'lower') &
    mad_filter(adata.obs['n_genes_by_counts'], N_MADS, 'upper') &
    mad_filter(adata.obs['n_counts'], N_MADS, 'lower') &
    mad_filter(adata.obs['n_counts'], N_MADS, 'upper') &
    mad_filter(adata.obs['pct_counts_mt'], N_MADS, 'upper')
)

# 6. 按发育时间点检查
for hpf in ['2hpf', '6hpf', '12hpf', '24hpf']:
    timepoint_cells = adata.obs['timepoint'] == hpf
    retention = (filter_mask[timepoint_cells].sum() / timepoint_cells.sum() * 100)
    print(f"{hpf}: {retention:.1f}% 保留")

# 7. 应用过滤
adata_qc = adata[filter_mask].copy()

# 8. 宽松的基因过滤
sc.pp.filter_genes(adata_qc, min_cells=5)  # 胚胎可能用更少

print(f"\n最终结果:")
print(f"细胞:{adata.n_obs}{adata_qc.n_obs} ({adata_qc.n_obs/adata.n_obs*100:.1f}%)")
print(f"基因:{adata.n_vars}{adata_qc.n_vars}")

# 9. 保存
adata_qc.write('zebrafish_embryo_qc.h5ad')

关键决策

  • 线粒体基因识别:同时检查 mt- 和 MT- 前缀
  • 预期调整:胚胎细胞的 n_genes 和 n_counts 天然较低
  • MAD 倍数:5(极宽松)
  • 基因过滤:min_cells = 5(胚胎表达谱更局限)

结果

  • 保留率:92.8%
  • 保留了稀有祖细胞群
  • 发育轨迹清晰

案例总结:QC 决策树

开始

识别数据类型

┌─────────────────────────────────────┐
│ 免疫细胞(PBMC)                    │ → 标准 MAD (3)
│  - 人类: MT-                        │
│  - 小鼠: mt-                        │
│  - 预期 mt%: 3-8%                   │
└─────────────────────────────────────┘

┌─────────────────────────────────────┐
│ 高能量组织(心脏、肌肉)            │ → 提高 mt% 阈值 (5 MAD)
│  - 预期 mt%: 15-25%                 │
│  - 验证:检查组织特异标志物         │
└─────────────────────────────────────┘

┌─────────────────────────────────────┐
│ 肿瘤样本                            │ → 极宽松 (5 MAD)
│  - 高度异质性                       │
│  - 稀有亚群可能关键                 │
│  - 不过滤低 UMI 细胞                │
└─────────────────────────────────────┘

┌─────────────────────────────────────┐
│ 胚胎/干细胞                         │ → 极宽松 (5 MAD)
│  - 转录活性低                       │
│  - 动态表达                         │
│  - n_genes 天然较低                 │
└─────────────────────────────────────┘

┌─────────────────────────────────────┐
│ 非模式生物                          │ → 检查基因命名
│  - 验证线粒体基因前缀               │
│  - 可能需要自定义基因列表           │
└─────────────────────────────────────┘

验证过滤结果
  - 检查保留率(> 70%)
  - 验证已知细胞群保留
  - 检查不同批次/样本的保留率

保存过滤后的数据

最佳实践清单

QC 前检查

  • 确认物种和基因命名规则
  • 检查数据类型(单细胞 vs 空间)
  • 了解组织特异性质(线粒体含量、转录活性)

QC 过程

  • 可视化 QC 指标分布
  • 根据组织类型调整 MAD 倍数
  • 检查不同批次/样本的保留率一致性
  • 验证关键细胞群未被过度过滤

QC 后验证

  • 重新可视化 QC 指标(确认改善)
  • 检查保留率是否合理(> 70%)
  • 快速聚类检查(确保无明显技术伪影)
  • 记录使用的所有参数和阈值

下一章

通过本章的案例学习,你已经掌握了不同场景下的 QC 策略。在第五章中,我们将深入探讨 QC 的理论依据和工具原理,帮助你从根本上理解这些方法。

→ 继续阅读:第五章 - 最佳实践与工具原理