# 前言：我与《单细胞交响乐》的缘分

## 写在前面

Hi 大家好，我是[生信星球](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-09-035101.png)的”豆豆“，也是[~~简书~~](https://www.jianshu.com/u/d7b77c171c15)上的”刘小泽“(由于简书平台封锁了太多文章，目前更新均在[博客](https://www.jieandze1314.com/))。\
这是我的第一本开源书，之后也许会有第二本、第三本...持续学习，不断努力:cowboy:&#x20;

**本书链接会持续更新( updated on 2021-11-01)：**<https://jieandze1314.osca.top/>

**关于引用：**&#x59;unzeLiu. (2020). Single Cell Symphony (Version 2.0). Zenodo. <https://doi.org/10.5281/zenodo.4044753>

## 吸引

我是在 2019年10月注意到： [Orchestrating Single-Cell Analysis with Bioconductor](https://osca.bioconductor.org/)

原因很简单，当时个人对单细胞很感兴趣，并且这本书还是Bioconductor团队。大佬出品，必是精品。\
为了增加辨识度，我给这本书取了个名字：**单细胞交响乐**。因为单细胞数据涉及了许多复杂数据结构和流程，就像一个大乐团中的各种乐器，相互配合环环相扣才能演奏美妙的乐章。

开始也是抱着试学的态度，从最简单的基础学起。后来数据结构那一章真正吸引了我，将复杂的单细胞数据结构解释得浅显易懂，还在2019.10.27写了一篇推送：[送你个对象](https://mp.weixin.qq.com/s/-zvslOg39KGodaxJcQJYaQ)。

![](https://1260378310-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MCv4XZpjUYDdSrsYVkw%2F-MCvVyV7HilEssxjj5_z%2F-MCvW986M2uulQlqS3cG%2Fimage.png?alt=media\&token=b428ed86-a814-4568-a154-001c51ffe6d3)

## 中断

我想大部分人接触单细胞数据，应该都是借助[CellRanger](https://support.10xgenomics.com/single-cell-gene-expression/software/pipelines/latest/what-is-cell-ranger)、[Seurat](https://satijalab.org/seurat/)流程学习的，也因此容易形成一个思维定势，说到scRNA分析，就想到Seurat这个R包。这本书的内容不太一样，它全篇都是基于自己的数据结构SingleCellExperiment，使用的R包也主要是自己团队开发的[Scater](http://bioconductor.org/packages/release/bioc/vignettes/scater/inst/doc/overview.html)、[Scran](https://bioconductor.org/packages/release/bioc/vignettes/scran/inst/doc/scran.html)包，并且用法和Seurat差距还蛮大的。

单细胞分析对于我来说一直只是一个爱好，暂时还没有实际项目的应用。有时心里就犯嘀咕：我为什么要花时间去学习两个用途差不多的包呢？用最流行的流程不就得了？

慢慢地，这本书在我心里的地位就下降了，后来也不再有热情去翻译、理解这本书了。

## 不舍

在放弃更新的一段时间，心中时常会有不舍和愧疚，开了个好头做出成了”烂尾“。

每次看到别人分享这本书，我都会偷偷打开之前的交响乐文件夹，看看里面的几篇笔记，然后叹一口气，再次关上，心想：等我有时间了，一定要补上后面的！

然而，这一等，就是大半年...

## 重拾

> 2020是一个注定特殊的年份\
> 万万没想到，从6.23到7.22，经历了一个月，完成了这个长久的小心愿

2020年的6月，无意间又打开了这本书的链接，看着团队已经更新到了30多章，此时”花花“（也就是域名jieandze1314中的jie）也开始了单细胞的学习，我答应她要提供一份学习资料。正好借着公众号推出的专辑功能，我尝试每写一篇都发表然后放在[公众号专辑](https://mp.weixin.qq.com/mp/appmsgalbum?action=getalbum&__biz=MzU4NjU4ODQ2MQ==\&scene=1\&album_id=1402375646780817409#wechat_redirect)中，当做对自己的一个提醒。

第一篇发表于2020-06-23，接下来的几天，我都保持了每天阅读一章、重复一遍、更新一篇的节奏。当然，开始的速度会很慢，但随着对整体数据结构以及R包、函数的了解越来越深入，我也能明白作者想要做什么事，又是如何去实现的，后来速度就快了许多。这里必须要感谢Bioconductor团队精心准备了各种测试数据集和详细的操作代码。

为了提供更好的阅读、交流体验，我将之前的学习笔记做成了现在这本开源书。个人认为，开源书比出版书的优势就是：持续更新；方便沟通；易于更改。

与原著作者交流了一下，他们表示还会继续更新，当然我也会一直保持同步。\
此外，我希望这本书不仅仅是中文翻译版，还希望能加入更多其他比较重要的知识，比如Seurat的各种操作、上游操作流程等等，让本书内容更加丰富。

## 更新

**2020-07-23**

* 新增：R包链接直达说明文档，文档链接直达腾讯文档，文献链接直达PubMed或原文中所在位置

**2020-08-21**

* 新增：第5章CellRanger的操作、Seurat、Monocle流程、多个数据集的整合

**2020-10-12**

* 新增：对单细胞领域文献阅读进行重新组织架构，方便阅读

**2021-10-20**

* 新增：最近会新增一些文献速览，抓住时事新闻

## 致谢

> 为了表达对作者的敬意，在本书上线前，特意征求了作者的同意\
> 感谢他们对Bioconductor社区的贡献

* ***Aaron Lun, PhD***
* ***Robert Amezquita, PhD***
* ***Stephanie Hicks, PhD***
* ***Raphael Gottardo, PhD***

**技术一直进步，学习从未停止。这本书也许不会有完结篇，只有\~未完待续**:nerd: \~


# 1 准备篇：背景知识

万事开头难，背景知识补充是必须的。


# 1.1 数据结构

刘小泽写于2019-10-26、27，更新于2020-06-23

> 分析单细胞数据，常见的一个名称就是`SingleCellExperiment`或者`sce`  \
> 那么这次就来认识一下这个基础知识点

## 前言

我们首先要对单细胞分析的流程有一个大概的认识：

### **上半场分析：**

这个差不多属于固定的流程了

* 首先是数据导入
* 紧接着进行质量控制和归一化，这个步骤包含了矫正实验因素以及其他影响因素，目的是从raw count得到clean count
* 有了clean count，就要挑取导致表达量差异的基因们（用于后面的降维） 因为降维的过程其实就是去繁存简，每个基因的变化都对整体有影响，对细胞来讲都是一个变化维度。但这么多维度我们看不了也处理不了，需要尽可能保证真实差异的前提下减少维度的数量，因此需要挑出那些更能代表整体差异的基因
* 降维

![](https://1260378310-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MCv4XZpjUYDdSrsYVkw%2F-MCvmjNucL9XrVg8h0Ng%2F-MCvnuUDDdWc9eNeCarp%2Fimage.png?alt=media\&token=5bfb3031-9fb0-42c8-8609-2dd0b17788bb)

### **下半场分析：**

这个就可以分出很多分支，例如

* 分群（clustering）：意在探索如何把scRNA数据集给拆分掉，变成一小块一小块的数据，这每一小块基因变化都是相似的
* 差异分析（differential expression）：不同组的细胞之间表达量差异是如何产生的
* 数据集的整合(Integrating datasets)：scRNA数据集越来越多，数据集之间的比较也日益显著
* 处理大型数据（large scale data）：这部分仅仅依靠内存存储的数据是不够的，还有更好的办法
* 还有：轨迹推断、细胞周期推断等等特定需求

> 知道了大体的流程，那么就先看看必须了解的数据结构

这是单细胞分析中的非常常用的S4对象，里面包罗万象，但依然有据可循。**那么它是如何组织的？存储了什么内容？**&#x8FD9;就是我们这次要探索的任务。

## 首先要上一张图

> 这张图会不断反复查看

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-23-131252.png)

图中最核心的部分，是蓝色的data部分；另外还有绿色的基因注释信息feature metadata、橙色的细胞注释信息cell metadata。除了这三大件，还会包含一些下游分析结果，比如PCA、tSNE降维结果就会保存在紫色的Dimension Reductions

这个`SingleCellExperiment`对象来自`SingleCellExperiment`R包，现在Bioconductor上的70多个单细胞相关的R包都使用了这个对象，**可以说是单细胞领域的通用货币**，包含了： gene-by-cell expression data、 per-cell metadata 、 per-gene annotation

> **开始发挥想象力了哈！**

如果把这个`SingleCellExperiment`对象比作一艘货船，上面就会装载许多集装箱`slots（理解为对象结构）` 。每个箱子`slot`都是独立的，箱子里面包含的东西不一样，比如有的装食物，有的装砖头，各有各的特征。就`sce`来说，有的接口必须是数值型矩阵结构，有的就需要数据框结构。

## 核心部分-`assays`

**创建一个`sce`对象很容易，只需要一个`assays`就可以**，这是一个列表，其中包含了许多表达数据，例如原始数据count或者其他标准化处理过的数据，行是基因，列是样本

可以构建一个10个基因，3个细胞的矩阵【一定要是矩阵】

```r
counts_matrix <- data.frame(cell_1 = rpois(10, 10), 
                    cell_2 = rpois(10, 10), 
                    cell_3 = rpois(10, 30))
rownames(counts_matrix) <- paste0("gene_", 1:10)

counts_matrix <- as.matrix(counts_matrix)
```

有了这个，就可以用一个list构建出SingleCellExperiment对象。当然，这个**list中可以包括任意个矩阵**

```r
# BiocManager::install('SingleCellExperiment')
sce <- SingleCellExperiment(assays = list(counts = counts_matrix))

>sce
## class: SingleCellExperiment 
## dim: 10 3 
## metadata(0):
## assays(1): counts
## rownames(10): gene_1 gene_2 ... gene_9 gene_10
## rowData names(0):
## colnames(3): cell_1 cell_2 cell_3
## colData names(0):
## reducedDimNames(0):
## spikeNames(0):
## altExpNames(0):
```

为了得到这个对象中的矩阵，可以用两种方式获得：

* `assay(sce, "counts")`：这个方法是最通用的办法，而且其中的counts可以换成其他的名称，只要是出现在之前的list中都可以
* `counts(sce)`：它实现的东西和上面一样，只不过这个方法只适合提取`counts`这个名称的矩阵

```r
counts(sce)
# 或者assay(sce, "counts")

##         cell_1 cell_2 cell_3
## gene_1       7      9     35
## gene_2       7      6     38
## gene_3      10     14     32
## gene_4       7      9     32
## gene_5      19     19     48
## gene_6       8      7     26
## gene_7      10     10     28
## gene_8       4     10     26
## gene_9      10      9     37
## gene_10      6     16     26
```

### **assays还能扩展**

> 既然有了核心，那么就可以根据它进行多向拓展，这也是它强大的一个原因。

### **使用标准函数扩展**

之前`assays`中只有原始表达矩阵，其实还能根据它扩展到归一化矩阵，例如使用一些R包的函数对包装的矩阵进行操作：

```r
sce <- scran::computeSumFactors(sce)
sce <- scater::normalize(sce)

> sce
## class: SingleCellExperiment 
## dim: 10 3 
## metadata(1): log.exprs.offset
## assays(2): counts logcounts
## rownames(10): gene_1 gene_2 ... gene_9 gene_10
## rowData names(0):
## colnames(3): cell_1 cell_2 cell_3
## colData names(0):
## reducedDimNames(0):
## spikeNames(0):
## altExpNames(0):
```

这样，`assays` 就从一个存储原始矩阵的`counts` ，又扩增了归一化矩阵的`logcounts` 。同理，这个`logcounts`也是能有两种提取方法：

```r
logcounts(sce)
# assay(sce, "logcounts")

##         cell_1 cell_2 cell_3
## gene_1    3.90   3.95   4.30
## gene_2    3.90   3.41   4.41
## gene_3    4.38   4.55   4.18
## gene_4    3.90   3.95   4.18
## gene_5    5.28   4.98   4.73
## gene_6    4.08   3.61   3.89
## gene_7    4.38   4.09   3.99
## gene_8    3.16   4.09   3.89
## gene_9    4.38   3.95   4.37
## gene_10   3.69   4.74   3.89
```

通过对比这个`logcounts`和`counts`数据，就能发现为什么要做normalization这一步：原始矩阵中1、2表达量差不多，但和3差别很大，很有可能是细胞3本身测序深度就比较高，因此得到的reads数也多；进行归一化以后，应该就去除了样本文库差异，结果看到1、2、3之间也变得可比了

到目前为止，我们总共得到了：

```r
assays(sce)

## List of length 2
## names(2): counts logcounts
```

### **自定义扩展assays**

这里的自定义指的是，我们不使用某个R包的某个函数，而是根据自己的想法，去根据原始矩阵得到一个新的矩阵

```r
# 例如自己创建一个新的counts_100矩阵，然后依旧是通过这个名称进行访问
counts_100 <- assay(sce, "counts") + 100
assay(sce, "counts_100") <- counts_100
```

看一下结果【注意：新增用的是`assay`单数，查看结果用的是`assays`复数】

```r
assays(sce)
## List of length 3
## names(3): counts logcounts counts_100
```

### **小结**

**再回到第一张图，看看`assays`那里，是不是画了深蓝和浅蓝？**&#x8FD9;也是为了更好地表达：`assays`可以包含多个矩阵；构建sce对象可以一次一次加入新的矩阵，也可以用列表的形式，一次加入多个矩阵

## 列的注释信息：`colData`

之前有了”核心“——表达矩阵信息，那么其次重要的就是添加注释信息，这部分来介绍列的注释，针对的就是实验样本、细胞。这部分信息将会保存在`colData`中，它的主体是样本，于是将行名设定为样本，列名设为注释信息（如：批次、作者等等），对应上面图中的橙色部分。

### **接下来先设置一个细胞批次注释信息：**

```r
cell_metadata <- data.frame(batch = c(1, 1, 2))
rownames(cell_metadata) <- paste0("cell_", 1:3)
```

### **有了注释信息，怎么把它加入sce对象呢？**

两种方法：一种是直接构建，一种是后续添加

* 直接构建：

  ```r
  sce <- SingleCellExperiment(assays = list(counts = counts_matrix),
                             colData = cell_metadata)
  ```
* 后续添加

  ```r
  colData(sce) <- DataFrame(cell_metadata)
  ```

然后看看sce对象添加了什么：

可以看到`colData`增加了之前设置的`batch`信息

```r
sce
## class: SingleCellExperiment 
## dim: 10 3 
## metadata(0):
## assays(1): counts
## rownames(10): gene_1 gene_2 ... gene_9 gene_10
## rowData names(0):
## colnames(3): cell_1 cell_2 cell_3
## colData names(1): batch
## reducedDimNames(0):
## spikeNames(0):
## altExpNames(0):
```

### **加入了sce对象以后，怎么获取它呢？**

```r
colData(sce)
## DataFrame with 3 rows and 1 column
##            batch
##        <numeric>
## cell_1         1
## cell_2         1
## cell_3         2
```

或者直接看结果信息：

```r
sce$batch
## [1] 1 1 2
```

### **这个注释信息只能自己手动添加吗？**

答案是no！有一些包可以自己计算，并且帮你添加进去。例如`scater`包的`calculateQCMetrics()`就会帮你计算几十项细胞的质量信息，结果依然是使用`colData`调用注释结果信息

```r
sce <- scater::addPerCellQC(sce)
colData(sce)[, 1:5]
## DataFrame with 3 rows and 5 columns
##            batch       sum  detected percent_top_50 percent_top_100
##        <numeric> <integer> <integer>      <numeric>       <numeric>
## cell_1         1        80        10            100             100
## cell_2         1        88        10            100             100
## cell_3         2       309        10            100             100
```

还能任意添加

```r
sce$more_stuff <- runif(ncol(sce))
colnames(colData(sce))
sce$more_stuff
## 0.04094262 0.41983904 0.51959352
```

### **既然colData可以包含这么多的注释信息，那么怎么从中选取一部分呢？**

`colData`的一个常用操作就是取子集，看下面操作：

例如想从`colData`中选择批次信息，和数据框取子集是类似的

```r
sce$batch
# 或者colData(sce)$batch

## [1] 1 1 2
```

然后如果再想取批次中属于第一个批次的信息，就可以继续按照数据框的思路取子集：

```r
sce[, sce$batch == 1]

## class: SingleCellExperiment 
## dim: 10 2 
## metadata(0):
## assays(1): counts
## rownames(10): gene_1 gene_2 ... gene_9 gene_10
## rowData names(0):
## colnames(2): cell_1 cell_2
## colData names(9): batch sum ... total more_stuff
## reducedDimNames(0):
## altExpNames(0):
```

这样看的`colnames`中就只剩两个细胞了

## 行的注释信息：`rowData/rowRanges`

既然样本有注释信息，那么同样的，基因也有自己的注释，它就存放在`rowData`或者`rowRanges`中，这两个的区别就是：

* `rowData`：是一个数据框的结构，它就存储了核心`assays`矩阵的基因相关信息

  它返回的结果就是这样：

  ```r
  # 一开始rowData(sce)是空的，可以添加
  sce <- scater::addPerFeatureQC(sce)
  rowData(sce)

  ## DataFrame with 10 rows and 2 columns
  ##              mean  detected
  ##         <numeric> <numeric>
  ## gene_1    16.0000       100
  ## gene_2    14.3333       100
  ## gene_3    16.0000       100
  ## gene_4    18.6667       100
  ## gene_5    15.3333       100
  ## gene_6    16.6667       100
  ## gene_7    17.6667       100
  ## gene_8    13.0000       100
  ## gene_9    16.6667       100
  ## gene_10   14.6667       100
  ```
* `rowRanges`：也是基因相关，但是它是GRange对象，存储了基因坐标信息，例如染色体信息、起始终点坐标

  它返回的结果一开始是空的：

  ```r
  rowRanges(sce) 
  ## GRangesList object of length 10:
  ## $gene_1
  ## GRanges object with 0 ranges and 0 metadata columns:
  ##    seqnames    ranges strand
  ##       <Rle> <IRanges>  <Rle>
  ##   -------
  ##   seqinfo: no sequences
  ## 
  ## $gene_2
  ## GRanges object with 0 ranges and 0 metadata columns:
  ##    seqnames    ranges strand
  ##       <Rle> <IRanges>  <Rle>
  ##   -------
  ##   seqinfo: no sequences
  ## 
  ## $gene_3
  ## GRanges object with 0 ranges and 0 metadata columns:
  ##    seqnames    ranges strand
  ##       <Rle> <IRanges>  <Rle>
  ##   -------
  ##   seqinfo: no sequences
  ## 
  ## ...
  ## <7 more elements>
  ```

### **怎么按行取子集？**

同样类似于数据框，可以按位置、名称取子集：

```r
sce[c("gene_1", "gene_4"), ]
# 或者 sce[c(1, 4), ] 

## class: SingleCellExperiment 
## dim: 2 3 
## metadata(0):
## assays(1): counts
## rownames(2): gene_1 gene_4
## rowData names(7): is_feature_control mean_counts ... total_counts
##   log10_total_counts
## colnames(3): cell_1 cell_2 cell_3
## colData names(10): batch is_cell_control ...
##   pct_counts_in_top_200_features pct_counts_in_top_500_features
## reducedDimNames(0):
## spikeNames(0):
## altExpNames(0):
```

看到`rownames`结果就剩2个基因

> 除了以上3大块，**还有一些重要的”集装箱“需要介绍**，这些前面用`附`标识

### **附1：对样本进行归一化：sizeFactors**

这里面装了根据样本文库计算的文库大小因子，是一个数值型向量，用于后面的归一化

```r
sce <- scran::computeSumFactors(sce)
sizeFactors(sce)
## [1] 0.5031447 0.5534591 1.9433962

# 或者手动添加
sizeFactors(sce) <- scater::librarySizeFactors(sce)
sizeFactors(sce)
##    cell_1    cell_2    cell_3 
## 0.5031447 0.5534591 1.9433962
```

前面提到的： `assays` (primary data), `colData` (sample metadata), `rowData`/`rowRanges` (feature metadata), and `sizeFactors` 。其实这其中前三个都来自于`SummarizedExperiment`这个对象。基于这个对象，还建立了一些新的对象接口，例如下面👇的：

### **附2：降维结果：reducedDims**

存储了原始矩阵的降维结果，可以通过PCA、tSNE、UMAP等得到，它是一个数值型矩阵的list，行名是原来矩阵的列名（就是细胞、样本），它的列就是各种维度信息

它和`assays`一样，也可以包含许多降维的结果，例如用`scater`包计算PCA：

```r
sce <- scater::logNormCounts(sce)
sce <- scater::runPCA(sce)
# 这个算法是利用了sce对象的归一化结果logcounts(sce)
reducedDim(sce, "PCA")
# PC1        PC2
# cell_1 -0.6897959 -0.4080860
# cell_2  0.8493039 -0.2145128
# cell_3 -0.1595080  0.6225988
# attr(,"percentVar")
# [1] 67.07301 32.92699
```

**除了PCA，tSNE的结果也是存储在这里：**

```r
sce <- scater::runTSNE(sce, perplexity = 0.1)
reducedDim(sce, "TSNE")
##               [,1]      [,2]
## cell_1    35.64851  5694.164
## cell_2 -4951.13619 -2815.978
## cell_3  4915.48768 -2878.186
```

**看下全部的结果都包含什么：**

```r
reducedDims(sce)
## List of length 2
## names(2): PCA TSNE
```

**调取方法也十分类似**：`assay`，数据矩阵存储在`assays`，而调用是`assay`；这里的降维结果存储在`reducedDims`，调用是`reducedDim`

**这个降维信息只能自己手动添加吗？**

答案也是no！和前面的`counts_100`加到`assays`的思路一样，我们也可以自己计算，而不用现成的函数，最后加到`reducedDims`这个接口。

例如，进行UMAP降维，虽然可以用`scater::runUMAP()`，但依然可以自己处理。

比如这里使用`uwot`包，不过这个包只能计算，不能添加到sce对象，需要手动添加：

```r
u <- uwot::umap(t(logcounts(sce)), n_neighbors = 2)
reducedDim(sce, "UMAP_uwot") <- u

##              [,1]        [,2]
## cell_1 -0.3952368 -0.03182602
## cell_2  0.2347576  0.60432243
## cell_3  0.1604792 -0.57249641
## attr(,"scaled:center")
## [1]  7.596012 19.251450
```

最后再来看一下：

```r
reducedDims(sce)
## List of length 3
## names(3): PCA TSNE UMAP_uwot
```

### **附3：metadata 接口**

虽然前面介绍了许多接口，但还是有很多DIY的，不能直接导入它们，不过我们仍然需要这些信息，于是medata接口诞生。它可以存储任意类型的数据，只要给它一个名字。

例如，我们有几个感兴趣的基因（比如是高变化基因），现在想要把它保存在`sce`中，方便以后使用：

```r
my_genes <- c("gene_1", "gene_5")
metadata(sce) <- list(favorite_genes = my_genes)

metadata(sce)
## $favorite_genes
## [1] "gene_1" "gene_5"s
```

既然是一个列表，就意味着支持扩展：

```r
your_genes <- c("gene_4", "gene_8")
metadata(sce)$your_genes <- your_genes

metadata(sce)
## $favorite_genes
## [1] "gene_1" "gene_5"
## 
## $your_genes
## [1] "gene_4" "gene_8"
```

### **附4：spike-in信息**

还是可以继续使用`isSpike`来添加，虽然会显示`'isSpike' is deprecated`：

```r
isSpike(sce, "ERCC") <- grepl("^ERCC-", rownames(sce))

# 结果就会在sce中添加：
## spikeNames(1): ERCC

spikeNames(sce)
## [1] "ERCC"

table(isSpike(sce, "ERCC"))
# 就能看存在多少Spike-in
```

**现在推出了：`alternative Experiments`**

```r
spike_counts <- cbind(cell_1 = rpois(5, 10), 
    cell_2 = rpois(5, 10), 
    cell_3 = rpois(5, 30))
rownames(spike_counts) <- paste0("spike_", 1:5)
spike_se <- SummarizedExperiment(list(counts=spike_counts))
spike_se
## class: SummarizedExperiment 
## dim: 5 3 
## metadata(0):
## assays(1): counts
## rownames(5): spike_1 spike_2 spike_3 spike_4 spike_5
## rowData names(0):
## colnames(3): cell_1 cell_2 cell_3
## colData names(0):
```

然后通过`altExp()`加入进来，它的操作和`assays`或`reducedDims()`类似：

```r
altExp(sce, "spike") <- spike_se
altExps(sce)
## List of length 1
## names(1): spike

# 提取数据也是类似的
sub <- sce[,1:2] # retain only two samples.
altExp(sub, "spike")
## class: SummarizedExperiment 
## dim: 5 2 
## metadata(0):
## assays(1): counts
## rownames(5): spike_1 spike_2 spike_3 spike_4 spike_5
## rowData names(0):
## colnames(2): cell_1 cell_2
## colData names(0):
```

### **附5：对列添加label**

使用`colLabels()` ，尤其在非监督聚类过程中对细胞添加label，进行分组

```r
colLabels(sce) <- LETTERS[1:3]
colLabels(sce)
## [1] "A" "B" "C"
```

例如`scran::findMarkers`就是通过`colLabels()`来提取细胞信息的

#### 小结

`SingleCellExperiment`对象兼容性很好，可以用于多个scRNA的R包的输入数据

中间分析的每一步都会向这个对象的`assays`, `colData`, `reducedDims` 等模块添加信息

这个对象方便了未来数据传输与协作，这本书的后续也会基于这个对象进行探讨


# 1.2 总览 | 从实验到分析

刘小泽写于19.10.30 更新于2020-06-24

## 一张非常重要的图

下面的这两张可以说是贯穿始终

![scRNA-Workflow](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-10-30-080845.png)

![贯穿始终的重点](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-10-26-094429.png)

## 实验设计环节

在正式分析之前，关于实验问题的探讨是很有必要的，**最重要的一个就是技术的选择：**

* **Droplet-based**: 10X Genomics, inDrop, Drop-seq
* **Plate-based** with unique molecular identifiers (**UMIs**): CEL-seq, MARS-seq
* Plate-based with **reads**: Smart-seq2
* Other: sci-RNA-seq, Seq-Well

每种方法都有优劣（[Mereu et al. 2019](https://pubmed.ncbi.nlm.nih.gov/32518403/); [Ziegenhain et al. 2017](https://pubmed.ncbi.nlm.nih.gov/28212749/))，目前以10X为代表的droplet-based方法由于高通量和低细胞成本成为了约定俗成的技术；Plate-based方法可以捕获其他的一些表型信息（如细胞形态），另外可以根据实验目的进行调整，灵活性比较好；Read-based方法可以覆盖全转录本，在分析可变剪切、外显子突变等方面很有用；UMI-based方法可以减轻PCR扩增偏差。

下图来自文章的评测：[Benchmarking single-cell RNA-sequencing protocols for cell atlas projects](https://pubmed.ncbi.nlm.nih.gov/32518403/)

![文章的评测](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-06-25-044413.png)

### **下一个问题就是：到底要捕获多少细胞？测序要测多深？**

答案言简意赅：**As much as you can afford to spend.**

如果再补充一下这个答案就是：想要发现罕见细胞类群，就要多获得细胞；想要探索潜在的微小差异，就要加大测序深度。目前常用的droplet-based仪器可以捕获1万到10万细胞，测序深度是每个细胞1000到10000 UMIs，在经济条件一定的前提下，它们之间一般是成反比。另外它还要权衡高细胞捕获通量和影响捕获效率的“双细胞（doublets）比例”。

### **实验设计和常规转录组类似**

也是要考虑一个实验条件下多个生物重复，而且实验条件最好不要混杂批次。需要注意的是：生物重复不是指的单个细胞，而是指的提供细胞的供体（donors）或者细胞培养体系（cultures）

## 获得表达矩阵（count matrix）

和常规转录组一样，单细胞转录组也是需要得到表达矩阵，才能进行下游分析。表达矩阵包含的信息就是：每个细胞中比对到每个基因的UMIs或者reads数。有一点需要注意：**它的定量方法和具体的实验技术相关**

* 10X的数据：使用`CellRanger` 软件，基于STAR比对到参考基因组，然后统计每个基因的UMIs数量
* Pseudo-alignment方法（如`alevin`）：就像之前用的salmon、kallisto意思一样，不需要比对参考基因组，节省时间、内存
* 对于一些高度multiplexed的方法：可以使用[scPipe](http://www.bioconductor.org/packages/release/bioc/vignettes/scPipe/inst/doc/scPipe_tutorial.html) 包：提供了一套综合的分析流程，利用`Rsubread`比对，然后统计每个基因的UMIs数量

  > multiplexed：翻译叫做”多路复用“，即：large numbers of libraries to be pooled and sequenced simultaneously during a single run，可以节省成本和时间
* CEL-seq、CEL-seq2数据：[scruff ](https://bioconductor.org/packages/release/bioc/vignettes/scruff/inst/doc/scruff.html)包可以专门分析
* read-based方法：可以使用常规bulk 转录组定量的流程（比如smartseq2就可以用hisat2+featureCounts）
* 任何包含spike-in转录本的数据：spike-in序列都要在比对、定量之前加到参考基因组中

定量结束后，一般是先导入表达矩阵然后创建一个`SingleCellExperiment`对象（例如：`read.table() + SingleCellExperiment()`）。除此以外，还有一些特定的文件格式需要用特定的包，比如[DropletUtils](https://bioconductor.org/packages/release/bioc/vignettes/DropletUtils/inst/doc/DropletUtils.html)可以分析10X数据，[tximport](https://bioconductor.org/packages/release/bioc/vignettes/tximport/inst/doc/tximport.html)/[tximeta ](https://bioconductor.org/packages/release/bioc/vignettes/tximeta/inst/doc/tximeta.html)可以分析pseudo-alignment数据

### **需要注意**

* 如果分析的是人类数据并且加入了ERCC，**我们很多时候直接用`^ERCC`在行名中进行正则匹配，但是这时要小心**，因为**ERCC基因家族在人类基因组注释中确实存在**，很有可能将真的基因作为外源转录本进行分析。这个问题可以通过将表达矩阵的行名设置为Ensembl,或Entrez来解决
* 一些定量工具会统计表达矩阵中的reads比对率，会存在一些未必对的情况。尽管这些信息可以用作质控，但这些数值如果被误认为是表达量信息，那么就会干扰下游分析。因此在进行下游分析之前，这部分信息可以去掉或者保存在`colData`中

## 数据处理与下游分析

1. **首先进行质控**：去掉低质量细胞。这些细胞可能在建库环节被破坏，可能没有被有效捕获（这就是所谓的“dropout”）。一般会统计：每个细胞的全部count数、spike-in或线粒体reads比例、检测到基因的数量
2. **表达矩阵归一化**：为了减小细胞文库的偏差（可能由于细胞捕获效率不同、测序深度的差异而造成文库大小差异），把细胞们放在同一起跑线上，才能进行下面的细胞相似性比较，后面再根据相似性进行细胞分群。一般是基于log转换（当然有的函数也涉及了一些size factor的计算），从而对均值-方差进行校正
3. **挑选一些特征基因**（一般是高变化基因HVGs，Highly Variable Genes）进行下游分析。原理是根据每个基因在细胞之间的差异构建变化模型，然后找那些变化差异大的基因。使用HVGs不用全部基因的原因一是为了减少计算量，二是减少不感兴趣基因（比如在细胞之间没什么差异）对分析产生的噪音
4. **降维处理**：让数据更“紧凑”，一般是线性降维PCA+非线性降维tSNE/umap。PCA一般是先获得初步的低维数据（可能会挑出几十个主成分），然后传给t-SNE进一步压缩，进行可视化
5. **细胞聚类：**&#x6839;据细胞归一化后的表达量相似性分成组，然后根据每个组marker基因（可理解为这一群细胞的标志性基因）的差异表达对分群进行生物学定义

比如用来自[scRNAseq](https://bioconductor.org/packages/release/data/experiment/vignettes/scRNAseq/inst/doc/scRNAseq.html)的一个droplet-based的视网膜数据【[Macosko et al. (2015)](https://pubmed.ncbi.nlm.nih.gov/26000488/)】，就从原始矩阵得到了分群结果，可以看到这里不使用Seurat也能做质控、挑高变化基因等等

```r
library(scRNAseq)
sce <- MacoskoRetinaData()

# 质控
library(scater)
is.mito <- grepl("^MT-", rownames(sce))
qcstats <- perCellQCMetrics(sce, subsets=list(Mito=is.mito))
filtered <- quickPerCellQC(qcstats, percent_subsets="subsets_Mito_percent")
sce <- sce[, !filtered$discard]

# 归一化
sce <- logNormCounts(sce)

# 挑高变化基因
library(scran)
dec <- modelGeneVar(sce)
hvg <- getTopHVGs(dec, prop=0.1)

# 降维
set.seed(1234)
sce <- runPCA(sce, ncomponents=25, subset_row=hvg)
sce <- runUMAP(sce, dimred = 'PCA', external_neighbors=TRUE)

# 聚类
g <- buildSNNGraph(sce, use.dimred = 'PCA')
sce$clusters <- factor(igraph::cluster_louvain(g)$membership)

# 可视化
plotUMAP(sce, colour_by="clusters")
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-10-30-090848.png)

最后注意这里的分群并不一定是真正有生物学意义的，根据不同的参数可以得到不同的分群结果，而且这里看到的多个小群也有可能是同属一个大群。最后的分群需要计算+生物知识共同实现。


# 2 积累篇：文献阅读

单细胞发展日新月异，每年都会有大量的优质文章发表，建议先阅读几篇综述类，掌握核心分析点；然后再看看解决了什么问题，又是怎么解决的

**优质的文献资源链接：**

> 为了方便使用，我把表格传到了腾讯文档

* [The single-cell studies database has reached 1000 studies ](https://docs.qq.com/sheet/DTmdWbFpEaHVUSGxO?tab=bb08j2)
* [scEpigenetics](https://docs.qq.com/sheet/DTlhlY2JBS0lhWlpz?tab=bb08j2)

**组织架构**

主要将文献分为了：综述（2.1开头）、研究（2.2开头）、算法（2.3开头）


# 2.1.1 综述 | 2019-单细胞转录组分析最佳思路

刘小泽写于2020.5.6-5.8

#### 1 文章信息

题目：Current best practices in single-cell RNA-seq analysis: a tutorial

发表日期：2019年6月19日

杂志：Mol Syst Biol

文章在：<https://www.embopress.org/doi/10.15252/msb.20188746>

DOI：<https://doi.org/10.15252/msb.20188746>

![图1](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-06-011634.png)

#### 2 摘要

单细胞领域日新月异，大量的工具被开发出来，但很难去判断是否好用，而且如何组建一个分析流程是一个难点。本文将详细介绍单细胞转录组数据分析的步骤，包括预处理（质控、归一化标准化、数据矫正、挑选基因、降维）以及细胞和基因层面的下游分析。并且作者将整个流程应用在了一个公共数据集作为展示（详细说明在：[https://www.github.com/theislab/single-cell-tutorial](https://www.github.com/theislab/single-cell-tutorial），目的是帮助新入坑用户建立一个知识体系，已入坑用户更新知识体系。) ）

#### 3 前言

> 需要注意，虽然在原文链接中这些文献链接可以打开，但会链接到原文的该文献位置，而不是直接打开该文献

现在已经可以利用scRNA研究斑马鱼、青蛙、涡虫的细胞异质性([Briggs *et al*, 2018](https://pubmed.ncbi.nlm.nih.gov/29700227/); [Plass *et al*, 2018](https://pubmed.ncbi.nlm.nih.gov/29674432/); [Wagner *et al*, 2018](https://pubmed.ncbi.nlm.nih.gov/29700229/)) ，重新理解以前的细胞群体，但这个领域面临的一个问题就是没有成熟的标准化流程。标准化之路的困难有：大量分析方法和工具的诞生（截止2019.3.7 已经有385种工具）、爆炸式增长的数据量([Angerer *et al*, 2017](https://www.sciencedirect.com/science/article/pii/S245231001730077X); [Zappia *et al*, 2018](https://pubmed.ncbi.nlm.nih.gov/29939984/))。另外根据不同研究目的，各种分支也突显，例如在细胞分化过程中预测细胞命运([La Manno *et al*, 2018](https://pubmed.ncbi.nlm.nih.gov/30089906/))。在我们眼界大开的同时，分析流程标准化就变得更加困难。

在未来分析流程标准化之路上，困难还会存在于技术整合层面。比如现在大量的scRNA工具都是用R和Python写的，跨平台分析需求在增长，而对编程语言的喜好也决定了工具的选择。很多好用的分析工具将自己限制在用各自的编程语言开发的环境中，例如[Seurat](https://satijalab.org/seurat/)、[Scater](http://bioconductor.org/packages/release/bioc/vignettes/scater/inst/doc/overview.html)、[Scanpy](https://scanpy.readthedocs.io/en/stable/)。

接下来，就一起看看作者列出了哪些他认为比较好的软件和流程吧

先上一个scRNA分析总体流程图：

![图2](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-06-014716.png)

#### 4 预处理和可视化

**4.1 首先看一下实验过程**

比较详细的介绍可以看：[Ziegenhain *et al* (2017)](https://pubmed.ncbi.nlm.nih.gov/28212749/); [Macosko *et al* (2015)](https://pubmed.ncbi.nlm.nih.gov/26000488/); [Svensson *et al* (2017)](https://pubmed.ncbi.nlm.nih.gov/28263961/).

原文描述的关键点是：

* 4步走：Typical workflows incorporate **single‐cell dissociation, single‐cell isolation, library construction, and sequencing**. 组织裂解=》细胞分离=》文库构建=》测序
* 第一步：As a first step, a **single‐cell suspension is generated** in a process called single‐cell dissociation in which the tissue is digested.
* 第二步：To profile the mRNA in each cell separately, cells must be isolated. 写了主要的2种方法：plate‐based、droplet‐based，当然也都存在一些问题： In both cases, errors can occur that lead to multiple cells being captured together **(doublets or multiplets),** non‐viable cells being captured, or no cell being captured at all **(empty droplets/wells)**
* 第三步：Each well or droplet contains the **necessary chemicals to break down the cell membranes** and perform library construction. Furthermore, many experimental protocols also label captured molecules with a **unique molecular identifier (UMI).**

  > **UMI的作用主要是区分**：UMIs allow us to distinguish between **amplified copies of the same mRNA** molecule and **reads from separate mRNA molecules** transcribed from the same gene.
* 第四步：Libraries are labelled with **cellular barcodes** and pooled together (**multiplexed**) for sequencing.

感觉原文描述的还没有illumina给出的详细，那么就看看illumina的图文并茂版：

> illumina单细胞测序工作流程：关键步骤和注意事项：<http://web.illumina.com.cn/landing/products_view.asp?newsid=324>

![图3](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-06-015501.png)

![图4](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-06-015722.png)

原始测序数据要经过处理得到表达矩阵，注意这里有两种表述方式：molecular counts (count matrices) 【也即是使用UMI的】和 read counts (read matrices)，取决于是否使用UMI。而作者介绍的流程中，默认使用 count matrices，除非readmatrices和 count matrices得到的结果存在差异，才会特别介绍read matrices

> 关于比较read and molecule counts，有人写了一个R流程：<https://jdblischak.github.io/singleCellSeq/analysis/compare-reads-v-molecules.html>

原始数据处理工具主要有：CellRanger、[indrops](https://github.com/indrops/indrops)、[SEQC](https://bioconductor.org/packages/release/data/experiment/vignettes/seqc/inst/doc/seqc.pdf)、[zUMIs](https://github.com/sdparekh/zUMIs)

它们主要做了这么几件事：

* read quality control (QC)
* assigning reads to their cellular barcodes and mRNA molecules of origin (also called “demultiplexing”）
* genome alignment
* quantification

得到的矩阵行是转录本，列是barcodes【这里用barcodes而不是直接叫细胞，是因为不同细胞的reads也可能属于同一个barcode =》如果出现一孔/液滴多细胞（doublet情况），那么barcode在多个细胞都是一样的】当然也会出现有barcode但实际没有细胞的情况（一个孔/液滴没有细胞即droplet，但这个孔/液滴也会赋予barcode）

> 反正记住：barcode和孔/液滴是对应的，但一个孔/液滴中有一个细胞还是多个细胞或者没有细胞，都会存在barcode。只不过最后可能会看到：多个细胞对应一个barcode、即使没有细胞也会有barcode这样的情况

关于10X实验环节，可以看我之前写的：[10X单细胞基础知识](https://mp.weixin.qq.com/s/0DEybX7GnuDFhfY1uj9t9A)

![图5](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-06-030755.png)

**4.2 质控**

在正式分析之前，先要确定barcode是不是对应真正的细胞（上面已经了解了barcode和细胞的关系），也就是进行Cell QC，主要考虑三个因素（这几个因素也就是现在流程中常用的过滤指标）：

* the number of counts per barcode (count depth)
* the number of genes per barcode
* the fraction of counts from mitochondrial genes per barcode

> 从下面👇图中，感觉过滤的一个方向就是：保留大山头，去掉小山头（把略有增长但不碍大局的小山头炸掉）

先看图A：其中这个小的直方图就是把count depth小于4000的放大，这里设定了一个阈值1500，也就是一个barcode中至少有1500的表达量

图B：每个细胞中包含的基因数直方图。可以看到横坐标有一个小的峰在400附近，这里设定的阈值是700

图C：依旧是看count depth。从高到低排列count depth值，可以过滤一些空的液滴（empty droplets），看到从”肘部“也就是纵坐标1500左右开始迅速下降

图D：看线粒体比例。如果占比很高并且细胞类型不是线粒体特别丰富的那种（如心肌细胞），可能说明这个细胞本身的基因数不多并且总体表达量也不高

![图6](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-06-031215.png)

以上三个指标固然重要，但**如果只关注其中某一个，也会产生误导作用**，所以作者建议看问题一定要全面，并且**要把数据和生物学知识结合起来**。作者举了个例子：比如线粒体表达量相对较高的细胞也可能参与了呼吸过程。细胞总体表达量低或者基因数量少，也可能是因为当时取的细胞处于静止；细胞表达量很高，也可能因为本身细胞体积就比较大。的确，细胞与细胞之间的总表达量还是存在较大差异的。未来也许QC会提供更多的选择。

除了检查细胞完整度，QC还要进行转录本层面上的检查。原始的count矩阵一般包含超过20000个基因。这里一般要根据在细胞中有表达的数量进行过滤，但这个阈值要根据总体细胞数和预计的分群情况来灵活调整。比如有的细胞类型本身就数量比较少（也许就50个），那么如果我们要设定”在少于50个细胞中有表达的基因“这种条件，那么可能会丢失那些总共就50个细胞中的marker基因，最终导致鉴定的细胞亚群会缺失。

质控的目的就是给下游提供更高质量的数据，但一开始谁也不知道这个质量高不高，只能先进行下游分析，看看结果（比如细胞分群结果）再判断。尤其是针对异质性高的细胞群体

> 文章的附件[又额外介绍了一些QC指标](< https://www.embopress.org/action/downloadSupplement?doi=10.15252%2Fmsb.20188746\&file=msb188746-sup-0001-Appendix.pdf >)：比如在CellRanger的结果报告中就会有：Q30指标（一般要高于60-70%）、比对到外显子的比例（一般认为比对到非外显子区域超过40%就造成了测序的浪费）、看看实验中用了多少个细胞，以及结果表达矩阵得到多个barcode

**小结**

* 三种QC指标（the number of genes、the count depth 、the fraction of mitochondrial reads）要放在一起思考，而不是单独看某一个
* 先尽可能地设定宽泛的QC阈值，如果下游聚类无法解释再回过头来反思QC
* 如果看到每个样本的QC指标分布不同，那么就要对每个样本分别设定阈值，而不是一刀切

**4.3 归一化/标准化**

> **作为背景知识，首先来看：**[归一化和标准化的区别](https://cloud.tencent.com/developer/article/1486102)\
> 但二者的界限也没有特别明显，也没有必要把这两个概念分的特别清楚。只要清楚它们大概的使用范围就可以了：
>
> * 常用的归一化是log处理，之前离散程度很大的数据就被集中了；
> * 常用的标准化是z-score：考虑到了不同样本对表达量的影响，消除到了表达的平均水平和偏离度的影响
>
> 它们的使用范围：
>
> * 如果对表达量的范围有要求，用log归一化
> * 如果表达量较为稳定，不存在极端最大最小值，使用归一化
> * 如果表达量离散程度很大，存在异常值和较多噪音，用标准化可以避免异常值和极端值的影响
> * 在分类、聚类、PCA算法中，使用z-score值的结果更好
> * 数据不太符合正态分布时，可以使用归一化
> * 机器学习的算法（SVM、KNN、神经网络等）要求归一化/标准化
>
> 绘制热图会经常用到z-score去除极端值
>
> ```
> pheatmap(dat) # scale之前
> n=t(scale(t(dat)))
> n[n>2]=2 # 限定上限
> n[n< -2]= -2  # 限定下限
> pheatmap(n,show_colnames =F,show_rownames = F) # scale之后
> ```
>
> **接着：在单细胞分析中，也会同时用到Normalize和Scale**（可以看：[单细胞Seurat包升级之2,700 PBMCs分析](https://www.jianshu.com/p/b46b6b6d344f)）
>
> * **归一化 Normalize做的就是将数据进行一个转换，可以让同一基因在不同样本中具有可比性（例如RPKM、TPM等）；另外降低离散程度**。看使用的函数`LogNormalize`背后的计算方法就是：`log1p(value/colSums[cell-idx] *scale_factor)` ，它同时考虑到了这两点
> * **标准化Scale**就是基于之前归一化的结果（也就是log后的结果），**再添z-score计算**
>
> 最后，在[对细胞文库差异进行normalization](https://mp.weixin.qq.com/s/7aOphp5UeuwJ-_l-Wf7B7w) 这一篇中也提到了：
>
> * **Normalization** "normalizes" within the cell for the **difference in sequenicng depth** / mRNA thruput
> * **Scaling** "normalizes" across the sample for **differences in range of variation of expression** of genes
> * normalization一般是对文库处理，目的消除一些技术差异；scale一般对基因表达量处理（典型的z-score：表达量减均值再除以标准差），目的是后续分析不受极值影响

表达矩阵中的每个count值都表示成功的细胞捕获、成功的反转录、成功的测序。但即使是相同类型的细胞，它们的count depth（也就是每个细胞的全部表达量）也会有变化，变化的来源就在于上面说的那三步。因此在比较两个细胞时，任何差异都可能由于实验测序误差产生，而不是真的生物学差异。归一化就是解决这个问题，它把要比较的两个count值根据各自身处的环境求出一个相对丰度，也就是放在了一个水平上考虑，减少实验测序误差，突出更多的生物学差异。

最常用的归一化方法就是：count depth scaling，也称为counts per million（CPM），这个方法常用于bulk转录组，它会根据每个细胞的总表达量计算一个 size factor ，然后对其中各个基因表达量进行normalize。

> 这里再回顾下其他一些方法：[跟着豆豆一起回顾标准化方法](https://mp.weixin.qq.com/s/avGEfZ_XpKwXJ7pSDoDXBA) 另外来自：<https://cloud.tencent.com/developer/article/1484078>
>
> * RPM没有考虑转录本的长度的影响。适合于产生的read读数不受基因长度影响的测序方法，比如miRNA-seq测序，miRNA的长度一般在20-24个碱基之间
> * RPKM/FPKM考虑了转录本的长度的影响。适用于基因长度波动较大的测序方法，如lncRNA-seq测序，lncRNA的长度在200-100000碱基不等
> * TPM是先去除了基因长度的影响，而RPKM/FPKM是先去除测序深度的影响。TPM实际上改进了RPKM/FPKM方法在跨样品间定量的不准确性。

单细胞测序中使用的归一化方法由于细胞种类和基因错综复杂，有人就在bulk的基础上进行了改动。例如：[Weinreb *et al* (2018) ](https://pubmed.ncbi.nlm.nih.gov/29228172/)先排除了表达量超过总体5%的基因，然后再计算size factor，主要是预防少量极高表达量基因的存在；Scran包有个pooling‐based size factor estimation方法，允许更高的细胞异质性存在；另外Scran包在批次矫正和差异分析环节也比其他归一化方法表现更好([Buttner *et al*, 2019](https://pubmed.ncbi.nlm.nih.gov/30573817/))。

在单细胞RNA测序领域，目前有三种常用方法：其一是以10x Genomics为代表的微滴(droplet-based)测序；其二是以Namocell为代表的PCR板(plate-based)测序；其三是以BD Rhapsody为代表的微孔(micro-well-based)测序。就测序长度来说，Smart-seq/C1和Smart-seq2基于full length的测序方案，CEL-seq2, Drop-seq, MARS-seq, SCRBseq是基于UMI的测序方案。

不能指望某一种方法适用于所有类型的scRNA数据，([Cole *et al*, 2019](https://pubmed.ncbi.nlm.nih.gov/31022373/))就发现不同的归一化方法对于不同类型数据集表现不同，使用scone工具可以帮助选择合适的方法。

一般在归一化后，数据都会变成`log(x+1)`的样子，但之后是否对基因进行z-score的标准化上，没有一个共识。Seurat的教程基本都使用了scale这一步，但Slingshot的作者就反对对基因进行scale ([Street *et al*, 2018](https://pubmed.ncbi.nlm.nih.gov/29914354/))。在本文中，作者倾向于避免对基因进行scale。

使用log转换的一个好处就是：让数据更加集中，减少数据的偏斜度，从而近似于许多下游分析工具对数据为正态分布的假设（尽管scRNA数据并不是真正的符合正态分布），比如在差异表达分析和批次矫正环节

**小结**

* 对于非全长scRNA数据（如10X），推荐使用[scran](https://bioconductor.org/packages/release/bioc/vignettes/scran/inst/doc/scran.html)的归一化方法；
* 对于plate‐based 的数据，可以用[scone](https://bioconductor.org/packages/devel/bioc/vignettes/scone/inst/doc/sconeTutorial.html)工具来进行评价，进而可以更好地处理plate之间的批次效应([Cole *et al*, 2019](https://pubmed.ncbi.nlm.nih.gov/31022373/))；
* 对于全长scRNA数据（如smart-seq）可以借用bulk的方法（如TPM），来矫正基因长度【这个问题在10X中不存在：in 10x single cell 3' or 5' gene expression assay, this gene-length bias does not exist.&#x20;
* 归一化的数据应该是`log(x+1)`这种形式的
* 是否进行scale没有共识，这里作者不推荐scale

**4.4 数据矫正与整合**

数据矫正的对象种技术和生物因素都有，例如：不同批次、捕获失败（dropout）、不同细胞周期。这些在之前的归一化中没有被矫正，但这些差异因素都可能会后面的分析产生影响，它们现在都是导致差异的”嫌疑人“之一。这里要做的就是把这些差异来源去掉（Regressing out 《=》【专门查的词典】 同义词partialling out ：剔除）

**4.4.1 首先是生物因素**

最常见的生物矫正因素就是：转录组中的细胞周期信息。简单一点的方式就像Scanpy和Seurat对细胞周期评分进行简单线性回归；复杂点的方式就像scLVM和f‐scLVM。用来计算细胞周期分数的marker基因可以从文献中获得 ([Macosko *et al*, 2015](https://pubmed.ncbi.nlm.nih.gov/26000488/))。另外，这些方法还能用来去除其他已知的生物因素，例如线粒体基因表达量（可以作为细胞应激的标记）。

需要注意的是：

* 细胞周期因素并非一无是处，例如在一个增殖的细胞群中，所有细胞不是同步增殖的，那么就可以根据细胞周期评分来识别。所以需不需矫正还要根据研究目的判断
* 需要结合具体分析的生物问题来判断是否去除。生物体的多个生物过程往往存在依赖性，因此矫正其中一个过程，可能无意间掩盖了另一个过程
* 有人认为细胞大小的变化和细胞周期有关 ([McDavid *et al*, 2016](https://pubmed.ncbi.nlm.nih.gov/27281413/))，因此在归一化过程中对细胞大小进行矫正，或者使用专用的工具如cgCorrect，也可以部分修正细胞周期的影响

**4.4.2 然后是技术因素**

最常见的技术矫正因素就是：样本测序深度、批次、噪音。

去除测序深度的影响，可以促进轨迹推断算法的表现，因为它需要在细胞之间找变化的路径，只要放在同一水平才能看到更准确的总体表达高低。

批次的来源可能是：细胞捕获的时期不同、文库制备使用的芯片不同、测序使用的lane不同。由此产生的效应存在于多个层面:一次实验中各个细胞群之间、同一实验室中进行的不同实验之间、或来自不同实验室的数据集之间。这里主要介绍第一种和最后一种情况：

* 第一种：一次实验中各个细胞群之间是最经典的情形，在bulk转录组也是常见的。使用线性方法进行矫正。例如**ComBat**工具就是利用线性矫正
* 最后一种：来自不同实验室的数据集之间的”数据整合“。使用非线性方法进行矫正。例如：CCA（Canonical Correlation Analysis）、MNN（Mutual Nearest Neighbours ）、Scanorama、RISC、scGen、LIGER、BBKNN、Harmony。虽然这些非线性矫正方法也能用于第一种经典的批次情形，但可能会由于自由度增加而导致矫正过度。例如，在第一种经典模式下，Combat表现就比MNN好 ([Buttner *et al*, 2019](https://pubmed.ncbi.nlm.nih.gov/30573817/))

看一下Combat矫正前后的差别：其中颜色表示不同样本

![图7](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-07-124154.png)

去噪也是矫正的一种类型。单细胞数据的一个特点就是含有许多噪音来源，其中一个就是dropout。一些工具就用来推断dropout，用适当的表达量来替代0，例如：MAGIC、DCA、scVI、SAVER、scImpute。去噪可以提高基因间相关性的估计。这一步可以和归一化、批次矫正及其他下游分析整合起来，例如基于Python的[scVI](https://scvi.readthedocs.io/en/stable/tutorials/index.html)工具。但任何方法都可能导致矫正过度或不足。

**4.4.3 小结**

* 判断要不要进行矫正生物因素：主要看后续分析是不是用于研究发育轨迹等特定生物过程
* 技术因素和生物因素需要放在一起矫正，而不是先矫正这个，后矫正那个
* plate-based数据的预处理一般需要利用非线性归一化方法
* 需要关注降噪前表达量为0，而降噪后才有表达的基因

**4.5 挑选基因、降维、可视化**

人类的scRNA数据中可能会包含25000个基因，但其中许多基因并非能提供有用信息，还有很多基因表达量直接为0。即使在QC阶段去掉这些表达量为0的基因，一个单细胞数据的基因空间依然会有超过15000个维度（一个基因表示一个维度），因此需要降低维度

**4.5.1 首先挑选基因**

就是挑那些真正”具有情报价值“的基因，也就是会数据变化起作用的基因。因此我们这里会挑选名为HVG的基因，也就是highly variable genes。根据数据集的复杂程度不同，HVGs一般会有1000-5000个（如下图就对不同数据集的HVGs做了个统计）

![图8](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-07-132313.png)

之前有研究表明，HVGs数量从200到2400，它们降维后的表现差不多 [Klein *et al* (2015)](https://pubmed.ncbi.nlm.nih.gov/26000487/)，作者建议先尽量多选一些HVGs。

比较流行的挑选HVGs的方法有Scanpy和Seurat，而且最好是在去除技术因素后挑选，避免因为批次、测序等因素导致错误挑选HVG。当然还有其他挑选的方法，看[Yip *et al* (2018)](https://pubmed.ncbi.nlm.nih.gov/29481632/).

**4.5.2 接着降维**

挑出来HVGs后，就是降维了，力求在最少的维度中捕捉到最多的数据特征。

常用的降维方法：A-F分别是：PCA、t-SNE、diffusion maps、UMAP、ForceAtlas2（force‐directed graph）、Variance explained by the first 31 principal components (PCs)。关于单细胞数据的降维方法，详细可以看：[Moon *et al* (2018)](https://www.sciencedirect.com/science/article/pii/S2452310017301877)

![图9](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-07-133512.png)

其中两个应用比较广的方法是：PCA（Pearson, [1901](https://www.embopress.org/doi/10.15252/msb.20188746#msb188746-bib-0101)）和diffusion maps (Coifman *et al*, [2005](https://www.embopress.org/doi/10.15252/msb.20188746#msb188746-bib-0027)) 【diffusion maps 于2015年在单细胞领域走红 Haghverdi *et al* ([2015](https://www.embopress.org/doi/10.15252/msb.20188746#msb188746-bib-0047)) 】

* 主成分分析PCA是一种线性方法，通过最大化每个其他维度中捕获的残差来生成缩减的维度。而且，PCA常作为非线性降维方法的预处理手段。PCA一般通过前N个主成分来表示整个数据集，其中N可以用F中的”肘elbow“部判断，或者用基于置换检验的jackstraw方法确定
* diffusion maps 是非线性的方法，它强调数据之前的转换。当研究连续型数据例如感兴趣的分化过程时会使用。它的每个成分（component）都能突出不同类型细胞间的异质性

**4.5.3 最后可视化**

可视化一般使用非线性降维的方法。最常用的就是2008年提出的t-SNE（ t‐distributed stochastic neighbour embedding）。**t-SNE的一个特性就是关注局部而忽视整体**，因此带来的一个影响就是：**可视化结果可能夸大了细胞群之间的差异，忽略了这些细胞群之间的潜在联系**

另外，使用t-SNE的一大难点就是`perplexity`参数的设定，因为这个数不同，结果显著的cluster数也会不同 (Wattenberg *et al*, [2016](https://www.embopress.org/doi/10.15252/msb.20188746#msb188746-bib-0140))。

除了t-SNE，还有2018年推出的UMAP和SPRING可以用，在缺乏明确的生物学问题时，可以用UMAP作为不错的数据探索。

**小结**

* 根据数据集的复杂性，推荐选择1000-5000个HVGs
* 推荐UMAP进行数据探索；PCA获得一般性数据总结； diffusion maps作为PCA的替代，可用于轨迹推断
* PAGA方法与UMAP连用适用于特别复杂的数据集

**4.6 「总结」 预处理的各个阶段**

作者贴心将预处理比作5种类型数据的处理：

原始数据（raw data）、归一化数据（normalized data）、矫正后的数据（corrected data）、挑选后的数据（feature‐selected data）、降维后的数据（dimensionality‐reduced data）

这5个阶段又分成3个层次：

* measured data：用于统计检验
* corrected data：用于数据比较可视化
* reduced data：用于下游分析

其中每个步骤适时调整，例如单一批次的数据集，就可以跳过矫正批次这一步

![图10](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-07-141018.png)

#### 5 下游分析之细胞层面

下游分析的目的是解释生物问题，例如根据表达量将细胞划分成不同的类型；相似细胞间表达量的微小变化也会体现连续的分化路径；基因表达量之间的相关性可能与基因共表达有关...

下游分析也是有细胞层面和基因层面：

* 细胞层面主要关注：分出多少群细胞、细胞的轨迹。细胞类型为了解释异质性的问题；轨迹作为一个动态发育过程中的一个”快照“，可以帮助理解某个动态发育过程
* 基因层面就是：差异分析、富集分析、互作网络

![图11](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-07-142645.png)

> 下面👇先看看看细胞层面的分析之分群和轨迹

**5.1 细胞分群**

**5.1.1 先是：分群方法**

> 这里主要都是算法相关，简单了解即可

将细胞分群基本就是任何单细胞分析的必经之路。群的划分就是根据细胞中基因表达谱的相似性，表达谱的相似性是由于欧几里得距离量度决定的，而距离量度又是利用的降维的数据。一般有两种方法计算：clustering algorithms、community detection methods

* clustering algorithms是直接基于距离的经典无监督机器学习方法。最常见的是k-means。k-means使用的空间距离量度也有不同（默认是欧氏距离），比如cosine similarity、 correlation‐based distance metrics、the SIMLR method。最近的研究表示correlation‐based distances优于其他距离 (Kim *et al*, [2018](https://www.embopress.org/doi/10.15252/msb.20188746#msb188746-bib-0062))。
* community detection methods是graph‐partitioning algorithms，利用了K近邻法 *K*‐Nearest Neighbour approach (KNN graph)作图。细胞就是图上的一个节点，每个细胞都和K个最相似的细胞连接，这个相似性也是根据降维后空间中的欧氏距离计算的。根据数据集的大小，K一般设为5-100

  community detection methods一般比clustering algorithms速度快，因为只有相邻的细胞对被认为属于相同的群，大大减少了可能的细胞群的搜索范围

**5.1.2 然后是：分群后的注释**

这个过程主要是基因层面的操作，为每个cluster找marker gene（也就是能代表这个cluster的基因，而这个基因又和已知的细胞类型有关）。任何的分群算法和参数设置都会将一整团细胞分成多个群，但这些群是否真的有意义，就要靠这一步来和生物背景结合起来。

我们希望看到的是存在很多类型的细胞，来说明细胞异质性的问题，但这里**关于细胞类型这个定义还是存在争议**。首先，细胞类型的划分怎样算是清楚，对于一些人来说，”T cells“这个名称可以叫一个细胞类型，但还有人认为，必须继续深入，像”CD4+ T cells“、”CD8+ T cells“才叫细胞类型；另外，即使是同一种细胞类型的细胞也会有不同的发育状态，因此它们也会显示不同的分群结果。但不管如何，它们都是当时细胞的一种身份（identity）

> 这个很好理解，就像人一样，人生阶段不同身份也不同，但不能简单说它的类型发生了变化。

因此，我们将分群的结果称为不同身份的细胞（**cell identities**）会比不同类型的细胞（**cell types**）要好一些【即**每个亚群可能并不是真的不同类型细胞，只是显示了此时此刻的细胞身份**】

对于不同细胞身份的注释，近年来也随之细胞图谱的研究而加速，例如小鼠脑细胞图谱 (Zeisel *et al*, [2018](https://www.embopress.org/doi/10.15252/msb.20188746#msb188746-bib-0153)) 、人类细胞图谱 (Regev *et al*, [2017](https://www.embopress.org/doi/10.15252/msb.20188746#msb188746-bib-0107))的发现，产生了许多参考数据库。在缺乏相关背景的情况下，我们可以借用数据库中已发现的细胞marker 基因套入我们的细胞，帮助判断细胞身份。需要注意：通常使用的细胞表面marker基因在细胞身份鉴定方面存在局限性(Tabula Muris Consortium *et al*, [2018](https://www.embopress.org/doi/10.15252/msb.20188746#msb188746-bib-0127))

> 看这个注释结果： **图A**是利用Louvain方法分群+UMAP可视化； **图B**是细胞身份的鉴定：stem cells (Slc12a2), enterocytes (Arg2), goblet cells (Tff3) and Paneth cells (Defa24)。但要注意marker基因可能也会在其他身份的细胞中表达，例如很多marker都在右上角（goblet and Paneth 细胞）中有表达，但最后还是根据表达量来指定特定的细胞身份（比如Slc12a2基因虽然在很多细胞都表达，但就是在中部偏右这一坨细胞中表达量相对高，所以把它当做stem cell） **图C**是近端(上图)和远端(下图)肠上皮区域的细胞身份组成图（颜色越深细胞密度越大）

![图12](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-08-013537.png)

> 上面提到根据marker基因进行细胞分群注释。那么**marker基因怎么获得？**

利用差异分析，分成两组：某个cluster中的细胞、数据集中其余全部的细胞。然后重点关注这个cluster中上调的基因，因为marker基因一般具有更强的表达作用。差异分析也会使用简单的统计检验，例如Wilcoxon rank‐sum test、t-test，将基因的差异大小排个序，选出排名靠前的基因来作为marker基因

> 有了marker基因，再进行注释

将数据集中选出的marker基因和参考数据集进行比对，统计方法可以是：enrichment tests、the Jaccard index、other overlap statistics

参考数据集可以是网页工具： [www.mousebrain.org](https://jieandze1314.osca.top/02/www.mousebrain.org)、 <http://dropviz.org/>，可以将选出的marker基因在参考数据集中进行可视化，帮助判断这个marker基因是什么细胞身份

> 注释并非一蹴而就，这个很麻烦...

**细胞分群、分群注释、重分群、重注释...这个循环很耗费时间**。自动化注释方法加快了这个过程，例如scmap (Kiselev *et al*, [2018b](https://www.embopress.org/doi/10.15252/msb.20188746#msb188746-bib-0064)) 、Garnett (preprint: Pliner *et al*, [2019](https://www.embopress.org/doi/10.15252/msb.20188746#msb188746-bib-0104)) ，但这样的方法有利有弊。自动化提高了速度，但相比手动注释也降低了灵活性。毕竟**自动化工具使用的参考数据集中可能并不包含我们数据中的这样细胞**。因此，有自动化工具也不能完全抛弃手动挑选，尤其针对大型数据集中多种多样的细胞。自动化的过程可以先帮我们粗略地给细胞加个标记，如果有需要，我们可以继续手动对这种细胞继续划分子细胞。对于小型数据集或者缺乏参考基因集的，手动注释就足够了。

**5.1.3 注意**

* 同一细胞身份的marker基因在不同数据集之间可能由于数据集细胞类型和状态组成而不同【**选出的marker基因并不是说以后遇到它，就一定等同于这种类型的细胞。只是说在某种细胞的某个状态下，这个marker基因更符合**】
* 如果存在参考数据集（例如 [www.mousebrain.org、](http://www.mousebrain.org、) <http://dropviz.org/），建议辅助自动化工具进行注释，减少手动查基因的时间>

**5.1.4 细胞分群衍生——细胞组成分析（Compositional analysis）**

就像上面的图12中的C图，显示的是近端(上图)和远端(下图)肠上皮区域的细胞身份组成图（颜色越深细胞密度越大）。研究细胞组成的变化也是一个新方向，例如沙门氏菌感染已被证明会增加小鼠肠上皮细胞的比例 (Haber *et al*, [2017](https://www.embopress.org/doi/10.15252/msb.20188746#msb188746-bib-0046))。

这个分析既需要足够多的细胞数量来推断各个cluser的占比，又需要足够的样本数量来证明是单纯一个样本得cluster数量这样变还是总体都会这样变。相关的分析工具还没有太多，未来的开发可能会借鉴单细胞质谱流式（mass cytometry）或者是宏基因组分析【单细胞与宏基因组的结合...】

**5.2 轨迹分析**

**5.2.1 轨迹推断Trajectory inference**

轨迹推断就是为了找到不同细胞身份、分化或者生物过程中渐进式非同步的变化，构建出的一个动态模型。它认为单细胞数据实际上就是一个连续过程中的快照（snapshot），这个过程可以通过在细胞空间中寻找最小化相邻细胞间转录变化的路径来重建

> 例如： **图A**就是利用Slingshot推断近端（proximal）和远端（distal）肠上皮细胞的分化轨迹； **图B**就是在PCA空间中进行的Slingshot推断。图中细胞的路径就叫做”拟时序“（pseudotime）

![图13](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-08-030157.png)

2014年Monocle和Wanderlust先推出了轨迹推断，之后诞生的分析方法更加丰富，它们在建模路径的复杂性上有所不同，从简单的linear or bifurcating(分叉) trajectories，到复杂的graphs, trees, or multifurcating(多叉) trajectories。Saelens *et al*, [2018](https://www.embopress.org/doi/10.15252/msb.20188746#msb188746-bib-0116))进行过轨迹推断方法的比较，结论是**没有一种方法对所有类型的轨迹推断有效**，应该根据预期轨迹的复杂度来选择。不过，**Slingshot在简单轨迹推断中优于其他方法**(Street *et al*, [2018](https://www.embopress.org/doi/10.15252/msb.20188746#msb188746-bib-0125)) 。如果期望得到更复杂的轨迹，PAGA值得推荐。轨迹推断是一个不确定的过程，可以用多种方法来进行佐证。

* **细胞内通常会同时发生多个生物学过程**，因此在进行发育轨迹推断时，可以将其他生物因素去掉，例如T细胞在逐渐成熟的过程中就可能会经历细胞周期转变(Buettner *et al*, [2015](https://www.embopress.org/doi/10.15252/msb.20188746#msb188746-bib-0018))。
* 另外轨迹推断**最好是在细胞分群之后进行**，因为一个cluster的形成可能意味着这一坨细胞处于比较稳定的状态了。
* 此外，RNA速率（RNA velocities）可以添加发育轨迹的方向，例如：[scVelo](https://scvelo.readthedocs.io/)![RNA速率](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-08-031904.png)
* 当然，推断的轨迹不一定就代表一个生物学过程，因为毕竟是根据”快照“数据中的转录状态推测的。后续可以借鉴：perturbation experiments、inferred regulatory gene dynamics、support from RNA velocity

**5.2.2 基因表达量的动态变化**

在拟时序（pseudotime）中变化的基因描述了轨迹，这组与轨迹相关的基因有望包含调控建模过程的基因，可以用来识别潜在的生物过程。

目前很少有专门分析基因表达动态变化的工具。BEAM将Monocle的轨迹推断整合进来，允许检测在轨迹分支过程中相关基因的动态变化。另外还有[LineagePulse](https://github.com/YosefLab/LineagePulse) 考虑了dropout技术噪音但还在开发中。

下面这样的图在[Slingshot](<https://bioconductor.org/packages/release/bioc/vignettes/slingshot/inst/doc/vignette.html >)的帮助文档就有提及：【4.1：Identifying temporally expressed genes】

```r
require(gam)
t <- sim$slingPseudotime_1
# for time, only look at the 100 most variable genes
Y <- log1p(assays(sim)$norm)
var100 <- names(sort(apply(Y,1,var),decreasing = TRUE))[1:100]
Y <- Y[var100,]

# fit a GAM with a loess term for pseudotime
gam.pval <- apply(Y,1,function(z){
    d <- data.frame(z=z, t=t)
    suppressWarnings({
      tmp <- suppressWarnings(gam(z ~ lo(t), data=d))
    })
    p <- summary(tmp)[3][[1]][2,3]
    p
})
topgenes <- names(sort(gam.pval, decreasing = FALSE))[1:100]
heatdata <- assays(sim)$norm[topgenes, order(t, na.last = NA)]
heatclus <- sim$GMM[order(t, na.last = NA)]

heatmap(log1p(heatdata), Colv = NA,
        ColSideColors = brewer.pal(9,"Set1")[heatclus])
```

![Slingshot基因表达量的动态变化](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-08-053740.png)

**5.2.3 细胞亚稳态分析 Metastable states**

> 亚稳态常见于物理化学。在物理学中，亚稳性（Metastable）是动力系统的一种稳定状态，而不是系统能量最低的状态 Metastable：stable provided it is subjected to no more than small disturbances. 另外这个状态可以用这个图帮助理解：大概就是「一个相对稳定但又会变化的一个状态」 ![image-20200508172550638](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-08-092551.png)

拟时序分析会展示出不同阶段细胞数量的多少。假设细胞以无偏的方式采样，其中轨迹中的稠密区域就表示转录时首选的方案。当把轨迹理解为一条时间线时（例如在发育这个时间线），这些密集的区域可能代表细胞的亚稳态，可以结合拟时间坐标来绘制直方图，找到这些亚稳态【因此看到B图中很多种状态，但C中直方图认为这几个密集的区域才属于亚稳态】

![Metastable states](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-08-054854.png)

**5.2.4 整合分群与轨迹分析**

> 分群是由整体到部分，是静态的；而轨迹又是由部分推断整体，是动态的。二者结合起来又产生了一种新的分析模式

将分群的结果当成节点（node），将轨迹当成节点之间的桥梁（edge），所以将动静数据结合在了一起。利用partition‐based graph abstraction（**PAGA**）这个工具就能得到类似下面这个图。

> It was **the only** reviewed method able to cope with **disconnected topologies and complex graphs containing cycles**.

![整合分群与轨迹分析](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-08-060316.png)

#### 6 下游分析之基因层面

之前都是对细胞进行分析，但细胞中的基因分析会提供更多的信息。例如差异表达分析、基因集分析和基因调控网络推断，不是表面上研究细胞异质性，而是基于异质性探索基因表达相关的原因

**6.1 差异表达分析**

> 基因层面的数据探索，一个经常遇到的问题就是：两个组之间有没有表达量的差异？

这个方法也是常规bulk转录组中经常做的。不过单细胞相比于bulk转录组的一个优势就是：可以深入一个层次，原来bulk只是看一块组织的平均表达量，但现在经过分群后，能得到一块组织中各种各样的亚群，再结合差异分析，对理解异质性问题更有帮助。

虽然都是朝着一个方向前进，但单细胞和bulk转录组的差异分析方法还是不同的。

* bulk转录组存在样本数量的限制，因此算法需要对少量样本进行准确估计，而单细胞则不同，一个细胞作为一个样本，成百上千不在话下；
* 单细胞数据又有自己的特点：特异的人为技术噪音（dropout、high cell‐to‐cell variability ），因此单细胞分析方法需要额外考虑这些因素

但最近(Soneson & Robinson, [2018](https://www.embopress.org/doi/10.15252/msb.20188746#msb188746-bib-0123))研究表明，基于大批量的差异分析，bulk分析方法的性能与最好的单细胞分析方法相当。当bulk方法进行改进，加入基因权重分析后，表现要好于单细胞原有工具。例如：bulk差异分析工具DESeq2/EdgeR + ZINB‐wave工具估算的权重。

不过，bulk差异分析工具的性能虽然好，但是计算的效率很难提升。毕竟单细胞数据样本数量越来越多，程序跑的时间长短也成了衡量工具优劣的重要因素。单细胞工具**MAST**脱颖而出。在单个数据集的小范围比较中，完胜bulk和其他单细胞方法(Vieth *et al*, [2017](https://www.embopress.org/doi/10.15252/msb.20188746#msb188746-bib-0136))。而且**MAST比bulk方法快了10到100倍** (Van den Berge *et al*, [2018](https://www.embopress.org/doi/10.15252/msb.20188746#msb188746-bib-0134)) 。

**小结**

* 差异分析使用MAST或limma
* 差异分析不能使用矫正后的数据（denoised, batch corrected, etc.），而是应该在计算过程中去指定需要矫正的技术因素
* 我们给差异分析算法提供的矫正的因素（称之为协变量covariates）不能太混乱，因为工具不会去智能识别，必须要清楚需要矫正什么

**6.2 基因集分析**

> 基因层面的分析，往往会产生大量的基因，但很难去解释。

例如差异分析我们往往能得到上千基因，为了比较方便解读，一般会把有共同特性的基因归为一组，然后检查我们归类的可靠性 【grouping the genes into sets based on shared characteristics and testing whether these characteristics are overrepresented in the candidate gene list.】

我们**一般关注基因在生物过程（biological processes, BP）中的富集**，可以使用MSigDB、GO、KEGG pathway、Reactome数据库

另外，单细胞中的一个新进展就是利用成对基因标签进行**配体受体分析（ ligand–receptor analysis）**

> 来自：<https://www.bio-equip.com/showarticle.asp?id=453107210> 肿瘤内细胞-细胞相互作用的研究将**通过对配体和受体的表达分析来探究细胞间的相互交流**。运用配体-受体复合物的数据库，通过scRNA-seq数据与肿瘤细胞亚群的定义相结合，来推断潜在的细胞-细胞相互作用，可以理解为其中一个群体产生配体，向另一个表达相应受体的群体发信号

配体-受体成对标签可以从：CellPhoneDB数据库获得，然后用来解释cluster之间高表达基因的联系

例如，利用[celltalker](https://arc85.github.io/celltalker/articles/celltalker.html#vignette-overview) 就可以做

![Celltalker分析](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-08-065826.png)

**6.3 基因调控网络  gene regulatory network (GRN)**

> 基因并非独立发挥作用的。相反，基因的表达水平取决于与其他基因和小分子之间的相互调控

方法例如：[SCONE](https://www.ncbi.nlm.nih.gov/pubmed/28379368)、[PIDC](https://www.ncbi.nlm.nih.gov/pmc/articles/PMC5624513/)、[SCENIC](https://github.com/aertslab/SCENIC) (Single-Cell rEgulatory Network Inference and Clustering)，但发展还不是很完善，推断的调控关系不是很稳定【谨慎使用】

#### 7 分析平台

现在开发了很多平台，整合了一套分析流程，有基于R的(McCarthy *et al*, [2017](https://www.embopress.org/doi/10.15252/msb.20188746#msb188746-bib-0087); Butler *et al*, [2018](https://www.embopress.org/doi/10.15252/msb.20188746#msb188746-bib-0020)) ，python的 (Wolf *et al*, [2018](https://www.embopress.org/doi/10.15252/msb.20188746#msb188746-bib-0146))，本地的(Patel, [2018](https://www.embopress.org/doi/10.15252/msb.20188746#msb188746-bib-0100); preprint: Scholz *et al*, [2018](https://www.embopress.org/doi/10.15252/msb.20188746#msb188746-bib-0120)) ，网页版带可视化的(Gardeux *et al*, [2017](https://www.embopress.org/doi/10.15252/msb.20188746#msb188746-bib-0040); Zhu *et al*, [2017](https://www.embopress.org/doi/10.15252/msb.20188746#msb188746-bib-0158))

> Zhu *et al* ([2017](https://www.embopress.org/doi/10.15252/msb.20188746#msb188746-bib-0158)) and Zappia *et al* ([2018](https://www.embopress.org/doi/10.15252/msb.20188746#msb188746-bib-0152)).列出了各种平台

Seurat是使用最广泛的，Scater在QC和预处理中表现优异；除此以外，基于Python的scanpy也逐渐发展起来，它对于大量细胞的标准化方面表现不错

如果不使用命令行，可视化界面也有，只不过用户只能跑人家已经写好的脚本，操作灵活性不足。这样的平台更多的用处是在可视化探索上，例如Granatum、ASAP。未来 Human Cell Atlas（HCA）会在数据可视化探索上迅速发展： <https://www.humancellatlas.org/data-sharing>

#### 8 结语

**8.1 作者的结语**

作者把流程测试和说明都放在了：<https://github.com/theislab/single-cell-tutorial>

感兴趣的可以跟着走一遍，比较一下不同的工具。作者希望这一篇能代表单细胞领域目前发展的一个最新动向。他也提到，新方法层出不穷，本文介绍的大量的方法是经过实践比较、验证过的。**目前可用的方法不管是运行效率还是易用性可能都不如最新开发的方法，但要注意：新方法在未被大量验证之前都需小心使用。**&#x800C;且新方法一般都是针对单个层面（比如降维、分群、轨迹推断等），大体的分析流程基本固定了。

未来整合深度学习和单细胞多组学是两个重要的发展方向，流程化运行更是趋势。

随着文库制备和测序技术的进步，未来的单细胞平台必将可以处理多种类型数据：DNA甲基化、蛋白丰度等等。

**8.2 刘小泽的结语**

> 截止到2020年3月8日下午15.35，打卡看完！

三天的时间，基本每天都会花半天时间在阅读这篇综述上。从第一眼看到它的文章逻辑，就感觉：嗯是它，没错了！连午觉都不想睡了。

一开始想强迫自己看下去，没想到，越看越精彩。尤其是将整个流程和自己的知识结合起来，就看得比较顺畅。为了更加易读，我在其中加了很多注释，包括之前自己写的一些推文和网上一些好的资源，可以帮助梳理知识点。

最后，希望看完本文对你有帮助🤓！


# 2.1.2 综述 | 2018-单细胞捕获平台

刘小泽写于19.8.13

> 每一次的测序技术变革都推动了检测灵敏度和通量，那么就来看看单细胞领域各种捕获技术的更迭 \
> 2018  [Platforms for Single-Cell Collection and Analysis](https://www.mdpi.com/1422-0067/19/3/807/pdf)

### 摘要

对单个细胞的分离最基本要求就是：保证捕获的质量。如果用三个词语概况就是：**fast、effective、gentle**

**传统的分离方法**是：显微操作（micromanipulation）、激光显微切割（laser capture microdissection）、荧光激活细胞分选仪（fluorescence-activated cell sorting，FACS）。过去的十年许多高效的新方法开发出来，它们不仅仅是作为传统方法的备选，更有望取代传统方法。

**新方法**基于微流控芯片、液滴、微孔板，并且利用毛细管、磁吸、电场等实现了细胞自动化收集。这篇综述总结了现有的方法和市场上的平台以及它们的发展。

### 1 前言

单细胞技术可以帮助检测细胞异质性、展示细胞对不同理化性质的复杂反应。另外还能鉴定罕见细胞群体，如：循环肿瘤细胞（circulating tumor cells， CTCs，它是存在于外周血中的各类肿瘤细胞的统称）、与治疗或疾病相关的残留细胞、干细胞等。生命系统的复杂性要求我们要在各个层次进行基因调控研究，包括DNA、mRNA的转录、不同的调控RNA（例如microRNA和一些non-coding RNA）、蛋白、细胞代谢、激素水平等，而且每个水平都有各自的分析手段，于是后来产生了针对**单个细胞的多层面分析手段**，最常用的就是针对核苷酸的qPCR、RNA/DNA-Seq，针对蛋白的免疫组化（immunohistochemistry，IHC）、定量质谱（quantitative mass spectroscopy, MS），其中RNA-seq就是目前曝光率最高的，因为它增加通量的同时降低了费用。

要做这些分析，首先关心的就是如何在保证最少干扰细胞原始表达量的同时，将细胞收集起来。这些年发展起来的方法、仪器都有各自的优缺点（考虑到**时间、通量、价格、空间分辨率等**），还有持续开发的（如：Stilla Technologies公司的**ddPCR技术--digital droplet PCR-based**）。因此本文着重将每个新方法的初衷、特性和潜在应用整合起来做了一个综述。

### 2 分离细胞的初衷—在混杂群体中找到自己想要的

选择特定类型的细胞经常是根据荧光标记，它包括两种方式：一种是直接使用**荧光抗体**；另一种是用**转荧光基因的蛋白**，会发出红、黄、绿颜色，而且只在目标细胞群体中表达。

第一种利用抗体的方法缺点就在于：抗体数量有限（尤其是对研究不多的物种）、在不同细胞群体中可能有交叉反应、背景标记模糊；

第二种方法虽然解决了第一种方法的一些问题，但是找这个合适的特异的marker基因比较困难，尤其是在一些病理条件下，细胞表达量骤增，可能这个marker也会在其他细胞类型中检测到。

这两种都不合适就要利用最粗暴的观察手段，依据大小、形状、在组织中的位置等特征

### 3 收集细胞的传统手段

主要有三种：显微操作、荧光激活细胞分选（FACS）、激光纤维切割捕获（LCM），这三种方式都有成熟、标准化的流程。前两种可以在体外或脱离组织条件下分离细胞，LCM需要将细胞或组织固定（因此大多会导致细胞死亡，不过现在也有利用LCM分析活细胞的例子）。另外这三种方法的**通量**也是不同的，FACS分选细胞非常高效，短时间内就能分选上千细胞；显微操作和LCM方法就非常慢，通量也低。

因为**前两种方法需要组织裂解**，所以基因的表达可能会受到影响，影响因素也有很多，例如：裂解方法、组织类型、试剂浓度、孵育时间、温度等等，LCM方法因为是直接将RNA固定住，因此它会受这些外部因素影响小一些。不过LCM使用的固定试剂（例如福尔马林）会使RNA产生降解，虽然现在有了不含福尔马林的固定剂，但RNA的质量还是会受到影响。

**LCM方法最大的一个优势就是它能记录空间信息**，其他两种因为做了组织裂解，所以得到的细胞不知道原来在组织的什么位置；另外一个优点就是残余的组织可以保存留用。不过这个方法需要技术的辅助，既然是切割，那么就存在混进不相关细胞的可能。细胞污染这个问题在显微操作中也会存在，不过它混进来的可能是一点溶剂。既然可能存在“混杂”的问题，那么**设置阴性对照**就显得很有必要。

这三种方法的具体对比如下图：

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-13-071927.png)

分离得到细胞后，一般会收集到裂解缓冲液中，准备后续的scRNA的寡核苷酸barcode结合或质谱技术的镧系元素barcode结合。目前的趋势是将细胞收集和后续反应整合在一个设备中（就像10X的设备），实现end-to-end solution。

### 4 收集细胞的现代手段

> 变革进化

**仪器公司的不断进步**

首先是高通量市场：

Illumina+BioRad生产的ddSEQ Single-Cell Isolator为不同起始细胞数提供了定制的试剂盒；

10X推出的Chromium可以在一个仪器中进行各种分析（基因组、外显子、转录组、免疫相关）；

BD Rhapsody可以定制化panel，实现靶标DNA/RNA测序；

Nadia Innovate可以实现单细胞收集参数的完全控制；

然后是低通量市场：

C1：新的C1 Fluidigm芯片能实现800个细胞的测序，实现从细胞捕获到结果报告的流程

ICELL8：可以分析大量的细胞，同时又可以可视化控制、挑选部分细胞进行下游分析

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-13-072745.png)

**高通量仪器**

大多数仪器都配置了微流控的“lab-on-chip”技术，能产生数百万的定制的液滴，其中会包含单个细胞、寡核苷酸（例如用：anchored oligo-dT，目的是捕获mRNA）、反转录试剂（产生cDNA），然后整个文库制备一般会在gel beads中进行（例如10X）；另外液滴也可以单纯作为捕获容器，后续的反应及文库制备会在magnetic beads中进行（例如BD）。这种建库测序的技术发展出了：**Drop-seq、inDrop-Seq、SCRB-Seq**或者公司自己设计的方案。以上的几种方法都是基于poly-A富集mRNA的，因此它们属于**3'端建库**。

为了将建库测序得到的reads成功回溯到某个细胞，就要在寡核苷酸上“做手脚”。每个细胞分配了一段特定的序列，叫做barcode。来自一个细胞的reads都共享同样的**barcode**。另外还有一种情况，就是Illumina短序列测序需要扩增，扩增后才能根据cluster（也就是簇，一簇的reads都相同）来增加测序的准确性。当然也就是在PCR这一步，可能会引入偏差（来源：不同的扩增模板的扩增效率不同，从而影响原始的数量比例）。对单细胞来讲，它和bulk 转录组不同，本来reads数量就不多，如果再加上PCR bias的影响，噪音就太大了。于是引入了**UMI（unique molecular identifiers，这个molecular就是指cDNA）**，每个初始cDNA分配一个独特的UMI。如果扩增完发现，有两个reads都属于同一个cDNA molecule，它们的UMI就是一样的，最后定量只会被计数一次

> 个人思考：PCR也不是十全十美，复制的结果就百分百和初始一样，也会有错误率。同一批cDNA扩增的结果也有可能差几个碱基，那么来自同一个cDNA的两条reads的UMI可能就不一样，差一两个碱基是可能的，因此后续计算的时候，可能会设置一个容错值（可能需要结合UMItools说明书看一下），就是说如果来自同一个cDNA的两条reads结果UMI之差1个碱基其余都一样，那么可能也会判断它们是来自同一个cDNA的

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-13-083853.png)

**尽管基于液滴的技术很先进，但是也有局限。**&#x6700;主要的缺点就是RNA捕获效率低，反转录施展不开（因为反转录过程只是发生在非常小的液滴空间中）、液滴很脆弱（泄露有风险），另外当起始细胞数量有限时，细胞捕获效率低。但同时如果起始细胞数量太多，又容易导致douplets现象（一个液滴中包含两个细胞），而且容易堵塞微流控芯片。因此，其他厂商探索了一些方法，**利用细胞板（其中包含了成千上万个微孔）**&#x6355;获大量细胞，并且每个微孔中也是放入了一个连接寡核苷酸的bead。之后的下游操作和基于液滴的方法就相似了。

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-13-085550.png)

> 下面👇分别来看一下

**4.1 Chromium System（10X）**

Chromium Single Cell 3′ Solution于**2016年10月**发布，亮点在于可以测几百乃至几万细胞，数量区间很大。利用了GemCode技术产生barcode。大体流程：

gel bead包含了barcode、UMI、反转录试剂 + 油滴中的细胞 =》 液滴（所谓的“油包水”）=》 这其中进行了细胞裂解、反转录反应，合成了cDNA =》 每个液滴混合在一起、然后溶解 =》 就构建好了cDNA文库

这个技术的细胞捕获率能到**65%**，双细胞率也很低，而且每次试剂更新都会降低这个比例，增加检测转录本的和数量。样本是在微流控芯片中进行处理的，**每个芯片10-20分钟可以处理8个样本**，产生100-80000个细胞。

**4.2 Rhapsody Single-Cell Analysis System （BD）**

2017年九月发布，它是Resolve平台的升级版，它是目前市场上**仅有的不依赖于液滴技术的高通量建库平台**。它的机器包含了一块布满20万微孔的板子，其中放了**UMI-barcoded 磁珠**，最多可以捕获2万个细胞。捕获的细胞裂解，然后mRNA附着在beads上。这些beads混合起来，然后在单个管中建库。当然，系统会在细胞裂解前进行扫描，统计捕获细胞数量、双细胞数量、空孔数量，帮助用户**自主选择多少beads进行建库**，样本的质量能不能满足后续要求。至于剩下的beads，可以留着后续使用，这样可以节省后续的费用。另外它**使用了targeted RNA-Seq的方法，也就是只会扩增感兴趣的转录本** 。

**4.3 C1 System and Polaris (Fluidigm)**

**C1可以说是现代单细胞领域的开拓者**，它于2012年发布，是第一个处理单细胞组学分析的仪器。它的操作主要在integrated fluidic circuit (IFC 芯片)上完成，每次可以处理96个细胞。目前至少有100篇文献使用这个技术，涵盖了转录组、全基因组、外显子、靶标DNA测序、表观、miRNA表达。这个平台的拓展性很强，可以使用不同的芯片（如：C1 Single-Cell Open App IFC chip）和自定义的实验方案（C1 Script Builder）；

另外这个公司2015年开发的一款叫Polaris平台，是第一个也是唯一一个 unique unified workflow for active cell selection, cultivation, and molecular analysis (up to 48 cells per run)

**4.4 Nadia and RNA-Seq System （Dolomite Bio）**

这家公司是2016年成立的，在2017年11月推出了Nadia的单细胞测序平台（也是利用Drop-seq的全自动平台）。它也是将单个细胞包裹在一个微珠（bead）中，微珠的表面富含捕获寡核苷酸和UMI。 **和上一个gel bead方案不同的是：** **这个液滴中不含有反转录试剂**。反转录过程是发生在微珠收集并破裂以后。这样做的好处就是：它不用受限于在皮微级别的微珠内进行，可以在外边大展拳脚。但话说回来，**它比gel bead的细胞捕获效率要低。**

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-13-094608.png)

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-13-094621.png)

#### 结论

* 细胞富集技术（如FACS）和高通量单细胞仪器整合起来是一个趋势，这样可以避免测一大批细胞，最后仅仅用了一小部分，造成各方面的浪费。这样整合起来可以有的放矢，我们可以仅仅选择感兴趣的细胞，然后高通量测序
* 一大挑战就是细胞质量的控制，目前没有评估单个细胞质量的工具。当然是可以根据bulk 转录组结果的表达量来定一个阈值，对每个细胞进行鉴定，比如看每个细胞中最少表达的基因数量。但不论怎样，这些方法都是后续的检验，我们的样本已经被处理完了，最优解还是测序之前就保证细胞的质量
* 多个组学联合分析是前景（ DNA, mRNA, regulatory RNA, proteins, metabolites），像10X就抓住了这个机会，好像BD也推出了Rhapsody蛋白检测的功能
* 最后就是针对分析工具整合的期待，还没有一个公认的分析流程出来，只有推广度高低之分，比如Seurat、Scater、Monocle这几个R包就推得比较多，那么说不定未来的标准流程就是它们


# 2.1.3 综述 | 2017-scRNA中的细胞聚类分群

刘小泽写于2019.3.3

这篇关于单细胞的综述发表于2017年7月的Molecular Aspects of Medicine，[Identifying cell populations with scRNASeq](https://pubmed.ncbi.nlm.nih.gov/28712804/) 第一作者是Tallulah，通讯是Martin Hemberg

比较早的英文scRNA教程就是他们实验室的<https://hemberg-lab.github.io/scRNA.seq.course/>

## Abstract 摘要

单细胞转录组在进行单个细胞的表达定量检测是强有力的工具，但是它产出的数据噪音和维度都比较高，相比bulk RNA-seq增加了分析难度。文章就介绍了几种不同的实验流程和最流行的分析方法，可以识别具有重要生物学意义的基因，可以将数据投射到低维，可以对细胞聚类推断亚群，可以解释验证鉴定到的细胞类型和细胞状态。

## 1 Introduction 介绍

人体中大约有$$4 \* 10^{13}$$ 个细胞([Bianconi et al., 2013](https://www.tandfonline.com/doi/full/10.3109/03014460.2013.807878))，形态与功能都具有多样性。传统的方法是根据形态学而非分子学特征将细胞分成200种（[Junqueria et al., 1992](https://scholar.google.com/scholar_lookup?title=Basic%20Histology\&author=L.C.%20Junqueria\&publication_year=1992)）。上世纪中叶以来，免疫荧光(immunofluorescence)和流式细胞分选技术( flow cytometry )可以基于细胞表面蛋白标记物存在与否进行更精确地分类([Coons et al., 1941](http://journals.sagepub.com/doi/abs/10.3181/00379727-47-13084p); [Fulwyler, 1965](https://www.ncbi.nlm.nih.gov/pubmed/5891056))，但是这些技术还仅限于易于分离的组织(如：血细胞谱系)，而且只能检测表面少量的标记物。

单细胞测序的发展允许使用整个转录组的数千个细胞去鉴定细胞类型，目前scRNA-seq已经应用在许多发育中的或者固定时间点的组织和器官，包括大脑不同区域的研究[(Darmanis et al., 2015](https://www.ncbi.nlm.nih.gov/pubmed/26060301); [Karlsson and Linnarsson,2017](https://bmcgenomics.biomedcentral.com/articles/10.1186/s12864-017-3528-6); [Liu et al., 2016](https://www.ncbi.nlm.nih.gov/pubmed/27081004); [Tasic et al., 2016](https://www.ncbi.nlm.nih.gov/pubmed/26727548); [Zeisel et al., 2015](https://www.ncbi.nlm.nih.gov/pubmed/25700174))、视网膜研究([Baron et al., 2016](https://www.ncbi.nlm.nih.gov/pubmed/27667365); [Jaitin et al., 2014](http://science.sciencemag.org/content/343/6172/776); [Macosko et al., 2015](https://www.ncbi.nlm.nih.gov/pubmed/26000488); [Zheng et al., 2017](https://www.ncbi.nlm.nih.gov/pubmed/28091601))、胰腺研究([Baron et al., 2016](https://www.ncbi.nlm.nih.gov/pubmed/27667365); [Segerstolpe et al., 2016](https://www.sciencedirect.com/science/article/pii/S1550413116304363); [Wang et al., 2016](https://www.ncbi.nlm.nih.gov/pubmed/27364731))、免疫细胞研究([Jaitin et al., 2014](http://science.sciencemag.org/content/343/6172/776); [Villani et al.,2017](https://www.ncbi.nlm.nih.gov/pubmed/28428369)) 、早期胚胎发育([Biase et al., 2014](https://www.ncbi.nlm.nih.gov/pubmed/25096407); [Goolam et al., 2016](https://www.ncbi.nlm.nih.gov/pubmed/27015307); [Xue et al., 2013](https://www.nature.com/articles/nature12364))、造血([Velten et al.,2017](https://www.ncbi.nlm.nih.gov/pubmed/28319093); [Wilson et al., 2015](https://www.ncbi.nlm.nih.gov/pubmed/26004780))

文章列出了一些方法可以根据scRNA数据识别细胞群 (图1)

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-10-12-043638.png)

【图中不同的颜色表示对第一步得到的表达矩阵进行的不同处理，例如第二个蓝色框"feature selection"是从原始表达矩阵中删除行，方法有HVG、M3Drop、Spike-in；再往下"dimensionality reduction "目的是降维，会计算出一个新的包含meta-features的矩阵，可以想象成把细胞分类，相似的群体汇集到一起有共同的meta元信息，方法如：PCA、tSNE、Diffusion map；接下来是聚类"Clustering"，包括计算细胞与细胞之间的聚类，如K-means、DBSCAN；或是"[K近邻算法](https://blog.csdn.net/juanqinyang/article/details/58250403)"，如Louvain、infomap、densityCut、SNN-cliq】

此外，文章还讨论了设计实验时需要考虑的不同方案，因为实验设计的好坏直接影响下游分析结果；讨论了鉴定生物学相关的细胞类群对scRNA数据分析的挑战以及应对的一些统计方法；然后就是非监督式聚类，用来细胞分群；最后讨论了如何去验证分群的细胞是否真的有生物学意义。

## 2 Experimental design considerations 实验设计

scRNA-seq并不是一成不变，需要根据具体实验进行调整。 比如一个常用的操作就是鉴定稀有（数量小于1%）细胞群([Campbell et al. (2017](https://www.ncbi.nlm.nih.gov/pubmed/28166221); [Grün et al., 2015](https://www.ncbi.nlm.nih.gov/pubmed/26287467); [Jiang et al., 2016](https://www.ncbi.nlm.nih.gov/pubmed/27368803); [Segerstolpe et al., 2016](https://www.sciencedirect.com/science/article/pii/S1550413116304363))，意味着需要大量的供试细胞。例如：Campbell作者对小鼠下丘脑的20921个细胞进行测序，结果鉴定了包含少于50个细胞的神经元亚群(占比<0.2%) 。

另一个scRNA-seq的应用就是确定相似的细胞类型之间有何差异，这就需要对低表达基因提高检出率，降低技术噪音。例如：分析造血干细胞之间的差异就需要检测低表达丰度的转录因子，反过来就需要敏感度更高的scRNA测序方法（[Tsang et al., 2015](https://www.ncbi.nlm.nih.gov/pubmed/26387834)）或者靶向检测(如RT-qPCR)（[Wilson et al., 2015](https://www.ncbi.nlm.nih.gov/pubmed/26004780)）。

### **2.1 实验方法**

**概述**

一般每个scRNA-seq都包含三个方面：1）单个细胞分离；2）文库制备；3）测序。

1）细胞分离需要先将样品解离，然后分选到PCR板的单独孔中，或者利用单独的液滴(droplets)、微孔(microwells)或微流控(microfluidic)捕获单个细胞；

2）文库制备需要反转录和扩增，可以利用全长转录本或者"3'或5'"标记的一端；

3）测序一般是[多重测序](https://www.illumina.com.cn/science/technology/next-generation-sequencing/multiplex-sequencing.html)(目的：单次实验中同时测序大量样本)，深度可以从平均25000reads/cell（[Macosko et al.,2015](https://www.ncbi.nlm.nih.gov/pubmed/26000488)），到5M reads/cell([Kolodziejczyk et al., 2015](https://www.sciencedirect.com/science/article/pii/S193459091500418X))

**两类方法**

对于需要高通量的研究，**基于液滴（droplet）的方法**，如InDrop（[Klein et al., 2015](https://www.ncbi.nlm.nih.gov/pubmed/26000487)）、Drop-seq（[Macosko et al., 2015](https://www.ncbi.nlm.nih.gov/pubmed/26000488)）、10X Chromium([Zheng et al., 2017](https://www.ncbi.nlm.nih.gov/pubmed/28091601))是比较流行的，可以一次制备成千上万细胞，捕获的性价比高，但是大量的细胞测序可能增加总体成本。不过有研究表明，确定细胞类型所需要的最低测序深度可以为25000-50000reads/cell (\[Jaitin et al., 2014]\(<http://refhub.elsevier.com/S0098-2997(17)30049-3/sref46>); [Pollen et al., 2014](https://www.ncbi.nlm.nih.gov/pubmed/25086649))。虽然droplet的方法通量比较高，但是细胞检测率和mRNA的捕获效率会偏低([Svensson et al., 2017](https://www.biorxiv.org/content/10.1101/073692v1); [Ziegenhain et al., 2017](https://repositori.upf.edu/handle/10230/34928))。近年来有一些可以替代droplet的方法出现，包括基于微孔的方法([Fan et al., 2015](https://www.ncbi.nlm.nih.gov/pubmed/25657253); [Gierahn et al., 2017](https://dash.harvard.edu/handle/1/34375305))和组合索引（combinatorial indexing）的方法([Cao et al., 2017](https://www.biorxiv.org/content/10.1101/104844v1.article-info))。以上这些方法需要再细胞裂解前加上barcodes，因此只支持3'/5'测序。

如果实验中细胞量不大，可以考虑**PCR plate-based的方法**（将少量的细胞分选到含有建库PCR引物的多孔板中），包括Smartseq2([Picelli et al., 2013](https://www.ncbi.nlm.nih.gov/pubmed/24056875))、SCRB-seq([Soumillon et al., 2014](https://www.biorxiv.org/content/early/2014/03/05/003236)) 、CEL-seq([Hashimshony et al.,2012](https://www.ncbi.nlm.nih.gov/pubmed/22939981))和MARS-seq\[(Jaitin et al., 2014]\(<http://refhub.elsevier.com/S0098-2997(17)30049-3/sref46))。细胞一般利用微流控芯片(如：Fluidigm> C1，它将细胞捕获和文库构建组合在一起)。以上的方法捕获细胞的性价比比较低，但检出率较高([Svensson et al., 2017](https://www.biorxiv.org/content/10.1101/073692v1); [Ziegenhain et al., 2017](https://repositori.upf.edu/handle/10230/34928))。另外这些方法既支持3'/5'端测序，也支持全长转录本测序。有研究表明，1M reads/样本细胞可以最大化基因检出率(Svensson et al., 2017; Ziegenhain et al., 2017)，但为了精确定量isofroms或者找到含量更低的ncRNAs，需要更多的测序([Huang and Sanguinetti,2017](https://www.biorxiv.org/content/biorxiv/early/2017/04/29/098517.full.pdf); Sims et al., 2014)。

**Doublet的问题**

scRNA测序方法中一个不可回避的问题就是："双细胞 doublet"，即一个液滴或一个微孔中包含了2个或多个细胞，这种情况必须通过进一步仔细的分析（[Segerstolpe et al., 2016](https://www.sciencedirect.com/science/article/pii/S1550413116304363); [Wang et al., 2016](https://www.ncbi.nlm.nih.gov/pubmed/27364731)）才能避免被误认成新的中间细胞类型。 对于高通量的捕获方法，需要权衡细胞捕获效率和doublet检出率，一般设定doublet的范围是1-5%（Ziegenhain et al., 2017），微流控Fluidigm平台为1-10%([Fluidigm Corporation, 2017](http://info.fluidigm.com/rs/673-MRG-416/images/C1-Med-96-IFC-Redesign_wp_101-3328B1_FINAL.pdf)) \[过去设定阈值竟然高达30%(Macosko et al., 2015)]。对于 plate-based的方法，没有这种明确的的规定。

除了doublet可能导致混合文库(mixed libraries)，还有可能是测序文库发生了"泄露"，有报道说Illumina的Hiseq 4000中有5-10%的reads会发生([Sinha et al.,2017](http://dx.doi.org/10.1101/125724))，在HiseqX中没有发现([Owens et al., 2017](https://www.sciencedirect.com/science/article/pii/S2211124715014916))

**批次效应**

Doublet只是实验中产生的一种情况，会混淆细胞类群的识别。另一个挑战是批次效应([Hicks et al., 2015](http://dx.doi.org/10.1101/025528); [Tung et al., 2017](https://www.ncbi.nlm.nih.gov/pubmed/28045081)) 。批次效应是不同时间或不同人员制备的实验重复之间的实验效率或细胞状态不同而产生的。如果对感兴趣的生物学类型(如突变型与野生型)进行不同批次的处理(如：不同日期提取或使用不同PCR板扩增)，那么基本不可能从数据分析角度消除批次效应(只能用一些算法比如quantile、SVA包的ComBat ([Stein et al., 2015](https://www.ncbi.nlm.nih.gov/pubmed/25887219)) )、RUVs([Risso et al.,2014](https://www.ncbi.nlm.nih.gov/pubmed/25150836))、linear mixed-modelling ([Tung et al., 2017](https://www.ncbi.nlm.nih.gov/pubmed/28045081))。

想要消除批次效应只能通过仔细的实验设计，将每个生物条件分散到各个实验批次中，做到"一视同仁"，例如：采用"balanced"方法([Hicks et al.,2015](http://dx.doi.org/10.1101/025528)）让每个批次包含不同生物处理的细胞，每个生物处理在不同的批次中都存在。

### **2.2 技术噪音**

**UMI与Spike-ins**

单细胞转录组一般会搭配unique molecular identifiers (UMIs) 或已知浓度的外源RNA分子(spike-ins) 来解决高技术噪音问题。

UMI是反转录过程中添加到每个cDNA的5'或3'端，长度为4-10bp的barcodes(Islam et al., 2014)。它的作用是将reads分配给每个反转录事件，区分哪些reads是来自于同一个原始的cDNA分子，然后估算原始分子数量(Islam et al., 2014; Kivioja et al., 2011)。因为它和转录本的一端结合后进行5'/3'测序，因此会存在丢失isoform信息、捕获的遗传变异较少等问题，评价等位基因表达会比较难。5'/3'测序的主要优势就是借助UMI，消除基因长度差异，消除了扩增的偏差，相比之下，全长转录本测序虽然捕获了转录本整体，但存在3'/5' bias。

标准的spike-ins是ERCC组织指定的一段细菌序列（Baker et al., 2005; Jiang et al., 2011），它们在转录长度、核苷酸含量、poly-A尾的长度和内含子缺失方面都和哺乳动物不同(因为目前单细胞主要应用于人和小鼠)。存在的问题是：ERCC spike- ins的捕获效率低于内源性mRNA (Svensson et al., 2017)；具有较高的技术变异性，有时会比内源基因的含量还多(Robinson and Oshlack, 2010; SEQC/MAQC-III Consortium, 2014)；spike-in的计数受到生物条件的影响，因此有时会失去作为control的优势。新开发的spike-ins是来自人类的序列，可能更能代表哺乳动物转录本，从而减轻一些旧spike-in的影响([Paul et al., 2016](http://dx.doi.org/10.1101/080747))。如果使用了spike-ins，比对前应该将spike-in序列和参考基因组序列合并作为共同”参考基因组”。

Plate-based方法既可以用UMI也可以用spike-ins，而基于液滴和基于微孔的方法是能用UMI ([Gierahn et al., 2017](https://dash.harvard.edu/handle/1/34375305); Macosko et al., 2015)；微流控的仪器不确定是否可以与UMI或者spike-ins兼容，取决于仪器的设计。

**多重测序（Multiplexed-sequencing）**

多重测序也是产生技术噪音的一个原因，因为它会导致不同细胞之间的reads数不在一个层次。使用标准化可以纠正不同细胞之间的测序深度影响([Vallejos et al.，2017](https://www.ncbi.nlm.nih.gov/pubmed/28504683))。可以利用CPM/TPM（counts/transcripts per million）进行校正。目前开发的方法，如Scran([Lun et al., 2016](https://www.ncbi.nlm.nih.gov/pubmed/27081004))分析含有许多差异基因的数据集比较有优势，SCnorm([Bacher et al., 2017](https://www.biorxiv.org/content/early/2016/11/29/090167))可以解释测序深度对基因不同表达水平的影响。如果数据集中包含有spike-ins，它们可能就被用于标准化，在鉴定差异基因中具有高鲁棒性，并且可以保留由于总RNA含量不同而产生的差异(Buettner et al., 2015; Grün et al., 2014; Owens et al., 2016; Risso et al., 2014; Vallejos et al., 2015)。

## 3 关于高维度的处理

**维数的诅咒 curse of dimensionality**

虽然scRNA-seq结果汇总包括所有基因的信息，也非常有用，但是我们同时分析数千个基因在计算上困难很大。数据集中测量的总基因数称作"维数(dimensionality)"，对于哺乳动物通常有1万个维度左右。当在一个高维基因表达空间中比较细胞时，细胞间的距离变得更加均匀，使得区分群体间或者群体内的差异就非常难。

解决这个诅咒有两种方法：

首先，将数据投射到一个较低的二维空间(称作"降维")，低维空间一般由算法定义，既降低维度，又最大化保留原始数据的某些特征。因为投影过程不可避免会丢失基因信息，所以投影方法的选择涉及到一组特定属性的优先级排序。

其次，可以取出信息量少的基因(在机器学习中称为"特征选择")，同样也是减少分析中用到的维度数量。这样不仅利于可视化，还可以降低噪音、加快计算。下面是一些无监督降维的方法和特征选择。

### **3.1 降维**

**主成分分析 （PCA）**

它将数据投射到较少的独立的线性维度中，从而捕捉到可能的最大方差。PCA相对较快，当与稀疏的矩阵（比如单细胞的表达矩阵中就包括了许多的0）一起使用时，它可以扩展到非常大的数据集。缺点是PCA限于线性维数，并且假设数据接近正态分布。针对单细胞数据的大量0值，PCA的变体 zero-inflation算法被开发出来([Pierson and Yau, 2015](https://genomebiology.biomedcentral.com/articles/10.1186/s13059-015-0805-z)) ，但是这个模型可能不适用于所有的数据集([Andrews and Hemberg, 2016](http://dx.doi.org/10.1101/065094,))。2017年又有人开发了一个类似PCA的方法，它是基于零膨胀负二项分布模型（zero-inflated negative binomial model ）取代了高斯模型Risso et al. (2017)。

**t分布随机邻域嵌入（tSNE）**

它也是一种用于大型高维数据可视化的统计方法([Maaten et al., 2008](http://www.jmlr.org/papers/volume9/vandermaaten08a/vandermaaten08a.pdf))。它使用概率分布来估计嵌入的情况，tSNE将数据投射到各个孤立的簇中，实现细胞群的可视化。tSNE的缺点就是算法的随机性，即使应用于同一个数据集，也会产生不同的嵌入结果，不过这种差异比较小并且不显著。因此最佳的操作就是多次运行该算法，确保结果的完整性。另外，tSNE对"perplexity"参数的选择很敏感，需要多次运行才能找到合适的perplexity。该方法的作者建议仅用tSNE作为可视化方法，而不是降维的方法。

**Diffusion maps (DM)**

DM是一种非线性的投影方法，主要用于分析细胞的连续发展（[Moon et al., 2017](http://dx.doi.org/10.1101/120378); [Angerer et al., 2016](https://www.ncbi.nlm.nih.gov/pubmed/26668002); [Haghverdi et al., 2016](https://www.nature.com/articles/nmeth.3971)）。它是基于扩散过程的模型，将高维数据嵌入低维空间。它假设低维空间是平滑的，并且空间可以从细胞之间的距离推断得到。与tSNE不同，DM保留了点自身位置和与远端点位置的关系。因为它假设细胞是相对平滑的连续体，因此在大量的scRNA或RT-qPCR实验中表现良好(细胞数> 1000)，对于细胞数量较少或存在异质性很高的细胞群时效果不好（[Qiu et al., 2017](http://dx.doi.org/10.1101/110668)）。

### **3.2 特征选择**

**Michaelis-Menten modelling of dropouts (M3Drop)**

M3Drop利用dropout rate（丢失率：本来有表达量却没有测到）与平均表达量之间相对紧密的关系进行特征选择。高丢失率的基因可能在细胞亚群中出现差异表达，因此从拟合关系中识别离群点是一种有效地特征选择方法。该方法改进了聚类算法，允许批量校正结果（[Andrews and Hemberg, 2016](http://dx.doi.org/10.1101/065094)）。

**Highly variable genes (HVG)**

它基于这种假设：基因相当于平均表达值而言，出现的较大的差异是由于生物学影响，而不仅仅是技术噪音。这种方法试图通过权衡方差与平均表达量之间的关系来找到比预期差异性更高的基因。这种关系很难拟合，实际中基因是按照与移动中位数（moving median）的距离进行排序的([Kolodziejczyk et al., 2015](https://www.sciencedirect.com/science/article/pii/S193459091500418X))，或者使用另一种源自方差的统计量，比如：方差的平方系数([Brennecke et al. 2013](https://www.nature.com/articles/nmeth.2645))

**Spike-in based methods**

它使用与HVG或M3Drop类似的算法确定感兴趣的特征。利用来自spike-in RNAs的数据进行技术噪音建模，以确定基因表现出的丢失率或显著升高的方差。基于spike-in的方法包括：BASiCS([Vallejos et al., 2015](https://journals.plos.org/ploscompbiol/article?id=10.1371/journal.pcbi.1004333)) 、scLVM([Buettner et al., 2015](https://www.nature.com/articles/nbt.3102))。

**Correlated expression**

相关表达法是另一种识别生物学相关基因的方法，可以专门用于识别细胞群(Andrews and Hemberg, 2016)。两种细胞类型之间的差异表达基因之间是相互关联的。如果他们在同一种细胞类型都表达，那么相关系数就为正；如果在不同细胞类型中同时表达，那么相关系数为负。特征选择利用的就是相关性的大小或显著性。另一种方法如PAGODA([Fan et al., 2016](https://doi.org/10.1038/nmeth.3734))结合了HVG和PCA的加载信息，可以鉴别高度相关或者有共同功能注释的基因集

> 以上的方法处理高维数据时并不排斥，可以使用多种方法。总的来说，PCA、tSNE、DM等容易受到批次效应和技术噪音的影响，这种影响会掩盖数据内部结构([Finak et al., 2015](https://www.ncbi.nlm.nih.gov/pubmed/26653891); [Hicks et al., 2015](http://dx.doi.org/10.1101/025528); Tung et al., 2017)。而降维之前进行特征选择进而去除一些生物意义较少的基因，可以减少批次和噪音的影响，例如：先进行spike-in based feature selection，再PCA（Liu et al., 2016; Tasic et al., 2016）；先HVG，后tSNE（Segerstolpe et al., 2016）；先HVG，后PCA+tSNE(Campbell et al., 2017)

## 4 非监督聚类鉴定细胞群

单细胞比较常用的用途是识别细胞群。从生物学角度看，细胞是有异质性的，一个细胞群通常包含不同的细胞亚群，例如大脑样本汇总的神经元和胶质细胞；另外还可以看同种细胞类型的不同状态，例如受刺激和为受刺激的T细胞。从数学角度看，对细胞群的从头识别是一个非监督聚类的问题。目前已经有几种成熟的方案应用到了单细胞中。

将大量细胞分成k个群的可能性多到不可想象，因此我们不能考虑所有的可能分群情况，而是应该寻求最优解。聚类的质量取决于群内与群间的相似性比较，不同的指标对数据的基础分部做不同的假设。比如："modularity"假设一个稀疏的图形结构，而计算k-means使用的数据到簇质心的距离就是假设数据中的圆形簇大致相同。将一种方法应用到和算法本身假设不同的数据上将导致错误的聚类，并且没有一种聚类方法具有普适性（[Wiwie et al., 2015](https://www.ncbi.nlm.nih.gov/pubmed/26389570)）。

### **K-means**

K-means是一种单细胞分析常用的聚类算法，一般在特征选择和降维后使用。它的计算比较快，将细胞迭代分配给最近的簇中心(或叫"质心centroid")，然后重新计算簇的质心。然而，K-means需要预先指定簇的数量，并为每个簇提供随机的起始位置，需要多次运行来检查这些参数的鲁棒性，这些结果可以再传递给SC3进行组合([Kiselev et al., 2017](https://www.repository.cam.ac.uk/handle/1810/264381))。K-means的一个缺点是：它先假设一个预先确定的等大小的圆簇数目，如果不符合假设，那么k-means就会沿着分化轨迹识别许多相邻的簇，将罕见的细胞与常见的细胞类型合并。当然，对于罕见的细胞群，可以结合k-means检测离群点(outlier)的方法，如RaceID(\[Grün et al., 2015]\(<http://refhub.elsevier.com/S0098-2997(17)30049-3/sref37))，当不包含罕见细胞群时，RaceID表现较差。>

### **Hierarchical clustering**

层次聚类是另一种常用的识别细胞群体常用方法。不同的层次聚类有不同的假设，比较常用的是"Ward"和"complete"，假设存在圆的和k-means大小一致的簇，不过层次聚类**比k-means要慢**。层次聚类的优点是可以做成树状图，因此可以确定不同[粒度](http://www.cnki.com.cn/Article/CJFDTotal-JSJX200208003.htm) 的聚类之间关系，然后在不同的高度"切割"树状图，可以生成不同数量的群体。对单细胞数据进行层次聚类的方法包括：pcaReduce([Zurauskiene\_ and Yau, 2016](https://bmcbioinformatics.biomedcentral.com/articles/10.1186/s12859-016-0984-y))，SINCERA([Guo et al., 2015](https://journals.plos.org/ploscompbiol/article?id=10.1371/journal.pcbi.1004575))，CIDR([Lin et al., 2017](https://genomebiology.biomedcentral.com/articles/10.1186/s13059-017-1188-0)) 。有研究将层次聚类拓展到了大脑神经元细胞类型（[Zeisel et al., 2015](https://www.ncbi.nlm.nih.gov/pubmed/25700174)）和胰腺中胰岛细胞类型分析([Baron et al., 2016](https://www.ncbi.nlm.nih.gov/pubmed/27667365))，这类方法倾向于识别同种类型细胞群。

### **Density-based clustering**

基于密度的聚类方法将聚类定义为细胞密度较高的相邻区域。与层次聚类或者k-means聚类不同，它不假设簇有特定的性状或大小，而是通常假设所有簇是一样密集的，比如细胞群是同样均匀的。另外，密度必须用一个或多个参数来定义。设置密度的参数类似于k-means选择簇的数量，或者像层次聚类中选择树的切割位置。基于密度的聚类需要**大量的样本**来准确估计，因此更适用于droplet实验的数据、大型RT-qPCR实验或几千上万的细胞([Campbell et al., 2017](https://www.ncbi.nlm.nih.gov/pubmed/28166221); [Jiang et al., 2016](https://www.ncbi.nlm.nih.gov/pubmed/27368803); Macosko et al., 2015)。主要方法是：DBSCAN([Ester et al., 1996](https://www.aaai.org/Papers/KDD/1996/KDD96-037.pdf))，它结合了Seurat包中的降维算法和GiniClust中的罕见细胞型特征选择算法。

### **Graph clustering**

图聚类，又叫"群体检测"，是基于密度聚类的一个拓展，专门用于以图形展示的数据，比如一组细胞用"边edges"相互连接。图可以轻松使用极小诊断假设(minimal assumptions) 表示复杂的非线性结构，因此可以识别不同大小、密度、形状的细胞群([Lancichinetti and Fortunato, 2009](https://link.aps.org/doi/10.1103/PhysRevE.80.056117))。另一个优势是可以拓展到数百万个细胞的聚类。

图中的密度可以根据连接一组细胞"edges"的数量测量，然后与零假设比较，例如：完全随机图或由一定程度控制的随机图中使用一个叫做"模量modularity"的度量。最常用的方法是：Louvain算法(Blondel et al., 2008; Lancichinetti and Fortunato, 2009)，在PhenoGraph (Levine et al., 2015) 和Seurat（V 1.4）中也使用。另外，密度可以通过图中的随机漫步" random walks"建模，并使用在每个细胞上建模消耗的时间来估计，这也是densityCut ([Ding et al., 2016](https://academic.oup.com/bioinformatics/article/31/13/2225/196315))的策略。另外一种估算密度的方法是使用每个细胞的k个最临近neibour之间的重叠，这在SNN-Cliq ([Xu and Su, 2015](https://www.ncbi.nlm.nih.gov/pubmed/25805722))被应用。主要的缺点就是：数据没有固定的图形结构。

### **总结**

聚类方法的一个关键选择因素就是要识别多少组，粗略聚类可以识别出少数非常不同的聚类，这些聚类与细胞类型可能相对应；而精细聚类可以识别大量但不明显的聚类，这些聚类可能对应不同细胞状态

大多的聚类算法需要我们预先定义个数(如k值)或者与聚类粗细相关的参数(如密度参数)，而选择合适的K值是比较麻烦的，因为没有一套标准的选择方法。

有许多样本，存在细胞类型和细胞状态的层次结构，可能都有研究价值。比如[2015年Zeisel](https://www.ncbi.nlm.nih.gov/pubmed/25700174)对大脑样本细胞进行聚类，粗略聚类发现9中细胞类型(从神经胶质等许多非神经元细胞类型中分离出神经元)，然后进行精细聚类发现神经元又分成了7个皮层特异性组

许多的聚类工具可以在[ASAP](https://github.com/DeplanckeLab/ASAP/blob/master/R_Python/clustering.R)中找到，它是一个web工具([Gardeux et al., 2016](http://dx.doi.org/10.1101/096222))

## 5 cluster的生物学鉴定

聚类容易解释难(相对来说)。首先，聚类算法有一种"启发式"效应，即使使用均匀分布的数据，他也能找到一些不同进行划分；另外，即使cluster有生物学效应而非噪音，它们依然可能没有细胞类型的差异。目前没有一个公认的标准去判断一个分析的细胞群真的是一类新型细胞。还有，利用转录差异来定义细胞类型比较困难(Buettner et al., 2015)，因为细胞状态(如细胞周期)的瞬时差异相比于细胞类型对转录组的影响更大。

### **5.1  计算角度**

为了避免多数聚类算法"启发式”的影响，为了评价细胞的重要程度，算法必须重新重复运行一个空模型，将结果与观察的结果比较。这个空模型数据集可以从观测数据中按一定的概率分布提取，也可以通过对每个基因的观测表达值进行独立的随机重排序得到。

为了确保得到一个质量比较好的聚类结果，可以对同一个数据集应用多个算法，并确保结果一致性，保证同一个数据不依赖于任何聚类方法自身的假设。此外，随机聚类方法如：k-means或Louvain maximum modularity，多次运行得到一致结果，比单独运行一次得到的结果更有说服力 (Goder and Filkov, 2008; Kiselev et al.,2017)。显著区分的cluster在不同的聚类算法结果中都是一样可以分开的，当然，如果clusters之间基本不分离，那么不同的算法结果差异也就比较大。

计算的方法主要是提高结果的可靠性，但真正要证明鉴定的细胞群是有生物学意义(如细胞类型和细胞状态是不是与特定的功能特征相关)，目前没有自动化的程序可以全部完成。

### **5.2 实验角度**

第一步通常是找差异表达基因，也就是能可靠区分两个或多个cluster的基因(又叫"**marker"基因**)，例如只有一个cluster高表达的基因就是marker。这里就需要利用功能注释、富集分析。得到的marker基因可以利用实验进行验证，例如：共表达的marker可以利用RT-qPCR、高通量测序或者细胞仪进行重复(Burns et al., 2015; Jaitin et al., 2014; Muraro et al., 2016; Tasic et al.,2016)。Marker基因可以用于分离细胞群进行培养和功能测定。Marker基因也可以用于小细胞群的原位成像，Burns等(2015)利用免疫荧光技术展示了不同细胞类型在内耳中的空间定位，免疫荧光也可用于确认细胞类型标记物的共表达或互斥表达(Tirosh et al.， 2016)。细胞类型的特异性标记可以使用FISH作为靶点，除了确定细胞类型在组织中的空间分布外，还可以验证它们的共同表达。Joost 采用免疫组织化学和单分子RNA-FISH方法，识别毛囊内不同假定细胞类型的空间位置，并分析了空间与分化相关的表达模式([Joost et al., 2016](https://www.ncbi.nlm.nih.gov/pubmed/27641957))。

验证cluster的另一种方法是比较不同物种的cluster（例如人和小鼠），从而确定cluster是否广泛保守，从而推断是否为真正的细胞类型。[Johnson等人(2015)](https://www.nature.com/articles/nn.3980)对人类、小鼠和雪貂的放射状胶质祖细胞种群进行了比较，结果发现了两种新的亚群，分别存在于人类和雪貂中，但在小鼠中却没有，通过对各自标记基因的比较基因组学发现，它们与哺乳动物的脑回畸形有关。

研究与特定细胞群相关的关键转录因子水平（增加或减少）可以辅助验证细胞群。[Olsson等人(2016)](https://www.nature.com/articles/nature19348)敲除了与不同的造血祖细胞有关Gfi1和Irf8，结果产生了不同的细胞类型，Gfi1的粒细胞祖细胞和Irf8的单细胞祖细胞。

> cluster的生物学验证是很有必要的，另外还可以提供关于新细胞群的特定功能或与疾病状态相关的有用信息

## 结论

确定新的或已知的细胞群可能仍然是未来scRNASeq实验的一个关键目标。然而，由于细胞数量和灵敏度之间的权衡，可能永远不会有仅有一个最优的scRNASeq实验平台。同样，对于降维、特征选择和无监督聚类，没有一种比较方法在所有情况下都是最优的。得到细胞分群以后，虽然利用现有的方法可以很容易地识别出新的细胞群，但这些发现必须通过外部数据或实验来验证，以确保它们具有生物学意义。


# 2.1.4 综述 | scRNA已经开发出超过1000款工具了，你用过几种？

刘小泽写于2021.8.24

## 速览

* 题目：Over 1000 tools reveal trends in the single-cell RNA-seq analysis landscape
* 日期：2021.8.14
* 链接：<https://www.biorxiv.org/content/10.1101/2021.08.13.456196v1>
* 工具列表：<https://www.scrna-tools.org/tools>

## 图1

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-08-24-083308.png)

* 这个网页从2016年开始搜集scRNA的分析工具，截止到2021.8.12，搜集了1027款
* 从2018年开始，增速明显，按这个趋势到**2022年预计会有1500款，2025年会有3000款**
* 大约三分之二的工具至少有一篇peer-review的文章，四分之一有预印本
* 可视化、降维聚类是工具开发的前沿阵地，像是多样本整合、轨迹推断也逐渐成为了标准流程，不过更高级的分析比如罕见细胞类型鉴定、可变剪切工具还较少
* 目前的多面手主要是seurat、scanpy，更多的工具主要集中在某一种或某几种分析

## 图2

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-08-24-084554.png)

* R工具目前还是居多，但越来越多的工具采用python开发，而基于R的开发数量正在下降；因为单细胞数据量和复杂度都在逐步提高，而性能方面的优势使得python逐渐提升
* 另一个因素在于：曾经的bulk RNASeq一般都是生命科学领域的研究人员关注，解决的也是生物问题；而scRNA具有更高的探索性，因此有时在降维聚类等方面需要结合机器学习这种更加复杂的统计方法，所以计算科学领域的研究人员也逐渐加入，而python也是他们所常用的机器学习工具
* 按这个速度推断，**2025年中期python就会超过R**，成为scRNA数据分析领域最为热门的语言
* B图中可以看到红线上方&#x7684;**”潜力股工具“：integration和classification**。早期受到测序条件和经费的限制，可能一个实验只能做一个样本或者很少几个样本，但现在**大样本量逐渐成为趋势**（比如最大的Human Cell Atlas），因此如何整合以及处理批次效应，就成了一个重点
* 随着研究逐渐深入，样本整合越来越多，细胞类型的细分也成为趋势，比如下面这一张。之前细胞类型的推断可能更偏向于根据表达量计算距离，后来的方案则更好地利用了公共参考数据库的资源辅助推测（比如SingleR中就包含了一些内置数据集，大部分是bulk RNA-Seq或芯片数据中经过筛选的细胞类型） ![image-20210824170550144](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-08-24-090550.png)

## 图3

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-08-24-091304.png)

* github成为scRNA工具开发的主阵地（超过90%），包括709个owner的960+个仓库，有超过1700个贡献者**提交了超过150,000次**
* 可以看到，scRNA的文章预印本呈现增长趋势，方便更多的工具提前问世，让读者更快地提交issue帮助改进


# 2.1.5 综述 | 2021-单细胞测序的微流控技术应用

刘小泽写于2021.10.18

## 前言

题目：Microfluidics applications for high-throughput single cell sequencing

日期：2021-10-11

期刊：Journal of Nanobiotechnology

链接：<https://jnanobiotechnology.biomedcentral.com/articles/10.1186/s12951-021-01045-6>

## 摘要

细胞群体中单个细胞的异质性在疾病的发展和进展中起着重要作用，但目前大多数传统的基因分析方法掩盖了单个细胞的差异。单细胞测序可以展示单个细胞的内在异质性，并揭示复杂和稀有的细胞群。近十年来，针对单细胞研究出现了不同的微流控技术，成为领域前沿。这篇综述介绍了单细胞测序的过程，并回顾了用于单细胞测序的微流体原理，讨论了常见的高通量单细胞测序技术及其优缺点。

![image-20211018111549187](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-18-031549.png)

## 背景

单个细胞是生物体的基本结构和功能单位。在相同的外部刺激或生理条件下，源自同一类型细胞的细胞可能会表现出细胞间差异。许多传统研究，例如细胞分化和基因表达，都着眼于细胞群，这是多个细胞的“整体”表现。异质性可以在看起来相同的细胞的形态、组成、功能和遗传行为中出现，而对大量异质细胞群的分析只能提供多个细胞的平均数据，从而丢失很多低丰度信息。为了解决这个困境，需要在单个细胞水平上分析细胞群。

2009年，汤富酬课题组首次报道了哺乳动物单细胞的mRNA-seq全转录组分析方法；2011 年，Navin 等人首次实现了人类单个细胞的基因组测序，用来研究乳腺癌肿瘤细胞群的结构和进化。近10年（2010-2020年）单细胞测序发展日新月异。

![image-20211018112035734](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-18-032036.png)

1990 年代初期，Manz 和Widmer 提出了微型化全化学分析系统（miniaturized total chemical analysis system, **μTAS**）的概念，使生物样品的分析更加高效和灵敏。微流控技术作为 μTAS 的关键组成部分，已迅速发展成为生命科学和分析化学领域的前沿研究方法。微流体，也称为“芯片实验室”，是一种以简单有效的方式分析单个细胞的新型工具。

与传统技术相比，微流体技术在分析样品方面具有多项优势：

* 首先，微流控芯片的结构和功能设计灵活，可以满足单细胞分析的需求
* 其次，典型的微流体通道具有几十到几百微米的尺寸，可以处理从皮升到纳升的溶液体积，从而减少样品损失和高灵敏度，并使高通量单细胞分析成为可能
* 此外，多功能单元和微流控芯片的集成可以实现自动化，防止人为操作产生的测量误差

## 单细胞测序过程

主要过程包括单细胞分离、单细胞裂解、核酸扩增、高通量测序、数据处理和数据分析

### **单细胞分离**

与传统测序方法不同，单细胞的分离是这项技术的**关键**，三个词形容： fast, effective, and gentle

目前，分离单细胞样品的方法包括有限稀释、显微操作、激光捕获显微切割（Laser capture microdissection, LCM）、荧光激活细胞分选（fluorescence activated cell sorters, FACS）和微流控技术。

* 有限稀释主要使用手动移液器或移液机器人来分离单个细胞，其每等份细胞数的概率服从泊松分布。这种方法已经过时了，因为它不能排除一些小细胞群
* 手动细胞采摘的显微操作通过结合显微镜和微量移液器，并应用吸力来捕获单个细胞，但是从显微操作中获得的细胞可能会受到机械损伤
* LCM 是一种先进的工具，可在切片和染色处理后从大部分实体组织样本中快速准确地获取单个细胞或细胞区室。该方法成功地保持了细胞形态和结构，并保留了空间位置信息
* FACS 系统通过在各种类型的流式细胞仪中用荧光标签标记细胞的特定表面分子，细胞流快速通过激光束以提供光激发，然后下游的光检测器用于捕获具有特定信号的细胞， 具有高通量，在细胞分选方面非常有效
* 微流控是一种在微观尺度上精确控制流体的新技术，具有体积小、样品消耗少、反应速度快、分选精度高、操作灵活等特点。微流体装置具有多通道结构，通道宽度从 10 到 100 μm 不等，使其能够适应每个单个细胞的大小和体积。大多数微流体设备使用以下微流体原理来隔离单个细胞：流体动力学细胞陷阱、气动膜阀和基于油滴的隔离。目前**最流行的微流体隔离方法是应用微滴将单个细胞封装在惰性载体油中**，从而形成一个封闭空间，降低样品污染的风险

![image-20211018112744818](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-18-032745.png)

### **单细胞裂解**

常见的细胞裂解方法，如物理、化学和酶法

* 物理方法：目前主要存在三种主要的物理细胞裂解形式：机械、热和电。机械裂解主要通过机械力分离细胞膜；热裂解取决于细胞膜蛋白的热诱导变性，需要额外的温度循环；电裂解通过电场诱导的分子重新定向破坏细胞膜
* 化学细胞裂解应用裂解缓冲液并诱导高效裂解以破坏细胞
* 酶促细胞裂解通常使用酶的组合来实现细胞的完全解离，这也是减少DNA断裂的最温和的方法。可以使用蛋白酶，如胃蛋白酶和胰蛋白酶

选择最合适的裂解方法时需要考虑多种因素，包括细胞类型、下游分析、基因组 DNA (gDNA) 纯化的难度

### **核酸扩增**

单个细胞中DNA或RNA等核酸的含量远低于测序所需的样本量（哺乳动物细胞中大约有 10 pg 的总 RNA），因此扩增过程对于后续分析至关重要。

目前，常见的扩增方法有两种：全基因组扩增（WGA）和全转录组扩增（WTA）。

* 目前可用的 WGA 方法包括简并寡核苷酸引发的聚合酶链反应 (DOP-PCR)、多重置换扩增 (MDA) 和多重退火和循环扩增循环 (MALBAC)
* WTA 需要一个逆转录过程来产生 cDNA 样本。大约 10-20% 的 mRNA 在这个阶段被逆转录。目前可用的 WTA 方法包括传统 PCR、改良 PCR、T7 体外转录 (T7-in vitro transcription, IVT) 和 Phi29 DNA 聚合酶介导的 RNA 扩增

在单细胞转录组中，cDNA 扩增从来都不是完全线性的，会导致细胞中cDNA的浓度与实际不成比例。因此可以使用独特的分子标识符 (unique molecular identifiers, UMI) 来标记primary RNA 以减少扩增偏差。

另外，测序前应评估RNA的质量，最常用的方法是Sanger测序，但研究表明droplet digital PCR (ddPCR) 具有更高的优势，它独立于标准曲线和循环阈值（CT）值，大大提高了检测微量核酸和稀有序列的灵敏度、特异性和精确度。

### **单细胞测序**

目前主要有基因组、转录组和表观基因组的测序，帮助反映单细胞发育轨迹的基本构成。

* 单细胞基因组测序，即scDNA-seq，揭示细胞基因组的突变和结构变化，突出体细胞克隆结构，追踪疾病的进化和传播
* 单细胞转录组测序，即 scRNA-seq，可以以单细胞分辨率测量转录组中的基因表达，识别细胞簇中的生物学相关差异
* 单细胞表观基因组测序侧重于不改变 DNA 序列的表型的可遗传变化，涉及 DNA 甲基化、染色质可及性、组蛋白修饰和 DNA 折叠，反映基因组结构变异如何影响细胞表型。其中单细胞亚硫酸氢盐测序 (scBS-seq) 被用作 DNA 甲基化研究的金标准。

生成 scRNA-seq 文库所需的常见步骤包括逆转录成第一链 cDNA、合成第二链 cDNA 和 cDNA 扩增 。在 scRNA-seq中，单个细胞中的所有转录本都通过逆转录转化为 cDNA，其中一些方法支持整个转录组测序，而另一些方法只允许对转录组的 5' 和 3' 末端进行测序。例如，Smart-seq2 支持 cDNA 测序的全读长覆盖，Fluidigm C1就可以自动完成Smart-seq，可以执行 96 次单细胞捕获、裂解、逆转录和预扩增；Drop-seq、inDrop 和 10X Chromium 系统仅提供 cDNA 的 5' 或 3' 端的序列信息，因此不适用于可变剪接模式的分析。

## 单细胞测序的微流控原理

微流控芯片大致可分为三种技术原理：traps-based microfluidics, valves-based microfluidics, and droplet-based microfluidics

![image-20211018114246804](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-18-034247.png)

### **Traps-based microfluidics**

如图A，一个凹槽就是一个细胞”陷阱“，然后很多个陷阱会放在微流控芯片上，能够高效捕获单个细胞，**培养细胞污染风险低**，高达 97% 的陷阱可以捕获单个细胞。陷阱的直径取决于所研究细胞的大小。通过将陷阱直径调整为样本中的平均细胞大小，可以最大限度地减少多细胞情况。

### **Valves-based microfluidics**

阀门用于隔离通道网络的特定区域，允许生成反应室并进行独立的反应。阀门**可以根据需要打开和关闭**，从而允许复杂的操作。基于阀门的微流体系统允许在芯片上进行自动操作，有助于以高精度和控制准确**模拟细胞微环境**的动态。

微型阀门可分为主动机械式、主动非机械式、被动机械式和被动非机械式四大类。一般来说，主动机械微阀因其最佳性能而最常用于微流体系统，而简单的被动阀更适合实际应用。基于阀门的微流体技术由 Quake 实验室于 2000 年首次开发，实现了大规模微流体集成和自动化的突破。他们也利用 (Microfluidic large scale integration, mLSI) 技术在全自动微流控芯片上分离 mRNA、合成 cDNA 和纯化 DNA。

### **Droplet-based microfluidics**

微滴技术可实现小样本量的高通量筛选，可以应用于单细胞水平的多种生物学检测，包括单细胞培养、基因组学和转录组学分析、数字PCR、RNA-seq ，抗体检测，药物递送和筛选，毒性筛选和诊断。

微流控芯片上，液滴会随着一种液相分离另一种不混溶的液体而产生。基于液滴的微流体技术可以精确地操纵微通道中的流体，并通过注入载体油来封装单个细胞，从而可以快速且一致地生成大小可控且均匀的液滴。基于液滴的微流体封装单个细胞并提供理想的微反应器，每个液滴在功能上相当于微孔板中的一个孔，但**反应体积小一百万倍**。每秒可以产生数千个单独的隔室，表面活性剂用于降低表面张力并防止液滴融合。此外，由于微反应器的数量**不受芯片物理尺寸的限制**，液滴本质上是可扩展的。

液滴是通过使用两种不混溶的流体，载体流体（ carrier fluid）和分散流体（dispersed fluid）产生的。

可以通过使用**三种不同几何形状**的微流体装置实现液滴的生成，例如 T-junctions, flow focusing and co-flow

![image-20211018115527219](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-18-035527.png)

大量文献表明，基于液滴的微流体技术已发展成为一种极好的单细胞培养方法。含有单个细胞的液滴可以在微流体装置内原位培养，甚至可以转移用于芯片外培养。例如Wong等人开发了一种基于液滴的微流体技术，用于在癌细胞系或原发性肿瘤的细胞中进行药物筛选。通过这种方式，单个细胞分散在液滴中，并在药物处理 24 小时内成像以评估细胞活力 (Wong AHH, Li HR, Jia YW, Mak PI, Martins RPdS, Liu Y, et al. Drug screening of cancer cell lines and human primary tumors using droplet microfluidics. Sci Rep. 2017;7(1):9109.)

不过，液滴中细胞的封装是随机的，并且在很大程度上依赖于泊松统计，因此基于液滴的微流体技术可能会产生空液滴和包含多个细胞的液滴。研发人员可以通过优化通道设计来最大限度地增加包含单个细胞的液滴数量，以提高单个细胞在生成液滴中的包裹率。

#### scRNA-seq测序方法比较

**基于微孔板的 scRNA-seq 技术**

这个技术依赖于将单个细胞捕获或分选到管或多孔板中进行单细胞分析。

* Smart-seq 旨在提高转录本读取覆盖率并增强对单核苷酸多态性的评估。相比之下，Smart-seq2 可以实现更多读取甚至全长读取覆盖和更高的灵敏度，这被称为 Smart-seq的升级版，并且可以提供有关基因突变、基因选择性剪接和等位基因特异性表达的信息，广泛用于单细胞全长 mRNA 分析。Fluidigm C1 是一个广泛使用的商业平台，它依赖于 Smart-seq ，可在单个芯片上为多达 800 个细胞提供自动化的单细胞裂解、RNA 提取和 cDNA 合成。
* CEL-seq 是第一个在体外使用 cDNA 转录线性扩增的方法，通过混合样本并且使用barcode区分的方法克服了 RNA 起始量小的限制。不过只能用于 3' 端测序
* 此外，还有单细胞标记逆转录测序（STRT-seq）、大规模平行RNA单细胞测序（MARS-seq）、单细胞RNA条形码和测序（SCRB-seq）

**基于微流控的 scRNA-seq 方法**

目前最流行的高通量平台是基于液滴的微流体，即微液滴，优点包括低试剂和样品体积，以及短细胞加载期。inDrop和 Drop-seq 于 2015 年首次应用，使用特定的油来生成包含裂解缓冲液、barcode和细胞的液滴。细胞裂解后，条形码寡核苷酸与释放的 mRNA 的 poly(A) 杂交，确保文库制备和测序。

* inDrop通过水凝胶微球引入寡核苷酸，在液滴中进行细胞裂解和逆转录。水凝胶珠溶解在液滴中，释放出寡核苷酸与 mRNA 杂交，逆转录反应在液滴内进行。然而，inDrop 的主**要缺点是细胞捕获效率极低**，每个细胞只能检测 20-50 个转录本。Drop-seq 库包含 1600 万个条形码，而 inDrop 技术仅产生约 150,000 个条形码，这意味着 inDrop 每次运行处理的细胞更少。**适用于分析非常小的组织样本**，因为它比 Drop-seq 捕获更高百分比的细胞。
* Drop-seq 倾向于使用条形码珠（ barcoded beads），而 inDrop 则使用条形码水凝胶微球（barcoded hydrogel microspheres）。 条形码珠包括了扩增序列、barcode、UMI和捕获单细胞 mRNA 分子的寡核苷酸 (dT) 序列。珠子连同附着的寡核苷酸和退火的 mRNA 都从液滴中释放出来并组合到一个管中，然后进行逆转录
* 10X genomics使用Gel bead in EMulsion (GEM) 中封装数千个单细胞，获得的数据比 Drop-seq 质量更高（在每个细胞中检测到更多的基因并降低了技术噪音）

![image-20211018120620194](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-18-040620.png)

<br>


# 2.2.1 研究 | 2018-单细胞转录组探索癌症免疫治疗获得性抗性机理

刘小泽写于2019.5.2

> 这一篇是免疫相关，美国Fred Hutchinson癌症研究中心于2018年9月发表在NC的[Acquired cancer resistance to combination immunotherapy from transcriptional loss of class I HLA](https://www.nature.com/articles/s41467-018-06300-3)，本文的重点在于单细胞数据的分析部分

由于是第一次接触免疫相关，因此相关的背景知识需要列一下

## 背景知识

* **HLA（human leukocyte antigen）**：人类白细胞抗原，它是一组紧密连锁的基因群，可以控制细胞间相互识别、调节免疫应答。HLA的部分基因可以编码细胞表面抗原受体，成为自身细胞的"独特的信号"，可以作为免疫系统区分自身和异体物质的基础

  HLA抗原决定簇在Chr6的短臂上，分为三类：Class I、II、III，是目前已知的人类染色体中基因密度最高，也是多态性最为丰富的区域，[IMGT/HLA](https://app.gitbook.com/s/-MCv4XZpjUYDdSrsYVkw/02/IMGT/HLA) 数据库中就记录了HLA Class I、II的基因型，[截止2019.4已经记录了22,528个等位基因](https://www.ebi.ac.uk/ipd/imgt/hla/stats.html)

  <img src="https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-10-12-042829.png" alt="image-20190502105550566" data-size="original">

  (图片来自文章：Role of MHC-Linked Susceptibility Genes in the Pathogenesis of Human and Murine Lupus)

  Class I基因编码的抗原受体几乎分布于整个细胞表面，作用是呈递内源性抗原；

  Class II编码的白细胞抗原只有在吞噬细胞(如巨噬细胞、B细胞)的表面存在，作用是呈递外源性抗原；

  Class III 编码的产物主要参与[补体系统](https://zh.wikipedia.org/zh/%E8%A1%A5%E4%BD%93%E7%B3%BB%E7%BB%9F) 的调节

  Class I、II基因参与免疫调节，因此它们的等位基因是研究热点(I类主要看A、B、C基因；II类主要是DR、DQ、DP基因)
* **Merkel细胞癌**：皮肤Merkel细胞癌(Merkel cell carcinoma, MCC)是一种罕见的皮肤侵袭性恶性肿瘤，由Merkel cell polyomavirus (MCPyV)病毒引起。常见于浅肤色的老年人，有局部复发和区域淋巴结转移的倾向。其他名称如：皮肤神经内分泌癌或原发性小细胞癌、小梁细胞癌等。[最早于1972年由Toker报道](https://www.uptodate.com/contents/pathogenesis-clinical-features-and-diagnosis-of-merkel-cell-neuroendocrine-carcinoma/abstract-text/5009611/pubmed) ，发病人群中95%为白种人，男性发病率高于女性，且随着年龄增长稳步升高。 疾病相关死亡率约为33%\~46%，局部MCC患者的5年总生存率为55.6%；晚期疾病患者区域性淋巴结患者5年生存率为35.4%，远处转移患者约为13.5%。临床试验最新结果表明，免疫检查点抑制剂可用过释放抵抗免疫原性肿瘤的抗肿瘤免疫力来改善治疗结果 来自：<http://news.medlive.cn/cancer/info-progress/show-141930_53.html>
* **免疫检查点：** 调节T细胞活化和增殖的主要分子。首个免疫检查点抑制剂靶向的是CD152（CTLA-4），ipilimumab是同类中第一个证实临床试验中转移性黑色素瘤患者总生存率改善的药物（2011年获批）。后来如PD-1/PD-L1抗体问世。免疫检查点抑制剂在多种癌症中的成功应用以及MCC的免疫易感性再次为研发更有效的MCC治疗方案带来希望。
* **ICIs(immune checkpoint inhibitors)/ICB(immune checkpoint blockade):** 免疫检查点抑制剂。T淋巴细胞是抗肿瘤免疫应答中的主要效应细胞，通过识别肿瘤特异性抗原(比如致癌病毒、分化抗原，表观遗传调控分子, 以及致癌过程中产生的新抗原等)产生细胞毒性反应。正常状态下, T淋巴细胞通过表达一系列激活性(促进T细胞分化增殖)和抑制性(抑制T细胞分化增殖)受体来调控免疫平衡，既可以调控生理性免疫应答, 又不会过度激活免疫系统而造成机体自我损伤。

  但是肿瘤微环境中，随着肿瘤抗原对T细胞的持续刺激, T细胞表面的一系列抑制性受体表达水平升高，同时配体在癌细胞或抗原呈递细胞表面表达水平增高，将抑制T细胞活化增殖并诱导T细胞凋亡，从而导致免疫抑制性肿瘤微环境形成，造成免疫逃逸。

  T细胞表面表达的抑制性受体主要包括CTLA-4、PD-1、Tim-3、LAG-3、KIR、CD47、TIGIT、CEACAM1、A2AR、IDO1、BTLA、VISTA、CD276、VTCN1等，这些**抑制性受体所对应的抑制性信号通路**称为免疫检查点

  （来自 <https://www.wjgnet.com/1009-3079/full/v25/i19/1714.htm>
* **肿瘤新生抗原负荷（tumor neoantigen burden， TNB）**：癌细胞不断扩增分裂，会产生很多新的突变。新的突变有一部分会形成新生抗原，新生抗原可以激活免疫系统。因此肿瘤新生抗原负荷越高，对这个药物的反应性就有可能越大
* **外周血单核细胞(peripheral blood mononuclear cell，PBMC)**：

## 研究背景

癌症免疫治疗在临床上前景巨大，但是目前有许多使用这种技术的患者后来出现复发的状况。

本文团队在研究Merkel细胞时发现，20%的患者对免疫治疗有初步反应，但后来存在复发的情况，预后不理想。为了提高预后，理解后期/获得性免疫治疗耐受性的机理是有必要的。利用细胞免疫治疗临床试验，可以表征T细胞《=》抗原的相互作用，进而了解复杂的肿瘤细胞-免疫细胞之间的关系，并且为避免肿瘤复发应采取何种免疫联合疗法提供帮助。

## 研究方法

> 大体思路就是：癌症患者免疫治疗=》发现抗性复发=》scRNA-Seq=》研究获得性抗性机理

最初采用联合疗法对两名Merkel患者进行治疗，发现肿瘤逐渐缩小，他们出现的显著的肿瘤消退都与活化的CD8+T细胞浸润到消退的肿瘤组织有关。

这两例患者分别为：

* The **primary** patient (2586-4) received hypofractionated radiation for HLA

  upregulation to some but not all disease sites
* The second **validation patient** (9245-3) is a 59-year-old man with metastatic MCC that had initially presented as stage IIIB disease, now metastatic at multiple sites

取了患者的血液和肿瘤，做了免疫组化、分选、外显子测序、10X scRNA-Seq、qPCR

### **外显子测序**

两个患者2586-4 (discovery) and 9245-3 (validation)，从外周血单核细胞(peripheral blood mononuclear cell，PBMC)中分离DNA作为germline对照，然后取出肿瘤免疫治疗前以及获得性抗性的样本。

患者2586-4采用Agilent SureSelect Human All Exon V6 芯片+HiSeq 2500，annovar比对hg19，MuTect检测somatic突变；

患者9245-3采用xGen建库+Hiseq4000(100X)，BWA比对到hg19，GATK call variants

### **单细胞测序\[重头戏]**

**1-- 原始数据**

患者2586-4：利用10X 3' Chromium v2.0平台建库 + Hiseq2500 "rapid run"模式；

[GSE117988](https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE117988)

```
ID                    Description

GSM3330559    Tumor Disc Pre
GSM3330560    Tumor Disc AR

GSM3330561    PBMC Pre
GSM3330562    PBMC Disc Early
GSM3330563    PBMC Disc Resp
GSM3330564    PBMC Disc AR
```

患者9245-3：利用10X 5' V(D)J 进行cell washing, barcoding and library prep+ NovaSeq 6000(gene expression) + Hiseq4000 (V(D)J)

[GSE118056](https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE118056)

```
ID                    Description

GSM3317833    PBMC Relapse - L001
GSM3317834    PBMC Relapse - L002
GSM3317835    Tumor Relapse - L001
GSM3317836    Tumor Relapse - L002
```

**2-- Transcriptome alignment, barcode assignment and UMI counting**

患者2586-4(discovery) 利用Cell Ranger v2.0.0， 9245-3 (validation)利用Cell Ranger v2.1.0，进行sample demultiplexing, barcode processing and single-cell gene counting。

首先，利用`mkfastq`流程将原始的BCL文件转为fastq；然后，利用`count`流程整合了STAR将每个fastq与hg38和Merkel cell polyomavirus序列（HM011556.1）比对，比对后的reads根据cell barcode以及UMIs进行过滤

> **Cell barcodes** with **1-Hamming-distance** from a list of known barcodes were considered； **UMIs** with sequencing **quality score >10%** and not homopolymers were retained.

接着利用`aggr`流程将Tumor与PBMC样本整合，生成两个gene-barcode表达矩阵(tumor和PBMC的)，同时进行了测序深度的校正

**3-- Data normalization and correction**

根据这篇文献的方法：Miller, N. J. et al. [Tumor-infiltrating Merkel cell polyomavirus-specific T cells are diverse and associated with improved patient survival](http://cancerimmunolres.aacrjournals.org/content/5/2/137). Cancer Immunol. Res 5, 137–147 (2017).

利用Seurat包中的ScaleData 函数进行校正和标准化：

* Only genes with **at least one UMI count detected in at least one cell** were retained for analysis.       &#x20;
* **Library-size normalization** was performed for each cell.
* UMI counts were **scaled** by the **total number** of UMI

  **in each cell** and **multiplied by the median** of the total UMI counts **across cells**        &#x20;
* **log2-transformed** and corrected for unwanted sources of variation   &#x20;

corrected-normalized **gene-barcode** matrix 文件将作为**下游降维、聚类**的输入文件；

normalized **gene-cell barcode** matrix文件将进行**MAST analysis**

**4-- Gene expression analysis: discovery patient tumor**

过滤得到了**7431 tumor cells** (2243 cells before and 5188 cells after T cell therapy)，然后利用corrected-normalized gene-barcode matrix进行PCA、tSNE

首先，利用Seurat挑出了873个表达量变化最大的基因进行PCA

> 根据：log-mean expression values > 0.0125 and dispersion (variance/mean) >0.5

接着，挑出前10个PCs进行tSNE

> **One thousand iterations** of tSNE using a **perplexity value of 30** were performed

然后利用MCC tumor markers(NCAM1, ENO2, CHGA and KRT20 and TILs)细胞分群、聚类，得到T细胞免疫**治疗前的1984个癌细胞和治疗后获得抗性的5131个细胞；**

又利用MAST的R包对前后的细胞进行差异分析，利用的是normalized gene-cell barcode，其中利用了cellular detection rate (CDR) 作为协变量，校正可能影响细胞中检测到基因数的生物和技术偏差。差异基因定义为FDR为5%，fold change>1.3

**5-- Gene expression analysis: discovery patient PBMC**

PBMC涉及到了4个时间点：pre-treatment、early post treatment day+27、responding post treatment day +37、replace/acquired resistance post treatment day +64

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-10-12-042838.png)

过滤后总共得到12,874个细胞，也是利用corrected-normalized gene-barcode matrix进行PCA、tSNE分析。

首先，选择1203个变化最大的基因； 然后选10个PCs进行tSNE(同上)；细胞聚类使用Seurat的FindCluster函数，得到13个不同的细胞群(参数：neighborhood size of 40 and resolution of 0.6)，根据分群的结果，移除了三个在red blood cell and megakaryocyte markers中富集的cluster，总共剩下11,021个细胞； 之后利用FindMarkers函数根据marker的富集情况，给细胞群加标签，结果有9个不同的cluster：CD4+ T cells, CD8+ T cells, CD8+ effector T cells, B cells, NK cells, CD14+ monocytes, CD16+ monocytes, myeloid cells and dendritic cells； CD8+与CD8+ effector T细胞的在第三个时间点（responding阶段）差异基因分析也是利用MAST。代码见Supplementary Data 3.

**6--Gene expression analysis: validation patient**

利用corrected-normalized gene-barcode matrix去做PCA、tSNE，方法同上=》19个不同的clusters。代码见Supplementary Data 4.

### 单细胞部分研究结果

重点关注单细胞部分，免疫原理部分这里不做深入研究。

> **先是PBMC细胞**

**首先是discovery(2586-4)患者PBMC在4个时间点鉴定的活化CD8+ T细胞分群：**

* 治疗前（pre-treatment）
* 治疗后早期（early post-treatment (day +27）；
* 治疗反应 （treatment response (day + 376)；
* 晚期/获得抗性（late/acquired resistance (day +614)

**对11021个细胞进行tSNE分析的分群结果显示：**

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-10-12-042844.png)

**代表性的marker基因如下：**

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-10-12-042849.png)

（从b-i都是为了辅助细胞分群鉴定，蓝色表示基因表达，且颜色越深表达量越高）

**另外根据4个先后时间点做出的细胞分群如下**

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-10-12-042859.png)

其中响应免疫疗法的激活的CD8+淋巴细胞簇是箭头指的红色部分，将不同时间点的CD8+对比发现Day +376(response时间点)的CD8+细胞簇最多

继续将Day +376(response时间点)的red activated cluster (n =170) 与 blue effector/EM cluster (n = 429) 进行差异分析，得到45个差异基因，热图展示一部分：

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-10-12-042950.png)

> **接下来是肿瘤组织**，时间点和PBMC不同，它只有两个治疗前的原位癌pre-treatment、复发癌 late relapse/acquired resistance

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-10-12-042953.png)

首先可以看到的是，这个组织中肿瘤细胞占比较大，并且治疗前后分的还是很开的，意味着治疗前（蓝色）与复发后（橘色）差别还是很大的，为了看这个差异是由哪些基因导致的，又做了差异分析+热图，得到255个差异基因

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-10-12-042957.png)

其中箭头标出的位置是HLA-B，它在复发瘤中表达量降低，那么下降的趋势如何呢？

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-10-12-043002.png)

发现与HLA-A相比，MCC在获得性抗性中显著下调HLA-B

之后在另一个患者那里进行验证（11267个细胞）：也发现类似的情况，只不过是HLA-A表达量降低

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-10-12-043006.png)

## 讨论

晚期/获得性免疫抗性是免疫治疗治愈的障碍，本文对两名接受T细胞免疫治疗与ICI的患者，他们都有持续的免疫治疗反应，之后是抗性产生。观察到CD8+ T细胞浸润到萎缩的MCC肿瘤中，支持T细胞介导的消退，当肿瘤复发的时候，存在着**明显的选择性转录下调HLA**。

肿瘤能够通过沉默能够被T细胞识别的蛋白的编码基因进而实现肿瘤的免疫躲避。HLA的三组基因通常全部同时关闭或打开，**HLA三组基因只要其中一个被关闭，那么肿瘤细胞就能逃避T细胞**

单个或所有的I类HLAs基因缺失导致的免疫逃避被描述为对细胞免疫治疗产生细胞免疫抗性机制和抗pd -1检查点抑制剂机制。第I类基因座的转录抑制也许也是其他免疫治疗产生耐药性的原因（包括免疫检查点治疗），这项发现将为设计新型免疫治疗手段带来积极影响。


# 2.2.2 研究 | 2018-人类结直肠癌单细胞多组学分析

刘小泽写于2019.3.31

> 这一篇理解难度稍大，当做背景知识的了解，至于其中数据的分析细节文中没有提及

之前一次只能研究单细胞层面的基因组、转录组或DNA甲基化组其中一种，不能在一个细胞中同时研究多个组学，2016年汤富酬研究组将三重组学研究方法scTrio-seq（single-cell triple omics sequencing technique）发表在[Cell Research](http://www.cell-research.com/arts.asp?id=2231)上。2018年11月北医三院付卫团队、乔杰团队和北大生命科学学院汤富酬团队又在Science合作发表了[Single-cell multiomics sequencing and analyses of human colorectal cancer](http://science.sciencemag.org/content/362/6418/1060)

文章优化了单细胞多组学测序技术，并对原发瘤、淋巴转移和远端转移区域分别采样，首次从单细胞分辨率解释了结直肠癌的发生与转移过程中中基因组拷贝数变异、DNA甲基化异常及基因表达改变的特点，证明了用单细胞多组测序重建遗传谱系和追踪其表观基因组和基因表达动力学的可行性

## **背景知识**

* **淋巴转移（lymphatic metastases）和远端转移(distant metastases)**

  [癌症可以以2种方式出现在淋巴结中](https://zhuanlan.zhihu.com/p/27480306)：一种是从淋巴结形成的肿瘤叫淋巴瘤，另一种是从其他部位扩散叫淋巴转移（更为常见）。淋巴转移一般会和乳腺癌、前列腺癌、肺癌、结直肠癌的不良预后相关，淋巴转移虽然不是致死因素，但会导致癌细胞扩散到重要器官。 [远端转移](https://zh.wikipedia.org/wiki/%E9%81%A0%E7%AB%AF%E8%BD%89%E7%A7%BB)也叫恶性转移，肿瘤细胞从原始发生的部位借由侵入循环系统，转移到身体其他部位继续生长，几乎不可能使用外科手术切除根治，多半只能用大范围循环全身的放射治疗或化疗等手段来抑制已转移的癌细胞。

  2017年发表在Science的[Origins of lymphatic and distant metastases in human colorectal cancer](http://science.sciencemag.org/content/357/6346/55) 中描述了结直肠癌之前的肿瘤扩散的TNM层级是：primary tumor(T)=》lymph node system(N)=》distant metastases(M)，但是有临床证据表明移除淋巴结并不会提高病人存活率，因此N和M之间的关系可能并不是简单的上下级。文章发现淋巴结和远端器官存在独立起源的证据，只是淋巴结转移机制形成的更快，因此淋巴转移形成更早，发生更频繁。因此作者不推荐直接假设淋巴结会引发远端转移而直接切除(<https://www.medscape.com/viewarticle/882502>)
* **结直肠癌colorectal cancer(CRC)** ：是结肠癌和直肠癌的统称，是消化道恶性肿瘤之一。2018年[Cancer Stats](https://www.ncbi.nlm.nih.gov/pubmed/29313949)统计显示：结直肠癌在男性中发病率第2死亡率第3，女性发病率第4，死亡率第3。

  约95％的结直肠癌是由结肠和直肠内壁的腺细胞发展而来，癌症通常开始于内壁最内层，并缓慢生长到外层。(<http://www.cancer.org/Cancer/ColonandRectumCancer/DetailedGuide/colorectal-cancer-what-is-colorectal-cancer>)

  结直肠癌发生率在40岁开始增加，60～75岁达高峰，结肠癌在女性患者较常见；直肠癌在男性患者常见；大约5%的结肠癌或直肠癌患者在结肠和直肠有两个或更多病灶，并非简单从一个病灶转移至另一个所致(<https://www.msdmanuals.com/>)

  结直肠癌典型的分子特征是：基因组不稳定性、表观遗传学异常、基因表达紊乱
* **瘤内异质性 Intratumoral heterogeneity (ITH)** ：恶性肿瘤的特征之一，[肿瘤异质性](http://www.xybiotech.com/)包括**肿瘤间异质性**（不同肿瘤细胞之间的基因与表型不同）和**肿瘤内异质性**（相同肿瘤细胞以内的基因与表型也不同），其中肿**瘤内异质性又包括空间异质性**（相同肿瘤不同区域不同，如未扩增的细胞背景中有成簇扩增细胞；少量扩增背景中有未扩增的细胞；孤立的细胞扩增【利用多位点取样方案或者[tissue microarrays (TMAs)](https://www.ncbi.nlm.nih.gov/pmc/articles/PMC6087434/) 调查】）**与时间异质性**（原初肿瘤与次生肿瘤不同）。

  异质性的产生是因为同一肿瘤由多种不同基因组特征的细胞组成，每一种细胞构成一个**亚克隆(subclone)**。肿瘤组织会存在对治疗药物有抗性的亚克隆，但比例不高。当治疗的药物除去敏感的亚克隆时，抗性的亚克隆细胞会不受药物抑制并且少了空间竞争，因此会加快生长速度，导致肿瘤复发或者发生转移，而且转移后的亚克隆对同种治疗方案也会有抗性。因此研究肿瘤细胞的亚克隆以及不同的亚克隆的转移是一个热点，尤其是亚克隆是如何从原位癌转移到其他脏器而形成转移癌。[2017的一篇文章专门研究了转移癌亚克隆与原位癌亚克隆的进化关系](https://academic.oup.com/annonc/article/28/9/2135/3979215) ，他们发现结直肠癌肿瘤转移癌高深度测序就可以找到肿瘤的大部分基因组变异，另外验证了"**转移癌多克隆起源说**(转移癌是由多个起源于原位癌的亚克隆发展而来，而非由单个细胞发育)"，发现了结直肠癌的淋巴与远端**并行转移** 。

## **方法**

* **scTrio-seq2技术**：用于somatic copy number alterations (SCNAs)拷贝数变异、DNA甲基化特征、细胞连续的转录信息；整合了单细胞重亚硫酸盐测序（scBS-seq）用于全基因组甲基化分析；研究的细胞数量从之前的25个增至1900个
* 分析了12个CRC患者（III期或IV期）的约1900个单细胞，7.6Tb高质量测序数据。DNA甲基化研究中平均每个细胞测序量为4.1Gb，平均覆盖到全基因组内870多万CpG位点；转录组研究中每个细胞测序量为0.9Gb，平均覆盖3700多个基因
* 多区域采集了10个患者的原发瘤、淋巴转移瘤或远端转移瘤样本(利用两种不同来源的细胞，可以发现每个患者因突变而产生的遗传谱系)
* 文章实验流程 图A是取样：**治疗前后的肿瘤区域**（包括原发瘤primary tumor，PT；淋巴结转移位 lymph node metastasis, LN；肝转移位 liver metastasis, ML；化疗后肝转移位 posttreatment liver metastasis, MP），然后测序分析了基因组、转录组、甲基化组；

  图B是化疗6个周期后的患者CRC01取样：一共取了ML（4个）、MP（5个）、LN（3个）、PT（4个）共16个肿瘤区域

  ![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-10-12-043355.png)
* **单细胞甲基化数据处理**

  首先raw reads去接头、引物、低质量碱基，然后利用`Bismark`（V0.7.6）clean reads比对到hg19基因组，PCR重复利用`samtools rmdup`(V 0.1.18)去除，数据统计(比对数、比对率、CpG位点数、亚硫酸氢盐转化率等)\[其中亚硫酸氢盐转化率是由非甲基化的lamda DNA的spike-in计算的]，CpG位点小于200万个或亚硫酸氢盐转化率小于98.5%的细胞被排除。仅使用甲基化水平大于0.9或小于0.1的CpG位点进行总体甲基化水平计算。

  启动子区设定为转录起始位点的上游1 kb到下游0.5 kb。为了计算RefSeq基因各基因体的DNA甲基化水平，将每个基因体划分为100个等分，并将其上下游侧翼区域(15 kb)分别划分为10个等分。基因组注释信息从UCSC获取。利用bedtools(V 2.17.0)和自定义脚本(没放Git链接)计算平均甲基化水平，设置滑动窗口大于等于3个CpG位点
* **根据甲基化测序数据估计拷贝变异数**

  主要基于Garvin等人开发的[Ginkgo算法](https://app.gitbook.com/s/-MCv4XZpjUYDdSrsYVkw/02/10.1038/nmeth.3578%20Medline) ，基因组被分成10856个不等长的bins，长度中位数为250kb，并根据算法的过滤器排除了一些异常的bins。

  BED文件是利用bedtools从BAM文件得到。每个bin的read counts值利用所有bins的count平均值进行标准化，采用低水平均一化(Lowess normalization)来校正基因组GC含量的偏差。另外，以正常的二倍体细胞作为对照，减少scRS-seq的其他误差。利用Circular binary segmentation (CBS) 对copy number文件进行分隔，参数为"`alpha = 0.0001`"和"`undo.prune = 0.05`" 。CBS分隔后，每一段的所有bins的计数重置为这一段的bin count的中位数。每个单细胞的基本拷贝数由smallest sum-of- squares (SoS) error和 scaled copy-number profile (SCNP)决定，其中SCNP又进一步四舍五入，最后得到了整数值copy-number profile (FCNP)。利用 GISTIC2.0 (<https://software.broadinstitute.org/cancer/cga/gistic>) 鉴定了重要的SCNAs和潜在的基因靶点。
* **TCGA数据分析**

  从<https://tcga-data.nci.nih.gov/docs/publications/coadread_2012/获得已发表的人类CRC的SCNA片段数据，用于SCNA频率统计(不包括X染色体)。将本文的数据与TCGA的进行比较时，本文研究的CMS3类型的患者CRC02是找不到对应的，只有Affymetrix> SNP 6.0芯片得到的178个non-hypermutated样本。SCNA谱进一步转变成长度不等的bins用于单细胞SCNA统计，拷贝数为>2.5的bin表示扩增，小于1.5表示缺失。Circos图是根据<https://github.com/venyao/shinyCircos制作的。>
* **WGS数据处理**

  raw reads =》trimmed =》[BWA](http://bio-bwa.sourceforge.net/) mem(V 0.7.12) 比对到hg19 =》 samtools sort =》[Picard](https://broadinstitute.github.io/picard/)(V1.139) merge BAM文件 + 标记重复 =》BAM文件用[GATK](https://software.broadinstitute.org/gatk/)(V 3.4-46) 预处理=》[muTect](http://www.broadinstitute.org/cancer/cga/mutect) (V 1.1.4) call SNVs ，自己脚本过滤=》取每个患者的外周血或邻近正常组织作为对照(somatic variants)，[在线Venn图](http://bioinformatics.psb.ugent.be/webtools/Venn/)做出SNV数量
* **单细胞RNA-seq处理**

  利用[汤教授自己的方法](https://app.gitbook.com/s/-MCv4XZpjUYDdSrsYVkw/02/10.1038/nmeth.1315%20Medline)得到的数据处理：预处理过程都一样，然后用[STAR](https://github.com/alexdobin/STAR)(V 2.5.0) 2步比对到hg19，[Cufflinks](https://cole-trapnell-/%20lab.github.io/cufflinks/)(V 2.2.1) 使用默认参数进行FPKM定量；

  利用[multiplexed scRNA-seq方法](https://app.gitbook.com/s/-MCv4XZpjUYDdSrsYVkw/02/10.1186/s13059-018-1416-2%20Medline)得到的数据，先利用read2的barcode信息将reads分配到每一个细胞，每个细胞中read2对应的read1利用read ID分隔，read1中的[TSO序列](https://kb.10xgenomics.com/hc/en-us/articles/360001493051-What-is-a-template-switch-oligo-TSO-) 利用自己的脚本过滤掉，然后利用[Tophat](http://ccb.jhu.edu/software/tophat)(V2.1.1) 单端比对到hg19，利用UMI实现TPM标准化(文中说道：大部分的表达量都使用`log2(FPKM + 1)`或者`log2(TPM/10 + 1)` ) ，然后统计了mapped read numbers, mapping ratios, RefSeq gene numbers等信息，根据 `FPKM > 1 or TPM > 1`去除了比对率 < 20%或有表达量的Refseq基因数量 < 1500

## **结果**

### **根据甲基化数据得到的基因组拷贝数变异来推断基因谱系**

> Genomic alterations in tumors provide markers for lineage tracing. 克隆变异出现在肿瘤早期阶段，亚克隆拷贝数变异标志着亚型的出现
>
> 结直肠癌患者单个癌细胞的染色体拷贝数变异谱+高精度的染色体内断点信息=》谱系追踪=》原发位肿瘤（PT）的亚克隆结构通常比转移位肿瘤更复杂

结果得到了5个患者的90个细胞以上的甲基化数据，细胞被分成了不同的基因亚型

其中，CRC01基于21个亚克隆的拷贝数断点，鉴定了来自2个不同谱系（A、B）的12个亚型，其中每个亚型都有4-8个亚克隆的断点(断点的上下位置和拷贝数变异数增加、减少对应)，A5亚型同时出现在了肝转移位和淋巴转移位，表明这两种移位有共同起源，这5个病人的原癌亚克隆结构比其他转移类型更复杂

CRC01患者癌细胞的单细胞染色体拷贝数变异谱：

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-10-12-043350.png)

CRC01患者亚克隆拷贝数断点：

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-10-12-043347.png)

CRC01患者亚克隆结构：

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-10-12-043338.png)

### **甲基化异质性**

结肠直癌细胞的DNA甲基化水平要低于癌旁的正常上皮细胞，同一肿瘤组织中同一谱系的甲基化程度相近，不同谱系出现差别。低甲基化基因组区域显著富集在LTR（long terminal repeats）、LINE-1 (long interspersed nuclear elemnt 1)和异染色质区域(H3K9me3)，而高甲基化的基因组区域显著富集在CpG岛、H3K4me3和开放染色质区域。

以CRC01患者为例：

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-10-12-043335.png)

甲基化的异质性主要来自同一个患者肿瘤内不同亚克隆之间的DNA甲基化差异，而不是同一个亚克隆内部不同细胞间的DNA甲基化差异

### **甲基化谱和基因表达谱的相互关系**

启动子区域的甲基化与相应基因的表达呈显著的负相关，而基因区的甲基化与相应基因的表达呈正相关

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-10-12-043333.png)

【The gray lines represents individual cells. The blue line represents the mean value for each patient. TSS, transcription start site; TES, transcription end site.】

### **同一遗传谱系的肿瘤细胞在转移过程中DNA甲基化及基因表达的变化情况**

同一患者同一谱系的肿瘤细胞从原发灶到转移灶全基因组DNA甲基化水平基本稳定，组内局部区域可能会有比较大的波动

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-10-12-043331.png)

### **结直肠癌去甲基化特点**

每个亚型内的[去甲基化](https://www.zhihu.com/question/65825562/answer/293106145>) 程度是一致的，不同亚型程度不同 ；

正常上皮细胞的基因组区域甲基化越高，它就越容易发生去甲基化；

去甲基化的程度与基因组重复序列L1（long interspersed nuclear elemnt 1）以及癌旁正常组织中H3K9me3修饰的密度呈正相关，与H3K4me3标记和正常组织的开放染色质区域密度呈负相关；

有趣的是，L1比LINE-2更活跃，在所有病人的癌细胞中显示了更强的去甲基化能力，这个与胚胎发育中情况相反(胚胎发育过程中L1一般比L2去甲基化能力弱) ，说明在肿瘤发生与发展过程中，L1和异染色质区域产生了异常的去甲基化过程，打破了正常的发育规律

癌细胞相比于癌旁细胞的DNA甲基化水平：

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-10-12-043327.png)

### **染色体水平的DNA甲基化异常和基因组不稳定性**

结直肠癌细胞中6条染色体（4号、5号、8号、13号、18号、和X染色体）倾向于发生更强烈的DNA去甲基化，其中三条低甲基化染色体（8、13和18）在TGCA和研究的患者中都有较高的拷贝数变异。结合WGS结果发现，有5条第甲基化的染色体(4号、5号、8号、13号、和X染色体)的单核苷酸变异(SNVs)发生显著富集

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-10-12-043323.png)


# 2.2.3 研究 | 2020-单细胞分析揭示葡萄膜黑色素瘤新的进化复杂性

刘小泽写于2020.5.5

> 看这篇的原因是看到它的方法部分写的很详细，提供了很多参数可以参考。但是CNV和免疫部分我研究不深，所以下面也不会介绍很详细

## 1 文章信息

题目：Single-cell analysis reveals new evolutionary complexity in uveal melanoma

发表日期：2020年1月24日

杂志：Nature Communications

文章在：<https://www.nature.com/articles/s41467-019-14256-1>

DOI：<https://doi.org/10.1038/s41467-019-14256-1>

附件在：<https://static-content.springer.com/esm/art%3A10.1038%2Fs41467-019-14256-1/MediaObjects/41467_2019_14256_MOESM1_ESM.pdf>

![图1](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-05-031821.png)

## 2 摘要

葡萄膜黑色素瘤（UM）是一种高度转移性癌症，与皮肤黑色素瘤不同，它对免疫检查点疗法无反应。

> 葡萄膜恶性黑色素瘤是成年人中最多见的一种恶性眼内肿瘤，在国外其发病率占眼内肿瘤的首位，在国内则仅次于视网膜母细胞瘤，居眼内肿瘤的第二位。此瘤的恶性程度高，眼后极部是好发部位。**易经经血流转移，85%转移至肝脏，预后较差。**

作者使用来自**8个原位癌和3个转移癌样本的59,915个肿瘤和非肿瘤细胞**进行单细胞测序，来检查肿瘤微环境。肿瘤细胞展示了新的亚克隆细胞基因组复杂性和转录状态。

> 肿瘤微环境是一个动态网络，它包括肿瘤细胞、细胞外基质和间质组织等，是影响肿瘤转移的关键因素

肿瘤浸润免疫细胞检测到了之前无法识别的多种细胞类型，包括主要表达LAG3（一种免疫检查点的marker）而非PD-1或CTLA-4的CD8 + T细胞。

> 免疫检查点是指免疫系统的内在调控机制，可保持自身耐受性，并有助于避免在生理性免疫应答期间的附带损伤 免疫检查点抑制剂的前世今生：<http://rehab.dxy.cn/article/503316>
>
> * CD4+ 或 CD8+ 的 T 细胞表面存在的一种免疫球蛋白，被称之为细胞毒性淋巴细胞抗原 4，亦称为CTLA-4。 CTLA-4 调节初始和记忆性 T 细胞的早期活化程度
> * 1992 年，日本学者 Ishida 从凋亡的小鼠 T 细胞杂交瘤 中发现了 PD-1。因其可令 T 细胞失活，将其命名为程序性死亡受体 1，亦称为PD-1。PD-1 主要限制慢性炎症、感染或癌症中的 T 细胞活性，从而限制自身免疫

V(D)J分析发现了克隆扩增的T细胞，表明它们能够发起免疫反应。

> Illumina的免疫学研究综述：<http://web.illumina.com.cn/landing/products_view.asp?newsid=155>
>
> B细胞和T细胞构成了免疫系统的适应性分支，并能够识别繁多的抗原。 与体细胞相比，B 细胞和 T 细胞是独特的，其发育和成熟是由生殖细胞系中未编码的 DNA 序列决定的。相反，在成熟过程中，这些细胞经历了**可变区（V）、多样区（D）和接合区（J）**&#x57FA;因片段的重排，以便形成独特的序列。

Class 1B UM的惰性肝转移被克隆扩增的浆细胞浸润，表明具有抗体介导的免疫

> UM分类参考：<https://www.myuvealmelanoma.com/health-care-professionals/decisiondx-um-summary/>
>
> * Class 1A: Very low risk, with a 2% chance of the eye cancer spreading over the next five years
> * Class 1B: Low risk, with a 21% chance of metastasis over five years;
> * Class 2: High risk, with 72% odds of metastasis within five years.

最后，对于高危UM患者，LAG3可能为新的免疫检查点抑制剂研究提供思路

## 3 方法

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-05-063543.png)

### **3.1 样本选择**

手术切除眼或肝后立即分离肿瘤区域的单细胞。转移性肿瘤组织特意从肿瘤-肝脏交界的远端选取，避免混入正常的肝脏组织。其余肿瘤组织样本则进行DecisionDx-UMseq的DNA和RNA分析

> DecisionDx-UM是一种预后测试，可准确判断与眼部黑色素瘤相关的转移风险 (<https://en.wikipedia.org/wiki/DecisionDx-UM>)

### **3.2 单细胞RNA测序**

总共11个样本，选择10X测序，样本编号与取样位点对应如图

![样本编号与取样位点对应如图](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-05-095108.png)

* UMM061, UMM062, UMM063, UMM064, UMM065, UMM066, UMM069, UMM067L, UMM041L以及BSSR0022样本使用三种文库制备方案：Chromium Single Cell 5’ Library & Gel Bead Kit v2、Chromium Single Cell V(D)J Human T Cell Enrichment Kit、Chromium Single Cell V(D)J Human B Cell Enrichment Kit。
* UMM059样本使用Chromium Single Cell 3′ Library & Gel Bead Kit v2文库制备。
* 5’端文库可以捕获10,000细胞，3‘端文库(UMM059样本)可捕获5,000细胞。每个样本使用独立的Chromium Single Cell A Chip。3‘和5’基因表达文库使用 NextSeq 500测序，B细胞和T细胞的VDJ文库使用MiSeq测序

> 关于10x Genomics 建库流程，可以看： <http://www.novogene.com/views/default/data/documents/10x_yk.pdf>

### **3.3 单细胞DNA测序**

UMM069 和 UMM041L样本使用Chromium Single Cell DNA Library & Gel Bead Kit + Chromium Single Cell C and D Chips进行文库制备，捕获500细胞。使用NextSeq 500测序

### **3.4 DecisionDx-UMseq的DNA和RNA分析**

肿瘤RNA样本用来看基因表达量，DNA用来找变异

> Harbour, J. W., Chen, R. The DecisionDx-UM gene expression profile test provides risk stratification and individualized patient care in uveal melanoma. PLoS Curr. 5 (2013).

## 4 数据分析

### **4.1 【重头戏】单细胞RNA分析**

**4.1.1 上游：**

使用CellRanger (version 2.1.1)，利用`mkfastq`从原始的BCL文件得到FASTQ，然后`count`定量【比对到GRCh38 Ensembl build 84 genome (version 1.2.0）】。

**4.1.2 下游：基于R 3.5环境**

读入数据：11个样本的数据利用**Seurat2.3.4** 的 `Read10X()`读入，利用`aggregate` 进行整合。接着进行数据过滤（**标准是：UMI大于400, 每个细胞包含100-8000个基因，线粒体含量小于10%**）。没有进行样本批次矫正，**结果得到了59,915个细胞**

归一、标准化：利用`LogNormalize`进行归一化，设置`scale factor = 10,000`，然后利用`Scale.Data()`进行标准化，指定`vars.to.regress`去除UMI和线粒体对差异的影响。

细胞周期分析：利用`CellCycleScoring()` 【来自：Tirosh, I. et al. Dissecting the multicellular ecosystem of metastatic melanoma by single-cell RNA-seq. Science 352, 189–196 (2016).】

找高变化基因：设定范围是normalized expression 介于0.125 和 3；quantile-normalized variance 大于0.5，找到1865个高变化基因

降维：PCA前20个主成分 + tSNE （利用`RunTSNE()`）

寻找Doublets（一个孔出现两个细胞）：`DoubletFinder V2.0.2`【来自：McGinnis, C. S., Murrow, L. M. & Gartner, Z. J. DoubletFinder: doublet detection in single-cell RNA sequencing data using artificial nearest neighbors. Cell Syst. 8, 329–337.e4 (2019).】发现除巨噬细胞或单核细胞以外，很少会出现doublets（<10%）

> 巨噬细胞 Macrophage：是一种位于组织内的白细胞，源自单核细胞 单核细胞 Monocyte：是人体免疫系统中的一种白细胞，单核细胞来源于骨髓中的前体细胞，在血管内为单核细胞，血管外就变成巨噬细胞 来自：<https://zh.wikipedia.org/wiki/%E5%B7%A8%E5%99%AC%E7%BB%86%E8%83%9E>
>
> 需要注意：两个细胞粘在一起这种Doublet现象，不一定是分析的假象。任何细胞都不是一个孤岛，在结核病和登革热发生时，单核细胞成为疾病的储存库，因此T细胞很有可能与单核细胞接触并形成doublet 来自：「在流式分析中排除doublet，我们可能做错了」<https://www.ebiotrade.com/newsf/2019-6/2019627161132438.htm>

细胞亚群：利用`FindClusters()` ，使用前20个主成分，`resolution=3`，得到58个cluster。【下图：a图按来源划分；b图是全部的cluster】

![a图按来源划分；b图是全部的cluster](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-05-064709.png)

然后用marker基因鉴定细胞类群：

* Tumor cells：*MLANA*, *MITF*, and *DCT*
* Tumor cell继续分：按照*PRAME* 和 gene expression profile (GEP)基因
* T Cells (*CD3D*, *CD3E*, *CD8A*)
* B cells (*CD19*, *CD79A*, *MS4A1* \[*CD20*])
* Plasma cells (*IGHG1*, *MZB1*, *SDC1*, *CD79A*)
* Monocytes and macrophages (*CD68*, *CD163*, *CD14*)
* NK Cells (*FGFBP2*, *FCG3RA*, *CX3CR1*)
* Retinal pigment epithelium (*RPE65*)
* Photoreceptor cells (*RCVRN*)
* Fibroblasts (*FGF7*)
* Endothelial cells (*PECAM1*, *VWF*)

![image-20200505145144203](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-05-065144.png)

免疫细胞分析：利用`SubsetData()` 最后得到了16470个免疫细胞，经过归一化、找高变化基因（4423个）、PCA、tSNE（resolution=10），得到74个cluster，再传给`AverageExpression()`计算每个cluster的平均RNA表达量

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-05-141256.png)

### **4.2 单细胞CNV分析**

利用Cellranger DNA (version 1.0.0)的`mkfastq`将BCL转为fastq，利用`cnv` 得到CNV数据（中间过程需要比对到GRCh37 build 87 genome），结果可视化利用Loupe scDNA Browser (version 1.0.0)。

过滤掉免疫浸润以及非肿瘤细胞：设定subtree depth

### **4.3 inferCNV和克隆性分析**

inferCNV：<https://github.com/broadinstitute/inferCNV>

* `inferCNV`用于分析体细胞大规模染色体拷贝数变化(copy number  alterations, CNA), 例如整条染色体或大片段染色体的增加或丢失(gain or deletions)。
* 原理：以一组"正常"细胞作为参考，分析肿瘤基因组上各个位置的基因表达量强度变化. 通过热图的形式展示每条染色体上的基因相对表达量
* 横坐标是各个基因在染色体的分布（染色体经过了排序），纵坐标是各个样本
* 表达量差异来源于肿瘤细胞的表达量减去正常细胞的表达量，红色部分表示这部分染色体区域的基因发生扩增，蓝色表示缺失

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-05-131024.png)

UPhyloplot2：<https://github.com/harbourlab/UPhyloplot2，绘制肿瘤系统进化树>

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-05-125500.png)

### **4.4 SCENIC 分析**

利用pySCENIC对8598个高质量UM细胞归一化后的表达矩阵进行计算，用GRNboost2进行基因调控网络重建。使用了24453个cisTarget Human motif database v9 motifs (<https://resources.aertslab.org/cistarget/motif2tf/motifs-v9-nr.hgnc-m0.001-o0.0.tbl>)

### **4.5 Monocle 2分析**

使用7947个高质量UM细胞

选轨迹推断基因：用`dispersionTable()`计算每个基因在细胞中的表达量相对于平均表达量的差异

结果可视化：`plot_cell_trajectory()`, `plot_complex_cell_trajectory()`

### **4.6 免疫细胞V(D)J 分析**

每个B细胞和T细胞文库的BCL文件也是用cellranger分析，先是`mkfastq`得到fastq，然后`vdj`

pipeline：<https://support.10xgenomics.com/single-cell-vdj/software/pipelines/latest/using/vdj>

## 5 结果

### **5.1 单细胞RNA测序分析**

目的是探索单细胞水平的肿瘤微环境

对59915个细胞进行分群，发现既有肿瘤细胞又有非肿瘤细胞

从原位癌class1（左）到转移癌class2（右），细胞类型复杂度上升

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-05-121736.png)

另外用GEP clinical prognostic test 方法，看12个基因在细胞类群的表达，其中5个基因（EIF1B, HTR2B, ECM1, CDH1, and ROBO1）主要在肿瘤细胞表达，有一个（SATB1）主要在T细胞中表达，其余二者均有。表示GEP测试的准确性可能取决于从复杂的肿瘤微环境取样过程

> GEP clinical prognostic test : Harbour, J. W., Chen, R. The DecisionDx-UM gene expression profile test provides risk stratification and individualized patient care in uveal melanoma. PLoS Curr. 5 (2013)

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-05-122647.png)

### **5.2 转录轨迹分析**

目的是探索UM细胞的转录状态。

SCENIC的结果是： class2中富集到了肿瘤蛋白基因MYC、JUN，bHLH-PAS缺氧相关转录因子ARNT

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-05-132207.png)

然后monocle2拟时序分析，得到了16种状态，主要分成2支（class1 和class2）

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-05-132720.png)

class1的细胞主要在1-4、14-16状态，class2主要在5-13，表示UM的整体分子特征主要就体现在class1和class2中

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-05-133214.png)

然后又用branched expression analysis modeling (BEAM) 和层次聚类的方法得到cluster，并且鉴定在不同状态富集的基因

下图是其中一个样本的结果（每个样本都进行了同样的操作），a是定量拟时序，b是细胞状态拟时序，c是亚克隆的拟时序，d是4个基因（JUN, MITF, HLA-A, and MYC）表达量的拟时序【这4个基因分别对应4种不同状态：TNFA/NFKB, differentiation, HLA/immune, and MYC】，e是细胞周期拟时序

可以看到单个样本中，这些转录状态在亚克隆和各个细胞周期中都有分布

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-05-135311.png)

整合的结果如下：

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-05-133438.png)

### **5.3 免疫细胞V(D)J 分析**

肿瘤微环境中9441个免疫细胞，分群如下：

![image-20200505221037964](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-05-141038.png)e

每个免疫细胞cluster的平均RNA表达量：

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-05-141505.png)

每个肿瘤样本免疫细胞中各个亚型占比：T细胞在所有样本都有体现，包括CD8+ cytotoxic T细胞、CD8+ T effector memory细胞，大部分T细胞都是CD8+，少部分是CD4+（包括：follicular helper cells, FOXP3+ regulatory cells, naïve lymphocytes）

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-05-141611.png)

单细胞VDJ结果：克隆扩增T细胞只存在于3个样本中

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-05-141734.png)

CD8+ T cell expression of exhaustion-associated immune checkpoint molecules is strongest for LAG3, variable for TIGIT, and minimal for PDCD1 (PD1), CTLA4, HAVCR2 (TIM3), and TNFRSF9

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-05-144318.png)

## 6 意义

Class1 UM的潜伏期长和转移率低可能是由于免疫监视，而免疫监视可能是由EIF1AX和SF3B1突变产生的新抗原引起的。 这种可能性可能具有临床意义，值得进一步研究。

scRNA-seq V（D）J分析显示UM样品中克隆扩增的T细胞或浆细胞表明肿瘤浸润的免疫细胞能够引发反应，表明肿瘤突变程度低并不是UMs对检查点抑制剂反应差的唯一原因。

发现LAG3是UM中的主要衰竭指标，至少可以部分解释先前针对CTLA4和PD1的检查点抑制的失败。 LAG3是第三个针对患者的免疫抑制受体，表现出与PD1的显着协同作用，不仅在CD8 + T细胞上表达，而且在NK细胞和调节性T细胞上表达，并且具有独特的特性，可以显著扩大检查点抑制剂疗法的疗效。 因此，LAG3抑制剂正在多种癌症的大量临床试验中进行评估。


# 2.2.4 研究 | 2020-COVID-19病人支气管免疫细胞单细胞测序分析

刘小泽写于2020.5.21

## 1 文章信息

题目：Single-cell landscape of bronchoalveolar immune cells in patients with COVID-19

投稿日期：2020年2月24日

接收日期：2020年4月23日

发表日期：2020年5月12日

杂志：Brief Communication

文章在：<https://www.nature.com/articles/s41591-020-0901-9>

DOI：<https://doi.org/10.1038/s41591-020-0901-9>

文章使用的代码在：<https://github.com/zhangzlab/covid_balf>

## 2 前言

### **2.1 摘要**

与COVID-19严重性相关的呼吸免疫特性目前还不清楚。作者选了COVID-19严重程度不同的患者以及健康人的支气管肺泡灌洗液免疫细胞，进行单细胞转录组测序。发现重度患者的支气管肺泡灌洗液中富含巨噬细胞，普通型患者的特点是存在高度克隆扩增的CD8+ T细胞。

> **支气管肺泡灌洗（bronchoalveolar lavage, BAL）**：经支气管镜向局部支气管肺泡注入生理盐水，随即进行抽吸而获取支气管肺泡表面被覆液与灌入生理盐水混合液的一种方法。BAL可通过支气管肺泡灌洗&#x6DB2;**（bronchoalveolar lavage fluid, BALF）**&#x7EC6;胞学、肿瘤标志物等检测**解决常规支气管镜未能见到的异常肺部病变的诊断**，特别是周围性、原发性或继发性恶性肿瘤等，往往被称为“**液相活检**” （<https://www.ncbi.nlm.nih.gov/pmc/articles/PMC6000434/）>

### **2.2 研究背景**

COVID-19的潜在免疫反应至今未知，在动物实验模型中发现了异常的免疫反应，特点是淋巴细胞减少。支气管肺泡灌洗液(BALF)含有细支气管和肺泡的微环境信息，因此选择它作为实验材料。

## 3 方法

### **3.1 样本收集**

2020年1-2月取自深圳第三人民医院的13个病人，并根据新型冠状病毒肺炎诊疗方案（<http://www.nhc.gov.cn/yzygj/s7653p/202003/46c9294a7dfe4cef80dc7f5912eb1989.shtml）将病人划分成普通型（moderate）、重型（severe）、危重型（critical）。>

用于scRNA-seq研究的有9名病人，其中普通型有3个（M1-M3），重型有1个（S1）、危重症有5个（S2-S6），他们平均年龄57岁，其中6名男性3名女性。它们的人口调查数据在附表一。为了评估细胞因子和趋化因子水平，检查了其他几名病人的支气管肺泡灌洗液（S7-S10，他们的人口调查数据在附表四）

> 来自维基百科： **细胞因子**（英語：**cytokine**，又稱细胞介素、细胞激素、细胞素、细胞活素），是一组蛋白质及多肽，在生物中用作信号蛋白。 这些类似激素或神经递质的蛋白用作细胞间沟通的信号。可以由多种**细胞**释放，尤其重要的是在先天性免疫反应和适应性免疫反应。 **趋化因子（chemokines）**，也称做趋化激素、趋化素或是化学激素。是一小分子细胞因子家族蛋白。主要作用是趋化细胞的迁移. 细胞沿着趋化因子浓度增加的信号向趋化因子源处的迁徙。有些趋化因子在**免疫监视**过程中控制免疫细胞趋化，如诱导淋巴细胞到淋巴结。 这些淋巴结中的趋化因子通过与这些组织中的抗原提呈细胞相互作用而监视病原体的入侵。这些被称为**稳态趋化因子**

除此以外，还有3个健康供体作为对照（HC1-HC3），他们均没有结核、肿瘤等肺部疾病（他们的人口调查数据也在附表四）

### **3.2 scRNA-seq和TCR-seq捕获、文库构建、测序**

对HC1和HC2利用10X Genomics的Chromium Single Cell 3g Reagent kits v.2进行捕获和构建文库，HC3利用kit v.3；COVID-19的所有病人利用Chromium Single Cell V(D)J Reagent kits用于单细胞免疫研究；利用BGI MGISEQ-2000或Illumina平台进行测序

> V(D)J：与体细胞相比，B 细胞和 T 细胞是独特的，其发育和成熟是由生殖细胞系中未编码的 DNA 序列决定的。在成熟过程中，这些细胞经历了可变区（V）、多样区（D）和结合区（J）【联系记忆：variaty、diversity、joint】基因片段的冲排，以便形成独特的序列 免疫学研究综述 - Illumina：<http://web.illumina.com.cn/landing/products_view.asp?newsid=155>

除了上面三个供体，scRNA-seq分析还利用了一个公共数据（ GSE128033 中的GSM3660650）进行佐证（记为HC4），是肺移植供体的新鲜支气管肺泡灌洗液的单细胞测序数据

### **3.3 scRNA-seq数据的比对和整合**

利用了cellranger v3.1.0，为了检测SARS-CoV-2的reads，在分析时将GRCh38和**SARS-CoV-2基因组序列（GenBank：MN908947.3）进行整合**；cellranger中利用STAR进行比对。

针对9个病人的表达矩阵的质控**阈值是：基因数量200-6000，UMI count大于1000，线粒体含量小于10%**。过滤后，总共得到66452个细胞

利用Seurat V3将来自不同供体的单细胞数据进行整合，并去除批次效应

### **3.4 降维和聚类分群**

基于Seurat V3，使用`LogNormalize` 的默认参数进行归一化，利用`FindVariableFeatures`的`vst`方法选top2000高变化基因（HVGs）；在标准化这步（scale），去掉了`nCount_RNA`和`percent.mito`的影响，后续利用2000个基因进行PCA。之后基于PCA结果，利用前50主成分进行UMAP，同时进行graph-based clustering（参数resolution=1.2）【例如scanpy就是graph-based clustering】

> 关于Graph-based methods ：<https://chanzuckerberg.github.io/scRNA-python-workshop/analysis/04-clustering.html>
>
> * Currently, the most widely used graph-based methods for single cell data are variants of the **louvain** algorithm
> * **Community detection** methods (also referred to as '**graph-based clustering**')   are typically faster than other clustering methods with similar effectiveness.
> * This **resolution parameter** controls how fine- or coarse-grained the inferred clusters are. This parameter can have major effects on your results!

### **3.5 巨噬细胞、NK、T细胞的重新整合**

利用Seurat V3将所有样本的巨噬细胞中前50个CCA（ canonical correlation analysis）和PCA进行整合【参数：`k.filter=115` 考虑到M3样本中细胞数量较少】，聚类这步参数`resolution=0.8` ；

对于所有的NK和T细胞的整合，由于T细胞数量在HC2和S3样本中较少，所以将它们排除。参数`k.filter=175`

另外单独将8个病人样本的T细胞（排出了S3）进行整合，为了之后的TCR分析，参数`k.filter=140`

### **3.6 差异基因表达分析**

Seurat3利用`FindAllMarkers`函数中整合的MAST算法进行差异分析（结果在附表5）

### **3.7 调控网络推断**

利用SCENIC构建了4个巨噬细胞群的调控网络，其中利用了SCENIC v.0.9.19中的GRNBoost2基于原始表达量进行推断。

### **3.8 富集分析**

利用clusterProfiler对差异基因进行GO、GSEA（利用了50个 hallmark gene sets in MSigDB）

### **3.9 TCR V(D)J 测序分析**

使用Chromium Single-Cell V(D)J Enrichment kit进行 5' 全长建库，每个T细胞使用Cell Ranger vdj pipeline (v.3.1.0)流程处理。同时具有TCR α- 和 β-chains的细胞保留，只要一个chain的细胞丢弃；对于含有两个以上α-chains或者 β-chains的细胞，比如一个细胞有α-chain-1、α-chain-2 、β-chain，但α-chain-2的UMI高于α-chain-1，于是舍弃α-chain-1

### **3.10 数据**

使用的所有数据在 [GSE145926](http://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE145926)，代码在：<https://github.com/zhangzlab/covid_balf>

## 4 结果

### **4.1 COVID-19的重度患者支气管肺泡免疫功能失调**

![Fig. 1](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-18-032201.png)

> 图A：对支气管肺泡灌洗液免疫细胞进行分群后进行细胞·类型鉴定，UMAP展示出13种细胞类型 图B：主要的支气管肺泡灌洗液免疫细胞在健康、中度、重度患者中的分布 图C：UMAP展示了4组巨噬细胞在健康、中度、重度患者中的分布 图D：支气管肺泡灌洗液巨噬细胞的三个基因表达（FCN1, SPP1 and FABP4）

**根据marker基因鉴定了31个亚群：**

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-18-033959.png)

其中包括：macrophages (CD68), neutrophils (FCGR3B), myeloid dendritic cells (mDCs) (CD1C, CLEC9A), plasmacytoid dendritic cells (pDCs) (LILRA4), natural killer (NK) cells (KLRD1), T cells (CD3D), B cells (MS4A1), plasma cells (IGHG4) and epithelial cells (TPPP3, KRT18)

主要的细胞类型（mDCs, pDCs, mast cells, NK cells, T cells and B cells）在大部分样本中都存在，但发现巨噬细胞在不同组中含量差别大

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-18-034846.png)

相比普通型患者，重度患者的巨噬细胞（macrophages）和中性粒细胞（neutrophils）占比增加，mDCs, pDCs and T细胞占比下降

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-18-035355.png)

**然后对20个巨噬细胞的亚群重新聚类**

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-18-035606.png)

并根据最新的分类标准和marker基因，将巨噬细胞分成4组（FCN1hi group 1, FCN1loSPP1+ group 2, SPP1+ group 3 and FABP4+ group）

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-18-035746.png)

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-18-035950.png)

看到这4个组在FCN1, SPP1 and FABP4基因表达量以及细胞组成方面差异都比较大

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-18-040553.png)

再看这几个基因：FABP4在对照和轻度患者中表达高；FCN1和SPP1在重型和危重型患者中表达高

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-18-040352.png)

**为进一步探究4个组之间差别，进行了差异分析、GO富集和GSEA**

第一组表达了外周单核细胞基因S100A8, FCN1 and CD14；

第二组高表达趋化因子CCL2, CCL3, CXCL10；

两组的表达模式更像M1型巨噬细胞

第三组表达免疫调节基因A2M, GPR183 and CCL13 以及纤维化基因TREM2, TGFB1 and SPP1，说明可能是M2型巨噬细胞

第四组表达肺泡巨噬细胞基因FABP4, APOC1, MARCO 以及脂质代谢功能相关基因，而且结合之前的分群结果，第四组主要在对照和轻度患者中存在

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-18-040845.png)

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-18-042134.png)

**利用SCENIC计算4组巨噬细胞基因的调节程度**

在第一组和第二组中，发现转录因子基因NFκB, STAT1, STAT2 和干扰素调控因子上调；在第三组中，促M2型基因TFEB, NR1H3, PPARA and CREB1上调；第四组中，肺泡巨噬细胞特异基因PPARG和CEBPB上调

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-18-042256.png)

表示：严重COVID-19的患者的肺中存在高度促炎症的巨噬细胞微环境，这与之前在稳态、发炎、恢复状态下的巨噬细胞群体的认识一致

### **4.2 COVID-19患者BALFs中T淋巴细胞和NK淋巴细胞的分析**

根据一些marker基因鉴定了6个T、NK淋巴细胞群

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-18-043222.png)

与中度感染患者相比，重症感染患者的CD8 + T细胞含量较低，而增殖T细胞的比例较高

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-18-043416.png)

这些CD8+ T细胞表达了更高水平的GZMA、GZMK和FASLG以及组织驻留标记ITGA1、CXCR6和JAML

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-18-043502.png)

轻度病例中涉及激活、迁移和细胞因子相关通路的基因上调，重度病例中涉及翻译起始、细胞稳态和核苷酸代谢通路的基因上调

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-18-043532.png)

### **4.3 COVID-19患者BALF 中T细胞克隆扩增分析**

使用scTCR-seq对8个COVID-19患者（排除了S3）BALF的7个T细胞群分析了T细胞克隆扩增

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-18-043859.png)

克隆性扩增的T细胞的UMAP结果显示：在轻度患者中聚集分布，而在重症患者中离散分布，说明它们在转录状态上就存在同质性和异质性的区别

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-19-122449.png)

轻度和重度患者的CD8+ T细胞在数量和克隆性扩增状态存在差异：

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-19-122725.png)

总之，重症/危重症患者提取的CD8+ T细胞扩撒范围更小，但具有更高的增值特性和表型异质性，在轻度患者中，CD8+ T细胞具有组织原位的特性

### **4.4 患者几种细胞素和趋化因子的测量**

重症/危重症患者比轻症患者的炎症细胞因子含量更高，尤其是IL-8、IL-6、IL-1β

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-21-003115.png)

IL1B, IL6, TNF 和几种趋化因子 (CCL2, CCL3, CCL4 and CCL7)在重症患者的巨噬细胞中高表达；CXCL9, CXCL10 and CXCL11在患者中表达量高于正常人；CXCL16（它的产物与CXCR6结合）在轻度患者中表达比重症患者高

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-21-004151.png)

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-21-003954.png)

总之，重症患者肺部的巨噬细胞可能通过CCR1和CXCR2招募炎症单核细胞和中性粒细胞，导致局部发炎；轻度患者的巨噬细胞通过CXCR3和CXCR6产生更多的T细胞趋化因子

## 5 意义与不足

说明了细胞素风暴与COVID-19的严重程度有关

> 细胞素风暴（Cytokine storm）：又称高细胞因子症（英语：Hypercytokinemia），一种不适当的免疫反应，因为细胞因子与免疫细胞间的正回馈回路而产生。这也被认为是1918年流感大流行、2003年SARS事件、2009年H1N1流感大流行，以及H5N1高致病性禽流感中病毒致死的原因 当免疫系统对抗病原体时，**细胞因子会引导免疫细胞前往受感染处**。同时，细胞因子也会激活这些免疫细胞，被激活的免疫细胞则会产生更多的细胞素。通常来说，人体会检查并控制这个反馈循环。但是在有些情况下，情况会失控，导致**一个地方聚集了太多被激活的免疫细胞**。

揭示了在COVID-19中潜在的免疫发病机制中巨噬细胞和T细胞异常反应

存在的不足：病人数量少，并存在异质性；缺少纵向比较（比如感染前后组织的收集测序）；B细胞的反应由于数量少没有进行分析；年龄及患者之前存在的问题没有全面考虑


# 2.2.5 研究 | 2020-原汁原味读--单细胞肿瘤免疫图谱

刘小泽写于2020.10.30

> 探索一种新的文献阅读方法，可以更快去理解到作者的主体思路 下面我会从这几块介绍：SOURCE（文章来源）、WHY（作者为什么做这个项目）、HOW（作者怎么做的项目）、GET WHAT（作者得到了什么主要结论）
>
> 其中不会有特别复杂的词语和语法，而且我会尽可能把文章逻辑层级写清楚

## SOURCE

Title: A Single-Cell Tumor Immune Atlas for Precision Oncology

Date: 2020-10-26

Team: Barcelona Institute of Science and Technology (BIST), Barcelona, Spain

Paper Link: <https://www.biorxiv.org/content/10.1101/2020.10.26.354829v1>

Data Link (Restricted Access): <https://zenodo.org/record/4036020#.X5uoHlMzaHF>

Code Link: <https://github.com/Single-Cell-Genomics-Group-CNAG-CRG/Tumor-Immune-Cell-Atlas>

## WHY？

* Immune microenvironments vary profoundly between patients and biomarkers for prognosis and treatment response lack precision
* To pinpoint predictive cellular states of tumor immune cells and their spatial localization

## HOW？

* Analyzing **>500,000** cells from **217** patients and **13** cancer types
* Data projection: Seurat's anchor-transferring method
* Using **SPOTlight** to combine single-cell and spatial transcriptomics data and identifying striking spatial immune cell patterns in tumor sections
* **ShinyApp** (in progress) to project external data and to apply the immune classifier

## GET WHAT?

### **GET 1: Generating a tumor immune cell atlas**

* Collected scRNA-seq datasets from **13 different cancer types**, 217 patients and 526,261 cells

  ![image-20201030104337329](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-10-30-024337.png)
* Immune cells clustered by cell identity rather than patient origin: integrated **317,111** immune cells using **canonical correlation analysis** =>  **25**  clusters

  ![image-20201030104248148](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-10-30-024248.png)

### **GET2: Tumor subtype classifier**

* **For Current:** to establish a pan-cancer immune classification system
  * used **immune cell type and state frequencies** of the reference atlas as input for similarity assessment across the 13 cancer types

    ![image-20201030105045062](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-10-30-025045.png)
  * A **hierarchical k-means clustering** using immune cell proportions as features defined six clusters with largely different compositions (almost all cancer types were presented in each cluster)

    ![image-20201030105413983](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-10-30-025414.png)
* **For future**: to facilitate the classification of immune profiles
  * trained an **RF(random forest) classifier** with the 25 immune cell population achieving a highly accurate classification&#x20;
  * using the classifier, the pan-cancer immune classification system could be extended to **additional cancer types**

### **GET 3: A resource for immune cell annotation**

> To demonstrate the potential value of the atlas

* The applicability of the atlas as reference **across different cancer types**

  * **First:** Project cells onto atlas using a reference-based projection (**Fig. A**)
  * **Next**: Typical clustering matching (**Fig. B**)

  &#x20;![image-20201030111226885](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-10-30-031227.png)

  * **Third**: Check correlation (**Fig. C**)&#x20;

  ![image-20201030112148461](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-10-30-032149.png)
* The applicability of the atlas as reference **across species**
  * two liver metastases derived from **mouse CRC organoids**
  * main subtypes and specific subpopulations could also be assigned **using the human reference**

    ![image-20201030112314187](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-10-30-032314.png)

### **GET 4: Spatial localization of immune cells in tumor sections**

> **Spatial** distribution of immune cells is important for **ICI (immune checkpoint inhibitors) response**

* Single-cell reference atlas immune profiles + Spatial transcriptome data
* **SPOTlight** : non-negative matrix factorization (NMF) based spatial deconvolution framework
* Analysis of oropharyngeal squamous cell carcinoma (**SCC**)

  * **cluster 1/2** (cancer cells) is surrounded by **cluster 0** (stroma) and **cluster 3** (immune cells)&#x20;

  ![image-20201030113032968](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-10-30-033033.png)

  * **cluster1/2** presented a similar immune infiltration pattern, with an enrichment of proliferative **T-cells** and **SPP1 macrophages**
  * **cluster 3** presented a distinct immune infiltration pattern characterized by an enriched presence of (proliferative) **B-cells**
  * **cluster 0** harbored regulatory T-cells and terminally exhausted CD8 T-cells and was specifically enriched in **M2 macrophages** and **naive T-cells.**

    ![image-20201030132328526](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-10-30-052329.png)
* Analysis of ductal breast carcinoma (**BC**)
  * also get a cancer-specific regional distribution:&#x20;

    ![image-20201030134113951](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-10-30-054114.png)
  * subclonal  was directly associated with local enrichment of distinct immune cell states

    ![image-20201030133721287](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-10-30-053721.png)
* **Foresee** the regional distribution of immune cell types to become an important feature for the prediction of immuno-therapy outcome.


# 2.2.6  研究 | 2021-多发性骨髓瘤发展过程中肿瘤和免疫细胞的共同进化

刘小泽写于2021.5.10

## 速览

* 作者：华盛顿大学Li Ding团队
* 链接：<https://www.nature.com/articles/s41467-021-22804-x>
* 发表在：Nature Communications&#x20;
* 日期：07 May 2021

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-05-10-024842.png)

## 摘要

* 多发性骨髓瘤(Multiple myeloma，**MM**)的特点是浆细胞的扩增不受控制。目前对该疾病进展不完全了解，因此仍无法治愈
* 使用single cell RNA and linked-read whole genome sequencing，对**14例患者**不同疾病阶段的**29个样本**进行分析，得到**17,267**个浆细胞和**57,719**个免疫细胞，发现了患者浆细胞和免疫细胞表达谱的变化
* 将具有相同变化的浆细胞和免疫细胞聚在一起，再通过bulk genomics & single cell mapping，追踪到了疾病各个阶段的浆细胞亚群，发现了**3种模式**：稳定型（癌前期到诊断期）、获得突变型与缺失突变型（从诊断期到复发期）
* 多个病人检测到“B细胞特性”的浆细胞，和B细胞群容易聚在一起，暗示了细胞起源
* 使用基于CyTOF（单细胞质谱流式技术）验证浆细胞亚群中AP-1转录因子复合体的差异表达，然后AP-1的下游靶标（IL6和IL1B）可能导致炎症

## 实验设计

左边是所有的病人样本，采用了很多技术：10xWGS, scRNA, Bulk RNA, WES, WGS。至少一个样本同时利用了scRNA-seq和10x Genomics linked-read whole-genome sequencing (10xWGS)

右边是每个病人的治疗过程

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-05-10-030547.png)

又看了14个病人的拷贝数变异、结构变异、驱动突变

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-05-10-031030.png)

## 14个病人的整合分析

先看每个病人的细胞组分：

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-05-10-032609.png)

然后是不同时期的病人整合

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-05-10-032105.png)

又加入了4个健康人的数据作参考，看不同病人肿瘤免疫微环境的差异

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-05-10-032812.png)

## 研究B细胞谱系以及正常、恶性浆细胞的转化

组合了21个肿瘤样本和4个正常样本的B细胞、浆细胞

* 病人的B细胞和正常样本的B细胞混在一起，分在了同一个群；并且存在三个小的B细胞群，还主要来自正常样本，它们高表达SOX4, VPREB3, MME，说明是primitive B细胞
* 正常样本的浆细胞混在了肿瘤样本浆细胞中，说明这些肿瘤浆细胞和正常的浆细胞存在相似的表达谱

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-05-10-033301.png)

* 分析了B细胞和浆细胞的拷贝数，发现有17个样本出现了chr13缺失。它也可以作为恶性浆细胞表达量的一个衡量指标

  ![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-05-10-034046.png)

## 从SMM到初期，浆细胞的亚群的变化

> **Smoldering multiple myeloma** (**SMM**) is an early precursor to a rare blood cancer known as **multiple myeloma**, which affects plasma cells.

病人（58408）的SMM和初期阶段都可以将浆细胞分成两群

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-05-10-050612.png)

然后要研究肿瘤初期的浆细胞是不是来自SMM时期，于是把两个时期的细胞整合在一起，发现：两个时期的cluster1和cluster2都分别混在了一起

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-05-10-060242.png)

之后又比较了这些cluster之间的基因组变异和表达量差异，发现SMM和primary stage的cluster1都出现了chr13拷贝数缺失，而它们的cluster2都是正常的拷贝数；另外chr5和chr15页表现一致，**说明：Primary 的第1亚群可能来自SMM的第1亚群，Primary 的第2亚群可能来自SMM的第2亚群**

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-05-10-051143.png)

然后同样又对另外的两个病人 (47491 and 37692) 进行同样分析，得到**结论：从SMM到初期，浆细胞的亚群是保持稳定的**

## 从初期到复发，浆细胞的亚群的变化

利用病人（27522）4个时期的数据：

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-05-10-051925.png)

初期有4个亚群 (P.1–P.4)，除了P.4都表现了chr13的部分缺失

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-05-10-052136.png)

之后看了Remission (RM), Relapse-1 (RL1), and Relapse-2 (RL2) 三个时期，发现：

* Relapse-2 包含了3个不同的亚群：恶性 RL2.1（somatic mutations and deep chromosome 13 deletion）、“B cell-like” RL2.2 （strong B cell marker expression）、“过渡态” RL2.3 （without somatic mutations detected but with shallow chromosome 13 deletion）
* 发现一些缓解期的细胞和复发期的细胞混在了一起，推测：缓解期的细胞可能是由于躲避治疗求生存的复发期的“种子”

  ![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-05-10-053137.png)

结论：从初期到复发，浆细胞亚群出现了动态的拷贝数增加和缺失

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-05-10-052757.png)

## 最后检测AP-1在浆细胞亚群中的差异表达

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-05-10-054645.png)

* 上图：每个亚群的FOS和JUN的表达量均值
* 下图：FOS和JUN在几个感兴趣的样本中的表达

其中AP-1高表达的亚群可能是骨髓瘤的发展过程中的重要一环

提出了一个AP-1对表型影响的机制模型：

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-05-10-055851.png)


# 2.2.7 研究 | 2021-多个组织的成纤维细胞图谱

刘小泽写于2021.5.13

## 速览

* 作者：Genentech团队

  > 基因泰克公司（英语：Genentech Inc.），全名基因工程科技公司（英语：Genetic Engineering Technology），是一家生物科技公司，由创投人罗伯特·史旺森（Robert A. Swanson）与生物化学家赫伯特·博耶（Herbert Boyer）博士于1976年成立，这件事也被视为是生物科技产业的起点。罗氏药厂于2009年3月26日以大约468亿美元完成了基因泰克的收购，并完全拥有此公司。 截至2019年2月为止，基因泰克有超过13,697名员工。
* 链接：<https://www.nature.com/articles/s41586-021-03549-5>
* 发表在：Nature
* 日期：12 May 2021
* 还发了一个在线工具：<https://www.fibroxplorer.com/> **FibroXplorer** is an interactive data portal that allows users to examine gene expression at the single-cell level in \~230,000 mouse fibroblasts from 17 tissues, 50 datasets and 12 disease states

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-05-13-014620.png)

## 摘要

* 很多研究都表明：成纤维细胞在单个组织中存在异质性，但是在不同健康和患病器官的组织中并没有证明
* 文章整合了2个物种的**17个组织**，涵盖**11种疾病状态**，50个数据集，得到了**230,000**个细胞scRNA数据
* 比较了人和小鼠的成纤维细胞图谱发现，成纤维细胞的转录状态是保守的
* 文章提出假设，成纤维细胞的异质性是由于稳态组织和疾病组织之间的波动差异导致的

## 实验设计

重点还是落在小鼠

* Mouse tissue digestion and stromal cell isolation or identification by FACS
* qPCR, RNA extraction and cDNA synthesis
* Mouse scRNA-seq and cell hashing
* Tissue processing for histology
* RNAscope in situ hybridization
* DSS-induced colitis
* Tumour inoculation
* Mouse bulk RNA-seq analysis
* Mouse bulk ATAC–seq analysis
* Human tissue digestion and stromal cell isolation
* Open chromatin regions (OCRs)  identification
* Motif enrichment analysis
* ATAC–seq and RNA-seq concordance
* Mouse scRNA-seq meta-analysis
* Human scRNA-seq meta-analysis
* Pseudo-bulk analytical strategy
* Projection of human gene sets onto mouse perturbed-state atlas

## 首先看稳态小鼠组织的成纤维细胞

先做了bulk RNA和ATAC-Seq：

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-05-13-022931.png)

单细胞图谱构建思路（图a）：

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-05-13-032730.png)

得到10个细胞群，marker分别是：Pi16+, Col15a1+, Ccl19+, Coch+, Comp+, Cxcl12+, Fbln1+, Bmp4+, Npnt+ and Hhip+

> Ccl19+ fibroblastic reticular cells (FRCs), Coch+ red pulp fibroblasts, Cxcl12+ mesenchymal stromal cells and osteolineage cells, Fbln1+ and Bmp4+ intestinal fibroblasts, Comp+ fibroblasts, Npnt+ alveolar fibroblasts Hhip+ peribronchial fibroblasts

几乎所有的组织都有Pi16+ and Col15a1+ clusters

> DEGs in the **Pi16**+ cluster (Pi16, Dpp4 and Ly6c1) suggested an identity similar to adventitial stromal cells **Col15a1**+ cluster exhibited an association with the basement membrane, evidenced by expression of Col4a1, Hspg2 and Col15a1

图d是：Slingshot lineage inference identified trajectories that emerged **from the Pi16+** cluster, passed **through the Col15a1+** cluster, and **ended at specialized** clusters

scRNA和bulkRNA的一致性还是很高的，说明scRNA在提高精度的同时，没有引入其他的技术误差：

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-05-13-022527.png)

结论是：在稳态小鼠组织中，Pi16+ and Col15a1+的成纤维细胞亚群普遍存在，当然还有一些特异的亚群，它们之间可能存在进化的关联

## 受伤或患病小鼠紊乱组织的成纤维细胞

组织的紊乱包括：感染、损伤、癌症、纤维化、代谢变化和关节炎

探索了**17 publicly** available scRNA-seq datasets across **13 tissues** to generate a perturbed-state fibroblast atlas (*n* = **99,596** cells

也是得到10个cluster：Pi16+, Col15a1+, Ccl19+, Cxcl12+, Comp+, Npnt+, Hhip+, Adamdec1+, Cxcl5+ and Lrrc15+

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-05-13-030407.png)

发现： Cxcl12+, Ccl19+, Comp+, Npnt+, and Hhip+ 这几个cluster在基因表达和组织分布和稳态组织的结果很相似； Cxcl5+, Adamdec1+ and Lrrc15+ 这几个是紊乱组织特有的，激活了稳态中没有的细胞状态

## 人的成纤维细胞图谱

假设：小鼠在稳定和紊乱状态的成纤维细胞亚型方面可能与人类表现出一些相似性

使用3个胰腺癌病人的癌和癌旁样本：n = 21,262 cells （图a-c），分成了CAF （c3和c8）和normal fibroblasts

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-05-13-033633.png)

在c8的20个高表达基因中，有12个也在稳态中高表达：Dpt+Pi16+ (DPT, IGFBP5, IGFBP6, C3, APOD) or Dpt+Col15a1+ (CXCL12, SMOC2, C7, FBLN5, MFAP4, LUM, FMO2)

接着，拿C3 PDAC CAFs + colon fibroblasts + lung fibroblasts from individuals with non-small cell lung cancer (NSCLC), IPF or COVID-19 (n = 10,355 cells)进行整合，得到6个cluster

> NPNT+ alveolar fibroblasts derived from individuals with lung disease
>
> ADAMDEC1+ and CCL19+31 clusters primarily from colitis samples
>
> PI16+ cluster, in which the human universal signature was most enriched
>
> two myofibroblast clusters defined by LRRC15+ and COL3A1+ expression:
>
> * LRRC15+ myofibroblasts were enriched in cells from individuals with pancreatic and lung cancer,
> * COL3A1+ cluster was enriched in cells from patients with COVID-19

进而验证了小鼠的图谱： LRRC15+ myofibroblasts and ADAMDEC1+ fibroblasts

另外还发现了COVID-19病人中存在一个独特的myofibroblast细胞群

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-05-13-033733.png)

最后留下疑问：It remains unclear **why two universal Dpt+ fibroblast subtypes exist**, though we speculate that this may represent a necessary division of labour within the lineage.


# 2.2.8 研究 | 2021-多组学分析肺结核队列的记忆T细胞状态

刘小泽写于2021.5.25

## 速览

* 题目：Multimodally profiling memory T cells from a tuberculosis cohort identifies cell state associations with demographics, environment and disease
* 作者：哈佛医学院 Soumya Raychaudhuri 团队

  > 首先感觉他们实验室主页做的很不错：<https://immunogenomics.hms.harvard.edu/lab> ![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-05-25-061211.png)
* 链接：<https://www.nature.com/articles/s41590-021-00933-1>
* 发表在：Nature Immunology （2021-05-24）
* 数据是公开的：[GSE158769](https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE158769) ，采用CITE-seq

  ```
  Platforms    GPL11154    Illumina HiSeq 2000 (Homo sapiens)
  Samples      GSM4810298    Memory_T_cell_CITE-seq

  GSE158769_exprs_norm.tsv.gz    3.0 Gb    (ftp)(http)    TSV
  GSE158769_exprs_raw.tsv.gz    747.0 Mb    (ftp)(http)    TSV
  GSE158769_meta_data.txt.gz    19.8 Mb    (ftp)(http)    TXT
  ```

  Genotype data：[phs002025.v1.p1](https://www.ncbi.nlm.nih.gov/projects/gap/cgi-bin/study.cgi?study_id=phs002025.v1.p1)
* 分析代码：<https://github.com/immunogenomics/TB_Tcell_CITEseq> ![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-05-25-082342.png)
* 还发开了一个网页：<https://immunogenomics.io/tbru/>

## 摘要

* 分析了来自秘鲁的259个肺结核（tuberculosis , TB)病人的500,089个记忆T细胞，得到31种细胞状态
* 在排除年龄、性别、季节、遗传等因素影响后，发现了一种多能型type 17 helper T (TH17) 细胞状态，在结核杆菌发展成肺结核病的过程中丰度和功能都有所下降

## 方法

**数据准备**

* PBMC sample preparation
* Flow cytometry of total PBMCs
* CITE-seq of isolated memory T cells
* Bulk RNA sequencing
* Genotyping and genetic data processing

**数据分析**

* scRNA比对+定量
* scRNA数据拆分
* scRNA数据质控
* 降维
* 聚类+注释
* bulk RNA-seq 比对+定量
* 利用MASC(modeling of associations of single cells )评价不同细胞状态与疾病的关系

## 先用CITE-seq测了>500,000个T细胞

* 在秘鲁首都利马进行了一次大型的流行病学调查（n= 18,544），从其中招募了264个人，采样了外周血单核细胞进行CITE-seq测定，同时还加入了基因表型数据和蛋白数据
* 图b是进行了6次QC，每一次过滤掉一些细胞，最后得到了500K
* 大部分样本的细胞数量在2k左右

![](https://media.springernature.com/full/springer-static/image/art%3A10.1038%2Fs41590-021-00933-1/MediaObjects/41590_2021_933_Fig1_HTML.png)

## 得到记忆T细胞的31种不同状态

利用canonical correlation analysis (CCA) 进行降维，选择top20 canonical variates (CVs)进行批次校正和聚类，根据基因和蛋白marker得到31个细胞状态

* 23/31 were CD4+; five were CD8+; one (C-24) was a mixture; Two clusters (C-30 and C-31) were CD4−CD8−
* **C-20**： contained a subset of CD4−CD8− and CD8+ cells expressing innate-like T cell markers, including ZBTB16 and CD161 and CD26 surface proteins
* one central memory cluster (**C-25**) and distinct *GZMK*+ (**C-28**) and *GZMB*+ (**C-29**) effector subsets, reflecting different cytotoxicities
* high expression of HLA-DR and CD38 surface protein and proliferation-associated *MKI67* (**C-15** and **C-27**) represent chronically activated cells
* TH17 cells (**C-12**, CCR6+ and *RORC*)&#x20;
* type 1 helper T cells (TH1; **C-17**, CXCR3+ and *IFNG* and *TBX21*)
* heterogeneous continuum of intermediate TH1/TH17 states (**C-13**, **C-16** and **C-19**) with varying degrees of CXCR3, CCR6, CCR5 and CD161 surface protein expression and *RORC* and *TBX21* expression
* CD161+ subset of type 2 helper T (TH2) cells (**C-14**), described as pathogenic, with higher expression of allergy-associated *HPGDS* and *IL17RB*
* a subset of *FOXP3*+ Treg cells (**C-5**) expressing higher CCR6 surface protein and *CTLA4* and *RORC* than other Treg cells

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-05-25-070418.png)

## 记忆T细胞的状态因人口特征和环境而异

发现： 年龄因素的影响最大；性别因素与T细胞状态也高度相关； type 2 helper T (TH2) 细胞状态在冬天（季节因素）搜集的样本中最为丰富

![](https://media.springernature.com/full/springer-static/image/art%3A10.1038%2Fs41590-021-00933-1/MediaObjects/41590_2021_933_Fig3_HTML.png)

## 之后发现一个感兴趣的C-12状态

探索每个细胞状态和肺结核病情进展的关系，同时校正其他可能存在影响的因素（年龄、性别、季节、遗传以及其他技术因素），发现：

**病人在肺结核发展时期，C-12显著降低了20%**，并且在年龄因素和男性因素下也会下降，而季节因素（冬季）会上升

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-05-25-073932.png)

又找到了**C-12 surface markers：CD4+CD26+CD161+CCR6+** ，这些marker已经被证明了以下功能：

* IL-17-producing TH17 state
* CD26 is a co-stimulatory molecule that promotes cytotoxicity
* CD161 is associated with innate-like function
* CCR6 is a homing marker that directs migration to inflamed sites

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-05-25-075041.png)

那么C-12的下降是疾病的原因还是结果呢？

利用外部数据进行验证，发现这种变化可能早于疾病发展

* predicted C-12 abundance was 9% lower in cases before progression compared to latent controls&#x20;

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-05-25-082142.png)


# 2.2.9 研究 | 2021-CancerSCEM: 人类癌症单细胞表达图谱数据库

刘小泽写于2021.10.16

## 前言

题目：CancerSCEM: a database of single-cell expression map across various human cancers

日期：2021-09-29

期刊：Nucleic Acids Research

链接：<https://doi.org/10.1093/nar/gkab905>

## 一句话概况

一个包含人类多种癌症的scRNA数据库CancerSCEM，除了常规的分析之外，还提供网站可视化和在线分析（ <https://ngdc.cncb.ac.cn/cancerscem>）

## 为什么要建这个数据库？

* 首先肯定是因为目前产生了大量的数据集，但是公共的数据库不多，比如Single Cell Portal，PanglaoDB，Single Cell ExpressionAtlas，Human Cell Atlas Data Portal，scRNASeqDB，大部分是人和小鼠的数据。但是这些数据库只做了初步的分析，比如细胞分群、差异分析
* 还有一些专注于疾病的scRNA数据库，比如CancerSEA、TISCH，它们提供了额外的注释和富集分析等。但CancerSEA当时只做了某些类型和某些状态下的细胞，TISCH又没有提供统一的标准化矩阵，容易导致后面用户拿到后引入批次效应

所以，CancerSCEM (Cancer Single-cell Ex- pression Map) 提供了数据搜集、整理、分析、可视化一体。目前包括人类20种癌症的208个样本的638,341个单细胞数据

![image-20211016185127208](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-16-105127.png)

## 数据搜集

数据来自：GEO、ArrayExpress、EBI、GSA、ZENODO，涵盖了10X Genomics, Smart-seq2, Drop-seq, Seq-Well and Microwell 5大平台，其中原始数据占比82.69%。

10X数据采用cellranger V5处理；非10X数据使用Fastp+Trimmomatic+zUMIs处理

## 常规数据处理

### **质控**

DoubletFinder用于doublets去除（标准是7% per 10 000 cells）

Seurat V3 进行初步质控过滤：200 ≤ nfeatures ≤ 5000 and MT < 10%

### **非监督聚类**

PCA + tSNE + UMAP 聚类

**细胞类型注释**

biomarker 基因来自Cell Marker数据库，细胞注释三步走：

* scCancer v2.2.0 + Copy- KAT v1.0.4： copy number variation assessment

  > A group of marker genes, such as *EPCAM*, *KRT8*, *KRT18*, *KRT19* and *EGFR* in glioblastoma cells that represent cancer cells or cancer stem cells, were investi- gated in parallel.
  >
  > Cells with significantly abnormal CNV levels and high expression levels of above marker genes were defined as malignant cells
* Manual annotation ：自己看marker基因表达

  > 常见的比如： T cells (e.g. *CD3D*, *CD3E*), B cells (e.g. *MS4A1*, *BANK1*), Macrophages/Monocytes (e.g. *CD68*, *CD14*), Mast cells (e.g. *SLC18A2*, *ASIC4*), Endothelial cells (e.g. *VWF*, *PECAM1*), Fibroblasts (e.g. *FAP*, *NECTIN1*), Oligoden- drocytes (e.g. *OLIG1*, *PLP1*) and Astrocytes (e.g. *SLC1A3*, *GFAP*)
  >
  > 网站的Documents也给出了所使用的全部marker基因列表
* SingleR: 工具注释

此外，还将T、B细胞继续进行细分亚群，最终得到了包括免疫细胞在内的33种细胞类型

### **差异分析**

FindMarkers用来对每个细胞群进行差异分析

## 个性化处理步骤

* 拿到受配体基因对：来自CelltalkDB、SingleCellSingalR、Cellinker、Cell–Cell Interaction Database、综述文章
* 拿到Oncogenes and tumor suppressor genes（TSG）：来自Cancer Gene Census (CGC)、OncoKB、Network of Cancer Genes (NCG)、TSGene、IntOGene、cancer gene clinical care study。
* 对这些基因进行了过滤（至少在三个数据库中存在，并且在数据集呈现出类似的表达模式）
* 拿到TCGA 的13个癌症项目的bulk RNAseq数据，看在不同癌症的组织水平上这些基因的表达模式，也当做scRNA的参考
* 用之前得到的差异基因进行GO、KEGG富集
* 用Hmisc进行基因表达关联分析
* 细胞通讯用CellphoneDB
* 生存分析用survival + survminer

![image-20211016193240909](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-16-113241.png)

## 数据库构建

* 前端：Thymeleaf (a Java template engine), HTML5, CSS, AJAX, JQuery and Bootstrap
* 后端：Spring Boot
* 数据存储：MySQL
* 数据读取：Mybatis
* 交互图：Echarts, Highcharts, svg3dtagcloud.js and plotly.js
* 表格：Bootstrap Table

<br>


# 2.2.10 研究| 2021-单细胞转录组分析COVID-19重症患者肺泡巨噬细胞亚型

刘小泽写于2021.10.18

## 前言

题目：Single-cell transcriptome identifies *FCGR3B* upregulated subtype of alveolar macrophages in patients with critical COVID-19

日期：2021-09-24

期刊：iScience

链接：<https://doi.org/10.1016/j.isci.2021.103030>

## 一句话概括

数据分析文章：scRNA分析重症COVID-19患者多个样本，得到一种单核细胞衍生的肺泡巨噬细胞 (MoAMs) 亚型，并且FCGR3B基因在其中特异性表达，提供了一个新的biomarker。

## 使用的数据

* 对照1：10例健康气管样本（ [Deprez et al., 2020](https://www.cell.com/iscience/fulltext/S2589-0042\(21\)00998-6?rss=yes\&utm_source=dlvr.it\&utm_medium=twitter#bib14)）
* 对照2：4例健康肺样本（[Madissoon et al., 2019](https://www.cell.com/iscience/fulltext/S2589-0042\(21\)00998-6?rss=yes\&utm_source=dlvr.it\&utm_medium=twitter#bib30)）
* BALF单细胞数据（支气管肺泡灌洗液, broncho-alveolar lavage fluid）：总共14个样本，包括对照、轻度和重症患者样本 ([Liao et al., 2020](https://www.cell.com/iscience/fulltext/S2589-0042\(21\)00998-6?rss=yes\&utm_source=dlvr.it\&utm_medium=twitter#bib29))

## BALF单细胞群初步注释

利用Liao文章的marker基因，大致分成macrophages, myeloid dendritic cells, and T-cells，但发现很多cluster的细胞类型不好确定【A) Control. B) Moderate. C) Severe BALF clusters using Liao et al markers】

![image-20211018212214987](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-18-132215.png)

于是自己又细分亚群，分别得到19, 17, and 18个cluster， 21939, 7316 and 37197 cells

![image-20211018212407400](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-18-132407.png)

然后又找了一些marker自行细胞注释：<https://www.cell.com/cms/10.1016/j.isci.2021.103030/attachment/24e25182-6d40-41fa-a4f5-fc33e365f86f/mmc2>

![image-20211018212524960](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-18-132525.png)

## 重症样本注释

包括了：basal cells, vascular cells, dendritic cells, ionocytes, monocyte-derived alveolar macrophages, plasma cells, and alveolar epithelial cells

![image-20211018212755234](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-18-132755.png)

![image-20211018212928624](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-18-132928.png)

## 重症样本的cluster11与COVID-19并发症基因之间的关联

并发症包括了：encoding cytokines and cytokine receptors, or associated with rare infectious diseases, rare syndromes, chronic obstructive pulmonary disease, cardiovascular disease, hypertension, obesity, and diabetes

基因列表：<https://www.cell.com/cms/10.1016/j.isci.2021.103030/attachment/0b421468-3a53-48b6-8c28-3eee4d7e06f7/mmc4>

发现重症的17个cluster中，**cluster11表现非常突出，9个基因列表中表达了8个，并且表达量还主要是上调**

因此，这个cluster11就被标记为monocyte-derived alveolar macrophages (MoAMs)，它的marker 基因就是*CCl3L1* ，而且这个MoAMs亚型在 moderate or control样本中都没发现

![image-20211018213458204](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-18-133458.png)

之后也看了一下富集分析，主要集中在：

* host immune response signaling networks related to TNFα
* cytokine and interferon gamma responses
* response to type1 interferon and biotic stimulus
* innate immune and inflammatory responses

![image-20211018213759747](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-18-133800.png)

## MoAM亚型top20基因分析

既然感兴趣的cluster找到，那么接下来就看其中的top基因（这里选择前20）

做了一个气泡图【 A) Control. B) Moderate and C) Severe BALF】，不过感觉没啥必要，既然选择cluster11的前20，那么肯定这些基因（横坐标）就主导啊

![image-20211018214139670](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-18-134140.png)

然后就定位到了其中一个差异最大的基因：*FCGR3B*

![image-20211018214440837](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-18-134441.png)

然后开始研究这个基因了：

* regulate both adaptive and innate immune responses which are crucial for the **defense against infection** and prevention of chronic inflammation or autoimmune diseases
* FcRs mediate important immune responses such as **release of cytokines or phagocytosis** ([Ben Mkaddem et al., 2019](https://www.cell.com/iscience/fulltext/S2589-0042\(21\)00998-6?rss=yes\&utm_source=dlvr.it\&utm_medium=twitter#bib5))

并且图C中cluster11表达的FCGR3B，比其他FcR族基因更多

## FCGR3B的数据集验证

在bulk PBMC data进行验证 ([Arunachalam et al., 2020](https://www.cell.com/iscience/fulltext/S2589-0042\(21\)00998-6?rss=yes\&utm_source=dlvr.it\&utm_medium=twitter#bib2)) ，并且发现在non-classical monocytes中表达更多

![image-20211018214849262](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-18-134849.png)

在 bulk data set from nasopharyngeal swabs数据集验证（左图），另外之前看cluster11特异性表达CCL3L1，那么同样在重症的CCL3L1高表达细胞中，FCGR3B表达同样高

![image-20211018215022234](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-18-135022.png)

不过，不同于*FCGR3B*，*CCL3L1*和*TNFAIP6* (indicator of COVID-19 severity) 在其他数据集中并非一直是重症表达量高于对照组。所以最后的目光又集中于*FCGR3B* 这一个基因了

![image-20211018215544716](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-18-135545.png)

## FCGR3B的实验验证

qPCR positive COVID-19 patients (n = 31) and qPCR negative controls (n = 11)，发现：

* 左图（单纯covid）：**50% had greater than 1.5-fold change** of *FCGR3B* compared with 28.1% in controls
* 右图（covid+并发症）：**57.1% of patients with severe COVID-19 with comorbidity had greater** than 1.5-fold change of *FCGR3B* compared with 28.1% in controls

![image-20211018215754823](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-18-135754.png)

当然，最后还拓展了一下，做了个体外并发症（肥胖）关联实验，发现：*FCGR3B* as a potential modulator of COVID-19 severity in patients with obesity

![image-20211018220058328](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-18-140058.png)

<br>


# 2.2.11 研究 |2021-单细胞转录组揭示肺腺癌特有的肿瘤微环境

刘小泽写于2021.10.22

## 前言

题目：Single-cell RNA sequencing reveals distinct tumor microenvironmental patterns in lung adenocarcinoma

日期：2021-10-18

期刊：Oncogene

链接：<https://www.nature.com/articles/s41388-021-02054-3>

代码：<https://doi.org/10.24433/CO.0121060.v1>

## 一句话概括

揭示两种不同的肺腺癌微环境模式，基于微环境提供额外的预后信息，预测潜在的目标细胞群用于治疗

## 肺腺癌的细胞组成

10个正常+10个肺腺癌样本，得到114489个高质量的单细胞转录组数据

![image-20211021154512130](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-21-074512.png)

UMAP结果表明不管是组织类型还是不同病人，细胞都”混在一起“，说明不存在批次效应

![image-20211021154652842](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-21-074653.png)

作者拿了epithelial, immune, stromal 三种类型细胞的 marker genes，分出了20,450 epithelial, 89,766 immune, and 4273 stromal，发现和其他文章一样，免疫细胞占据主体（D图中黄色部分占绝对优势）。不同病人之间的上皮细胞占比差异明显，而且solid/sarcomatoid（实体瘤/肉瘤样型）这种tumor的上皮细胞占比不到10%，而**lepidic/acinar carcinomas（鳞屑状/腺泡型）这种占比大于40%**

![image-20211021154941680](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-21-074941.png)

## 肿瘤上皮细胞的异质性

> 重点发现：不同病人的上皮细胞存在异质性，但组织类型变化的过程中，又存在一些共性的变化

### **分群差异**

既然不同肿瘤样本展示了不同的上皮细胞占比差异，接下来作者将epi单独拿出来，重新分群，并且根据组织类型（normal和tumor）进行拆分（即：一整个epi的UMAP按照组织类型拆成了2张图）

![image-20211021155936251](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-21-075936.png)

对正常组织的上皮细胞再进行细胞注释（按照图B的marker基因），发现了：alveolar type 1 and 2, club, ciliated, and even a small cluster of neuroendocrine cells。肿瘤样本的epi体现出了明显的病人特异性（每个病人是单独的一团细胞）

![image-20211022094850060](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-22-014850.png)

### **拷贝数变异**

inferCNV，发现重新分群结果和组织类型以及拷贝数变异结果都能对应上，并且肿瘤纯度高于90%，侧面反映了分群的准确性

![image-20211021160209530](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-21-080210.png)

### **差异基因**

然后看了肿瘤上皮细胞的几个明显的差异基因表达量（EGFR, TFF3, CDKN2A, and SFTPA2），和免疫染色结果正相关

![image-20211021161221429](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-21-081221.png)

### **信号通路**

根据一些致癌信号通路基因，看了这些通路在不同病人之间的确存在差异，尤其是EGFR, TGFβ, JAK/STAT, Hypoxia, and PI3K信号通路

![image-20211021164721087](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-21-084721.png)

不过同时也发现，像是P034、033、030这几个病人，虽然他们的信号通路活性普遍比较高，但是他们的有丝分裂活性并不高

![image-20211022094346190](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-22-014521.png)

### **从病人视角切换到组织视角，发现一些共性**

这次不按病人，而是按组织类型进行cluster的划分，并且看到第一个主成分（认为代表了最主要的生物差异因素）随着组织类型的变化，呈现梯度式的变化

![image-20211022101334064](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-22-021334.png)

根据PC1 的PCA score，又找到了：**top 30 genes** positively and negatively correlated with PC1 were defined as an “alveolar/club-like” and “undifferentiated” tumor cell signature，其中*SCGB3A1* and *SCGB3A2* 又和肺的发育相关

![image-20211022101447716](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-22-021447.png)

从undifferentiated到alveolar/club-like表型，肿瘤上皮细胞呈现出类似的组织类型和通路活性的变化，比如JAK/STAT, Hypoxia, EGFR and TGFβ信号在undifferentiated处于高位，而PI3K后来在alveolar/club-like表型中升高

![image-20211022101602380](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-22-021602.png)

## 肿瘤基质细胞微环境

> 重点发现：存在2个肌成纤维细胞群，分别表现出“normal-like” and “cancer-associated” 的特性，并且各自都能主导基质的微环境

在A图中的第三个fibroblastic/muscle cell clusters中，发现从normal到tumor，原来fibroblast占主导（两个蓝色），变成了myofibroblast占主导（两个绿色）

B图中展示的是myofibroblast和fibroblast的maker gene：Myofibroblast clusters were characterized by expression of **both fibroblastic marker genes**, such as PDGFRA and LUM, **and smooth muscle marker genes**, such as MYLK and ACTA2

![image-20211022111551049](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-22-031551.png)

然后看到myofibroblast的cluster2，都存在于tumor样本；而cluster1则是在tumor、normal均有，其中几个基因（COL5A2、COL6A3、SULF1、MMP11）与extracellular matrix remodeling有关

![image-20211022111922027](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-22-031922.png)

对比myofibroblast的2个cluster通路活性，发现cluster2在TGFβ 、JAK/STAT和hypoxia-induced pathways 均高于cluster1

![image-20211022112255732](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-22-032256.png)

另外，这两个cluster在fibroblastic/muscle cell的占比，在不同病人之间也是负相关的

![image-20211022112506029](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-22-032506.png)

## 肿瘤免疫微环境

> 重点发现：正常肺组织到腺癌的过程中，免疫细胞组成发生了什么变化；导致不同患者之间异质性的肿瘤免疫微环境是什么样的

拿到的细胞类型包括：tissue-resident and monocyte-derived macrophages, monocytes, myeloid and plasmacytoid dendritic cells, mast cells, and T, NK, B, and plasma cells

比较明显的是不同细胞类型的数量变化：

* tumor myeloid（骨髓） cell中，monocyte-derived macrophages、dendritic cells数量上升，tissue-resident macrophages、monocytes数量下降
* tumor lymphoid（淋巴） cell中，CD8+ T, B, and plasma cells数量上升，NK and conventional T cells下降

![image-20211022113222401](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-22-033222.png)

## 研究肿瘤微环境的构成模式

PCA将不同的病人样本进行划分：

* P018, P019, P024, P031, P032, and P033： **N³MC pattern**【**normal-like myofibroblasts**，non-inflammatory monocyte-derived macro- phages, NK cells, myeloid dendritic cells and conventional T cells】
* P023, P027, P030 and P034：**CP²E pattern**【**cancer-associated myofibroblasts**, proin- flammatory monocyte-derived macrophages, plasmacytoid den- dritic cells and exhausted CD8+ T cells】

![image-20211022115457976](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-22-035458.png)

![image-20211022132938932](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-22-052939.png)

![image-20211022133819567](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-22-053820.png)

做了细胞通讯分析，发现： **tumor cells in the CP2E** environment receive potential paracrine signals from cancer-associated **myofi- broblast cluster 2** activating **Ephrin, FGF, WNT, TGFβ, and BMP signaling**, and from proinflammatory monocyte-derived **macrophages cluster 2** potentially activating **JAK/STAT** signaling

![image-20211022132819928](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-22-052820.png)

分析了TCGA lung adenocarcinoma cohort，发现 N³MC 相关的gene signature预后更好

![image-20211022133923384](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-22-053924.png)

最后得出结论：

* 免疫活化的CP²E微环境由癌症相关的肌纤维细胞，促炎单核细胞衍生的巨噬细胞，血浆骨质树突树突细胞和排出的CD8 + T细胞组成，并且预后不利
* 惰性N³MC 微环境主要包括正常的肌纤维细胞，非炎症单核细胞衍生的巨噬细胞，NK细胞，骨髓树突细胞和常规T细胞，并与良好的预后有关


# 2.2.12 研究 | 2021-单细胞转录组揭示乳头状甲状腺癌起始与发展

刘小泽写于2021.10.26

## 前言

题目：Single-cell transcriptomic analysis of the tumor ecosystems underlying initiation and progression of papillary thyroid carcinoma

日期：2021-10-18

期刊：nature communications

链接：<https://www.nature.com/articles/s41467-021-26343-3>

## 一句话概括

文章关注肿瘤微环境：先初步分群，然后依次看了几个重要组成部分：thyrocytes、immune细胞群、基质细胞（fibroblasts 、endothelial cells）

## 使用的数据

11个病人的23个组织，得到158,577个细胞

![image-20211026100416227](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-26-020416.png)

## 初步分群结果

得到**6个主要的细胞群：**

* T/natural killer (NK) cells (CD3D, CD3E, CD3G, CD247)
* B cells (CD79A, CD79B, IGHM, IGHD)
* thyrocytes (TG, EPCAM, KRT18, KRT19)
* myeloid cells (LYZ, S100A8, S100A9, CD14)
* fibroblasts (COL1A1, COL1A2, COL3A1, ACTA2)
* endothelial cells (PECAM1, CD34, CDH5, VWF)

![image-20211026100646905](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-26-020647.png)

接下来重点关注免疫细胞（T, NK, B, and myeloid cell ）的细分，得到**22个亚群**：

* T细胞可以根据CD4和CD8的表达量继续细分，得到7 clusters for CD4+ cells & 4 clusters for CD8+ cells
* NK, B, and myeloid cells同样细分得到 2, 3, and 6 subsets

并且每个亚群都有各自的组织偏好性（图e）

其中，CD4-c6中的FOXP3上调，CD8-c4中的PDCD1上调，分别对应了 regulatory CD4+ T cells (Treg) 和 exhausted CD8+ T cells (Tex)，它们主要存在于皮下和肿瘤组织中；另外CD8-c3 中的GNLY上调，对应了效应T细胞，主要存在于肿瘤组织

这几种免疫细胞均在肿瘤组织中富集，说明了宿主免疫应答与肿瘤免疫逃逸共存

![image-20211026101100992](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-26-021101.png)

## 看甲状腺细胞群的异质性

拿thyrocytes这群细胞继续细分，得到9个亚群，其中1、2亚群主要是para-tumor，可以代表non- malignant thyrocytes；3-9则是malignant thyrocytes

![image-20211026102825082](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-26-022825.png)

接下来就用3种方法证明这样划分是对的：

![image-20211026103431062](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-26-023431.png)

* 首先是相关性（图c），发现1、2最相关
* 然后（图d）计算每个亚群的thyroid differentiation score (TDS)。TDS用来评价分化程度，其中1、2最高，并且照应了para-tumor最高的情况
* 最后使用TCGA的PTC样本bulk转录组，构建一个分类器，达到97% sensitivity and 96% specificity ；再将这个分类器用在这个单细胞数据，发现与推测的组织类型相似度达到了：95%, 97% and > 98% of c01, c02, and c03–09 cells

于是，下面就认为**malignant (c03-c09)** and **non-malignant (c01, c02)** thyrocytes

探索了恶性vs非恶性、恶性肿瘤 vs恶性淋巴、恶性肿瘤 vs恶性皮下，发现：**TMSB4X** as a suggestive biomarker that potentially involves in PTC initiation and progression

![image-20211026103921664](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-26-023922.png)

#### 既然分开了恶性和非恶性，那就先研究非恶性的两个亚群

之前图d看到，c1和c2的分化指标虽然比其他群高，但二者还是有差距，于是就开始探索这两群非恶性细胞的差异

看到c1到c2会有中间态，然后看到c1=》c2=》c3-c9的过程中，TMSB4X也确实逐渐升高

推测：**c2这个群可能不是完全的正常状态，是出于正常和恶性之间的过渡态**

![image-20211026104550993](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-26-024551.png)

通过实验验证了c2的属性是：cancer-primed nature of these outwardly normal but **transcriptionally altered** premalignant cells, which provide both seeds and soil for the eruption of malignant growths，将c2归结为premalignant

最后对比了c1和c2，对PKHD1L1这个基因感兴趣，推测 PKHD1L1 might function as a tumor suppressor gene in multiple solid tumors

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-26-025250.png)

## 接下来重点看恶性的细胞——发育轨迹

正常的c1和premalignant的c2在右上角，作为发育起源（normal-cell-initiated State 1），看看三个发育状态有什么区别：

![image-20211026110343493](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-26-030344.png)

* state1有一个明显的转录因子SOX9，作用是branching folliculogenesis（卵泡生成） of normal thyroid gland，于是state1的名称是follicular-like thyrocyte

![image-20211026110630820](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-26-030631.png)

* State 2 was basically a half-half mixture of tumor and LN-metastatic thyrocytes ![image-20211026110928873](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-26-030929.png)
* State 3, featured by the **lowest TDS score and RAS score**, and **highest** **BRAF** score, contained the vast majority of RAIR subcutaneous metastatic thyrocytes；另外state3高表达GATA2, MYC, SOX4（去分化相关的转录因子）

## 拿发育轨迹的结果做点什么

得到了480 pseudotime-associated genes (PAGs)，因为它们主要就是在甲状腺上皮细胞中，再结合2个bulk转录组数据，可以用来更新BRAF-/RAS-like 分子亚型方法。发现和之前的分型方法一样，**BRAF-like tumors也是存在很强的异质性，将其分成2个亚型**（BRAF-like-A and BRAF-like-B），发现BRAF-like-B更加特别：associated with TCV pathology ； lower TDS scores；advanced staging；significantly compromised DFS；

![image-20211026123054006](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-26-043054.png)

GSEA结果发现BRAF-like-B在免疫相关通路活性更高

![image-20211026123531415](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-26-043531.png)

并且三个亚型RAS-like, BRAF-like-A, BRAF-like-B分别对应了三种表型：follicular-like, p-EMT-like, dediff-like

![image-20211026123856784](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-26-043856.png)

## 接下来看基质细胞——CAFs

> 其中重点关注cancer-associated fibroblasts (CAFs)

初步分群的fibroblasts这群，表达了CAF的marker：VIM, S100A4, ACTA2 (α-SMA), and PDGRFA

因此可以直接把fibroblasts成为CAF，然后把CAF分群，一开始分成4群，其中cluster 0, 1 and 3可以当成myofibroblastic CAFs (myoCAF)；cluster 2是inflammatory subtype (iCAF) ，它的marker基因是CFD, PLA2G2A, CCDC80

![image-20211026124527891](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-26-044528.png)

正式有了iCAF和myoCAF，再看和分型的关系，发现CAFs主要存在于BRAF-like中

接着做了iCAF和myoCAF的细胞通讯，发现：

* regulation of cellular immunity is an important function for **iCAFs** in PTC
* **myoCAFs** tends to exert mechanical and chemical influence on tumor progression rather than through direct cell communications, as described in other solid tumors

![image-20211026124310210](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-26-044310.png)

## 再看基质细胞——内皮细胞（endothelial cells, ECs）

根据已有的marker，将EC分成5个类型：arterial, venous, lymphatic, immature and tip

* arterial 高表达 arterial development and remodeling (**FBLN5, GJA5, JAG1**) and smooth muscle contraction (**PPP1R14A**)
* venous 高表达 VWF (von Willebrand factor) and genes associated with leukocyte recruitment (**ACKR1**) or adhesion (**SELE**)
* Lymphatic 高表达 canonical marker **LYVE1** and a chemokine ligand **CCL21**
* immature 高表达 Notch signaling and target genes (**JAG1, HES1, ID1, ID2, ID3**), and genes involving in barrier integrity (**ENG, PLVAP, HSPG2, APLNR**), which may resemble the stalk-like cells
* tip 高表达 cell migration **(NRP1, ENPP2**), adhesion (**THY1**) and vessel formation (**FLT1**, also called VEGFR1; **KDR**, also called VEGFR2; **NRP1**, also called VEGF165R)；并且tip高表达一些转录因子，比如 endothelial migration and sprouting, such as ZEB1, HOXB5 and STAT family

图h看到，只有lymphatic在正常的甲状腺组织中富集，其他均存在于原发或转移癌中

最后的细胞通讯看了EC和免疫细胞，发现：

* lymphatic 和免疫细胞通过atypical chemokine receptor 2 (ACKR2)进行关联【它用来调控chemokine availability】（在附图）
* venous, immature and arterial ECs 和免疫细胞通过 Intercellular Adhesion Molecule 1 (ICAM1) 关联 （在附图）
* tip 和免疫细胞通过key angiogenic VEGF-VEGFR signalings 关联（图i）

![image-20211026125434470](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-26-045435.png)


# 2.2.13 研究 | 2021-解析食管鳞癌化疗病人的单细胞转录组

刘小泽写于2021.11.01

## 前言

题目：Dissecting the single-cell transcriptome network in patients with esophageal squamous cell carcinoma receiving operative paclitaxel plus platinum chemotherapy

日期：2021-10-26

期刊：Oncogenesis

链接：[https://www.nature.com/articles/s41389-021-00359-2](#qian-yan)

## 一句话概括

分段式作文：5个常规治疗病人和5个化疗病人得到的单细胞数据，先初步分成上皮、基质和免疫，然后每一类继续细分，分别探讨

## 首先是初步的分群

> 三大群：上皮、基质和免疫，然后基质又细分一下：内皮和成纤维细胞；免疫又细分为T、NK、B、髓细胞、肥大细胞

选择的样本：5个病人underwent surgery alone (SA-ESCC) + 5个病人underwent preoperative paclitaxel plus platinum chemotherapy (NACT-ESCC)，总共得到113,581个细胞（63,837 SA-ESCC derived cells and 49,744 NACT-ESCC derived cells）

使用`SingleR`结合`CellMarker`的数据库以及其它文献的marker基因进行注释，得到了**epithelial**, **stromal** (fibroblasts and endothelial cells), and **immune** cells (T, NK, B, Myeloid, and Mast cells)

**图D**是细胞比例分布：NACT-ESCC 病人的免疫细胞（尤其是T、B细胞）比例很高，但它的上皮细胞和基质细胞比较少，说明NACT病人的宿主抗肿瘤免疫应答反应被激活

**图E**展示了几个通路的关键基因的热图：

* cytokines和NF-kB相关基因（TNFSF13B, IL6, and NKFB1）在monocytes上调，说明monocytes与食管癌发生相关
* hypoxia genes (e.g., LDHA, HIF1A, and EPO)在SA-ESCC病人中高于NACT-ESCC，说明化疗影响缺氧微环境

> Cytokines, nuclear factor-kB (NF-kB), and hypoxia signaling pathways play an essential role in tumorigenesis

![image-20211101101344645](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-11-01-021345.png)

## 然后看上皮

上皮细胞细分得到7个亚群：

* Tumor epithelial cell markers (EPCAM, MDK, and SOX4) 在cluster1、6中高表达
* non-malignant epithelial cell markers (KRT5 and KRT14)在cluster0、2、4、5高表达
* cluster3同时表达tumor和normal的上皮细胞marker

同时CNV也显示tumor epi的malignant scores要比normal epi高（**图C**）

另外，`scpred`R包发现基本没有误判的情况：

* 86% (3662/4245) of malignant epithelial cells were not identified as healthy epithelial cells
* 95% (6977/7324) of non-malignant epithelial cells were not identified as tumor epithelial cells

再次证明细胞分群注释的正确性

![image-20211101102815761](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-11-01-022815.png)

然后是**差异分析：**

* 预后差相关基因（SOX4 and MDK）在SA-ESCC-malignant epithelial cells富集
* 免疫相关基因（IGLC2, FABP5, and S100A2）在non-malignant epithelial cells上调
* SPRR3 and CEACAM6在NACT-malignant epithelial cells高表达

另外，non-malignant clusters也是存在异质性的，可以按起源继续分成2个cluster

然后是**拟时序分析：**

* 最开始是non-malignant epithelial cells derived from SA-ESCC patients
* 然后是 NACT-derived non-malignant epithelial cells
* 然后分叉：一个是SA-ESCC-malignant epithelial cell，一个是epithelial cells from different sources (NACT-ESCC or SA-ESCC patients)

最后看不同cluster的**通路活性差异**：

* TME formation related pathways (e.g., hypoxia and angiogenesis)主要在non-malignant epithelial cells
* ESCC development and progression (e.g., G2M-checkpoint and DNA-repair pathways)主要在 SA-ESCC malignant epithelial cell
* inflammatory response, and IL2/IL6 related signaling pathways 在NACT-ESCC malignant epithelial cells和non-malignant epithelial都有表达

#### SA-ESCC和NACT-ESCC的细胞通讯

在SA-ESCC中，fibroblasts与其他类型细胞联系密切

在NACT-ESCC中，免疫细胞（尤其是T细胞和单核细胞）作用明显

针对这两种类型，然后分别对SA-ESCC的基质细胞和NACT-ESCC的免疫细胞取top50差异基因做通路，发现：

* SA-ESCC的基质细胞主要调控extracellular matrix
* NACT-ESCC的T和单核细胞主要抑制肿瘤发展

![image-20211101104446457](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-11-01-024446.png)

接着做了代谢分析，发现：

* Oxidative phosphorylation, glycolysis/gluconeogenesis, and citrate cycle (TCA cycle) pathways在SA-ESCC病人的恶性与非恶性细胞都上调
* 而在NACT-ESCC中，只在非恶性细胞中上调
* NACT-ESCC has a unique TME compared to SA-ESCC
* **mitochondria** is a potential therapeutic target for SA-ESCC

![image-20211101103900818](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-11-01-023901.png)

## 接着看基质细胞（内皮+成纤维）

基质细胞包括了**19,977** and **6588** fibroblasts and endothelial cells (EDCs)

* 内皮细胞的maker选取了CLDN5 and RAMP2，又细分为8个cluster，整合为4个子细胞类型：immune EDCs (CCL5 and CXCL13), lymphatic EDCs (PDPN and PROX1), tumor EDCs (ACKR1 and POSTN), and vascular EDCs (PLVAP and SLC9A3R2)【图A】
* 成纤维细胞marker选取了C1R and COL1A2，得到11个cluster，整合为3个子细胞类型：COL14A1 matrix fibroblasts (COL14A1 and GSN), myofibroblasts (CTHRC1 and MMP11), and vascular smooth muscle cells (DES and MYH11)【图B】

### **看内皮细胞**

在图A发现：NACT-ESCC的tumor EDC虽然少于SA-ESCC，但它的immune EDCs却高，于是接下来做了tumor EDC和immune EDC的GSVA和差异分析【图C、D】，发现：

* metabolic pathways, including cholesterol homeostasis and fatty acid metabolism在NACT-ESCC的tumor EDCs中更高
* **cancer-promoting** genes (e.g., S100A family genes) were highly expressed in **SA-ESCC**
* **HSP** family genes were significantly elevated in the **NACT-ESCC**
* marker genes for **SA-ESCC** derived immune EDCs were implicated in biological regulation and **cell growth**
* marker genes for immune EDCs in the **NACT-ESCC** group were mainly involved in cellular and **immune responses**

> HSP family genes promote cancer growth and metastasis in several tumors

### **看成纤维细胞**

图B看到：

* COL14A1+ matrix fibroblasts 主要在 NACT-ESCC
* myofibroblasts 主要在 SA-ESCC

图F看到：

* POSTN and WNT5A 在 myofibroblasts高表达，和Wnt and Notch signaling pathways相关
* ACTA2在myofibroblasts and smooth muscle cells高表达

另外图G的GSVA也看到，Wnt and Notch signaling也是在myofibroblasts富集

![image-20211101105031858](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-11-01-025032.png)

## 接着看髓细胞

基于CD68 and LYZ基因，拿到16,305 myeloid cells，继续细分为monocytes, macrophages, dendritic cells, and undefined myeloid cells

NACT-ESCC的macrophage细胞相对少，但monocyte多

**图B**选了一个marker基因Secreted phosphoprotein1 (SPP1)，它与预后差相关，发现它主要在SA-ESCC的macrophage

**图C**想看从单核到巨噬细胞的转变过程：State 1 and 2包括了NACT-ESCC and SA-ESCC的巨噬细胞；但3和4主要就是NACT-ESCC

另外，GSVA看到anti-tumor (e.g., Interferon-gamma (IFN-gamma) and Interferon-alpha (IFN-alpha)) response pathways主要在NACT-ESCC富集；pro-tumor responses (e.g., KRAS and EMT pathways)在SA-ESCC富集，TNF-α-NF-κB pathway也是在SA-ESCC富集

**图E**用`SCENIC`做了转录因子分析：

* SA-ESCC中，NFIA, TWIST1, and MAFB是在macrophages中top3上调；RXRA, TGIF1, and XBP1在monocytes中上调
* NACT- ESCC中，SPI1 and KLF4在macrophages中上调

**图F**中显示：PDCD1LG2 (PD1-L2), CD274 (PD-L1), CD276 (B7-H3), and LAG3这些与抑制CD8+ T 细胞活性相关的基因，在macrophages中上调；促炎相关免疫检查点基因（IL1A and IL6）在monocytes上调

![image-20211101110603940](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-11-01-030604.png)

## 最后看B细胞和T细胞

根据CD79A and IGKC，拿到2999个B细胞，分成3个cluster，整合成2个亚型：Plasma B cells和follicular B cells

发现 plasma B cells在NACT-ESCC更多

图C做了富集分析，发现：

* NACT-ESCC的plasma B cells会富集 interferon（干扰素） response-related pathways, MYC, and allograft（异源移植） rejection pathways
* SA-ESCC的plasma B cells会富集unfolded protein responses and angiogenesis pathways

同样是转录因子分析：

* STAT3 were elevated in follicular B cells
* ATF3 were observed in plasma B cells

![image-20211101111834562](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-11-01-031834.png)

对于T细胞，根据CD3D and CD3E拿到36,706 T cells，细分为10个cluster，整合成6个亚型，发现CD8+ T在NACT-ESCC中更多，不过NKT cells, CD8/CD4 mixed Th, and CD4+ T cells数量减少

图C发现：

* naïve and co-stimulatory molecule markers在CD4+ T细胞上调
* Cytokines, effector molecules and inhibitory receptors 在CD8+ and NK T上调

图D计算了Bhattacharyya距离，用于估计NACT-ESCC and SA-ESCC的T细胞亚型相似性，发现它们的**差异主要来自CD8+ T细胞（图D）**

然后将 CD8+ T 继续细分（图E），得到10个cluster，整合成3个亚型：cytotoxic CD8+ T cells, exhausted CD8+ T cells, and naïve CD8+ T cells。在NACT-ESCC中，exhausted CD8+ T cells and naïve CD8+ T占比明显升高，表示化疗改变了ESCC的免疫微环境

![image-20211101112607678](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-11-01-032608.png)

之后又对 CD8+ T cells进行了拟时序分析，轨迹从naïve CD8+ T & cytotoxic CD8+ T cell开始，分叉之一是cytotoxic CD8+ T cells，另一个分叉是exhausted CD8+ T cells；

轨迹热图发现了3个时期：

* phase 1主要是Naïve CD8+ and cytotoxic CD8+ T cells，GZMK and IL7R高表达，HAVCR2 and LAG3 低表达；
* phase2 高表达STMN1, CENPE, and GNLY，包含了exhausted CD8+ and cytotoxic CD8+ T cel
* phases 3 主要包括exhausted CD8+ T cells

另外，发现了SA-ESCC和NACT-ESCC免疫检查点相关的差异：PDCD1LG2基因在NACT-ESCC的exhausted CD8+ T cells高表达，而在SA-ESCC中是在cytotoxic CD8+ T中高表达，说明SA-ESCC and NACT-ESCC在免疫治疗反应方面存在差异

![image-20211101124629150](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-11-01-044629.png)


# 2.2.14 研究 | 2021-单细胞水平看骨髓瘤的细胞状态和基因调控

刘小泽写于2021.11.04

## 前言

题目：Dynamic transcriptional reprogramming leads to immunotherapeutic vulnerabilities in myeloma

日期：2021-10-21

期刊：Nature Cell Biology

链接：<https://www.nature.com/articles/s41556-021-00766-y>

## 一句话概括

7张主图+10张附图，而且每一张都至少有5-6张小图，使用**上百张图片**来说明：利用单细胞转录组+表观遗传分析骨髓瘤的细胞状态，从而研究异质性，帮助理解其中的基因调控网络，并帮助识别潜在的治疗靶点

## 需要补充一点背景知识

> <https://www.zs-hospital.sh.cn/zsyy/n33/n35/n48/n399/n403/u1ai3894.html>

**骨髓**位于人体较大的骨骼的腔中，约占人体体重的 5%，含有间充质干细胞和**造血干细胞**

人体所有的血细胞都是由造血干细胞分化、增殖而成的，包括：红细胞、白细胞和血小板

* 红细胞：可以将氧气从肺部输送至身体的各个部位
* 血小板：具有止血功能
* 白细胞：人体免疫系统的基石，包括淋巴细胞、单核细胞、粒细胞

浆细胞是淋巴细胞中 B 淋巴细胞分化的产物，它能分泌抗体，消灭入侵人体的病毒、细菌等。如果 B 淋巴细胞在分化成浆细胞的过程中 DNA 受到破坏，这个异变的浆细胞回到骨髓后，又再次受到环境的刺激时，就会发生癌变，也就成为了**骨髓瘤细胞**。

相比于正常的浆细胞，骨髓瘤细胞会以更快的速度分裂，并且分泌一种异变蛋白 - Paraprotein，这种异变蛋白没有抗体的免疫功能，医学上叫单克隆伽马球蛋白，或者单克隆丙种球蛋白

## 转录机制part1——表达

> 不过文章写了8个病人，但主图只显示了7个，而且也没看到说明原因

选取8个relapsed/refractory MM病人骨髓或血液中的骨髓瘤细胞以及CD45+ 免疫细胞，以及2个健康供体，使用SmartSeq2得到单细胞全长转录组，共 6,955 cells，利用`PAGODA2`进行分群，其中 clusters 20, 1, 5, 7, 14, 12, 3 and 13对应了CD4+ and CD8+ T cells, NK cells, B cells, monocytes and neutrophils（中性粒细胞）；clusters 2, 4, 6, 8, 10, 11, 15, 16, 17 and 19对应了plasma cell

> multiple myeloma (MM)，多发性骨髓瘤

然后分析CNV区分恶性和非恶性细胞，不管是malignant score（图f）还是CNV扩展、缺失（图e），MM样本都要比ND正常样本高。另外还使用随机森林模型，寻找区分这两种类型细胞的marker基因，像是*CCND1*、FRZB13都是之前报道过的，而且这个CCND1在3/8的MM病人中都高表达。另外，还看了其他的signature，比如*NFKB* signature主要在MM1病人表达，proliferation signature *PR* 主要在MM8病人表达

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-11-01-084420.png)

### **既然表达有差异，那么接下来继续深入探索**

**首先推测：细胞周期对表达异质性会有影响**

> G、S、M期(<https://en.wikipedia.org/wiki/Cell\\_cycle>)
>
> * G1期（间期的DNA合成前期）
> * S期（间期的DNA合成期），启动DNA合成
> * G2期（间期的DNA合成后期）
> * M期（有丝分裂期），存在检测点和抑制点
>
> <img src="https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-11-04-023909.png" alt="image-20211104103909490" data-size="original">

发现分群之间没有太大的细胞周期差异（除了cluster10，可能因为这个cluster覆盖的病人样本比较多吧）

**然后，使用非负矩阵分解(negative matrix factorization，NMF)**

得到6个表达模式，除了细胞周期，还有很多细胞信号通路，包括KRAS–MAPK signalling, IL-2–STAT5 signalling, the interferon (IFN) response and IL-6–STAT3 signalling

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-11-04-023740.png)

### 探索骨髓瘤细胞转录水平变化的方向

> BLUEPRINT不同的细胞类型：<https://www.blueprint-epigenome.eu/index.cfm?p=7BCEDA45-EC73-3496-2C823D929DD423DB>

使用BLUEPRINT数据集的不同细胞谱系的signature，发现骨髓瘤细胞的转录状态主要集中于浆细胞（PC）和一些未成熟的前体细胞（Megakaryocyte-erythroid precursor ，MEP）【图ab】，确实存在不同的变化阶段

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-11-04-063208.png)

**接着利用RNA velocity analysis看了分化轨迹**

得到一个由低分化向高分化的轨迹，正常样本的浆细胞聚集在尾部（标志着分化的结束），而正在分化的细胞聚在头部。再结合上图的e图，使用CytoTRACE看到骨髓瘤细胞具有比正常浆细胞更高的CytoTRACE score，标志着更好的分化潜能

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-11-04-063752.png)

**接下来，看分化轨迹两端的差异**

拿到CytoTRACE 分数高的细胞（也代表了未成熟状态），找到他们的top基因，做了通路（上图g），发现与代谢途径、Myc激活有关，还有一个plasmablast-like signature。

另外发现头尾具有截然相反的通路激活情况，比如上面的图h中MAPK通路，在头部高尾部低；而PI3K是尾部更高

## 转录机制part2——调控

### **首先看基因调控网络GRN**

【图a】利用SCENIC看转录因子活性，利用BLUEPRINT数据，找到正常造血中的主导调控因子存在于浆细胞（PC module）；

【图b】再看自己的数据：正常的供体中，这个PC module确实占主导；而骨髓瘤样本中，PC module除了在浆细胞，还在haemopoietic stem cell 、macrophage等类型中发现

接下来就是寻找骨髓瘤细胞特异的调控网络（图c），但其中好多调控元件在正常的数据中都没有或者很弱，比如ELF3、TEAD4（图d）

* ELF3 is a member of the epithelium-specific ETS TFs expressed predominantly in epithelial tissues
* TEAD4 acts as a downstream regulator of the Hippo pathway and binds to the M-CAT motif found primarily in muscle-specific genes

另外还发现了恶性和正常的浆细胞共有的一些调控元件，比如*XBP1*, *IRF4* and *PRDM1* ，说明骨髓瘤细胞还是保留了一些原有细胞谱系的印记

接着，引入一个指标（**rewiring score**），意思是看看一个调控元件改变后，与之相关的基因变化程度（在网络中可以理解为node对targets的影响；或者简单理解为rewiring score代表了影响力）。发现ELF3和TEAD4是两个得分最高的，其次是XBP1，说明它们对基因影响是比较大的。

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-11-04-081342.png)

### **调控差异的产生，对染色质可接近性有影响吗？**

对5个病人+3个正常做了scATAC-seq，得到 1,483个细胞，注释分群得到：

* clusters 1–6：plasma cell
* clusters 7, 8 and 9：monocytes, B cells and NK cells

同时对比图b、c发现了cluster分群存在病人特异性，说明不同病人存在不同的染色质差异

![image-20211104165510661](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-11-04-085510.png)

接着做了peak calling，其中MM特有的是29,761个peaks，而正常特有的只有不到8000个；另外用DESeq2找了MM和正常的差异peaks，差异也是一致的（图c-d）

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-11-04-083106.png)

用`ChromHMM`对peaks进行注释，发现很多MM的peaks落在了异染色质，并且在intronic and intergenic区域更多，说明MM可能募集到更多的增强子enhancer；同时发现MM的可开放区域距离TSS很远，这个和之前报道的骨髓瘤H3K27 乙酰化程度升高一致

![image-20211104163825168](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-11-04-083825.png)

为了阐释scATAC数据集的差异，做了拟时序分析。发现在MM中比较重要的一些基因，比如CCND1和ELF3，在轨迹的”头部“表现更高的开放程度【主图e】，又分析了motif accessibility，发现NF-κB family members *REL*, *RELA* and *NFKB1* 得分比较高，并且集中于”尾部“的正常样本。用`chromVAR` 进行差异分析，得到276个TFs。

图i-j基于*ELF3* and *TEAD4* 预测了顺式作用因子， 为MM中存在增强子激活再次提供证据


# 2.3.1 算法｜2020-BatchBench比较scRNA批次矫正方法

刘小泽写于2020.5.29

> 这篇文章做了一件事，就是帮助我们区分不同的批次矫正方法，然后比较了一下优劣

文章题目：Flexible comparison of batch correction methods for single-cell RNA-seq using BatchBench

文章在：[Flexible comparison of batch correction methods for single-cell RNA-seq using BatchBench](https://www.biorxiv.org/content/10.1101/2020.05.22.111211v2)

上传时间是：2020.05.22

BatchBench地址 ：<https://github.com/cellgeni/batchbench>

> BatchBench is a Nextflow workflow for running the following scRNA-Seq data batch effect correction methods:
>
> * mnnCorrect
> * limma
> * ComBat
> * Seurat 3
> * Scanorama
> * Harmony
> * FastMNN
> * BBKNN

## 前言

### **首先为什么要进行批次矫正？**

单细胞分析经常会整合一些公共数据，不同的实验时间、文库制备、测序方案，都会产生一些技术误差，如果太多，可能会干扰真实的生物信号。因此来自这些非生物因素的干扰就称作批次效应

**作者将8种常用的批次矫正方法分为3类：**

* mnnCorrect、limma、ComBat、Seurat 3、Scanorama：产生一个整合、矫正后的表达矩阵
* Harmony、FastMNN：不是直接操作原始表达矩阵，而是对降维后的结果操作（they operate on a low-dimensional embedding of the original expression matrices），因此如果下游分析如果要用到原始表达矩阵的话，这类方法就会受限
* BBKNN：基于表达矩阵构建k-nearest neighbor graph（KNN），只能进行后续基于细胞的分析（如聚类、分群可视化），不能进行基于基因的分析（如marker基因鉴定、基因网络）

关于这8种方法：

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-29-031053.png)

以及这三类针对什么进行分析以及后续可以做什么，作者也作图说明：

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-29-030946.png)

但真正使用哪种方法，还是要依赖一个评测结果。但传统的评测只能针对已发表的方法，并且评测缺少一些高质量的数据集（比如尽可能多的包含批次效应的因素）

作者使用BatchBench，针对3种研究深入的数据集，对8种方法进行评测，这个方法的流程是：

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-29-031026.png)

### **三个数据集**

**Pancreas dataset**

Baron (GSE84133)、Muraro (GSE85241)、Segerstolpe (E-MTAB-5061) 分别由inDrop, CEL-Seq2 和 Smart-Seq2产生。过滤细胞：细胞中基因表达量少于200；过滤基因：在少于3个细胞中表达。另外只保留有注释的细胞类型（去掉了unclassified这类的细胞）

**Mouse Cell Atlas datasets**

数据来自：<https://figshare.com/s/865e694ad06d5857db4b>

按照组织进行整合，得到了包含37个器官的数据集，其中选取了18个数据集（它们中包含大于1个批次并且有合理的细胞类型分布）。过滤细胞：基因表达量少于250；过滤基因：在少于50个细胞中表达；过滤细胞类型：细胞数量少于整体1%的类型；过滤批次：细胞数量少于总体5%的批次

**Tabula Muris datasets**

数据来自：<https://www.google.com/url?q=https://figshare.com/projects/Tabula_Muris_Transcriptomic_characterization_of_20_organs_and_tissues_from_Mus_musculus_at_single_cell_resolution/27733&sa=D&ust=1589187433512000&usg=AFQjCNFC_0CGNwum-u2nka-OvFAmxoECtA>

来自两个平台的同一组织的不同数据混合，得到11个器官的数据集。过滤细胞：基因表达量少于1000；过滤基因：在少于50个细胞中表达；过滤细胞类型：细胞数量少于整体1%的类型；过滤批次：细胞数量少于总体5%的批次。结果得到4168个基因，60828个细胞（40,058 from 10X and 20,770 from Smart-Seq2）

### 直接上结论

Seurat的整体效果最好，它既正确地整合了批次，又没有丢失不同细胞类型；

Harmony在pancreas和MCA的数据中表现也不错，但在矫正Tabula Muris数据时失败；Scanorama 和 fastMNN表现也算良好；

这里使用的熵评估方法，可能不太适用BBKNN，因此它需要额外的评测方法；

另外对于处理大量的细胞数量和批次，Harmony表现优秀，并且计算资源分配合理。除了Harmony和BBKNN，其他方法当遇到上百个批次的处理时（即使一个批次中的细胞数量不多）也会捉襟见肘，因此未来的批次效应处理方法应该向数据可扩展性（scalability）上发展。

如果想使用处理批次效应后的表达矩阵进行下游分析（如鉴定marker基因），这些方法都会遇到问题。因为marker基因并不是保守存在的，任何基于基因的分析（例如 找差异基因或者鉴定marker基因），都是基于基因表达量，而批次矫正方法需要保证不会干扰表达量的变化，这一点也是未来需要改进的。

## 结果

### **1 测试批次整合与细胞分群**

使用了人类胰腺癌的3个scRNA数据集，原始数据的UMAP结果是：

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-29-032155.png)

> 左边是三个数据集，右边是各种细胞类型 但不得不说，两个图例使用的颜色太相近，容易引起混淆

可以看到，所有的方法都能将不同数据集的细胞混合起来，而依然可以分离不同的细胞类型

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-31-013319.png)

然后为了评估处理（先整合不同批次的细胞，然后分离不同的细胞类型）的效果，提出了计算一个”熵“：normalized Shannon entropy。如果批次方面的熵比较高，说明混合的批次之间更接近，也就是混合效果更好；如果细胞类型方面的熵比较低，说明细胞类型依然可区分

可以看到，不同的方法都保持较低的细胞类型方面的熵，因此它们都能够保证分离不同类型的细胞；但批次方面的熵差别较大。其中Seurat和Harmony整体表现较好，汽其次是Scanorama和fastMNN；而mnnCorrect, Limma 和 ComBat的表现较差

并且大部分方法对MCA（Mouse Cell Atlas）数据集的整合效果更好

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-31-014004.png)

> 图例：pancreas data (red), Mouse Cell Atlas (green), and Tabula Muris (blue)

### **2 当细胞数量或批次数量增加时，批次矫正变得困难**

利用 Tabula Muris数据集（总共60,828 cells），取了它的1%、3%、5%、10%、20%、50%作比较

当细胞数量从608（1%）增至60828时，除了Scanorama、Harmony、Seurat，其他方法的批次熵都下降了50%左右。但是Scanorama在混合批次的同时，也混合了细胞类型（可以看到蓝色的虚线基本不变，说明细胞类型熵不变，也就是没有分离细胞类型）

Harmony是唯一一个在增加细胞数量后，批次熵增加的（图a）。除了Scanorama，其余方法的细胞类型熵都降低，说明细胞数量增多，细胞分群更容易

批次数量增加时，BBKNN, Seurat 和 Harmony表现最稳定（图d）

在时间方面，mnnCorrect和fastMNN随细胞数量增长，运行时间也呈现指数增长，mnnCorrect运行最慢。不过时间消耗在大部分软件中差别不大

在内存方面，所有的方法随细胞数量增长，内存消耗都呈现指数增长，其中Seurat消耗内存最多。综上，Seurat, mnnCorrect, ComBat 和 fastMNN是比较消耗资源的，而Harmony, Scanorama 和 BBKNN资源需求最小

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-31-022157.png)

> a、d：熵的变化；b、e运行时间的变化；c、f：消耗内存的变化

### **3 批次处理对非监督聚类和marker基因鉴定的影响**

使用三种非监督聚类方法：Leiden、Louvain、SC3，然后比较矫正前后的数据聚类结果。这个结果相似性的量化是利用Adjusted Rand Index (ARI)，图a可以看到：MCA数据集利用不同的方法聚类后结果相似，但组织之间的差异比较大；Tabula Muris数据集也是如此，不过与MCA不同的是，利用SC3方法得到的聚类结果会比Louvain或Leiden结果普遍（11个组织中有7个）有更高的ARI。奇怪的是，对于心脏和乳腺组织，最佳的聚类结果发生在：SC3+非批次矫正的数据。对于pancreas数据，SC3倾向于得到更大的ARI，而且不想MCA数据，Seurat和Harmony的聚类结果与之前的熵分析结果也一致。

对整合后的数据进行marker基因鉴定，只有ComBat和Limma的结果可以找到大部分细胞类型的marker基因，Seurat只能对少部分细胞类型进行鉴定（图b），但如果检测单个数据的marker基因与混合矫正后的marker基因之间的一致性，Seurat的一致性更强（图c）。Seurat的一致性表现是牺牲细胞类型数量得到的

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-31-035810.png)


# 2.3.2 算法 | 2021-scPhere——用地球仪来展示降维结果

刘小泽写于2021.5.25

> 常见的降维是把细胞落在2维空间平面图，这个工具比照地球仪进行细胞的降维

## 速览

* 作者：Jiarui Ding & Aviv Regev（通讯作者大家都熟知），来自Klarman Cell Observatory, Broad Institute of MIT and Harvard, Cambridge, MA, USA
* 链接：<https://www.nature.com/articles/s41467-021-22851-4>
* 发表在：Nature Communications&#x20;
* 日期：2021-05-05
* 项目地址：<https://github.com/klarman-cell-observatory/scPhere>

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-05-25-023115.png)

## 摘要

* scRNA数据分析中，降维是非常重要的一步，用来解释各个细胞之间的亲疏远近，但目前的降维方案经常受到技术误差的影响，引起和真实生物差异的混淆
* 开发的scPhere（读作：sphere）可以在降维过程中区分出多层次复杂的批次效应，尤其对大型数据来说，结果不会把细胞们都堆在一起以至于看不出分化轨迹和细胞分群

  > 文章是这么定义这个工具的： scPhere, a scalable deep generative model to embed cells into low-dimensional hyperspherical or hyperbolic spaces to accurately represent scRNA-seq data
* 利用了9个大型数据集（包括病人、正在发育的动物）进行验证

## 首先看一下工具的模型

* 把scRNA的表达量和多个维度的批次信息读进来，批次信息包括了技术和生物两方面（比如不同的病人、疾病类型），在有批次效应的情况下，学习细胞潜在的数据结构
* 学习得到的模型可以被用来：（1）找某个生物因素（如疾病）对表达量的影响；（2）得到一个不受批次效应影响的参考数据，然后把新的数据集去和它比较；（3）看看细胞的空间分布

![Fig. 1](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-05-25-023928.png)

## 用小型和大型数据集比较不同的降维方法

采用的数据集是：

* “**small**” datasets were: (1) a blood cell dataset with only 10 erythroid cell profiles and 2293 CD14+ monocytes; (2) 3314 human lung cells, (3) 1378 mouse white adipose tissue stromal cells, and (4) 1755 human splenic nature killer cells spanning four subtypes
* “**large**” datasets were: (1) **35,699** retinal ganglion cells in 45 cell subsets; and (2) **599,926** cells spanning 102 subsets across 59 human tissues in the Human Cell Landscape

比较对象是：t-SNE, UMAP, and PHATE

图中上面8张是小数据集，下面8张是大型数据集

* 对小数据集来讲，t-SNE, UMAP, and PHATE的降维结果都还不错，都基本没有批次效应
* 当切换到大型数据集，scPhere的优势就体现出来了：保证了数据全局的层级结构，能将同属一个大群的不同亚群聚在一起
* 当数据量增大，t-SNE的图变得越来越”臃肿“，在2D图中，即使是非常不同的细胞类型，也会被”挤“得非常近（图k）；而UMAP中多个cluster出现混乱的情况（图I）

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-05-25-030909.png)

* 当然，PHATE对大型数据集的处理结果不理想

  ![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-05-25-032926.png)

## 测试scPhere的批次效应处理性能

> 大部分的批次效应处理工具只能处理单个的批次信息，而scPhere的特性是能结合多个批次信息进行学习

测试数据集：**301,749** cells we previously profiled in a complex experimental design from the colon mucosa of **18 patients** with ulcerative colitis (UC 溃疡性结肠炎), a major type of inflammatory bowel diseases (IBD 发炎性肠症), and **12 healthy individuals**

其中除了病人这个批次信息，还有：

* individuals were either healthy or with UC
* cells were collected separately from the epithelial&#x20;
* lamina propria fractions of each biopsy
* two replicate biopsies for each healthy individual&#x20;
* samples were collected at two time periods, separated by over a year

比较工具：Harmony, LIGER, and Seurat3 CCA（因为后两个只能处理一个批次信息，就选取了不同的人作为批次）

得到结论：**scPhere’s batch correction** on this complex dataset (30 patients with disease and location factors) performed **better than Harmony, Seurat3 CCA, and LIGER** based on classification accuracies of cell types for stromal, epithelial, and immune cells

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-05-25-043453.png)

图n和o都是scPhere的两个展示方式，分别是Embedding和Equal Earth map projection，对300,000个stromal, epithelial, and immune cells进行降维。其中还加入了是否患病、疾病类型、患病位点作为批次信息。

图p、q是利用Harmony的批次处理结果，分别采用t-SNE和UMAP的降维，结果就不理想。比如WNT2B+ fibroblasts, RSPO3+ fibroblasts 以及inflammatory fibroblasts就无法区分，而plasma cells就被莫名其妙地拆分，其他不同谱系的细胞竟然也离得很近

## 用scPhere帮助轨迹推断

when we quantify time continuity, by comparing the k-nearest neighbor time point classification accuracies, accuracies from **scPhere** (in 2D) were **higher** than those from t-SNE, UMAP, and PHATE

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-05-25-052222.png)

## 总结

几大特性：

* **Accounting for multilevel complex batch effects**: ScPhere’s ability to handle complex batch factors is an advantage over previous methods for batch correction (e.g., SAUCIE, scVI, LIGER, Seurat3 CCA, fastMNN, Scanorama, and Conos), which handle only one batch vector.
* Especially useful for **analyzing large scRNA-seq datasets**: does not suffer from “cell-crowding” even with large numbers of input cells; better preserves hierarchical, global structures;  forms a reference to annotate new profiled cells from future studies (这一点对大型项目非常有帮助，比如健康群体的Human Cell Atlas项目，疾病群体的Human Tumor Atlas Network项目，都需要构建一个reference map)
* modifying it to spatially map cells

未来扩展：

* include semi-supervised learning to **annotate** cell types
* imputing **missing counts** in scRNA-seq data and removing ambient RNA **contamination**
* integrative analysis of **multimodal** data (e.g. spatial transcriptomics, single-cell ATAC-seq)
* learn **discrete hierarchical trees** for betterd interpreting developmental trajectories
* model perturbation data

最后看一下这种降维结果的动画：

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-05-25-053726.gif)


# 2.3.3 算法 | 2021-单细胞差异分析方法评测

刘小泽写于2021.10.16

## 前言

题目：Confronting false discoveries in single-cell differential expression

日期：2021-09-28

期刊：Nature Communications

链接：<https://www.nature.com/articles/s41467-021-25960-2>

## 一句话概括

使用18个已发表的“金标准”数据集，评测了14个目前常用的差异分析方法，pseudobulks方法要优于single-cell分析方法，指出现在的很多发表的差异分析方法是错误的，会有太多的假阳性

> Our findings suggest that many published findings **may be false.**

## 图1：系统性评测

目的就是看差异分析方法能不能得到最接近生物学差异的结果，因此作者使用了真实实验得到的数据，而不是模拟的数据。

作者选择数据的标准是：对相同类型的细胞群，使用bulk 和scRNA-seq都可以获得近似的生物结论，两种测序方法进行的处理相同，测序的环境相同

在最近的500篇文献中，有近90%的文献采用了其中的方法（图b）。为了比较每个方法对bulk、scRNA处理的一致性，测定了area under the concordance curve (AUCC) ，其中前6个（也就是常见的edgeR、DESeq2、limma）一致性最好（图c）。它们的逻辑是：先把生物重复的样本整合，形成“**pseudobulks**”，再进行统计分析，而不是直接进行单个细胞间的比较，这两种逻辑的方法差异还是很明显的（图d）。

> 简单理解，pseudobulks就是一群vs一群，而single-cell方法就是一个vs一个

既然两种逻辑有差异，那么对后面的生物学功能解释影响大吗？发现也是pseudobulks方法得到的差异基因更贴合真正的生物学通路（图e），比如f这个通路基因在single-cell检验方法中就没有得到

![image-20211016122128234](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-16-042129.png)

## 图2：single-cell的方法对高表达基因存在bias

目的是看为什么pseudobulks方法要优于single-cell

图a可以看到，single-cell方法拿到的每个细胞表达量都不高，并且很离散（存在很多0表达量），而pseudobulks进行了一步整合，就大大减少了0表达量的情况（即使某些基因表达量本身就比较低）。所以作者怀疑是基因表达量低，导致single-cell方法不准确。

接下来，作者将基因按照表达量分成了三等份（高中低），然后用和图1一样的AUCC方法测了每一份中bulk 和scRNA-seq一致性。**出乎意料，和作者想的相反，表达量低的那组，不同方法结果一致性却是最好的**，而表达量高，导致了不同方法结果的一致性下降！（图b）

那么作者又问了：为什么表达量高，却成了single-cell方法的制约因素呢？

作者又基于bulk数据集，找到scRNA数据集中的那些假阳性”差异“基因，而这些被认为是差异基因的”假差异“基因，在single-cell方法中更多是高表达的（图c）；接着基于定量的spike-in数据，使用single-cell方法进行鉴定，发现很多高表达的spike-in被误认为是差异基因，相比之下pseudobulk方法没有这个bias（d、e）

作者又想，这个bias是广泛存在的，还是只在这几个数据集呢？

又使用了不同物种、不同细胞类型、不同技术、不同处理的46个scRNA数据集合，发现确实single-cell的bias是广泛存在（f）

![image-20211016133844203](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-16-053844.png)

## 图3：single-cell数据的差异分析必须考虑生物重复

目的是看看pseudobulk方法为什么好。

pseudobulk原理是两步走，先进行生物重复的整合。那么这里作者想：如果我先不整合，直接对每个细胞进行接下来的统计分析，效果如何呢？（a就是把原本的生物重复给打散，就是原本不同处理的样本也被随机组合成”生物重复“）

发现效果大打折扣，甚至不如single-cell方法（图b中灰色是不整合，红色是整合）

所以作者想：整合这一步至关重要啊！那么具体怎么整合，这个重要吗？

于是又进行了随机整合生物重复，效果也不理想，所以整合的顺序也是至关重要。

> failing to account for biological replicates causes single-cell methods to systematically underestimate the variance of gene expression

![image-20211016140254381](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-16-060254.png)

## 生物重复之间的波动也会影响差异分析结果

有几点重要的结论：

* variability between biological replicates can **confound** the identification of genes affected by a biological perturbation
* Many of the factors that produce this variability between replicates can be **minimized** in animal models, including the genetic background, environment, intensity and timing of the biological perturbation, and sample processing.
* 人和小鼠的scRNA数据对比，人的生物学重复波动要更大，因此解决单细胞组织差异性，对于差异分析至关重要

<br>


# 2.3.4 算法 | 2021-细胞分群新方法——CNA（co-varying neighborhood analysis）

刘小泽写于2021.10.22

## 前言

题目：Co-varying neighborhood analysis identifies cell populations associated with phenotypes of interest from single-cell transcriptomics

日期：2021-10-21

期刊：nature biotechnology

链接：<https://www.nature.com/articles/s41587-021-01066-4>

## 方法描述

我们一般进行细胞分群时会进行降维聚类，这个方法也是为了分群，但它认为聚类的方法一般需要假设数据很好地捕获到生物信息，而且聚类的方法一般都需要反复调整聚类参数（比如resolution）。这个方法的**核心是”邻域“**（定义是very small regions in transcriptional space），再根据不同样本中邻域的共同特性去整合数据。

下面这个图展示的非常明白：

* a左：这是一个4个样本组成的单细胞数据，进行了一个降维操作。这里展示的二维图形可以是一个主成分，也可以是多组学数据的canonical co-variates，这个过程一般还需要去除批次效应。反正目的就是把大量的不同来源细胞”铺“在一个面上。
* a中：CNA会将每个细胞划给一个邻域，划分的依据就是其他细胞到达该细胞时随机游走的步数。如果步数最短，那么这个细胞就是Anchor cell。中间的密度图就展示了红色圆圈的细胞被认定为”小范围的C位“

  > 划分依据: every other cell *m*′ belongs to the neighborhood anchored at cell *m* according to the probability that a random walk in the graph from *m*′ will arrive at *m* after *s* steps。
* a右：按照（a中）的方法，随机找几块区域（具体几块CNA会根据数据集计算）去寻找它们的邻域。比如这里的A-E就是（a左）图中的一部分，只是根据这几个小部分内部寻找了邻域而已
* b：代表密度的邻域图找出来以后，就要对应到每个样本了（1-4就是那4个样本）
* c：看每个样本（1-4）在5个区域（A-E）的密度，比如样本1在A区域的细胞数量少，那么就是蓝色（low）；样本3在A区域的细胞数量多，就是红色（high）；如果没有细胞（比如样本4的A区域），那就是紫色
* d：最后将细胞密度信息整理成一个矩阵，蓝色（low，数字 -1表示）；红色（high，数字1表示）；没有细胞就是0

![image-20211022153557677](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-22-073557.png)

### 方法优势

* 不需要调参数（比如分辨率）
* 运行速度大大提高：CNA has favorable runtime properties: given a nearest neighbor graph, computing the NAM and conducting permutation-based association testing takes **less than 1 min** (and **579 MB** of memory) for a dataset of more than **500,000 cells** and more than 250 samples.

### 方法劣势

* CNA依赖于样本间的差异，所以如果样本数量比较少效果就不好
* 因为CNA不需要假设数据符合生物背景知识，所以如果样本量小但的确符合生物背景知识的数据，CNA可能就不如现有的统计模型
* 虽然CNA可以支持现有的细胞类型注释和轨迹推断方法，但这类方法一般只寻求单一的信息；而CNA由于关注面比较广，可能会得到多方面的信息，可能会产生信息冗余
* CNA的核心就是邻域的划分，那么是否每次都能找到合适的anchor cell，又是否存在划分的边界呢？


# 2.3.5 工具 | 2018-iSEE：单细胞数据可视化辅助网页工具

刘小泽写于2021.10.27

## 前言

题目：iSEE: Interactive SummarizedExperiment Explorer

日期：2018-06-14

期刊：F1000Research

链接：<https://f1000research.com/articles/7-741>

GitHub：<https://github.com/iSEE/iSEE>

这篇文章虽然发表的比较早，但最近看到其中一个作者Federico Marini和大佬们交流hdf5数据支持的问题，所以还是简单了解一下这个工具吧

![image-20211027125344809](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-27-045345.png)

他们讨论的结果是

```
library(zellkonverter)
sce_h5ad <- readH5AD("file_as_anndata.h5ad")
assayNames(sce_h5ad) <- "logcounts" 
HDF5Array::saveHDF5SummarizedExperiment(sce_Bcells_h5ad, dir = "see_as_hdf5")
#And to read that in -> 
sce_read_in_again <- HDF5Array::loadHDF5SummarizedExperiment("see_as_hdf5")

library(iSEE)
iSEE(sce_read_in_again)
```

## 设计初衷

> 重在数据展示，而非数据分析

它不是单纯为某一个课题设计的网页工具，而只要是`SummarizedExperiment`它就可以支持可视化，而我们知道单细胞数据不仅仅是seurat格式，还有很大部分是`SummarizedExperiment` 。当然，除了单细胞，`SummarizedExperiment` 在其他领域（比如甲基化）也有涉及，因此这个工具可以无缝衔接支持此格式的R包下游，用来展示rowdata、metadata等。可以说，它最大的亮点就是兼容性和可拓展性

那么它为何对`SummarizedExperiment`格式这么偏爱呢？

就像我之前在公众号里介绍的，这个对象可以整合 基因组信息（行）以及样本信息（列），并且可以容纳多种表达量类型（比如raw count、normalized count），甚至后期分析的结果也可以存储（比如降维结果）

![image-20211027130031898](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-27-050032.png)

## 工具结构

![image-20211027130553046](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-27-050553.png)

* **Column data plots**, for visualising sample metadata stored in the colData slot of the SummarizedExperiment object.
* **Feature assay plots**, for visualising experimental observations for a particular feature (e.g. gene) across samples from any assay in the SummarizedExperiment object.
* **Row statistics tables**, to present the contents of the rowData slot of the SummarizedExperiment object.
* **Row data plots**, for visualising feature metadata stored in the rowData slot of the SummarizedExperiment object.
* **Heatmaps**, to visualise assay data for multiple features where samples are ordered by one or more colData fields.
* **Reduced dimension plots**, which display any two dimensions from pre-computed dimensionality reduction results (e.g., from PCA or *t*-SNE). These results are taken from the reducedDim slot if this is available in the object supplied to iSEE.

还设置了大量的参数调节，比如可以对这个数据的列数据进行选取：

![image-20211027130653904](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-27-050654.png)

目前提供了一些数据作为示例：

* <http://shiny.imbei.uni-mainz.de:3838/iSEE>
* <https://marionilab.cruk.cam.ac.uk/iSEE\\_allen>
* <https://marionilab.cruk.cam.ac.uk/iSEE\\_tcga>
* <https://marionilab.cruk.cam.ac.uk/iSEE\\_pbmc4k>
* <https://marionilab.cruk.cam.ac.uk/iSEE\\_cytof>

还支持TCGA数据的可视化

![image-20211027133643944](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-27-053644.png)

## 至于怎么实现的可视化

作者提供了一些rmd作为参考：<https://github.com/iSEE/iSEE\\_instances>

![image-20211027134159378](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-27-054159.png)

```
# 上游分析得到sce对象
# Once the processing steps above are done, we can call `iSEE` with the subsampled `SingleCellExperiment` object. 
if (!requireNamespace("BiocManager", quietly = TRUE))
    install.packages("BiocManager")
BiocManager::install("iSEE")
# or also...
BiocManager::install("iSEE", dependencies = TRUE)

if (require(iSEE)) {
  iSEE(sce)
}
```


# 2.3.6 工具 | 2021-MACA: 一款自动注释细胞类型的工具

刘小泽写于2021.10.27

## 前言

题目：MACA: Marker-based automatic cell-type annotation for single cell expression data

日期：2021-10-25

期刊：biorxiv预印本

链接：<https://www.biorxiv.org/content/10.1101/2021.10.25.465734v1>

GitHub：<https://github.com/ImXman/MACA>

## 设计的初衷

目前细胞类型鉴定工具中，Support Vector Machine(SVM) 的准确性超过大多数监督注释方法

> “Support Vector Machine” (SVM) is a **supervised machine learning** algorithm that can be used for both classification or regression challenges. However, it is mostly used in classification problems.

但是，由于监督注释方法在大多数单细胞数据中缺乏真实参照，所以易用性没有非监督方法好，这也是目前非监督方法占主流的原因之一。既然使用非监督方法，那么就需要人工介入，调整分群的分辨率，以及提供marker基因，这样带来的问题就是挑选marker基因耗费时间，并且重复性差（因为每个人选择的marker基因也不同）。

基于第一个问题（挑选marker基因耗费时间），有人做了数据库，专门收录这些基因，比如PanglaoDB, CellMarker涵盖了人和小鼠多种细胞类型的marker 基因；另外NeuroExpresso是大脑组织的marker基因数据库。

这个工具MACA，全称是marker-based automatic cell-type annotation，旨在解决细胞注释的速度和准确性

## 设计的结构

> 整个设计逻辑还是很容易理解的：先判断单个细胞属于什么类型，然后聚类再判断一次属于什么类型

读入数据：single cell或者single nuclei RNA-Seq的表达矩阵

对每个细胞计算2个label：

* 首先，结合marker数据库，使用raw count表达矩阵，计算每个细胞的cell-type score =》 将 gene expression matrix转换成cell-type score matrix
* 然后，对每个细胞分配细胞类型：将score最大的细胞类型对应到这个细胞 =》 Label 1 产生
* 同时，利用cell-type score matrix + Louvain community detection algorithm，将细胞聚类 =》 Label 2 产生（也就是某群细胞属于什么类型）

因为一开始不知道具体有几种细胞类型，这里**MACA默认将分辨率调大**，避免很多同源的细胞被拆分成很多小的cluster

之后就是通过一系列统计知识，将Label1 和Label2利用起来：MACA records **significant or at least the top-3 celltypes for each cell** in cluster based on cell-type scores

意思就是挑出个人很能打，并且整体也很符合要求的那组

![image-20211027151738178](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-27-071738.png)

## 实际测试

### **使用的方法、数据**

* investigated **4 scoring methods** that have been proposed to transform gene expression matrix to cell-type score matrix
* 2 public marker databases
* 6 single cell studies comprised of 3000 to 20000 cells
* 使用Adjusted Rand Index (ARI) and Normalized Mutual Information (NMI)进行评测

> ARI & NMI are measuring similarity or agreement between our annotations and authors’ annotations

### **评测打分工具**

发现全部6个数据集中 annotations using **PlinerScore** with markers in **PanglaoDB** have the largest agreement。于是采用PlinerScore作为打分方法

### **接下来和自动细胞注释工具比较**

利用PanglaoDB的marker，和CellAssign, SCINA, Cell-ID, and scCATCH比较

发现速度差异：

* **MACA** can finish annotation **within 1 minute (cells around 3,000)** and **less than 2 minutes** for a relatively large dataset (cells up to **20,000** cells)
* scCATCH and Cell-ID took longer than MACA
* SCINA took around 20-minute for a large dataset
* CellAssign took the longest time with > 20,000 cells due to lack of memory

发现注释结果差异：

* **MACA** labels cells had a higher consensus than CellAssign, SCINA, Cell-ID, and scCATCH
* 和作者的结果相似：**MACA and scCATCH** identify similar numbers of cell-types to author’s annotations


# 2.3.7 工具 | 2021-一个很有想法的工具——Ikarus，想要在单细胞水平直接鉴定肿瘤细胞

刘小泽写于2021.10.28

## 前言

题目：Identifying tumor cells at the single cell level

日期：2021-10-25

期刊：biorxiv预印本

链接：<https://www.biorxiv.org/content/10.1101/2021.10.15.463909v2>

代码: [https://github.com/BIMSBbioinfo/ikarus](#qian-yan)

图表复现: [https://github.com/BIMSBbioinfo/ikarus---auxiliary](#qian-yan)

## 设计初衷

目前主要有三类细胞注释工具：

* manifold matching software：aligns the shape of an unlabeled dataset to an expertly labeled dataset
* deep learning based tools：model the batch effects through latent space embedding
* gene set based classifiers：使用已知的marker辅助分类

目前使用最多的应该是使用marker gene辅助

作者提出一个问题：能否做出一个分类器（**重点就是选出一系列基因**），可以直接从一群细胞中区分出tumor和normal？这样可以帮助肿瘤抗原的自动预测，帮助检查肿瘤细胞状态，还可以帮助诊断病理组织

## 设计思路

Ikarus工具为了检查肿瘤细胞，设计了2步：

* 首先还是根据比较靠谱的，经过专家审核的单细胞数据集，从中找到一些共有的肿瘤细胞基因signature
* 构建逻辑回归分类器 + network-based propagation of cell labels using a custom built cell-cell network

作者先整合了结直肠癌+肺癌的10X数据，其中数据注释了细胞是否是癌细胞。接下来寻找癌细胞相关的marker gene：

* 每个数据差异分析，然后取交集（**但是不同癌症的marker基因直接取交集是否有意义呢？那些特异性的gene岂不是被抛弃？**）=> 得到162 个交集基因集
* 同理选出正常组织的marker基因，它包含两部分：cell type specific markers & genes which are specifically depleted in the tumor cells

![image-20211028111814469](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-28-031814.png)

## 验证

找几个数据集验证一下tumor和normal 各自的基因集，找了5种癌症类型的patient-derived xenograft (PDX)、cancer cell line encyclopedia (CCLE)，发现 tumor signature score was significantly higher than the normal signature score

![image-20211028112352974](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-28-032353.png)

用几个测试数据集比较了不同方法区分tumor和normal细胞，包括标准的机器学习（SVM, random forest, and logistic regression）、SingleCellNet、ACTINN。发现**Ikarus的平均准确度可以达到0.98（A图），并且AUROC也很高**。

使用Lambrechts lung cancer dataset（图DE），看到Ikarus也能基本得到和作者一样的肿瘤细胞分布

![image-20211028122522894](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-28-042523.png)

### **测试一下如果没有足够的基因，或者缺少某几个关键基因，Ikarus还能预测准确吗**

* 图A：当然，使用的tumor gene 数量越多越准确
* 图B-D：当缺少serum amyloid A (SAA1) 和fibrinogen beta chain (FGB)，会导致准确度大大降低（**也就是说这个工具还是很依赖关键的marker gene的，因此前期还是要筛选合适的marker基因作为输入**）；当然作者说只在Lambrechts这个数据集中发现了这个情况，其他没发现

![image-20211028123705043](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-28-043705.png)

### **作者认为自己的signature找的很有效，于是看看它们具体有哪些特性**

发现了

* 在各个数据中，这些基因之间的Pearson相关性竟然大部分接近0
* tumor gene signature is partially related to cell cycle, and DNA replication（C图）
* tumor gene signature preferentially overlapped with the cell cycle hallmark（D图）

![image-20211028124223866](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-28-044223.png)

### **看一下筛出来的基因集和预后的联系**

* **more than 75%** of tumor signature genes are predictive of unfavourable prognosis in **at least one cancer** type
* 在5种癌症中（liver, renal, pancreatic, lung and endometrial）这个基因集对预后不利
* 与CNV： significantly **higher overlap with the known CNV regions** in the majority of profiled cancer types

![image-20211028125011561](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2021-10-28-045011.png)


# 3 流程篇：分析框架

基本的单细胞转录组分析都包括哪些内容呢？理解了这些内容，才能明白各个R包的共同点


# 3.1 质控

刘小泽写于19.12.3-4 ，更新于2020-06-26

## 1 质控必要性

### **前言**

scRNA-seq数据的低质量文库可能来自于：细胞分选环节的破坏、文库制备失误（没有足够的反转录或PCR次数）... 表现在：细胞总表达量低、基本没有表达的基因、高线粒体或spike-in占比。

它带来的下游分析问题有：

* **聚类问题：**&#x8FD9;些低质量细胞肯定是最相似的了，“近朱者赤近墨者黑”，它们也会聚集成一群，对结果的解释造成干扰，因为从这群细胞中得不到什么有用的信息，但是它的确也是一群。这种现象产生的原因有可能是：细胞破坏以后，线粒体或核RNAs占比升高。 最差的情况就是：不同类型的低质量细胞，也能聚在一起，因为相比于固有的生物差异，更相似的低质量让它们相依相偎。除此以外，本来非常小的细胞文库也能聚成一群，因为log转换后它们的平均表达量会发生很大的变化([A. Lun 2018](https://www.biorxiv.org/content/10.1101/404962v1)).

  > 这篇文章举了个例子： ![image-20200626093348817](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-06-26-013349.png)
  >
  > 另外还说：
  >
  > * Artifacts are most pronounced when the counts are low and there is large variation in the size factors across cells
  > * Quality control is typically performed to remove cells with small library sizes. This reduces the variation in the size factors and the potential for artificial differences upon log-transformation
  > * Low-abundance genes can be filtered out, which provides further protection as genes with low counts are most affected by the log-transformation
* **差异估算或主成分选择**：首先在PCA分析时，低质量和高质量之间的差异相比于生物学差异会体现更明显，占据主要的成分，减少降维结果的可靠性。另外，某个基因可能在两个细胞之间没什么表达差异，但是如果所处环境差异很大（一个细胞质量很低，另一个细胞质量正常），那么在差异估算过程中，就会把这个差异也会被当成差异表达基因。例如：一个低质量细胞文库的总表达量非常低（接近0），但恰巧还存在一个基因有表达量，那么这个基因的表达量在后续的文库归一化过程中就会尤为突出
* **意外发现奇怪的转录本上调**：实验难免会混入外源的污染转录本，但这个量很少并且在所有细胞中都应该是差不多水平的。但如果某个细胞质量低，文库小，那么在校正文库差异过程中（就像上面👆说的一样），其中的污染转录本表达量就会“突飞猛进”，看起来是一些明显上调的“奇怪基因”。实际上，这些奇怪的基因依然在其他细胞中存在，并且真实的表达量差不多，并且是不应该占据主体地位的

为了最大程度避免上面一种或多种情况的发生，应该在归一化之前去掉这些低质量的细胞，这个过程就是**细胞的质控** （尽管对于droplet-based数据来讲，一个液滴/文库未必是一个细胞，因为存在没细胞dropout和双细胞doublet，甚至是多细胞的情况；但是这里为了方便介绍，不对”cell“和”library“名词进行区分）

### **使用的测试数据**

下面将会使用[A. T. L. Lun et al. (2017)](https://pubmed.ncbi.nlm.nih.gov/29030468/)的小型scRNA数据（未进行QC）

```r
# BiocManager::install("scRNAseq")
library(scRNAseq)
sce.416b <- LunSpikeInData(which="416b")
```

需要注意的是，在下载数据的时候，**它需要在/home下新建一个目录，问你yes/no** ![image-20191203102213205](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-12-03-022213.png)

如果你对/home目录没有操作权限，那么就会报错：

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-12-03-022256.png)

**当然，如果不能新建也没有关系，我已经把这个数据替大家保存成了RData并放到了网盘，直接下载后加载就好**

> sce.416b链接：<https://share.weiyun.com/5pQf0jg> 密码：vab6wu

```r
load('sce.416b.RData')
# 看到有两个96孔板的细胞
> table(sce.416b$block)
20160113 20160325 
      96       96 

sce.416b$block <- factor(sce.416b$block)

> sce.416b
class: SingleCellExperiment 
dim: 46604 192 
metadata(0):
assays(1): counts
rownames(46604): ENSMUSG00000102693 ENSMUSG00000064842 ... ENSMUSG00000095742 CBFB-MYH11-mcherry
rowData names(1): Length
colnames(192): SLX-9555.N701_S502.C89V9ANXX.s_1.r_1 SLX-9555.N701_S503.C89V9ANXX.s_1.r_1 ...
  SLX-11312.N712_S508.H5H5YBBXX.s_8.r_1 SLX-11312.N712_S517.H5H5YBBXX.s_8.r_1
colData names(9): Source Name cell line ... spike-in addition block
reducedDimNames(0):
spikeNames(0):
altExpNames(2): ERCC SIRV
```

## 2 质控的指标

### **前言**

鉴定细胞是否是低质量的，需要用到几个指标。虽然下面这些指标是使用SMART-seq2数据进行展示的，但依然适用于UMI数据（比如MARS-seq、droplet-based技术）

* **文库大小（library size）** ：指的是每个细胞中所有基因的表达量之和。细胞的文库如果很小，说明在文库制备过程中存在RNA的损失，要么是由于细胞裂解，要么是cDNA捕获不足导致后续的扩增量少
* **每个细胞中表达基因的数目（number of expressed features in each cell）**：指的是细胞中非0表达量的基因数量。如果细胞中基本没有基因表达，很可能是转录本捕获失败
* **比对到spike-in转录本的reads比例（proportion of reads mapped to spike-in transcripts）**：计算方法是：`spike-in counts / all features (including spike-ins) for each cell`。每个细胞都应该加入等量的外源转录本（spike-in），如果哪个细胞的spike-in比例提高了，说明它的内源RNA含量减少（比如在细胞分选阶段出现的细胞裂解或者RNA降解）
* **比对到线粒体基因组的reads比例（proportion of reads mapped to genes in the mitochondrial genome）** ：如果没有spike-in，那么使用线粒体指标也是能说明问题的([Islam et al. 2014](https://pubmed.ncbi.nlm.nih.gov/24363023/); [Ilicic et al. 2016](https://pubmed.ncbi.nlm.nih.gov/26887813/))。比对到线粒体的reads增多，说明细胞质中的RNA减少，可能存在细胞穿孔的情况，而这个孔的大小，可能只是将细胞质中存在的mRNA流出去，但线粒体的体积超过了孔的大小，所以还留在了细胞中，造成一定程度的富集，导致线粒体RNA占比升高。

  > 另外对于单核转录组测序（snRNA, single-nuclei RNA-seq），高线粒体占比也能反映细胞质是否被成功剥离。 下面是关于[snRNA的介绍](http://www.ebiotrade.com/newsf/2019-12/2019129172619642.htm)：
  >
  > * 2018年，仅仅单核RNA-seq（snRNA-seq）就有160多篇论文发表。这是一种相对较新的方法，分析的是细胞核，而不是完整的细胞
  > * 在**有些情况下，细胞是很难完整分离**的，比如长期保存的组织，或者大脑细胞和脂肪细胞。在分离细胞时所用的酶和破坏力往往会影响其他细胞区室的内容。此时，单核RNA测序就显得特别重要。
  > * snRNA-seq采用微流体技术，将带有条形码的微珠和单个细胞核一起装入微小的液滴内。这些液滴作为纳升级的反应管，实现了成千上万个细胞核分离和建库，从而大幅降低了建库的成本
  > * 单核RNA测序通过揭示复杂肿瘤的细胞类型、状态、遗传多样性和相互作用，拓宽了单细胞研究的能力，**特别是长期保存或冷冻保存的样品**。它克服了解离偏倚的问题，也适用于结构复杂的组织

对于每个细胞，可以用scater包的`perCellQCMetrics()`函数进行计算，其中`sum`这一列表示每个细胞的文库大小；`detected`这一列表示检测到的基因数量；`subsets_Mito_percent`这一列表示比对到线粒体基因组的reads占比；`altexps_ERCC_percent`表示比对到ERCC spike-in的reads占比

### **上手操作之统计线粒体信息**

通过上面👆查看`sce.416b`发现，其中有spike-in的信息，但却没有线粒体相关的信息。这个没有关系，其实可以自己统计

**第一种方法：**

官网教程提供的是`AnnotationHub()`，这个方法需要下载80多M的数据库文件，对网速要求很高，动不动就失败。而且即使`AnnotationHub()`成功，后面的提取`ah[["AH73905"]]` 也很悬。不管怎样，把这个方法先放在这里，以供参考即可。

```r
library(AnnotationHub)
ah <- AnnotationHub()
ens.mm.v97 <- ah[["AH73905"]]
location <- mapIds(ens.mm.v97, keys=rownames(sce.416b),
                   keytype="GENEID", column="SEQNAME")
is.mito <- which(location=="MT")
```

**第二种方法：**

```r
library(TxDb.Mmusculus.UCSC.mm10.ensGene)
location <- mapIds(TxDb.Mmusculus.UCSC.mm10.ensGene, 
                   keys=rownames(sce.416b), 
                   keytype="GENEID",column="CDSCHROM")
is.mito <- which(location=="MT")
summary(location=="chrM")
> summary(location=="chrM")
   Mode   FALSE    TRUE    NA's 
logical   22428      13   24163 
# 看到只有13个线粒体基因
```

**第三种方法：**

如果可以调用`rowRanges`来获取基因组坐标数据的话，就可以结合`seqnames`找到MT

```r
location <- rowRanges(sce.416b)
is.mito <- any(seqnames(location)=="MT")
```

### **上手操作之计算各种qc指标**

```r
library(scater)
df <- perCellQCMetrics(sce.416b, subsets=list(Mito=is.mito))
df
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-06-26-032607.png)

另外，还可以使用`addPerCellQC()`，它会把每个细胞的QC指标加到`SingleCellExperiment`对象的`colData`中，方便后面调取

```r
sce.416b <- addPerCellQC(sce.416b, subsets=list(Mito=is.mito))
colnames(colData(sce.416b))
##  [1] "Source Name"              "cell line"               
##  [3] "cell type"                "single cell well quality"
##  [5] "genotype"                 "phenotype"               
##  [7] "strain"                   "spike-in addition"       
##  [9] "block"                    "sum"                     
## [11] "detected"                 "percent_top_50"          
## [13] "percent_top_100"          "percent_top_200"         
## [15] "percent_top_500"          "subsets_Mito_sum"        
## [17] "subsets_Mito_detected"    "subsets_Mito_percent"    
## [19] "altexps_ERCC_sum"         "altexps_ERCC_detected"   
## [21] "altexps_ERCC_percent"     "altexps_SIRV_sum"        
## [23] "altexps_SIRV_detected"    "altexps_SIRV_percent"    
## [25] "total"
```

当然，这里做QC统计的前提假设是：每个细胞的qc指标都是独立于生物学状态的。也就是说，qc指标不会那么智能地识别细胞类型然后进行判断。它会认为（如文库太小、高线粒体占比）都是由技术误差引起的，而非生物因素。

**那么问题来了，如果某些细胞类型本身的RNA含量就很低，或者它们本来就含有很多的线粒体转录本呢？再根据这个指标进行过滤，会不会损失一些细胞类型呢？** 【这个问题会在下面👇第4部分和第6部分进行说明】

## 3 鉴定低质量细胞

### **3.1 使用固定的阈值--简单粗暴**

这是最简单粗暴的方法，例如设定文库低于10万reads的细胞是低质量的，或者表达基因数量少于5000个，spike-in或线粒体占比高于10%...

```r
# 基于之前perCellQCMetrics计算的QC结果df
qc.lib <- df$sum < 1e5
qc.nexprs <- df$detected < 5e3
qc.spike <- df$altexps_ERCC_percent > 10
qc.mito <- df$subsets_Mito_percent > 10
# 都设定好以后传给discard
discard <- qc.lib | qc.nexprs | qc.spike | qc.mito
```

还可以统计一下每种过滤掉多少细胞

```r
> DataFrame(LibSize=sum(qc.lib), NExprs=sum(qc.nexprs),
           SpikeProp=sum(qc.spike), MitoProp=sum(qc.mito), Total=sum(discard))

DataFrame with 1 row and 5 columns
    LibSize    NExprs SpikeProp  MitoProp     Total
  <integer> <integer> <integer> <integer> <integer>
1         3         0        19         0        21
# 这里看到按照线粒体部分按照阈值为10%，是不能对细胞进行过滤的，因为我们根据TxDb.Mmusculus.UCSC.mm10.ensGene一共才找到了13个线粒体基因；而作者的教程中可能找到的线粒体基因比较多，因此他最终过滤掉了14个细胞

# 另外看到Total这里并不是简单地将四项指标相加，这是因为可能一个细胞同时符合两项或多项过滤条件，最终只计算一次
# 比如下面就是查看：既符合qc.lib过滤又符合qc.spike过滤条件的细胞=》第191个细胞
> intersect(which(qc.lib == 1),which(qc.spike == 1))
[1] 191
```

虽然看起来简单，但使用这种方法需要丰富的经验，了解实验设计和细胞状态；另外即使使用同一种文库制备方法，但由于细胞捕获效率和测序深度的不同，这个阈值依然需要适时调整

### **3.2 使用”相对“的阈值**

**3.2.1 鉴定离群点**

先假设大部分细胞都是高质量的，然后去找离群点作为低质量。

那么按照什么方法找离群点呢？常用的一个函数`isOutlier`使用的是MAD指标（绝对中位差来估计方差,先计算出数据与它们的中位数之间的偏差，然后这些偏差的绝对值的中位数就是MAD，median absolute deviation）

函数认为**超过中位数3倍MAD的值就是离群值**

使用`isOutlier`时，如果要相减（例如：`df$sum - 3* MAD`），就用`type="lower"` ，此时一般还要做个log转换`log=TRUE` ，保证得到的结果不是负数

```r
qc.lib2 <- isOutlier(df$sum, log=TRUE, type="lower")
qc.nexprs2 <- isOutlier(df$detected, log=TRUE, type="lower")
qc.spike2 <- isOutlier(df$altexps_ERCC_percent, type="higher")
qc.mito2 <- isOutlier(df$subsets_Mito_percent, type="higher")
```

看下得到的结果

```r
> attr(qc.lib2, "thresholds")
   lower   higher 
434082.9      Inf 
> attr(qc.nexprs2, "thresholds")
   lower   higher 
5231.468      Inf 
> attr(qc.spike2, "thresholds")
   lower   higher 
    -Inf 14.15371 
> attr(qc.mito2, "thresholds")
   lower   higher 
    -Inf 6.472705
```

> 注意到：最后的线粒体部分，官方教程计算的结果是：
>
> ```r
> ##  lower higher 
> ##   -Inf  11.92
> ```

用相对阈值过滤的细胞数量统计：

```r
discard2 <- qc.lib2 | qc.nexprs2 | qc.spike2 | qc.mito2

> DataFrame(LibSize=sum(qc.lib2), NExprs=sum(qc.nexprs2),
           SpikeProp=sum(qc.spike2), MitoProp=sum(qc.mito2), Total=sum(discard2))

DataFrame with 1 row and 5 columns
    LibSize    NExprs SpikeProp  MitoProp     Total
  <integer> <integer> <integer> <integer> <integer>
1         4         0         1         2         6
```

最后过滤掉的细胞数量与官方教程一致，根据线粒体过滤的细胞都是2个。

**除此以外，还有一种更快的计算方法，一步整合了上面的操作：**

```r
# 看帮助文档得知，需要提供额外的percent信息：
# quickPerCellQC(df, lib_size = "sum", n_features = "detected",
#   percent_subsets = NULL, ...)
reasons <- quickPerCellQC(df, percent_subsets=c("subsets_Mito_percent",
                                                "altexps_ERCC_percent"))
colSums(as.matrix(reasons))
##              low_lib_size            low_n_features high_subsets_Mito_percent 
##                         4                         0                         2 
## high_altexps_ERCC_percent                   discard 
##                         1                         6
```

> 小结：使用”相对“的阈值一个好处就是可以根据测序深度、cDNA捕获效率、线粒体含量等等进行阈值的调整，在经验不是足够丰富的时候，可以辅助判断。但仍需要注意的是，**使用相对阈值是有前提的，那就是：认为大部分细胞都是高质量的**

**3.2.2 离群点检测的假设**

* 第一点：上面提到，离群点的检测的假设是大部分细胞的质量都不错。这一点假设可以通过实验去验证（比如肉眼检查细胞完整性）。**但当大部分细胞质量都很低，使用相对阈值结果就对大量的低质量细胞无计可施**。因为它使用了MAD值，和中位数有关系，那么可以试想：如果一堆数都不合格，那么它们的中位数也不合格，因此原来正确的值，其实在这群不合格的数值中，就是“离群”的，因此它只能从总体中剔除个别，而不能为了个别牺牲总体【简单理解：“谁人多势众相对阈值就听谁”】
* 另一个假设就是：**每个细胞的qc指标都是独立于生物学状态的**。也就是说，qc指标不会那么智能地识别细胞类型然后进行判断。在异质性很高的组织中， 就是存在内源RNA含量低，而线粒体基因占比高的细胞。如果使用”一刀切“的固定阈值，它们就很可能会被当成离群点被过滤。而是用MAD计算方法检测的结果可能就是：虽然一堆细胞的某个qc指标差异很大，但中位数也在变，随之变化的还有MAD值，这样最后的结果不至于和真实生物学情况差太多

因此，绝对阈值和相对阈值各有千秋，如果没有对”一刀切“方法有强烈的需求（比如当大部分细胞质量都很低时，就可以考虑一刀切），一般情况还是使用相对阈值为好

另外，这些假设知道即可， 不会影响后续的分析，也不用太多在意，先完成后完美

**3.2.3 检测离群点还需要考虑实验设计（批次信息）**

很多大型的实验都包含多个细胞系，而且可能采用的实验方法不同（比如选用不同的测序深度），这就产生了实验的不同批次。**这种情况下， 应该对每个批次分别进行检测。**&#x800C;不要混在一起再去检测MAD，那样势必有一批会被”矫枉过正“，一批会”放任自流“。

如果每个批次都是一个`SingleCellExperiment`对象，那么`isOutlier()`函数可以按上面的方法直接使用；但是如果不同批次的细胞已经混合成一整个`SingleCellExperiment`对象，那么`batch=`这个参数就派上用场了。

还是以这个416B数据集为例，我们之前看到包含了两组实验信息

```r
# 一个是实验批次，两个细胞板
> table(sce.416b$block)
20160113 20160325 
      96       96 
# 一个是细胞表型，两种生物状态
> table(sce.416b$phenotype)
induced CBFB-MYH11 oncogene expression  96            
wild type phenotype  96
```

而它们现在是混合在一起的，于是就可以**设定batch信息**

```r
batch <- paste0(sce.416b$phenotype, "-", sce.416b$block)
# 得到2x2=4个批次信息
table(batch)
# induced CBFB-MYH11 oncogene expression-20160113 
# 48 
# induced CBFB-MYH11 oncogene expression-20160325 
# 48 
# wild type phenotype-20160113 
# 48 
# wild type phenotype-20160325 
# 48 

batch.reasons <- quickPerCellQC(df, percent_subsets=c("subsets_Mito_percent",
                                                      "altexps_ERCC_percent"), batch=batch)

> colSums(as.matrix(batch.reasons))
##              low_lib_size            low_n_features high_subsets_Mito_percent 
##                         4                         2                         2 
## high_altexps_ERCC_percent                   discard 
##                         4                         7
```

**但是，`batch`参数不是万能的**，之前也说过，这种相对阈值需要一个**假设：每个批次的大部分细胞都是高质量的**。当某个批次的细胞整体质量偏低，离群点检测很有可能失败。举个例子，[Grun et al. (2016)](https://pubmed.ncbi.nlm.nih.gov/27345837/)做了一个人类胰腺癌的数据集，里面总共有5个donor的数据，但事实上有两个donor的实验是失败的。它们的ERCC含量相对其他批次高，增加了中位数和MAD值，导致过滤低质量细胞失败。因此这种情况下，可以先算其他几个批次的中位数和MAD值，然后参考这些值去对有问题的批次进行过滤

```r
library(scRNAseq)
sce.grun <- GrunPancreasData()
# 批次信息
> table(sce.grun$donor)
D10 D17  D2  D3  D7 
288 480  96 480 384

sce.grun <- addPerCellQC(sce.grun)
# 分批次进行检测，看看效果
discard.ercc <- isOutlier(sce.grun$altexps_ERCC_percent,
    type="higher", batch=sce.grun$donor)
with.blocking <- plotColData(sce.grun, x="donor", y="altexps_ERCC_percent",
    colour_by=I(discard.ercc))
with.blocking
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-12-04-015357.png)

> 图中每个点表示一个细胞。可以看到，**D10和D3的检测是失败的**，因为它们的大部分细胞ERCC含量本身就很高，所以最后并没有检测到太多的离群点（黄色点）

这时，就可以先算其他几个批次的离群点：

```r
discard.ercc2 <- isOutlier(sce.grun$altexps_ERCC_percent,
    type="higher", batch=sce.grun$donor,
    subset=sce.grun$donor %in% c("D17", "D2", "D7"))

without.blocking <- plotColData(sce.grun, x="donor", y="altexps_ERCC_percent",
    colour_by=I(discard.ercc2))
# 拼图的方法
gridExtra::grid.arrange(with.blocking, without.blocking, ncol=2)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-06-26-115205.png)

> 可以看到，左图是按照每个批次分别鉴定的离群点；右图是用质量好的批次计算的阈值，然后运用到差的批次上，群策群力得到的离群点

**如何鉴别有问题的批次呢？**

可以先将每个批次分别计算结果，然后比较它们的阈值，如果比同类批次超出太多，就要警觉

```r
ercc.thresholds <- attr(discard.ercc, "thresholds")["higher",]
ercc.thresholds
##        D10        D17         D2         D3         D7 
##  73.610696   7.599947   6.010975 113.105828  15.216956
```

从数值就能看到D10、D3的阈值就超过其他批次太多

```r
names(ercc.thresholds)[isOutlier(ercc.thresholds, type="higher")]
## [1] "D10" "D3"
```

但还是那句话，这么做的前提都是：我们认为批次中的细胞质量整体还不错。

但如果我们保证不了细胞质量，那么这种计算相对阈值的方法就不成立，还是要使用绝对阈值，手动去除

### **3.3 另辟蹊径的检测方法**

利用[robustbase](https://cran.r-project.org/web/packages/robustbase/index.html) 包，将细胞放在高维空间，根据他们的QC指标（文库大小、表达基因数、spike-in比例、线粒体比例）

> 它的用法是：`For an n * p data matrix (or data frame) x, compute the “outlyingness” of all n observations`

先做一个行是细胞，列是QC指标的数据框

```r
stats <- cbind(log10(df$sum), log10(df$detected),
               df$subsets_Mito_percent, df$altexps_ERCC_percent)
> dim(stats)
[1] 192   4
```

然后j就像做PCA分析一样，剔除共性，找出最有差异的部分

```r
library(robustbase)
outlying <- adjOutlyingness(stats, only.outlyingness = TRUE)
multi.outlier <- isOutlier(outlying, type = "higher")

> attr(multi.outlier, "thresholds")
   lower   higher 
    -Inf 2.146625 

> summary(multi.outlier)
   Mode   FALSE    TRUE 
logical     182      10
```

### **3.4 其他**

除此以外，有时**还可以根据基因表达量进行过滤**，不过在bulk转录组中用的比较多，但是在scRNA中这样操作可能会损失一些本身数量就比较少的高质量细胞群体（比如一些罕见细胞，本身基因表达量就不是很高）

## 4 画图检查

> 就是对QC指标的分布进行可视化，来检查是否存在问题

### **第一张图 不同批次的QC指标**

**首先 把QC指标和原来的sce.416b细胞信息整合起来**

```r
> dim(colData(sce.416b))
[1] 192   9
> dim(df)
[1] 192  16

colData(sce.416b) <- cbind(colData(sce.416b), df)
> names(colData(sce.416b))
 [1] "Source Name"              "cell line"               
 [3] "cell type"                "single cell well quality"
 [5] "genotype"                 "phenotype"               
 [7] "strain"                   "spike-in addition"       
 [9] "block"                    "sum"                     
[11] "detected"                 "percent_top_50"          
[13] "percent_top_100"          "percent_top_200"         
[15] "percent_top_500"          "subsets_Mito_sum"        
[17] "subsets_Mito_detected"    "subsets_Mito_percent"    
[19] "altexps_ERCC_sum"         "altexps_ERCC_detected"   
[21] "altexps_ERCC_percent"     "altexps_SIRV_sum"        
[23] "altexps_SIRV_detected"    "altexps_SIRV_percent"    
[25] "total"
```

修改一下整合后的信息

```r
sce.416b$block <- factor(sce.416b$block)
# 让表型信息更简洁
sce.416b$phenotype <- ifelse(grepl("induced", sce.416b$phenotype),
                             "induced", "wild type")
# 增加之前过滤的结果
sce.416b$discard <- reasons$discard
```

**然后作图**

```r
gridExtra::grid.arrange(
    plotColData(sce.416b, x="block", y="sum", colour_by="discard",
                other_fields="phenotype") + facet_wrap(~phenotype) + 
        scale_y_log10() + ggtitle("Total count"),

    plotColData(sce.416b, x="block", y="detected", colour_by="discard", 
                other_fields="phenotype") + facet_wrap(~phenotype) + 
        scale_y_log10() + ggtitle("Detected features"),

    plotColData(sce.416b, x="block", y="subsets_Mito_percent", 
                colour_by="discard", other_fields="phenotype") + 
        facet_wrap(~phenotype) + ggtitle("Mito percent"),

    plotColData(sce.416b, x="block", y="altexps_ERCC_percent", 
                colour_by="discard", other_fields="phenotype") + 
        facet_wrap(~phenotype) + ggtitle("ERCC percent"),

    ncol=2
)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-12-04-025837.png)

### **第二张图 线粒体占比与文库大小**

为了确保不存在这样的细胞：虽然细胞文库大，但它的线粒体占比也高。另外也是为了避免意外过滤掉本来是高质量但同时具有高代谢活性（即高线粒体占比）的细胞（如肝脏细胞）

**主要关注右上角（代表大文库，高线粒体含量）**，如果真的存在，就要看与这些细胞的生物类型是不是相符

```r
plotColData(sce.416b, x="sum", y="subsets_Mito_percent", 
    colour_by="discard", other_fields=c("block", "phenotype")) +
    facet_grid(block~phenotype) +
    theme(panel.border = element_rect(color = "grey"))
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-12-04-030517.png)

### **第三张图 比较ERCC和线粒体比例**

有时会存在这样的情况：**细胞质量低，文库大小低，它的线粒体占比也低，但是spike-in占比很高**

它可能是因为细胞破坏非常严重，导致细胞质组分基本丢失，最后建库只捕获了外源的RNA

**而另一种相反的情况：高线粒体占比，低spike-in占比**，则有可能是说明细胞没有损伤，只是它的代谢活性很强。

这种分析思路同样适用于单核RNA-Seq（snRNA-Seq），但关注点从细胞文库质量转移到了细胞核文库质量

```r
plotColData(sce.416b, x="altexps_ERCC_percent", y="subsets_Mito_percent",
    colour_by="discard", other_fields=c("block", "phenotype")) +
    facet_grid(block~phenotype) + 
    theme(panel.border = element_rect(color = "grey"))
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-12-04-031332.png)

如果在右下角存在许多点时就要注意了，可能细胞受到损伤；而左上角的点还需要进一步结合细胞生物学类型进行判断

## 5 针对Droplet数据的细胞判断（例如10X）

### **5.1 前言**

由于这种建库方法的独特性，我们没办法事先知道某一个细胞文库（比如一个cell barcode）是真正包含一个细胞还是只是一个空的液滴（droplet）。因此，**第一步是需要根据得到的表达量信息，来推断液滴是不是空的**。但仅仅根据表达量判断还是不太靠谱（是不是很复杂的问题？），因为还有可能在空的液滴中依然包含外源RNA，最后的细胞文库依旧不为0，但确实不包含细胞。

这里为了说明这个问题，**使用了一个未过滤的10X PBMC数据**

```r
# 学习一下数据下载的方式（新建一个目录，然后把链接放进函数进行下载）
library(BiocFileCache)
bfc <- BiocFileCache("raw_data", ask = FALSE)
raw.path <- bfcrpath(bfc, file.path("http://cf.10xgenomics.com/samples",
                                    "cell-exp/2.1.0/pbmc4k/pbmc4k_raw_gene_bc_matrices.tar.gz"))
# 把数据解压到当前目录
untar(raw.path, exdir=file.path(gwtwd(), "pbmc4k"))

library(DropletUtils)
library(Matrix)
fname <- file.path(gwtwd(), "pbmc4k/raw_gene_bc_matrices/GRCh38")
sce.pbmc <- read10xCounts(fname, col.names=TRUE)
```

当然，如果你想省去下载这一步，还可以用我保存的数据【`sce.pbmc`的网盘链接：链接：<https://share.weiyun.com/5LRF8Jn> 密码：sz7wmv】

```r
load("sce.pbmc.Rdata")
# 298M的对象
> sce.pbmc
class: SingleCellExperiment 
dim: 33694 737280 
metadata(1): Samples
assays(1): counts
rownames(33694): ENSG00000243485 ENSG00000237613 ... ENSG00000277475
  ENSG00000268674
rowData names(2): ID Symbol
colnames(737280): AAACCTGAGAAACCAT-1 AAACCTGAGAAACCGC-1 ... TTTGTCATCTTTAGTC-1
  TTTGTCATCTTTCCTC-1
colData names(2): Sample Barcode
reducedDimNames(0):
spikeNames(0):
altExpNames(0):
```

**看看不同的barcodes（不一定都是真实的细胞）的文库大小分布：**

```r
bcrank <- barcodeRanks(counts(sce.pbmc))
# 为了作图速度，每个rank只展示一个点（去重复）
uniq <- !duplicated(bcrank$rank)
plot(bcrank$rank[uniq], bcrank$total[uniq], log="xy",
     xlab="Rank", ylab="Total UMI count", cex.lab=1.2)

abline(h=metadata(bcrank)$inflection, col="darkgreen", lty=2)
abline(h=metadata(bcrank)$knee, col="dodgerblue", lty=2)

legend("bottomleft", legend=c("Inflection", "Knee"), 
       col=c("darkgreen", "dodgerblue"), lty=2, cex=1.2)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-12-04-040628.png)

看到各个barcodes的文库大小有高有低，并且相差较大，因此可能对应着真实存在的细胞和空液滴。当然最简单的办法还是”一刀切“，**留下那些文库较大的细胞**，不过还是有损失真实细胞类型的风险。

### **5.2 【作了解】检测空的液滴**

主要使用`emptyDrops()`函数，检查每个barcode的表达量是不是和外源RNA的表达量有显著差异([Lun et al. 2019](https://pubmed.ncbi.nlm.nih.gov/30902100/))。如果存在显著差异，就说明barcode中更有可能是一个细胞。这种方法可**以帮助区分：测序质量好的空液滴 和 包含细胞但RNA含量较少的液滴**。尽管它们总体的表达量可能很相似，但本质不同，还是要区分的。

这个函数假设总体UMI含量低就是空的液滴，使用Monte Carlo统计模拟计算p值，如果要重复结果，需要设置随机种子。另外设置 false discovery rate (FDR)为0.1%，意味着不超过0.1%的barcodes是空的

```r
set.seed(100)
e.out <- emptyDrops(counts(sce.pbmc))
> summary(e.out$FDR <= 0.001)
   Mode   FALSE    TRUE    NA's 
logical    1056    4233  731991
```

将最后的结果保存：

```r
sce.pbmc <- sce.pbmc[,which(e.out$FDR <= 0.001)]

> ncol(counts(sce.pbmc))
[1] 737280
> length(which(e.out$FDR <= 0.001))
[1] 4233
```

可以看到从73万个液滴中，最后只得到了4000多个带细胞的液滴

**这个过程，自己很有可能是用不到的**，CellRanger V3提供了一套检测细胞的算法，和`emptyDrops`的功能相似。**因此我们一般读进来直接是几千个细胞（CellRanger处理之后的）**，而不会是几十万的液滴（包含空液滴）。当然，**如果用已过滤的数据再去检测空细胞，结果一样是不准确的**。

### **5.3 与其他质控指标的结合**

虽然上一步`emptyDrops`检测了空的液滴，但是依然不能知道有细胞的液滴中细胞质量如何。**因为即使包含细胞，还是有可能是死细胞或损伤的细胞。**

假设我们知道这些细胞中没有代谢活性很高的细胞（也就是说可以根据线粒体占比去除细胞）：

```r
is.mito <- grep("^MT-", rowData(sce.pbmc)$Symbol)
pbmc.qc <- perCellQCMetrics(sce.pbmc, subsets=list(MT=is.mito))
discard.mito <- isOutlier(pbmc.qc$subsets_MT_percent, type="higher")
> summary(discard.mito)
   Mode   FALSE    TRUE 
logical    3922     311
```

作图检查：

```r
plot(pbmc.qc$sum, pbmc.qc$subsets_MT_percent, log="x",
    xlab="Total count", ylab='Mitochondrial %')
abline(h=attr(discard.mito, "thresholds")["higher"], col="red")
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-12-05-033403.png)

最后的过滤依然是需要自己对数据的把握：

* 一方面考虑这些筛选出来的”不太合格“的细胞对下游分析的影响到底有多大；
* 二是考虑如果去掉这些细胞，会不会损失一些细胞类型？

## 6 去掉低质量细胞

> **一旦细胞质控完成，可以选择是直接去掉这些细胞，还是先标记出来给个”缓刑“的机会**

直接去除很简单，直接对sce对象的列取子集即可，例如：

```r
filtered <- sce.416b[,!reasons$discard]
```

不过，质控过程的最大难题是不小心丢掉了某些细胞类型，毕竟质控指标总是和生物状态有着千丝万缕的关系。

### **好，下面先给那些不符合要求的细胞一个”缓刑“的机会：**

计算了舍弃组和保留组细胞的平均表达量以及舍弃组与保留组之间的logFC

```r
# 首先计算每个feature的平均表达量
lost <- calculateAverage(counts(sce.416b)[,!discard])
kept <- calculateAverage(counts(sce.416b)[,discard])

library(edgeR)
# 对lost和kept分别计算log(cpm + 2)
logged <- cpm(cbind(lost, kept), log=TRUE, prior.count=2)

> head(logged)
                       lost     kept
ENSMUSG00000102693 1.013482 1.013482
ENSMUSG00000064842 1.013482 1.013482
ENSMUSG00000051951 1.013482 1.013482
ENSMUSG00000102851 1.013482 1.013482
ENSMUSG00000103377 1.019356 1.013482
ENSMUSG00000104017 1.013482 1.013482

abundance <- rowMeans(logged)
logFC <- logged[,1] - logged[,2]
```

做个图：

```r
plot(abundance, logFC, xlab="Average count", ylab="Log-FC (lost/kept)", pch=16)
points(abundance[is.mito], logFC[is.mito], col="dodgerblue", pch=16)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-12-05-041732.png)

如果说目前被丢弃的细胞真的是某个特别的细胞类型，那么这个细胞类型应该有属于自己的marker基因，那么这些基因应该在被丢弃的这群中显著高表达。看到图中纵轴0以上应该是舍弃组比保留组中高表达的基因，但是呢，这部分基因在X轴上展示的平均表达量又没有特别高（主要在2-10之间）

### **只看一个例子还不够，再看一个反例：**

假如这里用固定阈值随意去除细胞（那么肯定有真实的细胞类型被去除）

```r
# 就是这么随意去除
alt.discard <- colSums(counts(sce.pbmc)) < 500
# 下面再次用👆的计算方法
lost <- calculateAverage(counts(sce.pbmc)[,alt.discard])
kept <- calculateAverage(counts(sce.pbmc)[,!alt.discard])

logged <- edgeR::cpm(cbind(lost, kept), log=TRUE, prior.count=2)
logFC <- logged[,1] - logged[,2]
abundance <- rowMeans(logged)

plot(abundance, logFC, xlab="Average count", ylab="Log-FC (lost/kept)", pch=16)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-12-05-042524.png)

发现这时就会有很多基因在丢弃组中上调表达，且表达量还不低

根据先验知识，推测有一些血小板相关基因在丢弃组中高表达（例如：PF4, PPBP and CAVIN2），进行可视化

```r
platelet <- c("PF4", "PPBP", "CAVIN2")
library(org.Hs.eg.db)
ids <- mapIds(org.Hs.eg.db, keys=platelet, column="ENSEMBL", keytype="SYMBOL")
points(abundance[ids], logFC[ids], col="orange", pch=16)
```

看图片，**果然血小板相关的基因在丢弃组中高表达**

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-12-05-042902.png)

如果感觉自己的过滤太过于严格，而担心损失一些细胞，那么可以尝试调高`isOutlier()`的`nmads=`参数

## 7 【看看就好】如果不去除，只是标记呢？

低质量细胞还可以只是标记出来，然后继续进行下游分析，这样为了防止自己随便过滤而造成细胞类型丢失。

这样其实可以在后面的聚类分析中，能看到标记出的这部分细胞可以单独聚在一起（而我们心里也会清楚，它们为何会聚在一起）

**如何进行标记？**

```r
marked <- sce.416b
marked$discard <- batch.reasons$discard
```

虽然这样做很保险，但会增加质控结果的解释难度。当QC解读起来太难，可能又会到后期根据marker基因去分辨低质量和正常的细胞。另外会增加后续的计算量。

## 总结

作者依然是建议提前去除的，长痛不如短痛，不要把低质量的信息加到后面更加复杂的计算中。

如果对去除的标准没有信心，可以在走完一遍常规流程后，从头回来再去进行标记，走一下另一条路，看看和常规的结果有什么不同，是不是真的多了一些”容易忽略“的细胞类型？


# 3.2 归一化

刘小泽写于2020.6.27

## 1 前言

scRNA数据中文库的差异还是很常见的，来源于：不同细胞的起始底物浓度不同，导致cDNA捕获或PCR扩增效率差异。归一化的目的就是去除细胞间与真实表达量无关的技术因素，方便后续比较。

这里需要说明：归一化与批次处理还是不同的。归一化不管实验的批次因素，只考虑细胞中存在的技术误差（比如测序深度），而批次处理既要考虑实验批次，又要考虑技术误差（比如不同实验时间、不同细胞系、不同文库制备方法、不同测序方法、不同测序深度）。技术误差对不同基因的影响是相似的，比如有的技术误差会影响基因长度、GC含量，那么基本上所有基因都会受影响。但不同的批次产生的影响是不同的，比如不同时间做的实验效果就是不一样，那么基因的表达量可能也会受到这个影响。尽管有的R包可以同时处理技术误差和批次效应（例如[zinbwave](https://bioconductor.org/packages/3.11/bioc/vignettes/zinbwave/inst/doc/intro.html)），但大部分的分析还是各顾各的。

因此，这里只需要记得：**归一化，归的是技术误差，而不是批次效应即可**

> 原文中提到：We will mostly focus our attention on **scaling normalization**, which is the simplest and **most commonly used class of normalization** strategies. 个人比较喜欢叫normalization为归一化，scale为标准化【只是一种称呼习惯而已】。 当然也有朋友喜欢叫normalization为标准化。其实，scale与normalization的定义也不用掰扯太清楚，谁包含谁不用区分太明白。只需要知道分别做了什么事就可以了，一般来说，应该先进行normalization，再进行scale，而scale的结果会用来后续的降维和聚类
>
> * scale：This involves dividing all counts for each cell by a cell-specific scaling factor, often called a **“size factor”** ([Anders and Huber 2010](https://pubmed.ncbi.nlm.nih.gov/20979621/))&#x20;
>
>   The size factor for each cell represents the estimate of the relative bias in that cell, so division of its counts by its size factor should remove that bias.
> * **Normalization** "normalizes" within the cell for the difference in sequenicng depth / mRNA throughput. 主要着眼于样本的文库大小差异
>
>   **Scaling** "normalizes" across the sample for differences in *range* of variation of expression of genes . 主要着眼于基因的表达分布差异
> * 在Seurat中，一般得到原始表达矩阵并过滤后，会进行`NormalizeData()`，当然也可以使用自己的归一化结果（例如TPM结果，只是需要再log一下）；在挑选HVGs之后，降维处理之前，还需要用到`ScaleData()`进行标准化。对每个进行center后的值再除以标准差（就是进行了一个**z-score的操作**）【详细探索可以看我之前写的：[标准归一，一对CP](https://mp.weixin.qq.com/s/6ioR3JE0wKg6M-YAsLBcTA) 】

**需要说明的是：**&#x672C;文的介绍基本都是基于Scater和Scran包，没有Seurat的影子，所以也看不到`ScaleData`的操作。大部分都是对文库差异进行的处理，集中体现在计算size factor，取log进行数据缩放，因此主要就是归一化处理，而没有看到z-score处理。这个也不用奇怪，每个包都有每个包的处理方法，也没有一个定论说就非要进行Seurat的scale处理

### **数据准备**

数据依然给大家准备好了，`sce.zeisel`链接：<https://share.weiyun.com/mNwJS8U9> 密码：g8379h

```r
library(scRNAseq)
# 数据大概18M
sce.zeisel <- ZeiselBrainData()
rownames(sce.zeisel)
# 会看到有一些奇怪的基因名
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-06-26-140928.png)

```r
# 将每个细胞的所有基因表达量加起来，得到每个细胞的文库大小
library(scater)
sce.zeisel <- aggregateAcrossFeatures(sce.zeisel, 
                                      id=sub("_loc[0-9]+$", "", rownames(sce.zeisel)))
# 基因注释
library(org.Mm.eg.db)
rowData(sce.zeisel)$Ensembl <- mapIds(org.Mm.eg.db, 
    keys=rownames(sce.zeisel), keytype="SYMBOL", column="ENSEMBL")
# 质控（先perCellQCMetrics，后quickPerCellQC）
stats <- perCellQCMetrics(sce.zeisel, subsets=list(
    Mt=rowData(sce.zeisel)$featureType=="mito"))

qc <- quickPerCellQC(stats, percent_subsets=c("altexps_ERCC_percent", 
                                              "subsets_Mt_percent"))

sce.zeisel <- sce.zeisel[,!qc$discard]

sce.zeisel
## class: SingleCellExperiment 
## dim: 19839 2816 
## metadata(0):
## assays(1): counts
## rownames(19839): 0610005C13Rik 0610007N19Rik ... Zzef1 Zzz3
## rowData names(2): featureType Ensembl
## colnames(2816): 1772071015_C02 1772071017_G12 ... 1772063068_D01
##   1772066098_A12
## colData names(10): tissue group # ... level1class level2class
## reducedDimNames(0):
## altExpNames(2): ERCC repeat
```

## 2 各种Size Factors的计算

### **2.1 文库相关的size factor | library size normalization**

> 这个的计算也是最简单最常用的

文库大小指的就是：每个细胞中所有的基因表达量之和

进行归一化时，需要根据每个细胞的文库大小各自计算一个”library size factor“，而这些factor的均值为1

```r
lib.sf.zeisel <- librarySizeFactors(sce.zeisel)
# 说明是对列操作
> length(lib.sf.zeisel)
[1] 2731
> ncol(sce.zeisel)
[1] 2731
# size factor 均值为1
> summary(lib.sf.zeisel)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.1725  0.5778  0.8701  1.0000  1.2716  4.0096
```

对size factor做个直方图

```r
hist(log10(lib.sf.zeisel), xlab="Log10[Size factor]", col='grey80')
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-06-26-142520.png)

文库相关的size factor 基于的**假设是：任意两对细胞间的差异表达基因都是平衡的**。也就是说，一组基因的上调表达程度，势必会被另一组基因的下调表达程度相抵消。但是在scRNA中差异基因表达并不是平衡的（也称为：存在composition biases），因此library size normalization可能不会得到准确的归一化结果，仅仅是能用，但不是最精确。

不过话又说回来，精确的归一化结果也不是scRNA数据探索的主要任务。还是要记得，归一化的目的是更好地为下游展示数据做铺垫。因此**使用library size normalization对细胞分群和鉴定每群的marker基因也是足够的。**

### **2.2 去卷积方法计算size factor| Normalization by deconvolution**

前面提到scRNA的数据存在composition biases，也就是样本间基因差异表达的不均衡性。

**先来理解composition biases**，现在想象一个例子：两个细胞A、B，一个基因X在A细胞相比于B细胞表达量更高。这种高表达意味着：

* 当细胞文库大小一定（例如实验采用library quantification/文库定量的方法），更多的测序资源偏向X基因，势必会降低其他本来不是差异表达基因的覆盖度【简言之，一个盘子就这么多水果，一个异类胃口大开，吃了很多，其他人本来也能吃饱，但现在也要挨饿】
* 当不限制细胞文库大小时，X基因的reads或UMIs增加，也会增加总体的文库大小，导致计算的library size factor增加，原本非差异基因的表达量也会由于size factor的增加而相对之前降低【简言之，一个异类拉高了整体平均成绩，导致其他本来成绩平平的学生看上去成绩发生了下降】

这两种情况都会导致一个结果：细胞A的其他非差异基因都会由于X基因的”过度表现“而不经意地”下调“

**那么如何去除这种composition biases呢？**&#x4E4B;前在bulk RNA-Seq中研究非常透彻了。例如DESeq2使用的`estimateSizeFactorsFromMatrix()`，edgeR使用的`calcNormFactors()` 都在归一化过程中考虑到了这点。**它的假设是：大部分基因都不是差异基因。**

但是单细胞数据不能使用bulk 转录组的方法，因为存在大量的低表达和0表达量。根据[Lun, Bach, and Marioni 2016](https://pubmed.ncbi.nlm.nih.gov/27122128/)的研究，单细胞可以这么操作：

* 先混合：Pool counts from many cells to increase the size of the counts
* 后去卷积：Pool-based size factors are then “deconvolved” into cell-based factors for normalization of each cell’s expression profile.

```r
library(scran)
set.seed(100)
clust.zeisel <- quickCluster(sce.zeisel) 
table(clust.zeisel)
# clust.zeisel
# 1   2   3   4   5   6   7   8   9  10  11  12 
# 215 147 237 565 162 350 276 291 175 109 103 101

# 然后去卷积
deconv.sf.zeisel <- calculateSumFactors(sce.zeisel, cluster=clust.zeisel)
summary(deconv.sf.zeisel)
# Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
# 0.1292  0.4954  0.8337  1.0000  1.3145  4.4807
```

`quickCluster`使用了 [irlba](https://cran.r-project.org/web/packages/irlba/vignettes/irlba.pdf) 的PCA方法来进行分群操作（注意这个分群和后面真正的分群操作不是一回事，这个顶多算是pre-clustering操作，目的就是给去卷积铺路），并且它每次运行结果都是随机的，因此需要设置随机种子来满足重复性。它得到的每群细胞都分别进行归一化，得到size factors。

```r
# 看一下这个数据包含了这么多的细胞类型
> table(sce.zeisel$level1class)

astrocytes_ependymal    endothelial-mural 
                 160                  132 
        interneurons            microglia 
                 290                   67 
    oligodendrocytes        pyramidal CA1 
                 749                  938 
        pyramidal SS 
                 395 
# 将去卷积的size factor与之前常规方法得到的size factor进行对比
plot(lib.sf.zeisel, deconv.sf.zeisel, xlab="Library size factor",
    ylab="Deconvolution size factor", log='xy', pch=16,
    col=as.integer(factor(sce.zeisel$level1class)))
abline(a=0, b=1, col="red") #截距为0，斜率为1
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-06-27-083152.png)

中间的红线上的点表示去卷积和常规方法得到的size factor相等；不同颜色表示不同的细胞类型。可以看到去卷积的方法的确能暴露出细胞类型对文库大小的影响，也证明了composition biases的确存在，并且在不同的细胞类型之间还很显著。利用去卷积的size factor会得到更准确的结果，因为它考虑的问题更全面。

上面也说过，使用library size normalization对细胞分群和鉴定每群的marker基因是足够的。这里使用去卷积得到更准确的结果，虽然对分群改善不大，但对于每个基因的表达量数据估计与解释还是很重要的，因为它考虑了composition biases的影响。

### **2.3 使用spike-in计算size factor**

它基于的假设是：外源RNA（spike-in）添加到每个细胞时的量都是一样的。如果出现同一spike-in转录本表达量在各个细胞之间的差异，问题只能是细胞相关，例如不同细胞捕获效率、不同细胞测序深度等等。

因此，计算spike-in size factor的目的也正是去除这方面的偏差。相比之前的两种方法，spike-in的方法不再基于生物学背景假设（比如是否存在很多差异基因），而是**假设：**

* 添加到每个细胞的spike-in的量都是一定的
* 对偏差的反应，和内源基因是一样的

**下面快速探索这个过程：**

```r
# 准备数据
library(scRNAseq)
sce.richard <- RichardTCellData()
sce.richard <- sce.richard[,sce.richard$`single cell quality`=="OK"]
sce.richard #看到有ERCC存在
## class: SingleCellExperiment 
## dim: 46603 528 
## metadata(0):
## assays(1): counts
## rownames(46603): ENSMUSG00000102693 ENSMUSG00000064842 ...
##   ENSMUSG00000096730 ENSMUSG00000095742
## rowData names(0):
## colnames(528): SLX-12611.N701_S502. SLX-12611.N702_S502. ...
##   SLX-12612.i712_i522. SLX-12612.i714_i522.
## colData names(13): age individual ... stimulus time
## reducedDimNames(0):
## altExpNames(1): ERCC
```

使用`computeSpikeFactors()` 计算所有细胞的spike-in size factor

```r
sce.richard <- computeSpikeFactors(sce.richard, "ERCC")
summary(sizeFactors(sce.richard))
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##  0.1247  0.4282  0.6274  1.0000  1.0699 23.3161
```

再做一个spike-in与去卷积结果的对比图：

```r
to.plot <- data.frame(
    DeconvFactor=calculateSumFactors(sce.richard),
    SpikeFactor=sizeFactors(sce.richard),
    Stimulus=sce.richard$stimulus, 
    Time=sce.richard$time
)

ggplot(to.plot, aes(x=DeconvFactor, y=SpikeFactor, color=Time)) +
    geom_point() + facet_wrap(~Stimulus) + scale_x_log10() + 
    scale_y_log10() + geom_abline(intercept=0, slope=1, color="red")
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-06-27-093019.png)

* 发现每个处理的spike-in size factor与去卷积的结果呈正相关，表示它们都捕获到了类似的技术偏差（测序深度、捕获效率）
* 同时注意到随着时间的增加（如左上图），spike-in factors在下降，去卷积factors在增加，可以解释为：随着时间的推移，生物合成活性在增加，总体RNA含量在增加，而spike-in的含量是一定的，因此每个文库中各个spike-in的比例减少（导致spike-in size factor结果减少），而总体文库增大（导致文库size factor增加）

### **小结**

对于归一化方法的选择，取决于生物学假设。大多数情况下，总体RNA含量的变化我们并不感兴趣，可以直接用library size或deconvolution factors当做系统性偏差去除。但如果总体RNA含量变化与某个感兴趣的生物学过程相关，例如细胞周期活性或T细胞激活，spike-in的归一化方法就可以把这种变化保留下来，而去除其他系统性偏差。

另外，不管我们关不关心总体RNA含量，对于spike-in转录本的归一化都有特殊对待，不能使用针对内源基因的方法，比如可以利用`modelGeneVarWithSpikes()` 得到单独的spike-in相关的size factor

## 3 得到Size Factors后进行数据转换

### **3.1 logNormCounts**

> log-transformation这种是最常用、最简单、最易理解的方法

一旦得到了size factors，就可以利用scater包的`logNormCounts()`函数计算每个细胞归一化后的结果。计算也很简单，就是用某个细胞中每个基因或spike-in转录本的表达量除以这个细胞计算的size factor，最后还进行一个log转换，得到一个新的矩阵：`logcounts` 【不过这个名字并不是很贴切，只是因为拼写简单，真实应该是：log-transformed normalized expression values。而不是单纯字面意思取个log】

```r
set.seed(100)
# 这里一看有pre-clustering就知道是利用的去卷积的size factor
clust.zeisel <- quickCluster(sce.zeisel) 
sce.zeisel <- computeSumFactors(sce.zeisel, cluster=clust.zeisel, min.mean=0.1)
sce.zeisel <- logNormCounts(sce.zeisel)

# 最后的结果多了一项
assayNames(sce.zeisel)
## [1] "counts"    "logcounts"
```

至于**为什么取log？** 就是为了不受真实值的影响，而关注变化倍数。这里举个例子，X基因在细胞A的表达量是50，在B细胞的表达量是10；而Y基因在细胞A的表达量是1000，在B细胞的表达量是1100。哪个基因更能吸引你的注意呢？我们是不是会关注变化的倍数？

另外，既然要取log，就应该要注意存在很多0表达量的数值，因此log前还需要给表达量加上1，`log1p = log(x + 1)`，这个1就称作：pseudo-count。

> **注意，每个包的log-transforming方法存在差异：** scran和scater使用的一样，是`logNormCounts`，用表达量除以这个细胞计算的size factor； 但Seurat使用的`LogNormalize` 计算方法是：normalizes the feature expression measurements for each cell by the **total expression**, multiplies this by a **scale factor** (10,000 by default), and **log-transforms** the result，翻译成公式就是：`log1p(value/colSums[cell-idx] *scale_factor)`

### **3.2 其他方法**

> 其实默认大家都在用log-transformation方法，只是有时还有更特殊的需求

`logNormCounts`还有一个参数：`downsample=T` ，当使用常规流程做完后发现PCA分群的结果可能受到size factor的影响（即使之前通过归一化试图去除文库大小的影响）。既然文库大小影响还在，那么就试图减弱这种影响，于是想到减少样本数量，用更少的样本再重新归一化【但这种情况非常特殊】

图中看到选取更少的样本，结果变得清楚许多，size factor的影响比之前小了很多。于是可以推断，之前PCA的结果很可能是还存在技术偏差导致的文库差异

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-06-27-154358.png)

另外还有更复杂的方法可以看：[sctransform](https://cran.r-project.org/package=sctransform) 、[DESeq2](https://bioconductor.org/packages/3.11/bioc/vignettes/DESeq2/inst/doc/DESeq2.html)，使用了variance stabilizing transformations方法


# 3.3 挑选表达量高变化基因

刘小泽写于2020.6.28

### 1 前言

我们经常使用scRNA数据探索细胞异质性。聚类分群和降维也是常用的操作，它们都是依赖于基因表达量（例如综合细胞中各个基因的表达量，然后把表达模式相近的细胞聚在一起）。因此**挑选哪些基因进行聚类分群和降维分析，这是非常关键的**，挑选的基因一定要有代表性，尽可能多的包含生物信息而剔除其他技术噪音。

单细胞分析的一个重点就是：用尽可能少的维度来展示数据真实的结构。降维的过程其实就是去繁存简，每个基因的变化都对整体有影响，对细胞来讲都是一个变化维度。但这么多维度我们看不了也处理不了，需要尽可能保证真实差异的前提下减少维度的数量，因此需要挑出那些更能代表整体差异的基因。

对每一个细胞来讲，2万多个基因都是它身上长向四面八方的维度，但其中有一些影响很大，一些影响很弱。我们试图把影响最大的几个维度找出来（比如Gene1、Gene2、Gene3），然后用这几个维度代表这个细胞的生物学特征。

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-06-23-012726.png)

**上面说了这么多，重点还是：如何挑选有价值的基因（feature）**

一般来说，如果一个基因在细胞群体中变化幅度很大，就是受关注对象，我们也会认为是生物因素导致了这么大的差异。这样的基因叫做：**高度变化基因（highly variable genes ，HVGs）**

### **数据准备**

**10X PBMC**

其中的处理步骤值得学习，其中`pbmc4k_raw_gene_bc_matrices.tar.gz`数据已经帮大家下载好，链接：<https://share.weiyun.com/s2wbVd7p> 密码：3iwypw

下载的内容包含三件套，然后使用`read10xCounts`读取：

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-06-28-005621.png)

```r
# 下载
library(BiocFileCache)
bfc <- BiocFileCache("raw_data", ask = FALSE)
raw.path <- bfcrpath(bfc, file.path("http://cf.10xgenomics.com/samples",
    "cell-exp/2.1.0/pbmc4k/pbmc4k_raw_gene_bc_matrices.tar.gz"))
untar(raw.path, exdir=file.path(tempdir(), "pbmc4k"))

# 读取
suppressMessages(library(DropletUtils))
fname <- file.path(tempdir(), "pbmc4k/raw_gene_bc_matrices/GRCh38")
sce.pbmc <- read10xCounts(fname, col.names=TRUE)
> dim(sce.pbmc)
[1]  33694 737280

# 基因注释之ID整合
library(scater)
rownames(sce.pbmc) <- uniquifyFeatureNames(
    rowData(sce.pbmc)$ID, rowData(sce.pbmc)$Symbol)
# 基因注释之添加位置信息
library(EnsDb.Hsapiens.v86)
location <- mapIds(EnsDb.Hsapiens.v86, keys=rowData(sce.pbmc)$ID, 
    column="SEQNAME", keytype="GENEID")
```

其中使用到了一个函数：`uniquifyFeatureNames()` ，它的作用是

> **uniquifyFeatureNames() make gene name unique and valid，如果有symbol name的，它会将Ensembl ID替**换为symbol name，没有name的仍然使用ID；如果name相同、ID不同（即两个Ensembl ID对应同一个Symbol name），它会将ID与name组合保证特异性（详见?uniquifyFeatureNames） 详见我之前写的：**来自刘小泽理解的一份诚意满满的单细胞教程：**<https://www.jianshu.com/p/e90f5d4d0ab6>

之后添加位置信息，得到了基因所在的染色体，可以看到存在13个基因在线粒体上

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-06-28-010242.png)

```r
# 接下来
# 空液滴检查
set.seed(100)
e.out <- emptyDrops(counts(sce.pbmc))
sce.pbmc <- sce.pbmc[,which(e.out$FDR <= 0.001)]
> dim(sce.pbmc)
[1] 33694  4233
# 可以看到，确实去除了很多空液滴，原来有737280个，留下百分之一不到

# 质控（尤其关注线粒体）
stats <- perCellQCMetrics(sce.pbmc, subsets=list(Mito=which(location=="MT")))
high.mito <- isOutlier(stats$subsets_Mito_percent, type="higher")
sce.pbmc <- sce.pbmc[,!high.mito]
> dim(sce.pbmc)
[1] 33694  3922

# 归一化
# 看到quickCluster和computeSumFactors就知道使用的是去卷积化方法
library(scran)
set.seed(1000)
clusters <- quickCluster(sce.pbmc)
sce.pbmc <- computeSumFactors(sce.pbmc, cluster=clusters)
sce.pbmc <- logNormCounts(sce.pbmc)
sce.pbmc
## class: SingleCellExperiment 
## dim: 33694 3922 
## metadata(1): Samples
## assays(2): counts logcounts
## rownames(33694): RP11-34P13.3 FAM138A ... AC213203.1 FAM231B
## rowData names(2): ID Symbol
## colnames(3922): AAACCTGAGAAGGCCT-1 AAACCTGAGACAGACC-1 ...
##   TTTGTCACAGGTCCAC-1 TTTGTCATCCCAAGAT-1
## colData names(3): Sample Barcode sizeFactor
## reducedDimNames(0):
## altExpNames(0):
```

**416B数据**

```r
# 加载数据
library(scRNAseq)
# sce.416b <- LunSpikeInData(which="416b") 
sce.416b$block <- factor(sce.416b$block)

# 基因注释
library(AnnotationHub)
ens.mm.v97 <- AnnotationHub()[["AH73905"]] 
rowData(sce.416b)$ENSEMBL <- rownames(sce.416b)
rowData(sce.416b)$SYMBOL <- mapIds(ens.mm.v97, keys=rownames(sce.416b),
    keytype="GENEID", column="SYMBOL")
rowData(sce.416b)$SEQNAME <- mapIds(ens.mm.v97, keys=rownames(sce.416b),
    keytype="GENEID", column="SEQNAME")

library(scater)
rownames(sce.416b) <- uniquifyFeatureNames(rowData(sce.416b)$ENSEMBL, 
    rowData(sce.416b)$SYMBOL)

# 质控（添加线粒体、ERCC、批次信息）
mito <- which(rowData(sce.416b)$SEQNAME=="MT")
stats <- perCellQCMetrics(sce.416b, subsets=list(Mt=mito))
qc <- quickPerCellQC(stats, percent_subsets=c("subsets_Mt_percent",
    "altexps_ERCC_percent"), batch=sce.416b$block)
sce.416b <- sce.416b[,!qc$discard]

# 归一化
# 看到computeSumFactors就知道使用的是去卷积化方法
library(scran)
sce.416b <- computeSumFactors(sce.416b)
sce.416b <- logNormCounts(sce.416b)
sce.416b
## class: SingleCellExperiment 
## dim: 46604 185 
## metadata(0):
## assays(2): counts logcounts
## rownames(46604): 4933401J01Rik Gm26206 ... CAAA01147332.1
##   CBFB-MYH11-mcherry
## rowData names(4): Length ENSEMBL SYMBOL SEQNAME
## colnames(185): SLX-9555.N701_S502.C89V9ANXX.s_1.r_1
##   SLX-9555.N701_S503.C89V9ANXX.s_1.r_1 ...
##   SLX-11312.N712_S507.H5H5YBBXX.s_8.r_1
##   SLX-11312.N712_S517.H5H5YBBXX.s_8.r_1
## colData names(10): Source Name cell line ... block sizeFactor
## reducedDimNames(0):
## altExpNames(2): ERCC SIRV
```

可以看到这里归一化没有使用`quickCluster()` ，这是因为416b数据自带了block，和cluster的效果一样

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-06-28-013155.png)

## 2 衡量每个基因的变化程度/方差(Variance)

### **2.1 方法一：使用log-counts衡量变化程度**

> 这也是最简单的处理方法，基因log后的表达量在细胞间变化幅度越大，就说明细胞间距离越远（这个距离指欧氏距离Euclidean distances）

计算每个基因的变化程度很简单，但要基于这么多基因的结果去挑选最有价值的基因（feature），还需要构建一个模型。这个模型叫：mean-variance relationship，看上去与均值、方差有关

**先看计算方法**

```r
library(scran)
dec.pbmc <- modelGeneVar(sce.pbmc)

# 可视化一条线（下图的蓝线），这条线指所有的基因都会存在的一种偏差
fit.pbmc <- metadata(dec.pbmc)
plot(fit.pbmc$mean, fit.pbmc$var, xlab="Mean of log-expression",
    ylab="Variance of log-expression")
curve(fit.pbmc$trend(x), col="dodgerblue", add=TRUE, lwd=2)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-06-28-020520.png)

* 每个点表示一个基因
* 图中蓝线指的是：技术因素导致的偏差
* 纵坐标表示总偏差：它等于技术偏差+生物因素偏差

因此，要衡量一个基因的生物因素偏差大小，就看对应的纵坐标减去对应的蓝线的值

最后可以得到一个表：

```r
dec.pbmc[order(dec.pbmc$bio, decreasing=TRUE),]
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-06-28-020940.png)

可以看到生物因素（bio）导致的偏差从大到小排列，然而会有一些负值存在，难不成总体偏差比技术偏差还要小？这是因为拟合曲线时会有一些基因处于线以下（看上图也能知道），这部分其实可以忽略，反正这部分基因也用不到。我们实则更关心那些`bio`值更大的

### **2.2 方法二：使用变异系数(CV)衡量变化程度**

> 这是另一种可选方案，和上面的log-counts是平行关系，它会使用`squared coefficient of variation (CV^2)`

**关于概念：** 可以看<https://www.jianshu.com/p/f2df679843f3> 和 <https://www.jianshu.com/p/3525e624946a>

* Coefficient of variation （CV）中文翻译是**变异系数**，它是**标准差与均值的比值** ，因此`CV^2` 就是方差与均值的平方比值
* 如果两组数据的观测值在一个数量级，那么可以用标准差来比较它们的离散度
* 如果两个数据差别太大（比如一群老鼠和一群大象的重量），然后就要用`CV`或`CV^2`，这样可以去除不同量纲均值差异的影响

我们这里可以利用`modelGeneCV2()`来计算

```r
dec.cv2.pbmc <- modelGeneCV2(sce.pbmc)
```

同样的，我们这里的假设是： most genes contain random noise and that the trend captures mostly technical variation，即大部分基因都包含外部噪音，并且拟合的曲线能捕获大部分技术偏差

**如果`CV^2` 越大，偏离拟合曲线越远，就可能包含更多的生物偏差，更具有生物学意义**

> 来自`modelGeneCV2()`的帮助文档： The ratio of the total CV2 to the trend is used as a metric to rank interesting genes, with **larger ratios being indicative of strong biological heterogeneity.**

```r
fit.cv2.pbmc <- metadata(dec.cv2.pbmc)
plot(fit.cv2.pbmc$mean, fit.cv2.pbmc$cv2, log="xy")
curve(fit.cv2.pbmc$trend(x), col="dodgerblue", add=TRUE, lwd=2)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-06-28-021748.png)

**最后也能得到一个表：**

其中偏离拟合曲线的数值用`ratio`表示，也是我们定义HVGs的途径

这个`ratio = total (CV^2) / trend (CV^2)` ，这样一相除，就抵消了均值的影响，最后实际上变成了方差的比值。但如果是相减，还是会带着均值【把公式从纸上一列就很清楚了】

```r
dec.cv2.pbmc[order(dec.cv2.pbmc$ratio, decreasing=TRUE),]
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-06-28-022009.png)

**总结：**

* log-counts与CV2都是衡量基因变化程度的有效途径
* 优先使用log-counts的结果，因为下游的分析也是基于log-counts值

### **2.3 考虑技术噪音**

> 有两种方式：一种是有spike-in的，一种是没有的，都可以操作

之前画的技术偏差拟合线都是基于一个假设：大部分基因的表达量都是受外界技术误差的影响。但实际上，除了技术误差，还有一些生物因素会导致数据波动（例如transcriptional bursting）。因此之前估计的技术偏差可能”超出了实际的大小“，有些过度估计。

之前估计的偏差实际上是：真正的技术偏差 + 一些无关紧要的生物学因素偏差。因此如果有spike-in的话，它和内源基因不同，不会受到生物因素的影响。利用spike-in画的拟合线，更能代表技术偏差

**如果有spike-in**

```r
dec.spike.416b <- modelGeneVarWithSpikes(sce.416b, "ERCC")
dec.spike.416b[order(dec.spike.416b$bio, decreasing=TRUE),]
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-06-28-032639.png)

```r
# 作图
plot(dec.spike.416b$mean, dec.spike.416b$total, xlab="Mean of log-expression",
    ylab="Variance of log-expression")
fit.spike.416b <- metadata(dec.spike.416b)
points(fit.spike.416b$mean, fit.spike.416b$var, col="red", pch=16)
curve(fit.spike.416b$trend(x), col="dodgerblue", add=TRUE, lwd=2)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-06-28-032709.png)

其中黑点是基因，红点是spike-in，蓝线是根据红点画的拟合线

**如果没有spike-in**

可以考虑利用数据分布来表示技术噪音，例如只考虑技术噪音的话，UMI counts通常会呈现近似泊松分布

```r
set.seed(0010101)
dec.pois.pbmc <- modelGeneVarByPoisson(sce.pbmc)
dec.pois.pbmc <- dec.pois.pbmc[order(dec.pois.pbmc$bio, decreasing=TRUE),]
head(dec.pois.pbmc)
## DataFrame with 6 rows and 6 columns
##              mean     total      tech       bio   p.value       FDR
##         <numeric> <numeric> <numeric> <numeric> <numeric> <numeric>
## LYZ       1.97770   5.11595  0.621547   4.49440         0         0
## S100A9    1.94951   4.58859  0.627306   3.96128         0         0
## S100A8    1.71828   4.45723  0.669428   3.78781         0         0
## HLA-DRA   2.09694   3.72690  0.596372   3.13053         0         0
## CD74      2.89840   3.30912  0.422624   2.88650         0         0
## CST3      1.49285   2.97369  0.695367   2.27833         0         0

# 作图
plot(dec.pois.pbmc$mean, dec.pois.pbmc$total, pch=16, xlab="Mean of log-expression",
    ylab="Variance of log-expression")
curve(metadata(dec.pois.pbmc)$trend(x), col="dodgerblue", add=TRUE)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-06-28-032808.png)

有趣的是，如果真的单纯考虑技术偏差，我们会得到比原来更多的变化基因。其中就包含了之前没有的一些管家基因。不过这些基因在后面分析（比如研究细胞异质性）中又不太感兴趣，因此**使用更准确的技术噪音拟合方法，可能不会得到更准确的HVGs排序**

### **2.4 考虑批次效应**

**2.4.1 方法一：绘制拟合曲线时加入批次信息（Fitting block-specific trends）**

有时我们看到的在不同细胞间高变化的基因，不一定是生物学因素驱动，也不一定是由于技术误差导致，而是由于不同细胞的批次不同，导致同一基因的表达量差异很大。

我们更想知道的是，在一个批次中的HVGs信息。一般可以一个批次一个批次地去处理，例如：

```r
# 这里用spike-in来拟合更准确的技术偏差，同时考虑上批次信息
dec.block.416b <- modelGeneVarWithSpikes(sce.416b, "ERCC", block=sce.416b$block)
head(dec.block.416b[order(dec.block.416b$bio, decreasing=TRUE),1:6])
## DataFrame with 6 rows and 6 columns
##              mean     total      tech       bio      p.value          FDR
##         <numeric> <numeric> <numeric> <numeric>    <numeric>    <numeric>
## Lyz2      6.61235   13.8619   1.58416   12.2777  0.00000e+00  0.00000e+00
## Ccl9      6.67841   13.2599   1.44553   11.8143  0.00000e+00  0.00000e+00
## Top2a     5.81275   14.0192   2.74571   11.2734 3.89855e-137 8.43398e-135
## Cd200r3   4.83305   15.5909   4.31892   11.2719  1.17783e-54  7.00721e-53
## Ccnb2     5.97999   13.0256   2.46647   10.5591 1.20380e-151 2.98405e-149
## Hbb-bt    4.91683   14.6539   4.12156   10.5323  2.52639e-49  1.34197e-47
```

看看不同批次的差异

```r
par(mfrow=c(1,2))
blocked.stats <- dec.block.416b$per.block
for (i in colnames(blocked.stats)) {
    current <- blocked.stats[[i]]
    plot(current$mean, current$total, main=i, pch=16, cex=0.5,
        xlab="Mean of log-expression", ylab="Variance of log-expression")
    curfit <- metadata(current)
    points(curfit$mean, curfit$var, col="red", pch=16)
    curve(curfit$trend(x), col='dodgerblue', add=TRUE, lwd=2) 
}
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-06-28-040016.png)

其中黑点是基因，红点是spike-in，蓝线是根据红点画的拟合线。看到这里的两个批次之间差异很小，表示重复效果不错

**2.4.2 利用实验设计矩阵（design matrix）**

当只有一个批次信息时（例如只考虑`sce.416b$block`信息），使用上面的绘制拟合曲线方法是足够的，但如果再加上其他的批次信息，例如：

```r
> table(colData(sce.416b)$phenotype)

induced CBFB-MYH11 oncogene expression 
                                    96 
                   wild type phenotype 
                                    96
```

就需要利用实验设计矩阵（很像DESeq2、edgeR等差异分析做的），像是`modelGeneVarWithSpikes()`和`modelGeneVar()` 都支持这个参数

```r
design <- model.matrix(~factor(block) + phenotype, colData(sce.416b))
dec.design.416b <- modelGeneVarWithSpikes(sce.416b, "ERCC", design=design)
dec.design.416b[order(dec.design.416b$bio, decreasing=TRUE),]
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-06-28-113935.png)

这种方法做起来很简单，但不太准确，因为没有考虑到各个批次中的平均表达水平。而这个表达水平又影响真正的技术偏差估计

> the **true technical** component is the average of the fitted values at the per-block means, which may be **quite different for strong batch effects** and non-linear mean-variance relationships

相比之下，第一种方法的`block=` 更稳妥一些

## 3 挑选出高变化基因HVGs

既然前面找到了每个基因的变化程度，并且排了个序。**但挑多少基因呢，这是个问题**。我们首先想是不是多多益善？

的确，一个大的基因集保留了更多的基因信息，可以防止丢失一些有趣的生物信号，但代价是增加一些和研究不相干的基因信号，造成干扰。而哪些是不相干，这个不好权衡，因为一种情况下的不相干，可能在另一种情况下又是有用的。

例如，T细胞活化反应中存在的异质性是有趣的，但如果我们关注点是区分主要的免疫表型，那么前面这个异质性就是无关的噪音。简言之，虽然都是有趣的生物学信号，但我们关注点只有一个，研究一个势必另一个会成为干扰因素。

关于HVGs的选择，有以下几种方案：

### **3.1 基于方差最大化**

这个方法是最简单理解的，就是提取在生物学因素中方差最大的（也就是对波动贡献最大）基因。优点就是可以自己控制基因的数量，方便对后面的分析复杂度有一个大体的估计。

比如可以挑选前1000个变化最大的基因：

对于`modelGeneVar()` 和 `modelGeneVarWithSpikes()` 基于方差的，直接根据结果表格`bio`这一列提取

```r
hvg.pbmc.var <- getTopHVGs(dec.pbmc, n=1000)
str(hvg.pbmc.var)
##  chr [1:1000] "LYZ" "S100A9" "S100A8" "HLA-DRA" "CD74" "CST3" "TYROBP" ...
```

对于`modelGeneCV2()`和`modelGeneCV2WithSpikes()` 基于CV2的，可以根据结果表格的`ratio`这一列提取

```r
hvg.pbmc.cv2 <- getTopHVGs(dec.cv2.pbmc, var.field="ratio", n=1000)
str(hvg.pbmc.cv2)
##  chr [1:1000] "HIST1H2AC" "GNG11" "PRTFDC1" "TNNC2" "PF4" "HGD" "PPBP" ...
```

**重点还是参数`n`的设定**，实际上大部分基因对生物学异质性的贡献不大。如果我们知道一个假设：不超过5%的基因是差异表达的，那么这个问题就轻松许多。我们之间设置`n`小于等于基- 因总数的5%即可。但事实上，我们实现不知道差异基因的占比，这个5%也只是虚构。但给我们一些提示：

* 如果数据异质性比较低，可以降低`n`的数值，保留最多的生物信号同时，尽可能剔除不相干的噪音
* 如果数据异型性很高，就应该使用更大的`n`值，在寻求主要差异的同时，保留更多的次要差异因素

从上面的操作和解读来看，**这个方法存在一定的弊端**：设置一个固定的数值，就让基因们形成了竞争关系，能被`n`选择成为HVGs的，势必会把其他有信息价值的基因排除在外，如果这些被排除的基因还是一些细胞亚群的marker基因【这种问题在高度异质性群体中尤为严重】

另外这个`n`的选择很主观，从500-5000之间似乎都能说得通。上面选择1000，其实也没有什么特别的原因，只是一个直觉。**如果选择这种方法，那么什么都不要管，根据直觉选择一个值**，进行剩余的分析，通过结果来判断这个值选的是不是合理，而不是在这里辗转反侧思考用什么数值。

### **3.2 基于假设检验**

原假设是：基因的方差与之前拟合的曲线相符（也就是对生物学角度的波动没什么贡献）。由此可以设置adjusted p-values的阈值，例如

```r
hvg.pbmc.var.2 <- getTopHVGs(dec.pbmc, fdr.threshold=0.05)
length(hvg.pbmc.var.2)
## [1] 651
```

这个方法适用于HVGs的确大部分都是与研究点相关，使用FDR只会让结果更准确；但缺点是：比第一种指定数量的方法更难以预测。既然是假设检验，就可能存在假阳性，可能得到更多不相干的基因。另外，我们的关注重点实际上不在于这些基因本身，而是看它们对下游分析有没有作用，因此也不能认为5%的FDR阈值可以得到低噪音的基因集。

或许还会想，之前计算的表格中有p值这一列，那么可不可以用这一列来替代`bio`或者`ratio`那一列进行排序，从而挑选基因呢？

最好不要，因为还是那句话，**表现显著的基因不一定对我们感兴趣的生物学问题有帮助。**&#x5F88;多基因确实p值很小，这是因为它们的技术偏差非常小，但同时总体偏差并不大，因此它看上去很显著，但实际我们不感兴趣。

因此这种方法还是慎用

### **3.3 保留之前拟合曲线上面全部的基因**

这个方法的目的很单纯，拟合曲线以下的基因都是不感兴趣的，因为它们的生物学偏差很小，于是把它们全部剔除，留下剩下的。这也是走了一个极端：为了偏差最小化，选择了噪音最大化（保留了很多与研究问题无关的基因）。看下面计算的数量就知道

如果之前使用了`modelGeneVar()`：

```r
hvg.pbmc.var.3 <- getTopHVGs(dec.pbmc, var.threshold=0)
length(hvg.pbmc.var.3)
## [1] 12791
```

如果之前使用了`modelGeneCV2()`：保留所有ratio大于1的

```r
hvg.pbmc.cv2.3 <- getTopHVGs(dec.cv2.pbmc, var.field="ratio", var.threshold=1)
length(hvg.pbmc.cv2.3)
## [1] 9295
```

这种方法对于研究罕见的细胞亚群最有帮助，另外对于异质性非常严重的数据（包含许多不同类型的细胞类型，例如多个数据集合并后的数据）比较适合

## 4 特殊情况一：Keep or not

> 场景就是：我们筛选出来HVGs，剩下的基因是删除还是继续保留呢？ 主要有三种方法

### **首先选取前10%的HVGs**

利用参数`prop=0.1`

```r
dec.pbmc <- modelGeneVar(sce.pbmc)
chosen <- getTopHVGs(dec.pbmc, prop=0.1)
str(chosen)
##  chr [1:1279] "LYZ" "S100A9" "S100A8" "HLA-DRA" "CD74" "CST3" "TYROBP" ...
```

### **方法一：直接删除非HVGs**

下游分析也全部基于HVGs，不过日后如果突然发现，一些非HVGs的基因也有点意思，想看看它们就变得很麻烦

```r
sce.pbmc.hvg <- sce.pbmc[chosen,]
dim(sce.pbmc.hvg)
## [1] 1279 3922
```

### **方法二：原数据不动，只是下游分析基于小部分HVGs**

这样的好处是，如果以后调整了chosen这个HVGs基因集，那么还是可以继续进行下游分析的

```r
# 例如只根据HVGs进行PCA，使用参数subset_row
library(scater)
sce.pbmc <- runPCA(sce.pbmc, subset_row=chosen)
reducedDimNames(sce.pbmc)
## [1] "PCA"
```

### **方法三：原数据先备份，下游分析也是基于原数据，最后再复原**

```r
# 首先保留一份原数据
altExp(sce.pbmc.hvg, "original") <- sce.pbmc
altExpNames(sce.pbmc.hvg)
## [1] "original"

# 然后按方法一得到处理的数据，并进行分析（这里就不需要指定参数subset_row）
sce.pbmc.hvg <- runPCA(sce.pbmc.hvg)

# 最后复原
sce.pbmc.original <- altExp(sce.pbmc.hvg, "original", withColData=TRUE)
```

## 5 特殊情况二：利用先验基因

如果想证明某个通路的基因不存在有意义的异质性，那么可以选取这部分基因作为先验基因，重复一遍分析，检查到底是否存在异质性。这个想法和荧光激活细胞分选（fluorescence activated cell sorting ，FACS）相似，只不多scRNA分析中可以更方便地更改先验基因

### **选取部分先验基因**

例如PBMC数据集中，可以用MSigDB数据库的免疫相关基因集：C7 immunologic signatures

```r
# 首先选取基因集
library(msigdbr)
c7.sets <- msigdbr(species = "Homo sapiens", category = "C7")
head(unique(c7.sets$gs_name))
## [1] "GOLDRATH_EFF_VS_MEMORY_CD8_TCELL_DN"  
## [2] "GOLDRATH_EFF_VS_MEMORY_CD8_TCELL_UP"  
## [3] "GOLDRATH_NAIVE_VS_EFF_CD8_TCELL_DN"   
## [4] "GOLDRATH_NAIVE_VS_EFF_CD8_TCELL_UP"   
## [5] "GOLDRATH_NAIVE_VS_MEMORY_CD8_TCELL_DN"
## [6] "GOLDRATH_NAIVE_VS_MEMORY_CD8_TCELL_UP"

# 利用Goldrath基因集来区分CD8细胞亚型
cd8.sets <- c7.sets[grep("GOLDRATH", c7.sets$gs_name),]
cd8.genes <- rowData(sce.pbmc)$Symbol %in% cd8.sets$human_gene_symbol
summary(cd8.genes)
##    Mode   FALSE    TRUE 
## logical   32869     825

# 利用GSE11924基因集来区分T helper细胞亚型
th.sets <- c7.sets[grep("GSE11924", c7.sets$gs_name),]
th.genes <- rowData(sce.pbmc)$Symbol %in% th.sets$human_gene_symbol
summary(th.genes)
##    Mode   FALSE    TRUE 
## logical   31786    1908

# 利用GSE11961基因集来区分B细胞亚型
b.sets <- c7.sets[grep("GSE11961", c7.sets$gs_name),]
b.genes <- rowData(sce.pbmc)$Symbol %in% b.sets$human_gene_symbol
summary(b.genes)
##    Mode   FALSE    TRUE 
## logical   28185    5509
```

以上就是我们自己筛选的先验基因集，然后利用这些作为`chosen`基因再进行下游分析，看看结果与我们推测的是否一致

### **剔除某些先验基因**

和上面相反，我们还可以先剔除一些比较不感兴趣的基因，再进行下游分析

例如，可以剔除核糖体蛋白基因或线粒体基因

```r
# 匹配核糖体蛋白基因
ribo.discard <- grepl("^RP[SL]\\d+", rownames(sce.pbmc))
sum(ribo.discard)
## [1] 99

# 另一种更准确的方法
c2.sets <- msigdbr(species = "Homo sapiens", category = "C2")
ribo.set <- c2.sets[c2.sets$gs_name=="KEGG_RIBOSOME",]$human_gene_symbol
ribo.discard <- rownames(sce.pbmc) %in% ribo.set
sum(ribo.discard)
## [1] 87
```

对于免疫细胞数据集，可以剔除免疫球蛋白基因和T细胞受体基因【当然这些都取决于生物背景】

```r
library(AnnotationHub)
edb <- AnnotationHub()[["AH73881"]]
anno <- select(edb, keys=rowData(sce.pbmc)$ID, keytype="GENEID", 
    columns="TXBIOTYPE")

# 去除免疫球蛋白基因
igv.set <- anno$GENEID[anno$TXBIOTYPE %in% c("IG_V_gene", "IG_V_pseudogene")]
igv.discard <- rowData(sce.pbmc)$ID %in% igv.set
sum(igv.discard)
## [1] 326

# 去除T细胞受体基因
tcr.set <- anno$GENEID[anno$TXBIOTYPE %in% c("TR_V_gene", "TR_V_pseudogene")]
tcr.discard <- rowData(sce.pbmc)$ID %in% tcr.set
sum(tcr.discard)
## [1] 138
```

**不过以上的操作要慎重**，如果下游分析没有发现问题，以及对先验基因存在十足的把握，还是不要随意使用先验基因


# 3.4 降维

刘小泽写于2020.6.29

## 1 前言

基本上scRNA数据分析步骤都会包含基于基因表达量来对细胞进行比较。例如：细胞聚类就是将相似表达谱的细胞聚集在一起，而如何判断基因表达谱是否相似，就是计算基因间的欧氏距离。

scRNA分析就是在于数据打交道。本来看不见摸不着的基因，现在体现在数据上，虽然还是摸不着，但能看得到。**每个基因现在都作为数据的一个维度存在**。假如我们有一个scRNA数据集，其中只有两个基因，其中两个坐标轴表示两个基因的表达量，图中的点就表示细胞，每个细胞在图上都由x、y轴确定。也就是说，**基因的表达决定了细胞的分布**

如果有两个基因，那么细胞的位置就由两个维度决定；三个基因，就由 三维空间决定；而我们有2万多个基因，那么就存在2万多个维度（高维空间）。

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-06-23-012726.png)

高维空间我们无法理解，最常见的当属二维和三维，如果再加上时间线，就是四维空间。

**降维，顾名思义，就是把各种离散的维度“去粗存精”**。因为各个基因并不是毫无关联，同一生物学过程的各个基因是相互关联的，因此它们各自的维度就可以进行整合，结果可以用一个维度表示相互关联的多个维度，例如基因共表达网络中的eigengene

降维的作用非常重大，下游分析中的聚类只利用少量的维度进行计算，会比使用全部的维度计算更快，结果也更容易理解（比如我们目前最多只能做出三维的图片）。

### **数据准备之sce.zeisel**

数据依然给大家准备好了，`sce.zeisel`链接：<https://share.weiyun.com/mNwJS8U9> 密码：g8379h

```r
library(scRNAseq)
# 将每个细胞的所有基因表达量加起来，得到每个细胞的文库大小
library(scater)
sce.zeisel <- aggregateAcrossFeatures(sce.zeisel, 
                                      id=sub("_loc[0-9]+$", "", rownames(sce.zeisel)))

# 基因注释
library(org.Mm.eg.db)
rowData(sce.zeisel)$Ensembl <- mapIds(org.Mm.eg.db, 
    keys=rownames(sce.zeisel), keytype="SYMBOL", column="ENSEMBL")

# 质控（先perCellQCMetrics，后quickPerCellQC）
stats <- perCellQCMetrics(sce.zeisel, subsets=list(
    Mt=rowData(sce.zeisel)$featureType=="mito"))

qc <- quickPerCellQC(stats, percent_subsets=c("altexps_ERCC_percent", 
                                              "subsets_Mt_percent"))
sce.zeisel <- sce.zeisel[,!qc$discard]

# 归一化（去卷积方法）
library(scran)
set.seed(1000)
clusters <- quickCluster(sce.zeisel)
sce.zeisel <- computeSumFactors(sce.zeisel, cluster=clusters) 
sce.zeisel <- logNormCounts(sce.zeisel)

# 利用spike-in找HVGs（前10%）
dec.zeisel <- modelGeneVarWithSpikes(sce.zeisel, "ERCC")
top.hvgs <- getTopHVGs(dec.zeisel, prop=0.1)

sce.zeisel
## class: SingleCellExperiment 
## dim: 19839 2816 
## metadata(0):
## assays(2): counts logcounts
## rownames(19839): 0610005C13Rik 0610007N19Rik ... Zzef1 Zzz3
## rowData names(2): featureType Ensembl
## colnames(2816): 1772071015_C02 1772071017_G12 ... 1772063068_D01
##   1772066098_A12
## colData names(11): tissue group # ... level2class sizeFactor
## reducedDimNames(0):
## altExpNames(2): ERCC repeat
```

### **数据准备之PBMC**

具体的操作在上一章也提到过

```r
# 下载
library(BiocFileCache)
bfc <- BiocFileCache("raw_data", ask = FALSE)
raw.path <- bfcrpath(bfc, file.path("http://cf.10xgenomics.com/samples",
    "cell-exp/2.1.0/pbmc4k/pbmc4k_raw_gene_bc_matrices.tar.gz"))
untar(raw.path, exdir=file.path(tempdir(), "pbmc4k"))

# 读取
suppressMessages(library(DropletUtils))
fname <- file.path(tempdir(), "pbmc4k/raw_gene_bc_matrices/GRCh38")
sce.pbmc <- read10xCounts(fname, col.names=TRUE)
> dim(sce.pbmc)
[1]  33694 737280

# 基因注释之ID整合
library(scater)
rownames(sce.pbmc) <- uniquifyFeatureNames(
    rowData(sce.pbmc)$ID, rowData(sce.pbmc)$Symbol)
# 基因注释之添加位置信息
library(EnsDb.Hsapiens.v86)
location <- mapIds(EnsDb.Hsapiens.v86, keys=rowData(sce.pbmc)$ID, 
    column="SEQNAME", keytype="GENEID")

# 空液滴检测
set.seed(100)
e.out <- emptyDrops(counts(sce.pbmc))
sce.pbmc <- sce.pbmc[,which(e.out$FDR <= 0.001)]

# 质控
stats <- perCellQCMetrics(sce.pbmc, subsets=list(Mito=which(location=="MT")))
high.mito <- isOutlier(stats$subsets_Mito_percent, type="higher")
sce.pbmc <- sce.pbmc[,!high.mito]

# 归一化（去卷积）
library(scran)
set.seed(1000)
clusters <- quickCluster(sce.pbmc)
sce.pbmc <- computeSumFactors(sce.pbmc, cluster=clusters)
sce.pbmc <- logNormCounts(sce.pbmc)

# 利用数据分布来表示技术噪音，并挑选HVGs
set.seed(1001)
dec.pbmc <- modelGeneVarByPoisson(sce.pbmc)
top.pbmc <- getTopHVGs(dec.pbmc, prop=0.1)
```

## 2 主成分分析( PCA)概览

> 它的计算过程之前写过：[StatQuest-PCA学习](https://mp.weixin.qq.com/s/WYIC-JdmgMC_olR-j-fsHw) 以及 [StatQuest--在R中拆解PCA](https://mp.weixin.qq.com/s/kBpwZgmY_A-UcRpGxM1G-A)，其中提到：
>
> * **PCA的作用**就是：对超过4维的数据降维到一个2D/3D平面图中，并且这个图中"**相似相聚"**
> * **为什么可以通过PCA可以看批次效应？**&#x56E0;为PCA图中的每个点都是一个样本，这个点中包含了大量的基因表达量信息；如果说本来属于生物学重复的几个样本在PCA图上离得很远，那么就意味着它们包含的基因表达量差异很大，这是不符合实际的，因此可能存在批次效应

PCA全称Principal components analysis，一般最常见的是二维的图，x轴是PC1，y轴是PC2。PC1捕获了细胞间最大的差异（暂且称它为V1），PC2与PC1正交，捕获了除V1外的最大差异。这样看来，**前几个PCs就能捕获整个数据集主要的差异因素**

我们**降维也是想得到这几个能包含主要生物差异的PCs，方便下游分析**【其实可以看到，一步步走来，都是上一步在为下一步省事省力做贡献，逐渐降低分析难度】。这几个包含主要生物差异的PCs其实也就是前几个PCs，PC越往后越集中在技术误差。

基本每个包都有自己PCA的函数，例如scater可以用`runPCA` ，seurat的`RunPCA`，monocle V3的`reduce_dimension` ，**下面以scater包为例：**

首先还是选择前多少的HVGs，然后给到`runPCA`默认计算前50个PCs，然后结果保存在`SingleCellExperiment`对象的`reducedDims`模块中

```r
library(scran)
top.zeisel <- getTopHVGs(dec.zeisel, n=2000)

library(scater)
set.seed(100) #因为PCA是随机的
sce.zeisel <- runPCA(sce.zeisel, subset_row=top.zeisel) 
reducedDimNames(sce.zeisel)
## [1] "PCA"
dim(reducedDim(sce.zeisel, "PCA")) # 50个PCs，2000多个细胞
## [1] 2816   50
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-06-29-023058.png)

如果是更大的数据，可以用近似的SVD算法（奇异值分解），scater或scran都可以直接通过函数计算SVD，利用参数`BSPARAM=`传递一个`BiocSingularParam`对象到`runPCA`中

* [SVD与PCA的介绍](https://www.cnblogs.com/bjwu/p/9280492.html)
* SVD是一种矩阵分解方法，相当于因式分解，目的纯粹就是将一个矩阵拆分成多个矩阵相乘的形式
* PCA从名字上就很直观，找到矩阵的主成分，也就意味这从一出生这就是个降维的方法。
* 对于稀疏矩阵来说，SVD更适用，这样对于大数据来说节省了很大空间

```r
library(BiocSingular)
set.seed(1000)
sce.zeisel <- runPCA(sce.zeisel, subset_row=top.zeisel, 
    BSPARAM=RandomParam(), name="IRLBA")
reducedDimNames(sce.zeisel)
## [1] "PCA"   "IRLBA"
> dim(reducedDim(sce.zeisel, "IRLBA"))
[1] 2816   50
```

## 3 选择合适的PCs数

> 看到这个问题，好像又回到了之前选择top HVGs的时候了，那么主成分数应该选多少合适呢？

* 如果选的PC多，可以避免丢弃一些生物信号，但同时又增加了噪音的风险
* 很多有经验的人会设置PC数在一个“合理”区间，例如10-50之间

下面我们来看看，如果经验不足，应该怎么帮助判断？

### **3.1 利用elbow point**

elbow point就是在它之前变化幅度很大，之后变化幅度很小，属于一个转折点。

```r
# 首先提取各个PCs对整体差异的贡献比例
percent.var <- attr(reducedDim(sce.zeisel), "percentVar")
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-06-29-024354.png)

可以看到前几个主成分的贡献最高，越往后越小

```r
# 辅助选择
chosen.elbow <- PCAtools::findElbowPoint(percent.var)
chosen.elbow
## [1] 7
# 再画个图
plot(percent.var, xlab="PC", ylab="Variance explained (%)")
abline(v=chosen.elbow, col="red")
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-06-29-024534.png)

上面基于的假设是：每个PCs都能捕获一些生物差异，而且前面的PC比后面的PC包含的差异信息更多，更有价值。就像“二八准则”，仅有20%的变因操纵着80%的局面，PCA中也一样。于是当我们从头到尾观察每个PC的差异贡献时，会有一个明显的“落差”，“落差”之前的那些PC就是“二八准则”中20%的变因。

这个方法得到的PC数一般是比较少而精的，但我们能保证elbow近邻的那几个PCs没有感兴趣的生物差异信号吗？

### **3.2 利用技术噪音**

这个方法是：设置一个差异贡献阈值，将每个PC的贡献率加起来，直到达到这个阈值，保留其中的所有PC。例如设置80%，也就是保留能够解释80%差异的PCs。

看到这里可能会奇怪，这个阈值的设置，不是也很主观吗？为了不显得这么主观，我们通过计算数据有多少比例的差异与生物因素相关，从而得到这个阈值。

> 更准确的描述是：Threshold is defined as the ratio of the sum of the biological components to the sum of total variances

整个计算是利用`denoisePCA()`函数完成，下面利用10X PBMC来展示

```r
# 需要考虑技术噪音，通过参数technical指定
library(scran)
set.seed(111001001)
denoised.pbmc <- denoisePCA(sce.pbmc, technical=dec.pbmc, subset.row=top.pbmc)
ncol(reducedDim(denoised.pbmc))
## [1] 8
```

**一般来说，`denoisePCA()`这种方法得到的PC数比elbow计算得到的要多**，但是它在去除技术噪音的时候，并没有去除一些不感兴趣的生物噪音（例如transcriptional bursting）。它**默认得到的PC数也是在5-50之间**，取决于技术噪音与生物偏差的多少（例如：当技术误差占主导地位，就会得到很少的PC；当技术误差很小时，PC数又会非常多）

```r
set.seed(001001001)
denoised.zeisel <- denoisePCA(sce.zeisel, technical=dec.zeisel, 
    subset.row=top.zeisel)
ncol(reducedDim(denoised.zeisel))
## [1] 50 #这个数据就达到了PC数的天花板
```

另外，`modelGeneVarByPoisson()` 和 `modelGeneVarWithSpikes()` 是考虑了技术误差，它们与`denoisePCA`的配合效果会更好。而基于log-counts估计的模型`modelGeneVar()` ，与`denoisePCA`的结果中，PC数就会减少

```r
dec.pbmc2 <- modelGeneVar(sce.pbmc)
denoised.pbmc2 <- denoisePCA(sce.pbmc, technical=dec.pbmc2, subset.row=top.pbmc)
ncol(reducedDim(denoised.pbmc2))
## [1] 5
# 之前可是得到了8个PC
```

不论如何，想要更多的PCs（`denoisePCA`方法）还是保留更“精”的生物偏差（`elbow`方法），最终取决于自己的想法

### **3.3 利用细胞分群的推断**

简言之就是根据亚群的数量来估计PC的数量，比如scRNA数据有10种不同的细胞类型，同时还要考虑一些噪音的存在，因此可以估计PC约等于10。不过这个想法有些理想，因为细胞分群的情况我们一开始不可能知晓，因此可以先进行预聚类

```r
pcs <- reducedDim(sce.zeisel)
choices <- getClusteredPCs(pcs)
metadata(choices)$chosen
## [1] 17

plot(choices$n.pcs, choices$n.clusters,
    xlab="Number of PCs", ylab="Number of clusters")
abline(a=1, b=1, col="red")
abline(v=metadata(choices)$chosen, col="grey80", lty=2)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-06-29-095517.png)

图中红线是cluster数量的最大理论值，灰线就是`getClusteredPCs()`建议的PC数，可以看到如果使用推荐的17个PCs，基本能得到18个cluster

不过这个方法基于的假设是：整个数据可以分出许多亚群，而它们也是体现了不同的生物学意义。**如果数据不能够分出这么多亚群，这种方法就不会得到太多的PCs，会导致信息丢失**（例如研究连续生物学过程——分化时，可能就不会产生许多亚群）

### **3.4 自定义PC数量**

如果之前获得了PC的数量，但还想再精简（比如之前得到了50个PC，现在可以根据自己需求来定义PC数量，比如取前20个）

**可以直接在PCA结果上修改**

```r
reducedDim(sce.zeisel, "PCA") <- reducedDim(sce.zeisel, "PCA")[,1:20]
ncol(reducedDim(sce.zeisel, "PCA"))
## [1] 20
```

**还可以新增一个PCA结果**

```r
# 赋值一个新的变量名
reducedDim(sce.zeisel, "PCA_20") <- reducedDim(sce.zeisel, "PCA")[,1:20]
reducedDimNames(sce.zeisel)
## [1] "PCA"    "IRLBA"  "PCA_20"
```

其实`runPCA()` 可以直接定义PC数，这个过程只是为了方便后期探索更多不同的组合，多设置几组PCs看看差异

## 4 了解非负矩阵分解

> 可以把它当成降维的高阶玩法

英文名是：Non-negative matrix factorization（NMF），非负矩阵分解 是一个用来替代主成分分析的方法。它利用两个低维的矩阵（W和H，一个代表基因，一个代表细胞）来估计整个表达矩阵，并且其中所有的数据都要求是非负。它的想法和PCA类似，都是“缩小”数据，NMF是利用小的矩阵来归纳大矩阵的主要特性，最终降噪、压缩数据。相比于PCA的坐标，NMF坐标更容易解释，因为更大的值表示相应因子中基因的表达量更高。

scRNA的数据使用NMF也是很常见的 ([Shao and Höfer 2017](https://pubmed.ncbi.nlm.nih.gov/27663498/); [Kotliar et al. 2019](https://pubmed.ncbi.nlm.nih.gov/31282856/)) ，因此数据正好符合要求（虽然稀疏矩阵存在大量的0，但没有负数，并且即使采用log转换，还是会使用`log(x+1)` 的方法）

> 比如有一个LIGER算法，就首先使用综合非负矩阵分解（iNMF）来学习低维空间，获得一组特异因子，之后根据因子的maximum factor loading构建邻域图，从而达到降维目的

使用起来也不麻烦，scater包中有一个`runNMF`的函数（也是基于`NNLM` 包）。它的计算结果存储在`reducedDims()`的`NMF`模块中，之后就可以直接应用于分群。 相比于PCA的优点是：可以通过找到基因相关的小矩阵（W）每个因子中表达量高的基因，然后通过这些基因推测相应的细胞，再看细胞相关的小矩阵（H）中相应的高表达细胞，从而将细胞与细胞类型对应起来

```r
# 运行很简单
set.seed(101001)
nmf.zeisel <- runNMF(sce.zeisel, ncomponents=10, subset_row=top.zeisel)

# 提取结果
nmf.out <- reducedDim(nmf.zeisel, "NMF")
nmf.basis <- attr(nmf.out, "basis")
colnames(nmf.out) <- colnames(nmf.basis) <- 1:10

# 画每个细胞与因子的关系
per.cell <- pheatmap::pheatmap(nmf.out, silent=TRUE, 
    main="By cell", show_rownames=FALSE,
    color=rev(viridis::magma(100)), cluster_cols=FALSE) 

# 画每个基因与因子的关系
per.gene <- pheatmap::pheatmap(nmf.basis, silent=TRUE, 
    main="By gene", cluster_cols=FALSE, show_rownames=FALSE,
    color=rev(viridis::magma(100)))

# 组合
gridExtra::grid.arrange(per.cell[[4]], per.gene[[4]], ncol=2)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-06-30-035316.png)

横坐标是定义好的10个因子，首先检查每个因子对应的高表达基因，看看其中有没有认识的marker基因。基于背景知识，Mog是少突细胞的marker基因，Gad1是中间神经元的marker基因。如果我们看到某个因子中Mog高表达（右图），那么这个因子就可能表示少突细胞。再看左图的细胞与因子的关系，找到对应的因子中高表达的细胞，那么这些细胞可能就是少突细胞。

因此，NMF在细胞类型识别中应该非常广。不过常规的流程还是基于PCA（入门选手必备），NMF可以用在更复杂的下游分析中（中高阶玩家选配）

## 5 降维后的可视化

> 一般的可视化算法都会基于10-50个PCs

### **5.1 PCA结果可视化**

```r
plotReducedDim(sce.zeisel, dimred="PCA", colour_by="level1class")
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-06-30-044804.png)

因为**PCA是线性降维模型**，每个PC捕获的是高维空间中线性的差异，不**能够在前两个PC中压缩全部的维度**。上图就能看到，前两个PC对亚群的显示效果不太理想，很多都没有分开。如果说第一个PC表示各个亚群之间最大的差异，PC2就是第二大差异，但是剩下的差异呢？没有显现！

虽然也可以在一张图画多个PC，但还是不好解释；另外即使用了这个方法，也不能将一些亚群分开

```r
# 在一张图画多个PC
plotReducedDim(sce.zeisel, dimred="PCA", ncomponents=4,
    colour_by="level1class")
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-06-30-050831.png)

PCA的优点就是：可预测、数据结构不引入人为因素、对输入数据的微小差异比较灵敏

### **5.2 t-SNE**

t-SNE是由SNE(Stochastic Neighbor Embedding, SNE; [Hinton and Roweis, 2002](https://www.cs.toronto.edu/~fritz/absps/sne.pdf))发展而来。于2008年提出，全称是： t-stochastic neighbor embedding，可以说是现在scRNA分析的标准可视化方法。与PCA不同，它**不局限于线性变换**，也不需要精确地表示各个细胞群之间的距离。能在高维空间中直接捕获细胞非线性关系，**它的分群更灵活**，可以在更复杂细胞群体中区分亚群

（因此**tSNE的点不表示数据的远近**，相距较近的点也不意味着细胞相似）

```r
set.seed(00101001101)

# runTSNE()的结果也是存储在reducedDims()中
sce.zeisel <- runTSNE(sce.zeisel, dimred="PCA")
plotReducedDim(sce.zeisel, dimred="TSNE", colour_by="level1class")
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-06-30-051928.png)

不过，**tSNE的最大缺点是**：计算量很大。不过可以在`runtTSNE()`中使用参数`dimred="PCA"` 来缓解一下；

**缺点二**：一般需要运行多次才能得到想要的结果，并且如果想要重复之前的结果，需要设置随机种子，因为tsne每次对细胞映射的坐标都不同（可能第一次运行这一团细胞在左下角，下一次又跑到右上角去了）

**缺点三：** 参数`perplexity`的使用。perplexity的含义是：the number of close neighbors each point has。 这个参数一般会多调整几次

**关于tsne这个流行的算法，有必要了解一下：**

* **tsne的作者Laurens强调**，可以通过`t-SNE`的可视化图提出一些假设，但是不要用`t-SNE`来得出一些结论，想要验证你的想法，最好用一些其他的办法。
* t-SNE中集群之间的**距离并不表示相似度** ，同一个数据上运行`t-SNE`算法多次，很有可能得到多个不同“形态”的集群。但话说回来，真正有差异的群体之间，不管怎么变换形态，它们还是有差别
* 关于perplexity的使用：(默认值是30) 如果忽视了perplexity带来的影响，有的时候遇到`t-SNE`可视化效果不好时，对于问题无从下手。**perplexity表示了近邻的数量**，例如设perplexity为2，那么就很有可能得到很多两个一对的小集群，也就意味着：perplexity如果很小，那么分辨率更高。

  ![https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-09-035102.png](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-09-035102.png)
* 有的时候会出现同一集群被分为两半的情况，但群间的距离并不能说明什么，解决这个问题，只需要跑多次找出效果最好的就可以了

> 引用自： <https://bindog.github.io/blog/2018/07/31/t-sne-tips/> \
> 很好的tsne可视化：<https://distill.pub/2016/misread-tsne/c>

下面看看不同`perplexity`参数的影响：

```r
set.seed(100)
sce.zeisel <- runTSNE(sce.zeisel, dimred="PCA", perplexity=5)
out5 <- plotReducedDim(sce.zeisel, dimred="TSNE",
    colour_by="level1class") + ggtitle("perplexity = 5")

set.seed(100)
sce.zeisel <- runTSNE(sce.zeisel, dimred="PCA", perplexity=20)
out20 <- plotReducedDim(sce.zeisel, dimred="TSNE",
    colour_by="level1class") + ggtitle("perplexity = 20")

set.seed(100)
sce.zeisel <- runTSNE(sce.zeisel, dimred="PCA", perplexity=80)
out80 <- plotReducedDim(sce.zeisel, dimred="TSNE", 
    colour_by="level1class") + ggtitle("perplexity = 80")

multiplot(out5, out20, out80, cols=3)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-06-30-053043.png)

看到，t-SNE的结果看上去更像是一幅“地图”，但正是因为这样更**容易引起误导**，让我们根据clusters的大小和位置产生一些联想。事实上，**上面的点不能说明什么问题**，t -SNE的算法会让原本稠密的点变膨胀，让原本稀疏的点变紧凑，**不可以用cluster的大小来衡量亚群的异质性。**&#x5B83;并没有义务帮我们保留cluster的相对位置，因此我们**不能根据点的位置远近来确定cluster之间的相似程度。**

### 5.3 UMAP

2018年提出[McInnes et al., (2018)](https://joss.theoj.org/papers/10.21105/joss.00861)，全称是：Uniform manifold approximation and projection，与t-SNE一样，也是一种非线性方法，都是在高维空间中寻找保持相邻关系的低维表示方法。不过它们的基础理论不同，因此图形展示也不同

```r
set.seed(1100101001)
sce.zeisel <- runUMAP(sce.zeisel, dimred="PCA")
plotReducedDim(sce.zeisel, dimred="UMAP", colour_by="level1class")
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-06-30-054144.png)

可以看到，**UMAP相比t-SNE，各个cluster更加紧凑，而且cluster之间的距离空间也更大**。UMAP比t-SNE保留了**更多的空间结构**。**运算速度也更快**，因此对于大型数据是个福音（尽管如此，还是推荐使用前几个PC进行UMAP）。另外它也需要指定随机种子。

**劣势一：**

UMAP也有自己的**复杂参数设定**，例如近邻的数量`n_neighbors` 和 点之间最短距离`min_dist` 都会结果展示都比较大的影响。如果这些值太小，随机噪音就会显现出来；如果设置太大，就会丢弃一些原本不错的数据结构。所以**建议也是：多试几组参数的设置**

**劣势二：**

UMAP旨在保留更多的全局结构，但这必然会降低每个cluster中的分辨率。

### **5.4 最后，作者对于可视化的建议**

Do not let the tail (of visualization) wag the dog (of quantitative analysis)


# 3.5 聚类

刘小泽写于2020.6.30 + 7.2

## 1 前言

聚类是一种无监督的学习过程，在scRNA分析中，根据表达谱相似性对不同细胞进行分组。

在后面根据marker基因对各个cluster进行注释后，cluster就可以代表不同的细胞类型或状态。因此，聚类是将枯燥的数据与感兴趣生物学意义联系起来的一个桥梁。

不过这里需要明确一下，聚类分的群和细胞类型还不是一回事

* 分群是根据经验计算的结果
* 细胞类型有真正的生物学意义

至于这种问题：“我应该设置多少cluster数量？”，则是个伪命题。因为我们可以根据需要，使用不同方法，得到不同数量的clusters，每个cluster都是高维空间数据的一部分。

不过倒是可以问：“我们的clusters与细胞类型之间的拟合程度如何？”。不过这个问题很难回答，因为取决于自己的分析目标。有的人对于能得到主要的细胞类型就很满意，而有的人还想再进一步，得到细胞亚群；还有的，更想基于细胞亚群，达到不同细胞状态的分辨率（如代谢活性、胁迫反应等）。另外，两种截然不同的分群结果，可能都具有生物学意义，只是关注点不同而已。

对于分群聚类的操作，更像是显微镜的操作，可以通过调整参数（使用不同的镜头组合）来获得不同的分辨率；另外还可以探索多种聚类方法，看到数据的不同面。

### **数据准备之PBMC**

具体的操作在上一章也提到过

```r
# 下载
library(BiocFileCache)
bfc <- BiocFileCache("raw_data", ask = FALSE)
raw.path <- bfcrpath(bfc, file.path("http://cf.10xgenomics.com/samples",
    "cell-exp/2.1.0/pbmc4k/pbmc4k_raw_gene_bc_matrices.tar.gz"))
untar(raw.path, exdir=file.path(tempdir(), "pbmc4k"))

# 读取
suppressMessages(library(DropletUtils))
fname <- file.path(tempdir(), "pbmc4k/raw_gene_bc_matrices/GRCh38")
sce.pbmc <- read10xCounts(fname, col.names=TRUE)
> dim(sce.pbmc)
[1]  33694 737280

# 基因注释之ID整合
library(scater)
rownames(sce.pbmc) <- uniquifyFeatureNames(
    rowData(sce.pbmc)$ID, rowData(sce.pbmc)$Symbol)
# 基因注释之添加位置信息
library(EnsDb.Hsapiens.v86)
location <- mapIds(EnsDb.Hsapiens.v86, keys=rowData(sce.pbmc)$ID, 
    column="SEQNAME", keytype="GENEID")

# 空液滴检测
set.seed(100)
e.out <- emptyDrops(counts(sce.pbmc))
sce.pbmc <- sce.pbmc[,which(e.out$FDR <= 0.001)]

# 质控
stats <- perCellQCMetrics(sce.pbmc, subsets=list(Mito=which(location=="MT")))
high.mito <- isOutlier(stats$subsets_Mito_percent, type="higher")
sce.pbmc <- sce.pbmc[,!high.mito]

# 归一化（去卷积）
library(scran)
set.seed(1000)
clusters <- quickCluster(sce.pbmc)
sce.pbmc <- computeSumFactors(sce.pbmc, cluster=clusters)
sce.pbmc <- logNormCounts(sce.pbmc)

# 利用数据分布来表示技术噪音，并挑选HVGs
set.seed(1001)
dec.pbmc <- modelGeneVarByPoisson(sce.pbmc)
top.pbmc <- getTopHVGs(dec.pbmc, prop=0.1)

# 降维（三种方法）
set.seed(10000)
sce.pbmc <- denoisePCA(sce.pbmc, subset.row=top.pbmc, technical=dec.pbmc)

set.seed(100000)
sce.pbmc <- runTSNE(sce.pbmc, dimred="PCA")

set.seed(1000000)
sce.pbmc <- runUMAP(sce.pbmc, dimred="PCA")

# 看下结果
sce.pbmc
## class: SingleCellExperiment 
## dim: 33694 3922 
## metadata(1): Samples
## assays(2): counts logcounts
## rownames(33694): RP11-34P13.3 FAM138A ... AC213203.1 FAM231B
## rowData names(2): ID Symbol
## colnames(3922): AAACCTGAGAAGGCCT-1 AAACCTGAGACAGACC-1 ...
##   TTTGTCACAGGTCCAC-1 TTTGTCATCCCAAGAT-1
## colData names(3): Sample Barcode sizeFactor
## reducedDimNames(3): PCA TSNE UMAP
## altExpNames(0):
```

**补充知识点之聚类与分类**

* 分类：类别是已知的，通过对已知分类的数据进行训练和学习，找到这些不同类的特征，再对未分类的数据进行分类。属于有监督学习。
* 聚类：事先不知道数据会分为几类，通过聚类分析将数据聚合成几个群体。聚类不需要对数据进行训练和学习。**属于无监督学习**。

## 2 基于图形的聚类 |  Graph-based clustering

### **2.1 是什么？**

这种聚类方法在**Seurat中使用最流行**。最基础的想法是：我们首先构建一个图，其中每个节点都是一个细胞，它与高维空间中最邻近的细胞相连。连线基于细胞之间的相似性计算权重，权重越高，表示细胞间关系更密切。

如果一群细胞之间的权重高于另一群细胞，那么这一群细胞就被当做一个群体 “communiity”

这个方法**最大的优点就是：可缩放性**。只需要根据 k-nearest neighbor（KNN）进行搜索，自己去寻找去拓展。相比于k-means、 Gaussian mixture models等方法，不需要预先对cluster形状以及cluster中细胞分布做一个估计。

这种方法**使得每个细胞都被强制连接到一定数量的相邻细胞**，这减少了仅由一两个离群细胞组成的无意义群体的风险

不过这个方法也有缺点：

**缺点一：**

它最后只保留了邻近细胞之间的联系，除此以外没有保存。

**缺点二：**

另外正是由于它主要关注相邻的权重，导致一些噪音也可能聚集在一起，形成一个community，从而影响聚类的分辨率，可能导致数据异质性的过度解读

### **2.2 怎么做？**

在使用时，需要考虑几个问题：

* 构建这个图需要设置几个邻居？
* 用什么方法确定边的权重？
* 用什么检测community的方法来定义cluster？

下面的例子中，就定义了使用一个细胞与10个邻居的关系，进而构建shared nearest neighbor graph（SNNG），如果两个细胞的邻居中有一个是共享的，则通过一条边连接，之后再根据highest average rank of the shared neighbors定义边的权重([Xu and Su 2015](https://pubmed.ncbi.nlm.nih.gov/25805722/))

**首先使用scran包的`buildSNNGraph()`函数，并且使用PCA的前几个PCs；之后利用 igraph包中的Walktrap方法去鉴定clusters**

```r
library(scran)
g <- buildSNNGraph(sce.pbmc, k=10, use.dimred = 'PCA')
clust <- igraph::cluster_walktrap(g)$membership
table(clust)
## clust
##   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18 
## 585 518 364 458 170 791 295 107  45  46 152  84  40  60 142  16  28  21
```

之后可以把cluster信息放回到`SingleCellExperiment`对象中，保存成一个因子

```r
library(scater)
colLabels(sce.pbmc) <- factor(clust)
plotReducedDim(sce.pbmc, "TSNE", colour_by="label")
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-06-30-065754.png)

从上面的计算方法可以看出，有一个很重要的参数是`k` ，含义是：the number of nearest neighbors used to construct the graph。如果k设置越大，得到的图之间联通程度越高，cluster也越大。因此这个参数也是可以不断尝试的

```r
# 比如设置较大的k，就会得到比k=10更少的cluster，但同时每个cluster平均的细胞数更多
g.50 <- buildSNNGraph(sce.pbmc, k=50, use.dimred = 'PCA')
clust.50 <- igraph::cluster_walktrap(g.50)$membership
table(clust.50)
## clust.50
##   1   2   3   4   5   6   7   8 
## 307 729 789 187 516 524 825  45

# 如果设置更小的k，会得到数量更多的cluster
g.5 <- buildSNNGraph(sce.pbmc, k=5, use.dimred = 'PCA')
clust.5 <- igraph::cluster_walktrap(g.5)$membership
table(clust.5)
## clust.5
##   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20 
##  81  45 457 296 168 350  79 432 428 897  64 171  68 135  79  26  18  30  21  16 
##  21  22  23 
##  36   9  16
```

这个结果可以利用`force-directed`的布局出图，它与t-SNE、UMAP的降维结果都是紧密相关的

```r
set.seed(2000)
reducedDim(sce.pbmc, "force") <- igraph::layout_with_fr(g)
plotReducedDim(sce.pbmc, colour_by="label", dimred="force")
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-06-30-070536.png)

### **2.3 其他的参数**

**关于如何计算权重**，在`buildSNNGraph()`函数中可以设置：

* `type="number"` ：根据近邻数量计算
* `type="jaccard"` ：根据Jaccard index of the two sets of neighbors计算

```r
g.num <- buildSNNGraph(sce.pbmc, use.dimred="PCA", type="number")
g.jaccard <- buildSNNGraph(sce.pbmc, use.dimred="PCA", type="jaccard")
```

上面得到的各种结果，都是来自igraph包的graph对象，因此可以用于igraph包的**各种community检测方法：**

```r
# 之前使用的是Walktrap聚类方法，还有这么多种
clust.louvain <- igraph::cluster_louvain(g)$membership
clust.infomap <- igraph::cluster_infomap(g)$membership
clust.fast <- igraph::cluster_fast_greedy(g)$membership
clust.labprop <- igraph::cluster_label_prop(g)$membership
clust.eigen <- igraph::cluster_leading_eigen(g)$membership
```

最后就可以**比较两种不同的聚类方法差异**

```r
library(pheatmap)

# 比较Infomap 和 Walktrap
# Using a large pseudo-count for a smoother color transition
# between 0 and 1 cell in each 'tab'.
tab <- table(paste("Infomap", clust.infomap), 
    paste("Walktrap", clust))
ivw <- pheatmap(log10(tab+10), main="Infomap vs Walktrap",
    color=viridis::viridis(100), silent=TRUE)

# Fast-greedy 和 Walktrap
tab <- table(paste("Fast", clust.fast), 
    paste("Walktrap", clust))
fvw <- pheatmap(log10(tab+10), main="Fast-greedy vs Walktrap",
    color=viridis::viridis(100), silent=TRUE)

gridExtra::grid.arrange(ivw[[4]], fvw[[4]])
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-06-30-071422.png)

可以看出，Infomap比Walktrap得到了更多的cluster（比较精细），而fast-greedy结果相对较少（比较粗糙）

**可以通过`cut_at()`指定分群的数量**

```r
# 指定5群
community.walktrap <- igraph::cluster_walktrap(g)
table(igraph::cut_at(community.walktrap, n=5))
## 
##    1    2    3    4    5 
## 3612  198   45   46   21

# 指定20群
table(igraph::cut_at(community.walktrap, n=20))
## 
##   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20 
## 533 364 458 442 170 791 295 107  45  46 152  84  40  60 142  76  52  16  28  21
```

scran默认使用rank-based的权重计算方法，Seurat使用Jaccard-based的方法（后面辅助Louvain聚类）

### **2.4 分群的评价**

当使用graph-based方法时，模块化（modularity）是一个评价cluster的指标。**modularity值越大，表示cluster内部的细胞与细胞之间连线（edge）数最多，内部分散效果越好**，从而避免了不同cluster之间邻近的细胞形成连线。

之前通过`cluster_walktrap`的方法得到了18个cluster，下面就看看这个方法做的如何：会用到一个函数`clusterModularity()` ，并且最好设置一个参数`as.ratio=TRUE` ，表示两两cluster之间比较，观察到的权重与预期权重之比，这个比值（ratio）越大，表示正在比较的两个cluster之间的细胞间连线数越多，联系越密切。

**下面看一个基于ratio的热图**

```r
# g是利用buildSNNGraph得到的，clust是基于g利用igraph::cluster_walktrap得到的
ratio <- clusterModularity(g, clust, as.ratio=TRUE)
dim(ratio)
## [1] 18 18
# 这是一个矩阵，每一行/列都表示一个cluster，中间的每个ratio值都表示观察到的权重与预期权重之比（可以衡量实际中细胞间连线的数量）。然后我们把ratio画出来看看
library(pheatmap)
pheatmap(log2(ratio+1), cluster_rows=FALSE, cluster_cols=FALSE,
    color=colorRampPalette(c("white", "blue"))(100))
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-01-151552.png)

从图中可以看到，深色区域（ratio较大的值）**基本都集中在对角线**，也就是同一个cluter之中。因此，**同一个cluster中会存在最多的细胞间连线数量，而不是与其他的cluster**，这不也正是设置cluster的目的么？一个cluster中的细胞的相关性就是应该高于其他cluster中的细胞

对角线说明了大部分的cluster划分合理，当然**也不排除有一些对角线以外的第二高ratio值**，表示那个cluster与其他的cluster之间也有细胞间连线。

**下面看一个基于ratio的点线图**

就像这种，和之前使用的graph不同，之前的点（node）是细胞，这里的node是cluster，看像不像分化轨迹做的”拟时序分析“？其实就是把细胞聚类后，再聚一次，找到它们的前后相互关系

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-01-152620.png)

这个图是这么做的：

```r
cluster.gr <- igraph::graph_from_adjacency_matrix(log2(ratio+1), 
    mode="upper", weighted=TRUE, diag=FALSE)
set.seed(11001010)
# 权重（weight）越大，线越明显
plot(cluster.gr, edge.width=igraph::E(cluster.gr)$weight*5,
    layout=igraph::layout_with_lgl)
```

## 3 k-means聚类

### **3.1 是什么**

顾名思义，就是通过**不断迭代找出k 个中心(centroid)，然后将各个细胞分配至最近的中心点**。看似晦涩，来看看[k-means 聚类解读](https://blog.csdn.net/huangfei711/article/details/78480078)

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-02-042204.png)

这个方法算法简单、易于实现，具有速度优势。但是存在几个严重的缺点，可能会导致分配的cluster不清不楚：

* k值需要提前定义。假如真实有10种细胞类型，但我现在只定义k=9，那么有两个细胞类型就会硬生生地”合二为一“。但其他方法，例如上面的基于图形的，即使分辨率设置的很低（意思就是看的很模糊），也是能看出这两种不是同一种类型
* 根据它的计算方法，需要不断重复多运行几次，保证得到的cluster结果是稳定的
* 既然k-means是找k个中心，那么有中心就会有区域半径，就会限定数据的范围。但实际上，很多分组并没有常规的数据大小和结构，也就是说，一组数据不会这么”乖乖地“落在我们画好的范围中

看了上面的优缺点后，k-means依然成为了最好用的样本数据压缩方法之一。另外它可以不受细胞密度的影响，保证了即使是数量最多的细胞类型也不会主导下游分析

### **3.2 怎么做？**

基础包中就有函数`kmeans()` ，我们这里基于PCA的结果，指定目标中心数为10，另外也是需要设置随机种子

```r
set.seed(100)
clust.kmeans <- kmeans(reducedDim(sce.pbmc, "PCA"), centers=10)
table(clust.kmeans$cluster)
## 
##   1   2   3   4   5   6   7   8   9  10 
## 472 200  46 515  90 320 241 865 735 438

# 作图
colLabels(sce.pbmc) <- factor(clust.kmeans$cluster)
plotReducedDim(sce.pbmc, "TSNE", colour_by="label")
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-01-155839.png)

当然，可以在一开始就设置k大一点（多找一些”核心人物“），避免发生细胞亚群重叠的情况。

```r
set.seed(100)
clust.kmeans2 <- kmeans(reducedDim(sce.pbmc, "PCA"), centers=20)
table(clust.kmeans2$cluster)
## 
##   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20 
## 153 172  47 254 125 207 160 334 204 442 163  68 192 271 113 168 124 420  45 260

# 作图 
colLabels(sce.pbmc) <- factor(clust.kmeans2$cluster)
plotTSNE(sce.pbmc, colour_by="label", text_by="label")
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-01-160738.png)

### **3.3 分群的评价**

之前了解了k-means的计算方法（计算每个小弟与各个中心大佬的距离，离哪个近就跟哪个），那么就可以用within-cluster sum of squares (**WCSS**) 方法对每个亚群进行评价。顾名思义，这个方法就是寻找最近的距离。

另外，基于WCSS又可以计算root-mean-squared deviation (**RMSD**)，反映的是偏离平均位置的程度。当然，它的计算也很简单，就是用每个亚群的WCSS值除以该亚群的细胞数量然后再开方。

**RMSD值低的好。如果RMSD值低，就表示和其他亚群的混杂程度更小，更像是一个纯粹的亚群**（例如rms值较小的8、10群，它们当中混杂的其他亚群数量很少）。反观rms值大的群，如16、19群，都混杂了其他亚群的细胞（尤其是19群，乍一看上去这个群颜色很单一，但放大看，就会发现其中除了蓝色还有灰色）

```r
ncells <- tabulate(clust.kmeans2$cluster)
tab <- data.frame(wcss=clust.kmeans2$withinss, ncells=ncells)
tab$rms <- sqrt(tab$wcss/tab$ncells)
tab
##         wcss ncells      rms
## 1   2872.081    153 4.332641
## 2   4204.124    172 4.943944
## 3   1443.475     47 5.541862
## 4   4275.279    254 4.102659
## 5   1711.482    125 3.700251
## 6   3054.815    207 3.841557
## 7   1632.526    160 3.194259
## 8   2159.987    334 2.543035
## 9   7026.528    204 5.868881
## 10  2858.314    442 2.542985
## 11  4259.492    163 5.111932
## 12  2288.852     68 5.801688
## 13  2111.912    192 3.316555
## 14  6391.151    271 4.856293
## 15  1603.372    113 3.766847
## 16 12399.822    168 8.591185
## 17  1823.082    124 3.834354
## 18  7026.462    420 4.090192
## 19  2590.561     45 7.587359
## 20  3639.745    260 3.741526
```

再结合上一张t-SNE的图，看到rms指标与t-SNE图上反映的cluster大小基本没有相关性（例如第3群比第12群细胞数量少，同样第12群rms大；第3群比第1群细胞数量少，却是第3群rms大）。这里也侧面反映了之前介绍t-SNE时说的：**t-SNE就是一个可视化的工具，不要试图量化t-SNE结果上的cluster**。**上面的点不能说明什么问题**，t -SNE的算法会让稠密的点变膨胀，让原本稀疏的点变紧凑，**不可以用cluster的大小来衡量亚群的异质性。**&#x53E6;外它并没有义务帮我们保留cluster的相对位置，因此我们**不能根据点的位置远近来确定cluster之间的相似程度，每运行一次点的位置都是变动的。**

> 那么可能会想，如果不通过t-SNE的结果来判断cluster之间的关系，那怎么看呢？

可以通过层次聚类

```r
cent.tree <- hclust(dist(clust.kmeans2$centers), "ward.D2")
plot(cent.tree)
```

同样看到，19群格格不入，与之前t-SNE的可视化结果，以及rms结果都相吻合

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-02-040843.png)

### **3.4 k-means的更高级用法**

之前提到过，k-means可以将邻近的细胞靠一个”中心大佬“来体现，用一个中心(centroid)来表示其余多个点，达到数据压缩的目的。因此，它可以用到其他更复杂的聚类方法中，作为铺垫先压缩一下数据。

例如scran包中有一个函数`clusterSNNGraph()` ，就是整合了k-means和graph-based

* 先利用k-means得到一些关键的中心点（centroid）
* 之后将中心点进行graph-based聚类
* 结果再把每个中心点（centroid）周围的”小弟们“接过来，放在中心点周围

```r
set.seed(0101010)
# 参数kmeans.centers是给第一步k-means用的；参数k是给第二步graph-based聚类用的
kgraph.clusters <- clusterSNNGraph(sce.pbmc, use.dimred="PCA", 
    use.kmeans=TRUE, kmeans.centers=1000, k=5)
table(kgraph.clusters)
## kgraph.clusters
##   1   2   3   4   5   6   7   8   9  10  11 
## 840 137 550 517 220 528 829  46 127  83  45

plotTSNE(sce.pbmc, colour_by=I(kgraph.clusters))
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-02-060743.png)

这种方法相比于单纯的graph-based聚类，速度大大提升。（回顾一下之前说的graph-based方法）因为不用去为每个细胞找邻居，从而构建一个图。另外之前说graph-based这种方法使得每个细胞都被强制连接到一定数量的相邻细胞，但k-means的”核心大佬“加入减轻了这个问题。

注意到，上面的代码使用了一个参数`kmeans.centers`， 指的是k-means获得cluster的数量，它的设置取决于是要处理速度还是要数据还原度。设置越大越能表示细胞真实分布，但也为第二步聚类造成了计算量激增。

## 4 层次聚类

### **4.1 是什么**

这是一种历史悠久的聚类方法，最后会给出样本的聚类树（dendrogram）。思路是这样的：**贪婪地将样本聚集成簇，然后将这些簇聚集成更大的簇，依此类推，直到所有样本都属于一个簇为止**。

**层次聚类包含的算法也有很多种，差别在于如何层层聚集**。例如complete linkage方法（`hclust`的默认方法）是保证合并簇之间样本距离最小， Ward’s方法是保证一个簇内部在聚合的同时方差增加的最少，也就是将每次合并时的信息损失最小化。

在实际使用中，层次聚类运行很慢，因此**只适用于小型scRNA数据**。因为需要计算一个细胞间的距离矩阵，如果动辄上千细胞，那么这个计算量会很大。

### **4.2 怎么做**

之前用的PBMC数据集中细胞数太多，于是切换到sce.416b这个数据集，毕竟一二百个细胞还是可以算的

```r
## class: SingleCellExperiment 
## dim: 46604 185 
## metadata(0):
## assays(3): counts logcounts corrected
## rownames(46604): 4933401J01Rik Gm26206 ... CAAA01147332.1
##   CBFB-MYH11-mcherry
## rowData names(4): Length ENSEMBL SYMBOL SEQNAME
## colnames(185): SLX-9555.N701_S502.C89V9ANXX.s_1.r_1
##   SLX-9555.N701_S503.C89V9ANXX.s_1.r_1 ...
##   SLX-11312.N712_S507.H5H5YBBXX.s_8.r_1
##   SLX-11312.N712_S517.H5H5YBBXX.s_8.r_1
## colData names(10): Source Name cell line ... block sizeFactor
## reducedDimNames(2): PCA TSNE
## altExpNames(2): ERCC SIRV
```

首先利用前几个PCs计算细胞间的距离

```r
dist.416b <- dist(reducedDim(sce.416b, "PCA"))
```

然后使用Ward‘s方法进行聚类

```r
tree.416b <- hclust(dist.416b, "ward.D2")
```

准备画复杂一点的聚类树

```r
library(dendextend)
# seq_along根据tree.416b$labels，也就是185个细胞的ID，产生了1-185个数字，结果就是把字符串变数字
tree.416b$labels <- seq_along(tree.416b$labels)
dend <- as.dendrogram(tree.416b, hang=0.1)
# 合并两种批次信息
combined.fac <- paste0(sce.416b$block, ".", 
    sub(" .*", "", sce.416b$phenotype))
> table(combined.fac)
combined.fac
20160113.induced    20160113.wild 20160325.induced    20160325.wild 
              46               47               47               45

# 首先按照不同处理设置蓝色和红色，然后按照批次设置淡蓝色和淡红色
labels_colors(dend) <- c(
    `20160113.wild`="blue",
    `20160113.induced`="red",
    `20160325.wild`="dodgerblue",
    `20160325.induced`="salmon"
)[combined.fac][order.dendrogram(dend)]

plot(dend)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-02-063949.png)

图中可以看到，处理前后确实形成了红、蓝分界

另外更推荐使用Ward’s方法，它在合并时受到的cluster差异影响最小（毕竟人家的出发点就是每次合并时的信息损失最小化，所以如果差异很大，那我就不合并呗）。

**为了得到更清楚地分群结果，可以对树进行”修建“**

* 最简单的是`cutree()` ，例如`cutree(hc, 4)` 手动切分数量
* 复杂一点但更好用的是`cutreeDynamic()`，来自`dynamicTreeCut` 这个包

```r
library(dynamicTreeCut)
clust.416b <- cutreeDynamic(tree.416b, distM=as.matrix(dist.416b),
    minClusterSize=10, deepSplit=1)
table(clust.416b)
## clust.416b
##  1  2  3  4 
## 78 69 24 14

labels_colors(dend) <- clust.416b[order.dendrogram(dend)]
plot(dend)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-02-070851.png)

一般这个结果与t-SNE的结果是吻合的

```r
colLabels(sce.416b) <- factor(clust.416b)
plotReducedDim(sce.416b, "TSNE", colour_by="label")
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-02-071938.png)

不过这里注意到，t-SNE中cluster2拆分开了，值得怀疑：是真的聚类出了问题，还是t-SNE本身的显示问题，毕竟我们知道t-SNE有时真的会把一个亚群拆开画。于是继续下面👇的探索评价

### **4.3 评价**

可以借助”轮廓图“（silhouette width）检查分群的质量。会对每个细胞都计算一个silhouette width值，如果一个细胞的**width值为正并且越大**，表示相对于其他亚群的细胞，这个细胞和它所在亚群中的细胞更接近，**分群效果越好**；如果width为负，就表示这个亚群的这个细胞和其他亚群的细胞更接近，即分群效果不太理想。

```r
library(cluster)
sil <- silhouette(clust.416b, dist = dist.416b)
> colnames(sil)
[1] "cluster"   "neighbor"  "sil_width"

# 设置每个cluster的颜色
clust.col <- scater:::.get_palette("tableau10medium") # hidden scater colours
# 这一句的意思是：如果sil_width>0，就属于和自己接近，否则属于和其他亚群接近
sil.cols <- clust.col[ifelse(sil[,3] > 0, sil[,1], sil[,2])]
sil.cols <- sil.cols[order(-sil[,1], sil[,3])]

plot(sil, main = paste(length(unique(clust.416b)), "clusters"), 
     border=sil.cols, col=sil.cols, do.col.sort=FALSE)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-02-092004.png)

图中可以反映的问题：

* 这个图是对上面得到的各个cluster中的细胞做的barplot，每个cluster是一种颜色。
* 这里看到cluster2都是正值并且横坐标width数值还是最大的，因此说明了分群没有问题，之前t-SNE的结果的确是它本身的问题
* 如果发现width值较小，表示分群结果一般，还有可能是分群多度，本来属于一个群的，又被拆分成小群
* 利用这个图，我们就能调整之前的参数，来调整分群效果，不过也不需要太过纠结完美的分群结果。因为即使图上看似合理的分群，可能实际上也不会得到更多的生物信息

## 5 评价分群的稳定性

### **什么叫做分群的稳定性？**

就是分群之前操作的小小波动，也不会影响到最后的分群结果；高稳定性的分群结果其实也方便了别人进行重复。scran利用自助法（bootstrapping）进行评价

> 在统计学中，自助法（Bootstrap Method，Bootstrapping，或自助抽样法）是一种从**给定训练集中有放回的均匀抽样**，也就是说，**每当选中一个样本，它等可能地被再次选中并被再次添加到训练集中**。 自助法由Bradley Efron于1979年在《Annals of Statistics》上发表。

细胞有放回的抽样，得到一个”自助重复“数据集，然后用他进行聚类，看看是否能得到相同的亚群

```r
# 例如使用graph-based聚类
myClusterFUN <- function(x) {
    g <- buildSNNGraph(x, use.dimred="PCA", type="jaccard")
    igraph::cluster_louvain(g)$membership
}

originals <- myClusterFUN(sce.pbmc)

# 进行自助法判断
set.seed(0010010100)
coassign <- bootstrapCluster(sce.pbmc, FUN=myClusterFUN, clusters=originals)
dim(coassign)
## [1] 19 19
```

结果返回一个coassignment的矩阵，表示随机从clusterX与clusterY中挑一个细胞，这两个细胞在经历”自助法“判断后，依然落在同一个cluster的概率。我们希望落在对角线上的概率更高（因为对角线表示自己和自己比较，说明多次重复后细胞还是属于这个cluster）

```r
# 借助热图判断
pheatmap(coassign, cluster_row=FALSE, cluster_col=FALSE,
    color=rev(viridis::magma(100)))
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-02-082643.png)

**需要注意的是**

* 这个自助法（Bootstrapping）是一个通用的评价cluster稳定性的方法，适用于各种聚类算法
* 高稳定性的亚群不一定是高质量的，因为如果一个亚群质量一直很差，它也很稳定

## 6 数据集取小再聚类 | Subclustering

首先通过常规操作得到分群结果，找到某些感兴趣的亚群，然后在某一个cluster中再一次进行特征基因挑选、聚类操作，从而增加分析的精确度。

举个例子：

首先利用整个PBMC数据集，根据T细胞的marker基因筛出可能是T细胞的亚群，然后推测其中某个亚群可能是记忆T细胞

```r
# 看到buildSNNGraph就知道是graph-based 画图，然后用igraph去鉴定
g.full <- buildSNNGraph(sce.pbmc, use.dimred = 'PCA')
clust.full <- igraph::cluster_walktrap(g.full)$membership

# 画几个marker基因的表达量，使用的是log-normalized表达量
plotExpression(sce.pbmc, features=c("CD3E", "CCR7", "CD69", "CD44"),
    x=I(factor(clust.full)), colour_by=I(factor(clust.full)))
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-02-084532.png)

**现在推测cluster6可能是记忆T细胞**

得到亚群cluster6的CD4+和CD8+ 的亚亚群

```r
memory <- 6 
sce.memory <- sce.pbmc[,clust.full==memory]
# 对它重新挑一遍特征基因，进行PCA
dec.memory <- modelGeneVar(sce.memory)
sce.memory <- denoisePCA(sce.memory, technical=dec.memory,
    subset.row=getTopHVGs(dec.memory, prop=0.1))

# 再走一遍聚类
g.memory <- buildSNNGraph(sce.memory, use.dimred="PCA")
clust.memory <- igraph::cluster_walktrap(g.memory)$membership

# 最后画图（最后就聚了2类，变成因子型放在x轴上），并且用CD8A和CD4进行验证
plotExpression(sce.memory, features=c("CD8A", "CD4"),
    x=I(factor(clust.memory)))
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-02-093404.png)

这种数据集取小再聚类的方法，可以**简化亚群的解读**。只需要在某一个亚群的背景下继续挖掘，就可能会获得重要信息，一层层递进。比如上面的思路：一堆细胞=》分18群=》鉴定T细胞的亚群=》取出某个T细胞亚群再去鉴定记忆T细胞


# 3.6 Marker/标记基因检测

刘小泽写于2020.7.3+7.5

## 1 前言

在上一章聚类分群的结尾，为了解释分群的结果，指定了几个基因进行区分，这几个基因就属于**marker基因或者叫标志基因**，它们是经过反复验证得到的。也就是说，一般看到相关的marker基因，就可以把某个cluster与某种细胞类型对应起来；另外这个思路还可以探索亚群之间发生的微小差异（例如通路激活、分化状态）与基因表达的联系

上面👆说的，主要还是验证，就是看看某个cluster到底是不是这个细胞类型，做这个的前提是你已经了解了你的细胞是什么类型，只是不确定。

对于常规流程来讲，更多的是探索，就是我们**得到了分群的结果，然后再怎么分析？怎么和marker基因联系起来？数万个基因，哪些才是这个cluster的marker基因？**

我们认为，导致cluster出现差异的那部分基因中，尤其是那些差异最显著的基因中，最可能包含marker基因。因此一个首要任务就是去进行cluster之间的差异分析，最后把每个cluster中最显著的前多少基因拿出来，放在一起（有没有想起来Seurat的那个热图？黑黄相间的那个）

### **下面还是使用10X PBMC数据**

这次就不从头开始处理了，如果想知道从数据读取到聚类之间的步骤，可以去看之前写的，里面有详细处理代码。这次直接加载聚类后的数据即可：`clustered.sce.pbmc.RData` 链接：<https://share.weiyun.com/4fOP3IDw> 密码：xswtct

```r
load('clustered.sce.pbmc.RData')
sce.pbmc
## class: SingleCellExperiment 
## dim: 33694 3922 
## metadata(1): Samples
## assays(2): counts logcounts
## rownames(33694): RP11-34P13.3 FAM138A ... AC213203.1 FAM231B
## rowData names(2): ID Symbol
## colnames(3922): AAACCTGAGAAGGCCT-1 AAACCTGAGACAGACC-1 ...
##   TTTGTCACAGGTCCAC-1 TTTGTCATCCCAAGAT-1
## colData names(4): Sample Barcode sizeFactor label
## reducedDimNames(3): PCA TSNE UMAP
## altExpNames(0):
```

## 2 检测方法

### **2.1 常规方法-t检验**

> `findMarkers()`提供了三种检验方法，分别是：t、wilcox、binom。默认使用t检验

针对大量细胞的检验，Welch t-test计算速度很快，并且有不错的统计学意义。使用`findMarkers()` 可以对每个基因在clusters之间进行两两比较，返回的列表中包括了每个cluster中排过序的候选marker基因

```r
library(scran)
markers.pbmc <- findMarkers(sce.pbmc)
markers.pbmc
## List of length 18
## names(18): 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
```

`findMarkers()` 默认根据`sce.pbmc`中的`colLabels()`信息提取各个cluster，使用的就是`groups`参数，因此也可以写成：

```r
same.markers <- findMarkers(sce.pbmc, groups=colLabels(sce.pbmc))
```

有了这个参数，就可以根据其他方法的分群结果再去探索不一样的marker基因，而不用修改原始的`sce.pbmc`结果，只要`groups`参数满足与`sce.pbmc`列数一致就可以

**怎么解释这个结果呢？--以cluster9为例**

```r
chosen <- "9"
interesting <- markers.pbmc[[chosen]]
colnames(interesting)
##  [1] "Top"           "p.value"       "FDR"           "summary.logFC"
##  [5] "logFC.1"       "logFC.2"       "logFC.3"       "logFC.4"      
##  [9] "logFC.5"       "logFC.6"       "logFC.7"       "logFC.8"      
## [13] "logFC.10"      "logFC.11"      "logFC.12"      "logFC.13"     
## [17] "logFC.14"      "logFC.15"      "logFC.16"      "logFC.17"     
## [21] "logFC.18"
```

可以看到其中有很多logFC值，表示`log2(cluster9/other_cluster)`

`findMarkers()` 默认会根据第一列Top进行排序，而其中第一列”Top“指的是：cluster9中差异表达的前多少位基因。**但Top1不代表只有1个基因，而是可能有很多并列第一**，但为了区分，还是用pvalue给各个并列第一又排了顺序

```r
# 比如这里看到的，并列第一就不止10个
interesting[1:10,1:4] 
## DataFrame with 10 rows and 4 columns
##                Top      p.value          FDR summary.logFC
##          <integer>    <numeric>    <numeric>     <numeric>
## S100A4           1  3.29706e-57  3.05195e-55      -4.52198
## TAGLN2           1  1.65522e-24  3.58425e-23       4.83531
## PF4              1  2.54870e-35  9.99719e-34       5.91366
## GZMA             1 1.41952e-120 7.71441e-118      -1.95444
## HLA-DQA1         1  1.79189e-88  4.75402e-86      -3.64622
## FCN1             1 1.13468e-246 4.77901e-243      -2.81179
## SERPINA1         1  1.12795e-68  1.72751e-66      -2.43278
## RPL23A           1  2.42151e-37  1.04737e-35      -4.07367
## RPL17            1  0.00000e+00  0.00000e+00      -2.82856
## RPS21            1  1.08454e-56  9.90316e-55      -3.99499
```

知道了这个概念，提取cluster9中的Top6的所有基因也不是难事

```r
best.set <- interesting[interesting$Top <= 6,]
# 看到Top6其实总共有53个基因
> dim(best.set)
[1] 53 21
# 提取这些基因在与各个cluster比较时的logFC
logFCs <- getMarkerEffects(best.set)
# 总共18个cluster，除去自己还剩17个
> dim(logFCs)
[1] 53 17

> logFCs[1:4,1:4]
                1           2         3           4
S100A4 -2.7287830 -0.89602358 -2.887000 -4.33065808
TAGLN2  3.8690448  3.72075587  3.955002  4.14646142
PF4     6.0988107  6.10290759  6.103301  6.07864054
GZMA   -0.4340303 -0.09184649 -1.954439 -0.07511017 

library(pheatmap)
pheatmap(logFCs, breaks=seq(-5, 5, length.out=101))
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-03-091314.png)

看到其中platelet factor 4 (PF4) 与 pro-platelet basic protein (PPBP)基因表达量高，推测cluster9含有血小板或者它的前体

**除了Top这一列，还有一列叫”summary.logFC“**，它可以帮我们快速判断基因在cluster9中是上调还是下调，例如看PF4在这里的logFC值就很高

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-03-091940.png)

**不同的比较方法**

* 注意到，**这里使用的差异比较方法是”两两比较**“，即将一个cluster和其余各个cluster进行比较。
* 当然还有**其他方法是将一个cluster与其他剩余cluster的均值进行比较**。如果是与均值比较，那么就会细胞群体组成比较敏感，如果其中出现一个”支配欲超强的“cluster，那么它会把整个平均的表达水平带偏，结果看到的差异分析也并不准确。
* 另外，使用两两比较的方法还会提供有关marker基因更多的信息，比如能看到哪些clusters是由某个marker基因区分的

### **2.2 如果只关注上调基因**

之前`findMarkers`目的是选取上调、下调基因作为marker基因的候选，但其实下调基因很难吸引我们的注意力，**我们会首先关注图上红色的，也就是上调的基因们**。另一方面，相比于上调，下调基因也很难通过实验去验证。因此，如果只关心上调的基因，可以**使用单边t检验，**&#x5C06;某个cluster和其他clusters进行比较，设置参数`direction="up"`

```r
markers.pbmc.up <- findMarkers(sce.pbmc, direction="up")
interesting.up <- markers.pbmc.up[[chosen]]
interesting.up[1:10,1:4]
## DataFrame with 10 rows and 4 columns
##                 Top     p.value         FDR summary.logFC
##           <integer>   <numeric>   <numeric>     <numeric>
## TAGLN2            1 8.27609e-25 9.29516e-21       4.83531
## PF4               1 1.27435e-35 4.29379e-31       5.91366
## SDPR              2 2.26416e-21 1.90722e-17       4.72820
## GPX1              2 1.79269e-20 1.00671e-16       4.83143
## TMSB4X            2 1.61389e-31 2.71891e-27       3.71343
## PPBP              3 2.67043e-20 1.28539e-16       5.54885
## NRGN              3 1.41986e-20 9.56813e-17       4.18416
## CCL5              5 2.55331e-18 9.55903e-15       4.62327
## GNG11             6 2.06623e-18 8.70243e-15       4.73606
## HIST1H2AC         7 1.05437e-17 3.55260e-14       4.76160
```

另外还可以根据logFC阈值过滤，其实这些都可以自己手动过滤，多个参数只是更方便一点。缺点就是需要记住这个函数有这个参数

```r
markers.pbmc.up2 <- findMarkers(sce.pbmc, direction="up", lfc=1)
interesting.up2 <- markers.pbmc.up2[[chosen]]
interesting.up2[1:10,1:4]
```

根据两重过滤的结果（direction + logFC），再看cluster9的marker基因热图

```r
best.set <- interesting.up2[interesting.up2$Top <= 5,]
logFCs <- getMarkerEffects(best.set)

library(pheatmap)
pheatmap(logFCs, breaks=seq(-5, 5, length.out=101))
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-03-111225.png)

**注意**

* 我们这里只是探索了上调基因。至于有一些亚群中可能部分基因下调才导致这个亚群与众不同，这样的亚群这里检测不到
* 这里的阈值可能会漏掉一些改变幅度不大，但依然重要的基因

### **2.3 寻找cluster特异的marker基因**

> 上面提到，`findMarkers()` 会对两两比较结果做一个排名，然后选择p值比较显著的一些作为Top基因，返回的结果包括了所有cluster的logFC情况，比如这里有18个cluster，那么返回的结果也包含18个cluster的logFC。**以上调差异表达为例：**&#x67D0;个基因在cluster9与cluster1相比下上调，但这个基因在cluster9和cluster2中不上调，依然会把这个基因列出来

有一种**更严格的过滤机制**，就是只选在某个cluster与其他各个clusters相比都差异表达的基因，结果返回17个cluster（不包括自己）。**以上调差异表达为例**：结果得到的每个基因，不管是cluster9与cluster1比较，还是与cluster2比较，都是上调的；并且，这个基因仅仅在cluster9中是上调的

```r
# 设置pval.type="all"就是做了这件事，并且还是选上调的基因
markers.pbmc.up3 <- findMarkers(sce.pbmc, pval.type="all", direction="up")
# 然后就想看看感兴趣的cluster9与其他各个clusters比较后差异基因
interesting.up3 <- markers.pbmc.up3[[chosen]]
interesting.up3[1:10,1:3]
```

* 这种做法过于严格，以至于如果一个基因除了在cluster9中上调，还在cluster4中上调，这个基因就不会写进结果。【它的想法很简单，就是单纯针对cluster9，只找在它里面上调的】
* 另外，如果细胞分群效果不好，这样的寻找方法会过滤掉太多的潜在的感兴趣基因
* 举个例子：如果一群细胞混杂了单纯CD4+、单纯CD8+、二者都有、二者都无这四种情况。如果设置`pval.type="all"`，那么Cd4或Cd8基因都不会列入marker基因结果，因为它们都会在两个亚群有差异表达情况

还有另一种方法：`pval.type="any"` ，就是只要在一个cluster中出现差异表达，就写入结果。但这个设置有有点过于宽松，因此一个折中的方法：`pval.type="some"` ，各个方法得到的`summary.logFC`这一列都是不同的，基因排名前后也略有变化。

> 我相信大家可能并关心每个方法是怎么去比较，怎么去做统计的，更关心的是，我用了这个方法，得到的结果可用性有多高？这个问题可以思考一下，如果真的有一种普适性的方法，开发者又怎么会去设置这么多参数呢？直接一个参数到底，不是更方便？
>
> 数据分析就是这样，存在太多的不确定性。但只有我们使用一种方法感觉走不通了，才会意识到**多个参数多条路**的滋味。

### **2.4 除了t检验，还有Wilcoxon、binomial检验**

**首先来了解一下各种统计检验函数**

> 参考：<https://shixiangwang.github.io/home/cn/post/2019-12-25-r-stats-funs-summary/>

**对于连续型数据**

基于正态分布的检验：

* 均值检验：`t.test()`
* 两总体方差检验：`var.test()`
* 多个组间均值的比较（ANOVA）：`aov()`
* 多组样本的配对 t 检验：`pairwise.t.test()`
* 正态性检验：`shapiro.test()`&#x20;
* 分布的对称性检验：`ks.test()`
* 检验两个向量是否服从同一分布：`ks.test()`
* 相关性检验：`cor.test()`

不依赖分布的检验:

* 均值检验：Wilcoxon ，也就是说，Wilcoxon 检验是 t 检验的非参数版本。默认是秩和检验`wilcox.test`&#x20;
* 多均值比较：`kruskal.test()`  Kruskal-Wallis 秩和检验
* 方差检验：`fligner.test()` Fligner-Killeen（中位数）检验完成不同组别的方差比较
* 尺度参数差异：`ansari.test()` 针对两样本尺度参数差异的 Ansari-Bradley 检验；`mood.test()` 使用 Mood 两样本检验

**对于离散型数据**

* 比例检验：`prop.test()` 比较两组观测值发生的概率是否有差异
* 二项式检验：`binom.test()`&#x20;
* 列联表检验：`fisher.test()` 用来确定两个分类变量是否相关； 针对小的列联表，Fisher 精确检验效果不错；大列联表可以用卡方检验代替；检验三个变量的混合影响，可以用Cochran-Mantel-Haenszel 检验`mantelhaen.test()` ；McNemar 卡方可以检验二维列联表的对称性`mcnemar.test()`&#x20;
* 列联表非参数检验：Friedman 秩和检验（非参数的双边 ANOVA 检验） `friedman.test()`&#x20;

**找marker基因的Wilcoxon方法**

```r
markers.pbmc.wmw <- findMarkers(sce.pbmc, test="wilcox", direction="up")
names(markers.pbmc.wmw)
##  [1] "1"  "2"  "3"  "4"  "5"  "6"  "7"  "8"  "9"  "10" "11" "12" "13" "14" "15"
## [16] "16" "17" "18"
interesting.wmw <- markers.pbmc.wmw[[chosen]]
interesting.wmw[1:10,1:4]
## DataFrame with 10 rows and 4 columns
##                 Top      p.value          FDR summary.AUC
##           <integer>    <numeric>    <numeric>   <numeric>
## PF4               1 3.13749e-164 1.05715e-159    0.988833
## TMSB4X            1  5.07215e-27  2.05905e-24    0.992149
## SDPR              2 2.12114e-145 3.57349e-141    0.955218
## NRGN              2 1.18240e-131 7.96793e-128    0.966119
## TAGLN2            3  1.55560e-28  6.98860e-26    0.967186
## PPBP              3 3.57148e-134 4.01125e-130    0.932743
## GNG11             3 2.46077e-126 1.38189e-122    0.932491
## TUBB1             3 7.55573e-133 6.36457e-129    0.921632
## HIST1H2AC         4  4.69094e-94  1.43688e-90    0.930973
## ACTB              5  1.53723e-23  5.28523e-21    0.949431
```

**找marker基因的binomial方法**

```r
markers.pbmc.binom <- findMarkers(sce.pbmc, test="binom", direction="up")
# 选出cluster9的marker基因
interesting.binom <- markers.pbmc.binom[[chosen]]
colnames(interesting.binom)
##  [1] "Top"           "p.value"       "FDR"           "summary.logFC"
##  [5] "logFC.1"       "logFC.2"       "logFC.3"       "logFC.4"      
##  [9] "logFC.5"       "logFC.6"       "logFC.7"       "logFC.8"      
## [13] "logFC.10"      "logFC.11"      "logFC.12"      "logFC.13"     
## [17] "logFC.14"      "logFC.15"      "logFC.16"      "logFC.17"     
## [21] "logFC.18"
library(scater)
top.genes <- head(rownames(interesting.binom))
plotExpression(sce.pbmc, x="label", features=top.genes)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-05-055703.png)

### **2.5 整合多个统计分析的结果**

这样可以检验哪些基因是强有力的marker基因（能撑得住三大检验方法的考验）

```r
combined <- multiMarkerStats(
    t=findMarkers(sce.pbmc, direction="up"),
    wilcox=findMarkers(sce.pbmc, test="wilcox", direction="up"),
    binom=findMarkers(sce.pbmc, test="binom", direction="up")
)
> combined
List of length 18
names(18): 1 2 3 4 5 6 7 8 ... 12 13 14 15 16 17 18

head(combined[["9"]][,1:15])
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-05-061802.png)

当然也可以重点关注其中的某一个指标，比如wilcox的结果AUC越大，就表示基因表达量在各个cluster之间的分布越分散；t-test的logFC结果越大，表示更容易解释一个基因在两个cluster之间的变化幅度

## 3 另外，可以封锁一些不重要因素

大型数据集一般会涉及许多变化因素（比如批次、性别差异、年龄差异等等），它们会对数据波动产生影响，但又不是感兴趣的生物因素。如果在检测marker基因的时候带着它们，可能会结果产生干扰。

因此**有一个参数`block`可以帮我们”锁住“它们**，之后检测的时候就不会把这些因素纳入考量

```r
# 举个例子，在416b数据集中有批次因素，可以锁定
m.out <- findMarkers(sce.416b, block=sce.416b$block, direction="up") 
demo <- m.out[["1"]] 
# 前Top5的marker基因
demo[demo$Top <= 5,1:4]
## DataFrame with 13 rows and 4 columns
##                          Top     p.value         FDR summary.logFC
##                    <integer>   <numeric>   <numeric>     <numeric>
## Foxs1                      1 1.37387e-12 4.35563e-10       3.07058
## Pirb                       1 2.08277e-33 1.21332e-29       5.87820
## Myh11                      1 6.44327e-47 3.00282e-42       4.38182
## Tmsb4x                     2 3.22944e-44 7.52525e-40       1.47689
## Ctsd                       2 6.78109e-38 7.90065e-34       2.89152
## ...                      ...         ...         ...           ...
## Tob1                       4 6.63870e-09 1.18088e-06       2.74161
## Pi16                       4 1.69247e-32 7.88758e-29       5.76914
## Cd53                       5 1.08574e-27 2.97646e-24       5.75200
## Alox5ap                    5 1.33791e-28 4.15679e-25       1.36676
## CBFB-MYH11-mcherry         5 3.75556e-35 3.50049e-31       3.01677
```

这个参数对上面的三种统计方法都适用


# 3.7 细胞类型注释

刘小泽写于2020.7.5

## 1 前言

scRNA数据分析具有挑战性的工作一般就是对结果的解读。获得不同亚群的细胞很简单，但如何给每个亚群赋予生物学意义就比较困难，既需要对数据掌握，有需要有生物背景，而生物背景知识更新并不是那么快。我们现在掌握的，可能和真实水平还有很大偏差。

为了减弱自身生物背景的不足，可以用一些先验知识帮助判断。最常见的就是通过去了解参与特定生物学过程的经过验证的基因，利用GO、KEGG数据库；另外还可以将我们的表达谱与之前发布的参考数据集做对比，因为参考数据集中样本或细胞的生物学状态都经过了专家的证实。这两种形式（参考数据集、基因集）接下来都会探讨。

### **准备数据**

下面2.2部分会用到

```r
# 准备数据
library(scRNAseq)
sce.seger <- SegerstolpePancreasData()

# 基因注释
library(AnnotationHub)
edb <- AnnotationHub()[["AH73881"]]
symbols <- rowData(sce.seger)$symbol
ens.id <- mapIds(edb, keys=symbols, keytype="SYMBOL", column="GENEID")
ens.id <- ifelse(is.na(ens.id), symbols, ens.id)

# 去除重复行
keep <- !duplicated(ens.id)
sce.seger <- sce.seger[keep,]
rownames(sce.seger) <- ens.id[keep]

# 样本注释
emtab.meta <- colData(sce.seger)[,c("cell type", 
                                    "individual", "single cell well quality")]
colnames(emtab.meta) <- c("CellType", "Donor", "Quality")
colData(sce.seger) <- emtab.meta

sce.seger$CellType <- gsub(" cell", "", sce.seger$CellType)
sce.seger$CellType <- paste0(
  toupper(substr(sce.seger$CellType, 1, 1)),
  substring(sce.seger$CellType, 2))

# 质控
low.qual <- sce.seger$Quality == "low quality cell"

library(scater)
stats <- perCellQCMetrics(sce.seger)
qc <- quickPerCellQC(stats, percent_subsets="altexps_ERCC_percent",
                     batch=sce.seger$Donor,
                     subset=!sce.seger$Donor %in% c("HP1504901", "HP1509101"))

sce.seger <- sce.seger[,!(qc$discard | low.qual)]

# 归一化
# 看到quickCluster和computeSumFactors就知道使用的是去卷积化方法
library(scran)
clusters <- quickCluster(sce.seger)
sce.seger <- computeSumFactors(sce.seger, clusters=clusters)
sce.seger <- logNormCounts(sce.seger)
```

## 2 使用参考数据集

> 非常直接的方法，将我们的表达量与参考数据集的表达量去比对，看看和参考的样本有多少类似

这个牵扯到一个分类的问题，可以辅助以标准的机器学习（比如随机森林、支持向量机），任何之前发表过的RNA-Seq数据（bulk或single cell）中对细胞的划分都可以提供帮助。

这里主要是用了2019年发表的细胞注释R包[`SingleR`](https://bioconductor.org/packages/release/bioc/vignettes/SingleR/inst/doc/SingleR.html)  ([Aran et al. 2019](https://pubmed.ncbi.nlm.nih.gov/30643263/)) 。它会计算我们数据中的细胞与参考数据集中的细胞之间Spearman相关性，找到最相关的属性赋予我们的细胞。为了减少数据的噪音，这个R包先鉴定marker基因，然后根据marker基因的表达量去计算相关性。

### **2.1 使用内置的参考注释数据**

SingleR中就包含了一些内置数据集，大部分是bulk RNA-Seq或芯片数据中经过筛选的细胞类型。

下面使用的参考数据集就来自Blueprint和ENCODE计划：([Martens and Stunnenberg 2013](https://pubmed.ncbi.nlm.nih.gov/24091925/); [The ENCODE Project Consortium 2012](https://pubmed.ncbi.nlm.nih.gov/22955616/)).

```r
# 我们的数据
sce.pbmc
## class: SingleCellExperiment 
## dim: 33694 3922 
## metadata(1): Samples
## assays(2): counts logcounts
## rownames(33694): RP11-34P13.3 FAM138A ... AC213203.1 FAM231B
## rowData names(2): ID Symbol
## colnames(3922): AAACCTGAGAAGGCCT-1 AAACCTGAGACAGACC-1 ...
##   TTTGTCACAGGTCCAC-1 TTTGTCATCCCAAGAT-1
## colData names(4): Sample Barcode sizeFactor label
## reducedDimNames(3): PCA TSNE UMAP
## altExpNames(0):

# 参考数据集
library(SingleR)
ref <- BlueprintEncodeData()

# 然后把我们的细胞在参考数据集中找对应的细胞类型
# 返回的pred结果是一个数据框，每行是我们自己数据的一个细胞
pred <- SingleR(test=sce.pbmc, ref=ref, labels=ref$label.main)
table(pred$labels)
## 
##      B-cells CD4+ T-cells CD8+ T-cells           DC  Eosinophils Erythrocytes 
##          525          755         1254            1            1            5 
##          HSC    Monocytes     NK cells 
##           14         1116          251

# 可视化
plotScoreHeatmap(pred)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-05-083229.png)

横坐标是各种细胞类型，纵坐标是我们数据中的细胞；Score也是经过归一化后的，介于0-1

理论上，我们数据中的每个细胞都会有自己对应的细胞类型，并且在所属的细胞类型中，比其他细胞相关性更高（也就是得分更高、颜色差异更明显）。看到只有B-cells、monocytes表现突出，与其他细胞类型区分明显。而像CD4+、CD8+细胞，颜色相近，区分不明显

**注意**

当然，会存在一些细胞归属不是很清楚的，好像一个细胞对两种细胞类型都有点沾边，但又不好判断。函数会通过一个阈值将这些划分不清楚的细胞定为”NA”

```r
sum(is.na(pred$pruned.labels))
## [1] 76

plotScoreDistribution(pred)
```

这个函数会把三种情况一起画出来，并且顺序依次是：匹配上的（黄色 assigned）、根本就不匹配的（灰色 other）、不清不楚的（橙色 pruned）

可以看到大部分都是灰色，没有匹配上的

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-06-070758.png)

另外，还可以看cluster对label的对应关系。理想情况是，一个cluster只对应一个label，但现实是：可能好几个clusters属于一个label；还有两个label的clusters很相似（例如CD4+、CD8+）

```r
tab <- table(Assigned=pred$pruned.labels, Cluster=colLabels(sce.pbmc))
library(pheatmap)
pheatmap(log2(tab+10), color=colorRampPalette(c("white", "blue"))(101))
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-06-072204.png)

可以看到，使用已知的细胞注释可以简化细胞的生物学解释过程，但同时也会将细胞们限定在已知的框架中。如果发现我们聚类分群的结果与注释后的结果相差很大，不用着急去掉某种结果，留着下游再继续探讨差异来源

### **2.2 使用自定义的注释数据**

比如手上哟一个之前注释过的数据，现在想用其中的注释信息对另一个数据进行注释

例如，之前有一个注释好的人类胰腺数据集 `sce.muraro` [Muraro et al. (2016)](https://pubmed.ncbi.nlm.nih.gov/27693023/)

```r
## class: SingleCellExperiment 
## dim: 16940 2299 
## metadata(0):
## assays(2): counts logcounts
## rownames(16940): ENSG00000268895 ENSG00000121410 ... ENSG00000159840
##   ENSG00000074755
## rowData names(2): symbol chr
## colnames(2299): D28-1_1 D28-1_2 ... D30-8_93 D30-8_94
## colData names(4): label donor plate sizeFactor
## reducedDimNames(0):
## altExpNames(1): ERCC
```

去掉那些不明确的细胞类型

```r
sce.muraro <- sce.muraro[,!is.na(sce.muraro$label) & 
    sce.muraro$label!="unclear"]
table(sce.muraro$label)
## 
##      acinar       alpha        beta       delta        duct endothelial 
##         217         795         442         189         239          18 
##     epsilon mesenchymal          pp 
##           3          80          96
```

现在有一个新的数据集`sce.seger` [Segerstolpe et al. (2016)](https://pubmed.ncbi.nlm.nih.gov/27667667/)，它本身也包括细胞注释，在数据准备代码中有提及：在`sce.seger$CellType`，这个注释是领域专家设定的

```r
# 以这个新数据集作为测试数据，sce.muraro作为参考数据，看看注释结果如何
pred.seger <- SingleR(test=sce.seger, ref=sce.muraro, 
    labels=sce.muraro$label, de.method="wilcox")

table(pred.seger$labels)
## 
##      acinar       alpha        beta       delta        duct endothelial 
##         188         889         279         105         385          17 
##     epsilon mesenchymal          pp 
##           5          53         169
```

用这个结果与测试数据本身带的细胞注释结果进行比较：

```r
tab <- table(pred.seger$pruned.labels, sce.seger$CellType)

library(pheatmap)
pheatmap(log2(tab+10), color=colorRampPalette(c("white", "blue"))(101))
```

可以看到这两种注释结果比较相近

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-07-013106.png)

### **2.3 使用marker基因集**

> 这部分可以作为2.2的延伸 它基于的想法是：高表达的marker基因与每个细胞的生物学功能相关，因此可以用marker基因作为桥梁，连接测试数据集与参考数据集

基于marker基因的方法会更快捷，同时也不受限于参考数据集是否有专家定义好的细胞类型

**参考数据集 sce.zeisel** [Zeisel et al. (2015)](https://pubmed.ncbi.nlm.nih.gov/25700174/)

目的是将数据集的marker基因于对应的细胞类型联系起来

需要注意的是：下面的`pairwiseWilcox()`代码**需要基于R 4.0**，需要加载归一化后的的数据，前面的数据处理在之前章节都有提及 数据在：<https://share.weiyun.com/fDePnIGC>

```r
load('normalized.sce.zeisel.RData')

> table(sce.zeisel$level1class)

astrocytes_ependymal    endothelial-mural 
                 179                  160 
        interneurons            microglia 
                 290                   78 
    oligodendrocytes        pyramidal CA1 
                 774                  938 
        pyramidal SS 
                 397 


library(scran)
wilcox.z <- pairwiseWilcox(sce.zeisel, sce.zeisel$level1class, 
                           lfc=1, direction="up")
markers.z <- getTopMarkers(wilcox.z$statistics, wilcox.z$pairs,
                           pairwise=FALSE, n=50)

> lengths(markers.z)
astrocytes_ependymal    endothelial-mural 
                  79                   83 
        interneurons            microglia 
                 118                   69 
    oligodendrocytes        pyramidal CA1 
                  81                  125 
        pyramidal SS 
                 149
```

**测试数据集 sce.tasic** [Tasic et al. (2016)](https://pubmed.ncbi.nlm.nih.gov/26727548/)

```r
library(scRNAseq)
sce.tasic <- TasicBrainData()
sce.tasic
## class: SingleCellExperiment 
## dim: 24058 1809 
## metadata(0):
## assays(1): counts
## rownames(24058): 0610005C13Rik 0610007C21Rik ... mt_X57780 tdTomato
## rowData names(0):
## colnames(1809): Calb2_tdTpositive_cell_1 Calb2_tdTpositive_cell_2 ...
##   Rbp4_CTX_250ng_2 Trib2_CTX_250ng_1
## colData names(13): sample_title mouse_line ... secondary_type
##   aibs_vignette_id
## reducedDimNames(0):
## altExpNames(1): ERCC
```

**接下来使用**[**AUCell**](https://bioconductor.org/packages/3.11/bioc/vignettes/AUCell/inst/doc/AUCell.html) **这个包**

首先拿到参考数据集中的marker基因集`markers.z`，并构建一个`GeneSetCollection`对象

然后对测试数据集中基因表达量进行排名，最后根据排名和之前的基因集进行统计，得到每个marker基因集在每个细胞中的area under the curve (AUC)指标，就能得出哪一个marker基因集在测试数据集的细胞中占比最高

```r
> names(markers.z)
[1] "astrocytes_ependymal" "endothelial-mural"   
[3] "interneurons"         "microglia"           
[5] "oligodendrocytes"     "pyramidal CA1"       
[7] "pyramidal SS"  

# 对marker基因集进行操作
library(GSEABase)
all.sets <- lapply(names(markers.z), function(x) {
    GeneSet(markers.z[[x]], setName=x)        
})
all.sets <- GeneSetCollection(all.sets)

> all.sets
GeneSetCollection
  names: astrocytes_ependymal, endothelial-mural, ..., pyramidal SS (7 total)
  unique identifiers: Apoe, Clu, ..., Gabrb2 (555 total)
  types in collection:
    geneIdType: NullIdentifier (1 total)
    collectionType: NullCollection (1 total)

# 对测试数据集进行操作
library(AUCell)
# 基因表达量排名
rankings <- AUCell_buildRankings(counts(sce.tasic),
    plotStats=FALSE, verbose=FALSE)
# 计算AUC(结果见下图)
cell.aucs <- AUCell_calcAUC(all.sets, rankings)
# 转置
results <- t(assay(cell.aucs))
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-07-035814.png)

这样就能清楚看到，**每个细胞对应最高的AUC值属于哪个marker基因集**，因此可以作为这个细胞的label

**结果的检查 ——方法一：与参考数据中的cluster类型作比较**

```r
new.labels <- colnames(results)[max.col(results)]
table(new.labels, sce.tasic$broad_type)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-07-040813.png)

可以看到一个cluster中的大部分细胞，都被赋予了一个相同的label

**结果的检查 ——方法二：计算AUC最大值减去中位数**

先说判断方法：如果注释的很明确，这个差值会较大；较小的差值说明结果不理想；当然我们可以自己定义一个阈值，过滤掉这些无法注释的细胞

下面的例子中，就是假定得到的大多都是注释较好的细胞，然后得到这个阈值

```r
library(scater)
library(DelayedArray)
deltas <- rowMaxs(results) - rowMedians(results)
discard <- isOutlier(deltas, type="lower", batch=new.labels)
table(new.labels[discard])
## 
## astrocytes_ependymal    endothelial-mural         interneurons 
##                   24                    1                    7 
##     oligodendrocytes         pyramidal SS 
##                   10                   16
```

看看这些被过滤掉的，在整体分布中处于什么位置

```r
par(mar=c(10,4,1,1))
boxplot(split(deltas, new.labels), las=2)
points(attr(discard, "thresholds")[1,], col="red", pch=4, cex=2)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-07-060634.png)

**AUCell 包的好处在于不依赖表达量**

只需要获得基因列表（可以基于文献或其他背景知识获得），例如从MsigDb上下载

```r
library(BiocFileCache)
bfc <- BiocFileCache(ask=FALSE)
scsig.path <- bfcrpath(bfc, file.path("http://software.broadinstitute.org",
    "gsea/msigdb/supplemental/scsig.all.v1.0.symbols.gmt"))
scsigs <- getGmt(scsig.path)

> scsigs
GeneSetCollection
  names: Zheng_Cord_Blood_C1_Putative_Megakaryocyte_Progenitor, Zheng_Cord_Blood_C2_Putative_Basophil_Eosinophil_Mast_cell_Progenitor, ..., Muraro_Pancreas_Endothelial_Cell (256 total)
  unique identifiers: ABCC3, ABCC4, ..., SEC31A (18656 total)
  types in collection:
    geneIdType: NullIdentifier (1 total)
    collectionType: NullCollection (1 total)
```

不基于表达量有好有坏，好处就是避免了表达量出现的噪音干扰，处理也更快；坏处是可能会丢掉发现更精细的亚群可能性。

再看一眼另一个数据，不需要运行重点看看差异即可

```r
# 先对测试数据集基因排名
muraro.mat <- counts(sce.muraro)
rownames(muraro.mat) <- rowData(sce.muraro)$symbol
muraro.rankings <- AUCell_buildRankings(muraro.mat,
    plotStats=FALSE, verbose=FALSE)

# 然后把MsigDb的scsigs应用在测试数据上
scsig.aucs <- AUCell_calcAUC(scsigs, muraro.rankings)
scsig.results <- t(assay(scsig.aucs))
# 准备作图数据1
full.labels <- colnames(scsig.results)[max.col(scsig.results)]
tab <- table(full.labels, sce.muraro$label)
fullheat <- pheatmap(log10(tab+10), color=viridis::viridis(100), silent=TRUE)

# 然后把MsigDb的一个子集（Pancreas相关）的scsigs应用在测试数据上
scsigs.sub <- scsigs[grep("Pancreas", names(scsigs))]
sub.aucs <- AUCell_calcAUC(scsigs.sub, muraro.rankings)
sub.results <- t(assay(sub.aucs))
# 准备作图数据2
sub.labels <- colnames(sub.results)[max.col(sub.results)]
tab <- table(sub.labels, sce.muraro$label)
subheat <- pheatmap(log10(tab+10), color=viridis::viridis(100), silent=TRUE)

# 组合
gridExtra::grid.arrange(fullheat[[4]], subheat[[4]])
```

看到一个明显的变化就是：取了MsigDb关于Pancreas的子集后，效果变好了；而使用全部的基因集会导致混淆。因此使用哪些基因还是要基于我们对自己测试数据集的认识

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-07-092916.png)

## 3 基于marker基因的富集分析

> 思路：还是先得到每个cluster的标志性marker基因，然后对这些代表该cluster的基因们进行富集分析，看看它们集中在哪些通路

### **示例数据**

再以一个新的小鼠乳腺数据 [Bach et al. (2017) ](https://pubmed.ncbi.nlm.nih.gov/29225342/)为例，还是经历了：基因ID注释=》质控=》归一化=》找HVGs=》降维=》聚类=》找marker基因\
[数据可以直接下载](https://share.weiyun.com/HXK6DM5U)

```r
#--- loading ---#
library(scRNAseq)
sce.mam <- BachMammaryData(samples="G_1")

#--- gene-annotation ---#
library(scater)
rownames(sce.mam) <- uniquifyFeatureNames(
    rowData(sce.mam)$Ensembl, rowData(sce.mam)$Symbol)

library(AnnotationHub)
ens.mm.v97 <- AnnotationHub()[["AH73905"]]
rowData(sce.mam)$SEQNAME <- mapIds(ens.mm.v97, keys=rowData(sce.mam)$Ensembl,
    keytype="GENEID", column="SEQNAME")

#--- quality-control ---#
is.mito <- rowData(sce.mam)$SEQNAME == "MT"
stats <- perCellQCMetrics(sce.mam, subsets=list(Mito=which(is.mito)))
qc <- quickPerCellQC(stats, percent_subsets="subsets_Mito_percent")
sce.mam <- sce.mam[,!qc$discard]

#--- normalization ---#
library(scran)
set.seed(101000110)
clusters <- quickCluster(sce.mam)
sce.mam <- computeSumFactors(sce.mam, clusters=clusters)
sce.mam <- logNormCounts(sce.mam)

#--- variance-modelling ---#
set.seed(00010101)
dec.mam <- modelGeneVarByPoisson(sce.mam)
top.mam <- getTopHVGs(dec.mam, prop=0.1)

#--- dimensionality-reduction ---#
library(BiocSingular)
set.seed(101010011)
sce.mam <- denoisePCA(sce.mam, technical=dec.mam, subset.row=top.mam)
sce.mam <- runTSNE(sce.mam, dimred="PCA")

#--- clustering ---#
snn.gr <- buildSNNGraph(sce.mam, use.dimred="PCA", k=25)
colLabels(sce.mam) <- factor(igraph::cluster_walktrap(snn.gr)$membership)

#--- find marker ---#
markers.mam <- findMarkers(sce.mam, direction="up", lfc=1)
```

### **然后我们想看cluster2是什么细胞类型**

首先得到cluster2的marker基因，并且还挑差异显著的基因

```r
chosen <- "2"
cur.markers <- markers.mam[[chosen]]
is.de <- cur.markers$FDR <= 0.05 
summary(is.de)
##    Mode   FALSE    TRUE 
## logical   27819     179
```

进行GO富集分析

```r
library(org.Mm.eg.db)
library(clusterProfiler)
gn=unique(rownames(cur.markers)[is.de])
trans=bitr(gn,fromType = "SYMBOL",
           toType = 'ENTREZID',
           OrgDb = org.Mm.eg.db)
go.out=enrichGO(gene  = trans$ENTREZID,
         keyType       = 'ENTREZID',
         OrgDb         = org.Mm.eg.db,
         ont           = "BP",
         pAdjustMethod = "BH",
         pvalueCutoff  = 0.05,
         qvalueCutoff  = 0.05,
         readable      = TRUE)
View(head(summary(go.out),20))
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-07-101643.png)

接下来就是考验自己对数据的认知了，假设我们这里对这个数据很熟悉。那么看到这里基因主要参与了lipid synthesis、 cell adhesion 、 tube formation。再结合乳腺的研究背景，推测cluster2可能包含与乳汁产生和分泌相关的管腔上皮细胞，进一步检查一下相关的基因Csn2和Csn3，也是上调的

```r
plotExpression(sce.mam, features=c("Csn2", "Csn3"), 
    x="label", colour_by="label")
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-07-102239.png)

如果想看cluster2中某个感兴趣通路中的基因表现情况

```r
# 比如想看与cell adhesion相关的通路
library(stringr)
adhesion <- go.out["GO:0022408",]
adhesion_gn=str_split(adhesion$geneID,"/",simplify = T)
head(cur.markers[rownames(cur.markers) %in% adhesion_gn,1:4], 10)
## DataFrame with 10 rows and 4 columns
##               Top     p.value         FDR summary.logFC
##         <integer>   <numeric>   <numeric>     <numeric>
## Spint2         11 3.28234e-34 1.37163e-31       2.39280
## Epcam          17 8.86978e-94 7.09531e-91       2.32968
## Cebpb          21 6.76957e-16 2.03800e-13       1.80192
## Cd24a          21 3.24195e-33 1.29669e-30       1.72318
## Btn1a1         24 2.16574e-13 6.12488e-11       1.26343
## Cd9            51 1.41373e-11 3.56592e-09       2.73785
## Ceacam1        52 1.66948e-38 7.79034e-36       1.56912
## Sdc4           59 9.15001e-07 1.75467e-04       1.84014
## Anxa1          68 2.58840e-06 4.76777e-04       1.29724
## Cdh1           69 1.73658e-07 3.54897e-05       1.31265
```

## TODO：补充12.5 Computing gene set activities


# 3.8 批次效应处理

刘小泽写于2020.7.7

## 1 前言

大型的scRNA分析一般都需要整合多个批次的数据集，但每个批次的质量可能存在不可调和的差异，例如仪器的调整、试剂质量的差异。结果就导致了不同批次细胞中基因表达量的系统差异，称之为“批次效应”。之前在 [3.2 归一化](https://jieandze1314.osca.top/03/03-2) 这一部分中也介绍了一些批次效应的事情，它最大的隐患就是：如果它这个差异成为整个数据的主力军，那么真正的生物学差异就难以检测，对结果的解读变得更难。

因此现在出了一些多数据整合时批次效应处理工具，早期的方法 ([Ritchie et al. 2015](https://pubmed.ncbi.nlm.nih.gov/25605792/); [Leek et al. 2012](https://pubmed.ncbi.nlm.nih.gov/22257669/))是基于线性模型，它假设细胞群体的组成要么是已知的，要么是在批次间也是同类型的。但很明显这个假设有局限，我们不能保证这个假设成立，于是定制的算法产生([Haghverdi et al. 2018](https://pubmed.ncbi.nlm.nih.gov/29608177/); [Butler et al. 2018](https://pubmed.ncbi.nlm.nih.gov/29608179/); [Lin et al. 2019](https://pubmed.ncbi.nlm.nih.gov/31028141/))，它不再需要对细胞组成有一个先验知识，保证了对未知的探索过程。

### **数据准备**

将使用两个不同批次的10X PBMC数据进行整合，它们都是从 [TENxPBMCData](https://bioconductor.org/packages/3.11/data/experiment/vignettes/TENxPBMCData/inst/doc/TENxPBMCData.html)这个数据包获得的，分别进行了前期的数据处理。各自前期处理的一个好处，就是可以根据各自的特点先过滤掉一些低质量数据（比如各自根据QC结果对低质量细胞进行过滤）

> 常规的前期处理步骤还是：质控=》归一化=》找HVGs=》降维=》聚类 \
> 只是**注意下面的lapply的使用**，多个数据的批量处理用列表很高效

```r
# 数据下载（数据我已做好，链接在：https://share.weiyun.com/iE0VjVD0）
library(TENxPBMCData)
all.sce <- list(
    pbmc3k=TENxPBMCData('pbmc3k'),
    pbmc4k=TENxPBMCData('pbmc4k'),
    pbmc8k=TENxPBMCData('pbmc8k')
)

# 批量质控
library(scater)
stats <- high.mito <- list()
for (n in names(all.sce)) {
    current <- all.sce[[n]] #接下来的每步还是和之前一样
    is.mito <- grep("MT", rowData(current)$Symbol_TENx)
    stats[[n]] <- perCellQCMetrics(current, subsets=list(Mito=is.mito))
    high.mito[[n]] <- isOutlier(stats[[n]]$subsets_Mito_percent, type="higher")
    all.sce[[n]] <- current[,!high.mito[[n]]]
}

# 批量归一化
all.sce <- lapply(all.sce, logNormCounts)

# 批量找HVGs
library(scran)
all.dec <- lapply(all.sce, modelGeneVar)
all.hvgs <- lapply(all.dec, getTopHVGs, prop=0.1)
> length(all.hvgs[[1]])
[1] 1001
> length(all.hvgs[[2]])
[1] 1352

# 批量降维(使用了一个更高级的mapply：Apply a Function to Multiple List or Vector Arguments)
library(BiocSingular)
set.seed(10000)
all.sce <- mapply(FUN=runPCA, x=all.sce, subset_row=all.hvgs, 
    MoreArgs=list(ncomponents=25, BSPARAM=RandomParam()), 
    SIMPLIFY=FALSE)
set.seed(100000)
all.sce <- lapply(all.sce, runTSNE, dimred="PCA")

set.seed(1000000)
all.sce <- lapply(all.sce, runUMAP, dimred="PCA")

# 批量聚类
for (n in names(all.sce)) {
    g <- buildSNNGraph(all.sce[[n]], k=10, use.dimred='PCA')
    clust <- igraph::cluster_walktrap(g)$membership
    colLabels(all.sce[[n]])  <- factor(clust)
}

# 把数据提取出来
pbmc3k <- all.sce$pbmc3k
dec3k <- all.dec$pbmc3k
pbmc4k <- all.sce$pbmc4k
dec4k <- all.dec$pbmc4k
```

### **为了方便下面的批次矫正，先做几件事**

**1 选出两个数据集的基因交集，并各自取子集**

这里两个数据都是Ensembl ID，因此这个过程很简单

```r
universe <- intersect(rownames(pbmc3k), rownames(pbmc4k))
> length(rownames(pbmc3k));length(rownames(pbmc4k));length(universe)
[1] 32738
[1] 33694
[1] 31232
```

然后取子集

```r
pbmc3k <- pbmc3k[universe,]
pbmc4k <- pbmc4k[universe,]

dec3k <- dec3k[universe,]
dec4k <- dec4k[universe,]
```

**2 矫正批次间测序深度的影响**

利用`multiBatchNorm()`函数，将两个批次放在一起，重新计算log-count，可以提高下游批次矫正的质量

> **还记得之前的size factor这个名词吗？** 它是为了去除一个批次数据中的各个细胞之间的文库差异 而这里，是为了去除两个批次之间的差异

```r
library(batchelor)
rescaled <- multiBatchNorm(pbmc3k, pbmc4k)
> class(rescaled)
[1] "list"
pbmc3k <- rescaled[[1]]
pbmc4k <- rescaled[[2]]
```

**3 将之前单独挑出来的模型构建结果再次整合**

当然还也可以把每个批次的HVGs取交集或并集，选多少基因这个没有成文规定。

这里只是因为scran提供了这么一个函数，并可以提供更多一些的基因，毕竟现在基因多一点没坏处，省的后面捉襟见肘

```r
library(scran)
combined.dec <- combineVar(dec3k, dec4k)
chosen.hvgs <- combined.dec$bio > 0
sum(chosen.hvgs)
## [1] 13431
```

## 2 检查批次效应

> 在进行批次矫正之前，肯定要先看看有没有批次效应以及有多严重吧

最简单的方法是把两个数据合并，然后PCA+t-SNE

### **合并数据**

如果简单的`cbind(pbmc3k, pbmc4k)`，会报错

```r
> cbind(pbmc3k, pbmc4k)
Error in FUN(X[[i]], ...) : 
  column(s) 'Symbol_TENx' in ‘mcols’ are duplicated and the data do not match
# 看似问题出现在mcols中的Symbol_TENx，看一眼
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-07-132149.png)

看样子的确存在不匹配的问题，第一行就暴露了，同一个Ensembl ID的Symbol就不同。这个问题先不深究。先看看两个数据的Ensembl 是否一致吧

```r
> identical(mcols(pbmc3k)[,1],mcols(pbmc4k)[,1])
[1] TRUE
# 一致，那么就把它们的变成一样就好了，symbol的问题我们不考虑
rowData(pbmc3k) <- rowData(pbmc4k)
pbmc3k$batch <- "3k"
pbmc4k$batch <- "4k"
uncorrected <- cbind(pbmc3k, pbmc4k)
```

### **进行PCA**

使用的HVGs也是合并两个数据集后得到的`chosen.hvgs`

```r
library(scater)
set.seed(0010101010)
uncorrected <- runPCA(uncorrected, subset_row=chosen.hvgs,
    BSPARAM=BiocSingular::RandomParam())
```

### **再看聚类分群结果**

如果说，这两个批次是真的重复，差异不大的话，那么分群的cluster应该在两个批次中包含差不多数量的细胞。但是下面发现基本clusters主要是仅仅包含了一个批次

```r
library(scran)
snn.gr <- buildSNNGraph(uncorrected, use.dimred="PCA")
clusters <- igraph::cluster_walktrap(snn.gr)$membership
tab <- table(Cluster=clusters, Batch=uncorrected$batch)
tab
##        Batch
## Cluster   3k   4k
##      1     1  781
##      2     0 1309
##      3     0  535
##      4    14   51
##      5     0  605
##      6   489    0
##      7     0  184
##      8  1272    0
##      9     0  414
##      10  151    0
##      11    0   50
##      12  155    0
##      13    0   65
##      14    0   61
##      15    0   88
##      16   30    0
##      17  339    0
##      18  145    0
##      19   11    3
##      20    2   36
```

### **看t-SNE结果**

```r
set.seed(1111001)
uncorrected <- runTSNE(uncorrected, dimred="PCA")
plotTSNE(uncorrected, colour_by="batch")
```

虽然说t-SNE图上的点大小、远近说明不了问题，但是两个批次分的这么开，也提供了两个批次的证据

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-07-133123.png)

当然，还可以继续找证据。利用上一次的细胞类型注释，看看两个批次中的细胞类型，是不是差异很大。

## 3 矫正批次效应之线性回归

bulk mRNA转录组中常用的矫正批次效应方法就是线性回归，对每个基因表达量拟合一个线性模型。例如limma的`removeBatchEffect()` ([Ritchie et al. 2015](https://pubmed.ncbi.nlm.nih.gov/25605792/)) 、sva的`comBat()` ( [Leek et al. 2012](https://pubmed.ncbi.nlm.nih.gov/22257669/))。如果要使用这类方法，就需要假设：批次间的细胞组成相同。另外的一个假设是：批次效应的累积的，对于任何给定的基因，在不同亚群中经过任何因素诱导的表达变化倍数是相同的。（其实，从这两个假设就看出来，这个方法不适合我们的单细胞数据，但还是要继续了解下去）

**下面将使用batchelor 包中的`rescaleBatches()`函数进行处理**

它也是对每个基因的log表达量进行了线性回归，并提高了一些运行性能。另外与`removeBatchEffect()`不同的是，`rescaleBatches()`保持了数据的稀疏性，而`removeBatchEffect()`会破坏稀疏性

```r
library(batchelor)
rescaled <- rescaleBatches(pbmc3k, pbmc4k)
# 依然进行PCA、聚类
set.seed(1010101010) 
rescaled <- runPCA(rescaled, subset_row=chosen.hvgs, exprs_values="corrected")

snn.gr <- buildSNNGraph(rescaled, use.dimred="PCA")
clusters.resc <- igraph::cluster_walktrap(snn.gr)$membership
tab.resc <- table(Cluster=clusters.resc, Batch=rescaled$batch)
tab.resc
##        Batch
## Cluster    1    2
##      1   278  525
##      2    16   23
##      3   337  606
##      4    43  748
##      5   604  529
##      6    22   71
##      7   188   48
##      8    25   49
##      9   263    0
##      10  123  135
##      11   16   85
##      12   11   57
##      13  116    6
##      14  455 1035
##      15    6   31
##      16   89  187
##      17    3   36
##      18    3    8
##      19   11    3
```

效果还是有的，现在大部分clusters都包含了两个批次的细胞，但是依然有存在批次差异的cluster（看cluster9，在batch2中细胞数为0），**表明处理有效果但不彻底** 。影响效果的原因是：我们的数据违背了这个方法的假设

**再做个图看看**

```r
rescaled <- runTSNE(rescaled, dimred="PCA")
rescaled$batch <- factor(rescaled$batch)
plotTSNE(rescaled, colour_by="batch")
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-07-135307.png)

**注意**

除了这个函数，还可以尝试更常规的`regressBatches()` ，只不过它的表现可能会更差，因为会丢失数据稀疏性

## 4 MNN矫正

### **4.1 先了解一下这个方法**

试想batchA中有一个细胞a，然后想根据挑选的特征基因（如HVGs）去在batchB中鉴定与a细胞空间相近的细胞们。同样的，对batchB中的细胞b 重复这个过程，也鉴定它在batchA中的近邻。于是这个MNN就是：Mutual nearest neighbors ，是对一个批次的细胞找到它们在另一个批次中对应的最相邻细胞集合。

> **Mutual nearest neighbors** are pairs of cells from different batches that belong in each other s set of nearest neighbors. [Haghverdi et al. (2018)](https://pubmed.ncbi.nlm.nih.gov/29608177/)

在进行批次矫正前，MNN找到的一对对细胞都是生物学状态相似的，因此如果再有不同，那么就姑且认为是外部的批次效应导致的，也就方便了去除。

与线性回归方法相比，MNN方法不会假设细胞群组成相同或者事先已知。MNN会自己学习细胞群的结构并进行估计。

### **4.2 还是应用到PBMC数据**

batchelor 这个包除了线性模型，还提供了MNN 的函数`fastMNN()`，但这个函数与单纯的MNN算法还有不同。`fastMNN()`先进行PCA降维，以加速下面的检测细胞近邻。

```r
set.seed(1000101001)
# 这里的d指的是前多少个主成分，k指近邻的数量（k增大会导致数据集合并更激进；一般不要设置太大，如果看到相同的细胞类型没有在批次之间充分合并，可以适当增加k）
mnn.out <- fastMNN(pbmc3k, pbmc4k, d=50, k=20, subset.row=chosen.hvgs,
    BSPARAM=BiocSingular::RandomParam(deferred=TRUE))
mnn.out
## class: SingleCellExperiment 
## dim: 13431 6791 
## metadata(2): merge.info pca.info
## assays(1): reconstructed
## rownames(13431): ENSG00000239945 ENSG00000228463 ... ENSG00000198695
##   ENSG00000198727
## rowData names(1): rotation
## colnames: NULL
## colData names(1): batch
## reducedDimNames(1): corrected
## altExpNames(0):
```

其中`mnn.out`的每列表示某个批次中的一个细胞（具体存储在`batch`），行表示`chosen.hvgs`中的基因

```r
head(mnn.out$batch) 
## [1] 1 1 1 1 1 1
```

看到降维相关模块`reducedDimNames`中多了一个`corrected`，它是根据50个主成分得到的所有6791在低维空间的坐标

```r
dim(reducedDim(mnn.out, "corrected"))
## [1] 6791   50

> reducedDim(mnn.out, "corrected")[1:3,1:3]
            [,1]       [,2]         [,3]
[1,] -0.12783777  0.0409469 -0.000116194
[2,] -0.03364614 -0.1466529  0.161690348
[3,] -0.09895631  0.1219669  0.010321992
```

看到表达量模块`assays()` 中多了一个`reconstructed`矩阵，是每个细胞中每个基因矫正后的表达量

```r
> dim(assay(mnn.out, "reconstructed"))
[1] 13431  6791

> assay(mnn.out, "reconstructed")[1:4,1:4]
<4 x 4> matrix of class LowRankMatrix and type "double":
                         [,1]          [,2]
ENSG00000239945 -2.522191e-06 -1.851424e-06
ENSG00000228463 -6.626821e-04 -6.724341e-04
ENSG00000237094 -8.077231e-05 -8.038006e-05
ENSG00000229905  3.838135e-06  6.179994e-06
                         [,3]          [,4]
ENSG00000239945 -1.198984e-05 -3.192269e-06
ENSG00000228463 -4.820230e-04 -6.330560e-05
ENSG00000237094 -9.630608e-05 -6.855333e-05
ENSG00000229905  5.432122e-06 -2.118592e-05
```

### **4.3 矫正后的检查**

这次函数已经做过PCA了，那就继续分群

```r
library(scran)
snn.gr <- buildSNNGraph(mnn.out, use.dimred="corrected")
clusters.mnn <- igraph::cluster_walktrap(snn.gr)$membership
tab.mnn <- table(Cluster=clusters.mnn, Batch=mnn.out$batch)
tab.mnn
##        Batch
## Cluster    1    2
##      1   337  606
##      2   289  542
##      3   152  181
##      4    12    4
##      5   517  467
##      6    17   19
##      7   313  661
##      8   162  118
##      9    11   56
##      10  547 1083
##      11   17   59
##      12   16   58
##      13  144   93
##      14   67  191
##      15    4   36
##      16    4    8
```

可以看到这里的效果不错，没有哪个cluster在某个batch中细胞数为0

再作图看看

```r
library(scater)
set.seed(0010101010)
mnn.out <- runTSNE(mnn.out, dimred="corrected")

mnn.out$batch <- factor(mnn.out$batch)
plotTSNE(mnn.out, colour_by="batch")
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-07-142622.png)

**注意**

`fastMNN()`函数其实也自带了检测数据

```r
metadata(mnn.out)$merge.info$lost.var
##             [,1]        [,2]
## [1,] 0.006617087 0.003315395
```

它的含义是：the proportion of variance within each batch that is lost during MNN correction。如果这个值很大（比如>10%），说明有些”矫枉过正“，把一些内在的生物学差异也给矫正了。

## 5 更为细致的检查

### **5.1 同一批次内clusters的比较**

两个批次数据混合后进行矫正，还是应该保留每个批次的特性。一个很不想看到的情况是：看上去细胞混合效果很好，以为矫正的效果不错，但没想到矫正过了，细胞中生物学异质性也被抹除了。

我们想在整合+矫正批次后，看到矫正后的一个cluster中既包含了原来的batch1信息，也包含batch2。当然表现的差异越大越好，体现在下图中就是：after1（即矫正后的cluster1）对应的PBMC3k 的clusters与PBMC 4k的clusters最好别重复，并且差别越大越好

```r
library(pheatmap)
# 画出二者混合后的cluster与各自之前的cluster对比
# batch 1
tab <- table(paste("after", clusters.mnn[rescaled$batch==1]),
    paste("before", colLabels(pbmc3k)))
heat3k <- pheatmap(log10(tab+10), cluster_row=FALSE, cluster_col=FALSE,
    main="PBMC 3K comparison", silent=TRUE)

# batch 2
tab <- table(paste("after", clusters.mnn[rescaled$batch==2]),
    paste("before", colLabels(pbmc4k)))
heat4k <- pheatmap(log10(tab+10), cluster_row=FALSE, cluster_col=FALSE,
    main="PBMC 4K comparison", silent=TRUE)

gridExtra::grid.arrange(heat3k[[4]], heat4k[[4]])
```

就以after5来说，分别对应3k混合之前的cluster3、4、7；以及4k混合前的cluster1、10、3，差别蛮大，说明混合后依然保持了各自的个性

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-07-151941.png)

**除了看图，还能通过数据体现——计算Rand index**

这个指标是矫正后各个batch原来差异的保留度，这个数值越大越好（但如果某个批次矫正方法不给力，这个值也是大的，所以还是要慎重对待）

```r
library(fossil)
# batch1
ri3k <- rand.index(as.integer(clusters.mnn[rescaled$batch==1]),
    as.integer(colLabels(pbmc3k)))
ri3k
## [1] 0.9335226

# batch2
ri4k <- rand.index(as.integer(clusters.mnn[rescaled$batch==2]),
    as.integer(colLabels(pbmc4k)))
ri4k
## [1] 0.9575746
```

### **5.2 利用marker基因检查**

目的就是看数据整合+矫正后，原始batch的内部结构是否还完整

首先分别从原来的两个批次数据中寻找marker基因，因为marker基因的存在就保证了这个批次数据集中”生物学的有趣性“存在

```r
stats3 <- pairwiseWilcox(pbmc3k, direction="up")
markers3 <- getTopMarkers(stats3[[1]], stats3[[2]], n=10)

stats4 <- pairwiseWilcox(pbmc4k, direction="up")
markers4 <- getTopMarkers(stats4[[1]], stats4[[2]], n=10)
```

然后用双方的marker基因合集，作为矫正的HVGs，看看算法会不会把这部分”有趣性“去除

```r
marker.set <- unique(unlist(c(unlist(markers3), unlist(markers4))))
length(marker.set) 
## [1] 314

set.seed(1000110)
mnn.out2 <- fastMNN(pbmc3k, pbmc4k, subset.row=marker.set,
    BSPARAM=BiocSingular::RandomParam(deferred=TRUE))
```

最后作图看看

```r
mnn.out2 <- runTSNE(mnn.out2, dimred="corrected")
gridExtra::grid.arrange(
    plotTSNE(mnn.out2[,mnn.out2$batch==1], colour_by=I(colLabels(pbmc3k))),
    plotTSNE(mnn.out2[,mnn.out2$batch==2], colour_by=I(colLabels(pbmc4k))),
    ncol=2
)
```

看样子根据这些marker基因进行的批次矫正，并没有去掉marker基因背后的生物学差异，因为两个批次数据还是能分出来群的，并且分群结果看似还不错

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-07-153535.png)

## 6 矫正后数据的应用

数据整合并矫正后，对合并后数据的cluster分析即可，不用再对每个batch的各个cluster单独分析。另一个好处是：batch整合后作为一整个数据集，细胞数量增加，相当于增加了群体结构的分辨率，**方便下游的细胞层面分析**（比如后面的轨迹推断）。

可能你还想利用合并后的数据进行基因层面的分析，例如差异分析marker基因鉴定。但一般不推荐，因为算法对多个批次进行合并时，不会保留每个基因表达的差异。**如果要探索基因表达相关，最好还是要在未矫正数据基础上进行**，并且还要把批次信息封锁掉（还记得之前`findMarkers`中的`block`参数吗？）

```r
# 例如
m.out <- findMarkers(uncorrected, clusters.mnn, block=uncorrected$batch,
    direction="up", lfc=1, row.data=rowData(uncorrected)[,3,drop=FALSE])
```

## TODO：对13.7 Using the corrected values继续补充


# 3.9 多样本间差异分析

刘小泽写于2020.7.10+7.15

## 1 前言

scRNA-seq的一个强大之处在于：方便多个实验条件下的样本比较，比如可以检测药物处理后 ([Richard et al. 2018](https://pubmed.ncbi.nlm.nih.gov/30013148/)) 或基因修饰后 ([Scialdone et al. 2016](https://pubmed.ncbi.nlm.nih.gov/27383781/)) 的细胞类型变化，比起常规的单一实验因素能提供更多信息。

对于多样本的scRNA差异分析，**主要分成两大类：差异表达（differential expression）和差异丰度（differential abundance）**，前者检测相同类型细胞在不同条件下表达的变化，后者检测不同条件下细胞类型(或状态等)组成的变化。下面就利用小鼠早期胚胎细胞数据([Pijuan-Sala et al. 2019](https://pubmed.ncbi.nlm.nih.gov/30787436/)) 来展示这两种分析。

### **数据介绍**

数据来自小鼠E8.5时期的嵌合体胚胎，每个嵌合体胚胎都是将td-Tomato-positive的胚胎干细胞（ESCs）注射到野生型囊胚中得到的。这里注射的细胞和囊胚细胞除了td-Tomato报告基因，没有其他基因上的差异。利用野生型嵌合体进行研究的目的是为了确定注射过程本身是否引入了其他的差异。

> 在小鼠胚胎干细胞（ES 细胞）中进行DNA 同源重组，将ES 细胞重新注射到囊胚腔中，可以形成嵌合胚胎，并能在假孕小鼠体内发育，最终发育为嵌合体小鼠，是判定胚胎干细胞系是否具有种系分化能力的唯一方法

`sce.chimera`数据总共包含6个样本，包含3个重复批次，每个批次2个处理，一个处理样本属于 td-Tomato阳性细胞，另一个处理样本属于阴性。样本是从6-7个嵌合胚胎中利用荧光活化分选得到的。每个样本利用10X Genomics方法建库([Zheng et al. 2017](https://pubmed.ncbi.nlm.nih.gov/28091601/))，得到2000-7000个细胞。

### **数据准备**

scater+scran标准处理模式如下，这个需要下载很多数据，整个过程还是很慢的\
[数据已分享，可直接下载](https://share.weiyun.com/5NE9lhXA)

其中merging这一步中指定出了批次信息，方便更好地进行数据整合

```r
#--- loading ---#
library(MouseGastrulationData)
sce.chimera <- WTChimeraData(samples=5:10)
sce.chimera

#--- feature-annotation ---#
library(scater)
rownames(sce.chimera) <- uniquifyFeatureNames(
    rowData(sce.chimera)$ENSEMBL, rowData(sce.chimera)$SYMBOL)

#--- quality-control ---#
drop <- sce.chimera$celltype.mapped %in% c("stripped", "Doublet")
sce.chimera <- sce.chimera[,!drop]

#--- normalization ---#
sce.chimera <- logNormCounts(sce.chimera)

#--- variance-modelling ---#
library(scran)
dec.chimera <- modelGeneVar(sce.chimera, block=sce.chimera$sample)
chosen.hvgs <- dec.chimera$bio > 0

#--- merging ---#
library(batchelor)
set.seed(01001001)
merged <- correctExperiments(sce.chimera, 
    batch=sce.chimera$sample, 
    subset.row=chosen.hvgs,
    PARAM=FastMnnParam(
        merge.order=list(
            list(1,3,5), # WT (3 replicates)
            list(2,4,6)  # td-Tomato (3 replicates)
        )
    )
)

#--- clustering ---#
g <- buildSNNGraph(merged, use.dimred="corrected")
clusters <- igraph::cluster_louvain(g)
colLabels(merged) <- factor(clusters$membership)

#--- dimensionality-reduction ---#
merged <- runTSNE(merged, dimred="corrected", external_neighbors=TRUE)
merged <- runUMAP(merged, dimred="corrected", external_neighbors=TRUE)
```

拿到数据，先要有个初步的认识

```r
> merged
class: SingleCellExperiment 
dim: 14699 19426 
metadata(2): merge.info pca.info
assays(3): reconstructed counts logcounts
rownames(14699): Xkr4 Rp1 ... Vmn2r122
  CAAA01147332.1
rowData names(3): rotation ENSEMBL SYMBOL
colnames(19426): cell_9769 cell_9770 ...
  cell_30701 cell_30702
colData names(13): batch cell ... sizeFactor
  label
reducedDimNames(3): corrected TSNE UMAP
altExpNames(0):

# 有哪些样本信息
> names(colData(merged))
 [1] "batch"           "cell"           
 [3] "barcode"         "sample"         
 [5] "stage"           "tomato"         
 [7] "pool"            "stage.mapped"   
 [9] "celltype.mapped" "closest.cell"   
[11] "doub.density"    "sizeFactor"     
[13] "label"  

# 看下三个批次
> table(merged$pool)

    3     4     5 
 3324  5644 10458 

# 看下两种处理
> table(merged$tomato)

FALSE  TRUE 
10331  9095 

# 看下分群结果
> table(colLabels(merged))

   1    2    3    4    5    6    7    8    9   10 
 947  112  868  680  606  507 2208  424 1259  252 
  11   12   13   14   15   16   17   18   19   20 
 104  727   58  423 1044  872  432 1264  454 1022 
  21   22   23   24   25   26 
 211  160  156 1640  860 2136 

# 当然也可以把三个批次和分群结果放一起看
table(colLabels(merged), merged$pool)
# 或者把两个处理和分群结果放一起看
table(colLabels(merged), merged$tomato)

# 再直观一点，看看数据整合的效果如何，也就是批次效应处理如何
gridExtra::grid.arrange(
    plotTSNE(merged, colour_by="tomato", text_by="label"),
    plotTSNE(merged, colour_by=data.frame(pool=factor(merged$pool))),
    ncol=2
)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-13-093633.png)

其实现在有了分群结果，下一步应该进行marker基因检测以及细胞注释了，但这里的目的不是这些，所以这些步骤略去了，直接使用([Pijuan-Sala et al. 2019](https://pubmed.ncbi.nlm.nih.gov/30787436/)) 小鼠早期胚胎发育数据集做的的细胞类型。这个信息放在了`merged$celltype.mapped`中。不过为了看一下使用他人注释数据用在我们自己的数据质量如何，可以用热图来看二者重叠程度

```r
by.label <- table(colLabels(merged), merged$celltype.mapped)
pheatmap::pheatmap(log2(by.label+1), cluster_cols=FALSE, cluster_rows=FALSE,
    color=viridis::viridis(101))
```

通过这个图可以看到，我们这里存在多对多的关系。也反映出：**针对发育分化的细胞，细胞类型注释是个难题。**

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-13-094008.png)

## 2 不同处理间差异基因表达分析 DE analysis

> 这也是最最常见的分析思路，一般拿到转录组表达量就会先想着把差异分析做一做 针对单细胞的差异分析方法有多种，一般的综述都会提及。这里使用的方法是“模拟bulk转录组表达量”的模式([Tung et al. 2017](https://pubmed.ncbi.nlm.nih.gov/28045081/))

### **2.1 首先构造一个“拟bulk转录组”的样本**

它的思路是：根据不同细胞类型和不同分群，把对应的细胞表达量加起来。比如根据上面得到的细胞分群信息和注释信息

```r
> colData(merged)[,c("celltype.mapped", "sample")]
DataFrame with 19426 rows and 2 columns
                        celltype.mapped    sample
                            <character> <integer>
cell_9769                    Mesenchyme         5
cell_9770                   Endothelium         5
cell_9771                     Allantois         5
cell_9772                    Erythroid3         5
cell_9773                    Erythroid1         5
...                                 ...       ...
cell_30698                   Erythroid2        10
cell_30699                   Erythroid3        10
cell_30700             Surface ectoderm        10
cell_30701 Forebrain/Midbrain/Hindbrain        10
cell_30702                   Erythroid3        10
```

然后根据这两个信息作为分组，将各自的组内细胞表达量分别加起来。

```r
summed <- aggregateAcrossCells(merged, 
    id=colData(merged)[,c("celltype.mapped", "sample")])

> dim(merged);dim(summed)
[1] 14699 19426
[1] 14699   186
```

可见大大降低了样本数量，原来是一个细胞当做一个样本对待；现在是整合后的一组细胞当做一个样本。这样做的原因有以下几个：

* bulk转录组软件的标准差异分析流程需要比较大的表达量，而单细胞中存在很多0表达量
* 将多个细胞汇成一组，也不是随便挑的几个细胞，就像上面它是根据两个条件选的同组细胞。就像是生物体内的复制过程，虽然这些细胞之间也存在着一些差异，但相比于组外的细胞，它们之间更相似。而如果直接把每个细胞的表达量传给差异分析工具，它会认为每个细胞就是一个独立的生物样本，这也是和真实情况不符的

### **2.2 进行差异分析**

差异分析基于edgeR的 quasi-likelihood (QL)方法，使用负二项广义线性模型(NB GLM) 来处理过度分散的表达量数据。

**先来回忆一下bulk转录组的edegR分析怎么做吧：**

```r
rm(list = ls())
options(stringsAsFactors = F)

load('airway.expreSet.Rdata')
library(edgeR)
##  第一步：创建edgeR对象
# 这里需要表达矩阵和分组信息
e <- DGEList(counts=expr,group=factor(grp))

## 第二步：预处理
# 进行一下过滤
keep <- rowSums(cpm(e)>1) >= 2
e <- e[keep, , keep.lib.sizes=FALSE]
# 进行一下校正
e$samples$lib.size <- colSums(e$counts)
e <- calcNormFactors(e)
e$samples

DEG=e
## 第三步：创建模型并分析
# 这里得到分组矩阵（实验设计矩阵）
design <- model.matrix(~0+factor(grp))
rownames(design)<-colnames(DEG)
colnames(design)<-levels(factor(grp))

DEG <- estimateGLMCommonDisp(DEG,design)
DEG <- estimateGLMTrendedDisp(DEG, design)
DEG <- estimateGLMTagwiseDisp(DEG, design)

fit <- glmFit(DEG, design)
# edgeR User guide (Page. 30 => "GLM Approach")
lrt <- glmLRT(fit,  contrast=c(-1,1)) # accoding to design to modify
# or we can use another way (glmQLFTest):
# CasevsCtrl <- makeContrasts(Case-Ctrl=trt-untrt, levels=design)
# lrt <- glmQLFTest(fit,contrast=CasevsCtrl)

nrDEG=topTags(lrt, n=nrow(DEG))
nrDEG=as.data.frame(nrDEG)
```

**由于我们前面使用了细胞类型+批次信息两个分组条件，因此这里需要先锁定一个再分析另一个**。意思就是我们可以先指定一个细胞类型，然后去进行这3个批次（各2个重复）共6个样本的差异分析，比如可以指定细胞类型是：间质细胞

```r
label <- "Mesenchyme"
current <- summed[,label==summed$celltype.mapped]
# 于是从原来的186组样本又再次过滤为了6组样本
> dim(current)
[1] 14699     6
```

**第一步：创建edgeR对象**

```r
library(edgeR)
y <- DGEList(counts(current), samples=colData(current))
y
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-13-132521.png)

**第二步：预处理**

首先是去除低质量样本（担心影响后面的归一化）：不过这里的文库大小不是像原来一样计算每个样本的表达量加和。因为这里已经将细胞分组，看到的6不是6个细胞，而是6组细胞。因此**这里的计算标准是：每组内含有不少于多少的细胞。** 例如(Crowell et al. [2019](https://osca.bioconductor.org/multi-sample-comparisons.html#ref-crowell2019discovery))就定义了如果一组包含少于20个细胞，就是非常低的文库

```r
discarded <- current$ncells < 20
y <- y[,!discarded]
summary(discarded)
##    Mode   FALSE 
## logical       6
```

然后去除低质量基因（目的是增加后面模型构建的准确度，减少多重矫正的压力）：先得到一个logCPM阈值，然后过滤。可以看到下面过滤掉了不少基因

```r
keep <- filterByExpr(y, group=current$tomato)
y <- y[keep,]
summary(keep)
##    Mode   FALSE    TRUE 
## logical    9011    5688
```

最后，归一化矫正：使用TMM（trimmed mean of M-values）方法，也算得上是`calcNormFactors()`函数的标志了

```r
y <- calcNormFactors(y)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-13-134040.png)

**第三步：模型并分析**

这部分的重点就是这个分组矩阵了，下面锁定样本中批次（pool）的差异，而保留注射导致的差异（tomato）

```r
design <- model.matrix(~factor(pool) + factor(tomato), y$samples)
design
##         (Intercept) factor(pool)4 factor(pool)5 factor(tomato)TRUE
## Sample1           1             0             0                  1
## Sample2           1             0             0                  0
## Sample3           1             1             0                  1
## Sample4           1             1             0                  0
## Sample5           1             0             1                  1
## Sample6           1             0             1                  0
## attr(,"assign")
## [1] 0 1 1 2
## attr(,"contrasts")
## attr(,"contrasts")$`factor(pool)`
## [1] "contr.treatment"
## 
## attr(,"contrasts")$`factor(tomato)`
## [1] "contr.treatment"
```

接下来就是新增部分了，使用`estimateDisp()`来估计负二项分布negative binomial (NB)

```r
y <- estimateDisp(y, design)
summary(y$trended.dispersion)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##  0.0103  0.0167  0.0213  0.0202  0.0235  0.0266
```

接着用`glmQLFit()` 估计 quasi-likelihood分布【具体的统计知识可以日后再理解，这里不是重点】

```r
fit <- glmQLFit(y, design, robust=TRUE)
```

用`glmQLFTest()`看一下表达量差异，差异基因是logFC不为0（`logFC=1`也就是`treat/control=1`）并且FDR小于5%的

```r
res <- glmQLFTest(fit, coef=ncol(design))
summary(decideTests(res))
##        factor(tomato)TRUE
## Down                    8
## NotSig               5672
## Up                      8
```

看到**很少有基因是差异表达的，说明注射这个条件对间质细胞的表达量影响不大**

最后，得到一个整合的结果

```r
topTags(res)
## Coefficient:  factor(tomato)TRUE 
##            logFC logCPM       F    PValue       FDR
## Phlda2   -4.3874  9.934 1638.59 1.812e-16 1.031e-12
## Erdr1     2.0691  8.833  356.37 1.061e-11 3.017e-08
## Mid1      1.5191  6.931  120.15 1.844e-08 3.497e-05
## H13      -1.0596  7.540   80.80 2.373e-07 2.527e-04
## Kcnq1ot1  1.3763  7.242   83.31 2.392e-07 2.527e-04
## Akr1e1   -1.7206  5.128   79.31 2.665e-07 2.527e-04
## Zdbf2     1.8008  6.797   83.66 6.809e-07 5.533e-04
## Asb4     -0.9235  7.341   53.45 2.918e-06 2.075e-03
## Impact    0.8516  7.353   50.31 4.145e-06 2.620e-03
## Lum      -0.6031  9.275   41.67 1.205e-05 6.851e-03
```

### **2.3 循环操作**

上面2.2部分是针对间质细胞这一种细胞类型，发现基本没有和注射这个因素相关的差异基因。但是还存在其他很多种细胞类型，因此想到了：如何进行批处理？

最简单的办法是利用scran包的`pseudoBulkDGE()` ，它会对每个细胞类型进行操作，但依然是需要准备好数据

**首先是过滤细胞**

直接基于“拟bulk转录组”的数据

```r
summed.filt <- summed[,summed$ncells >= 20]
> dim(summed.filt)
[1] 14699   126
```

**然后构建分组矩阵**

在2.2中，因为指定了某个细胞类型，所以最后就是得到了这个批次下的6个处理

这里也是，即使使用了全部的细胞类型，但还是要限制为6个处理，因此可以只获得每个处理出现第一次的细胞

```r
targets <- colData(merged)[!duplicated(merged$sample),]
> dim(targets)
[1]  6 13

# 再根据这个分组信息构建矩阵
design <-  model.matrix(~factor(pool) + factor(tomato), data=targets)
rownames(design) <- targets$sample
```

**最后就可以使用pseudoBulkDGE()**

```r
library(scran)
de.results <- pseudoBulkDGE(summed.filt, 
    sample=summed.filt$sample,
    label=summed.filt$celltype.mapped,
    design=design,
    coef=ncol(design),
    condition=targets$tomato 
)
```

这个结果是这样的：

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-15-014106.png)

至于这5列是什么内容，可以看看，**里面既有logFC判断上下调，又有FDR判断显著性**：

```r
> de.results$Allantois[1:3,1:5]
DataFrame with 3 rows and 5 columns
          logFC    logCPM         F    PValue       FDR
      <numeric> <numeric> <numeric> <numeric> <numeric>
Xkr4         NA        NA        NA        NA        NA
Rp1          NA        NA        NA        NA        NA
Sox17 -0.139844     4.909  0.335911  0.562224  0.891031
```

### **2.4 看一下循环操作的结果**

**检查一下结果，例如看看每个细胞类型选FDR小于5%（即显著）的差异基因数量**。注意到这里有一列NA，它指的是：要么这个细胞类型中基因由于表达量太低被过滤掉，要么就是在这个细胞类型中没办法比较。另外1表示显著上调，-1表示显著下调，0表示差异不显著

```r
is.de <- decideTestsPerLabel(de.results, threshold=0.05)
> dim(is.de)
[1] 14699    26

# 记录了每个基因在每个类型中是否差异
> is.de[1:3,1:3]
      Allantois Blood progenitors 2 Cardiomyocytes
Xkr4         NA                  NA             NA
Rp1          NA                  NA             NA
Sox17         0                  NA             NA

summarizeTestsPerLabel(is.de)
##                                -1    0  1    NA
## Allantois                      69 5048 66  9516
## Blood progenitors 2             1 2472  2 12224
## Cardiomyocytes                  6 4361  5 10327
## Caudal epiblast                 0    0  0 14699
## Caudal Mesoderm                 0    0  0 14699
## Def. endoderm                   0    0  0 14699
## Endothelium                     3 3222  6 11468
## Erythroid1                     12 3035 25 11627
## Erythroid2                      5 3389  8 11297
## Erythroid3                     13 5048 16  9622
## ExE ectoderm                    0    0  0 14699
## ExE mesoderm                    2 5097 10  9590
## Forebrain/Midbrain/Hindbrain    8 6226 11  8454
## Gut                             5 4482  6 10206
## Haematoendothelial progenitors  7 4347 17 10328
## Intermediate mesoderm           6 3256  8 11429
## Mesenchyme                      8 5672  8  9011
## Neural crest                    6 3311  8 11374
## NMP                             6 4107 10 10576
## Paraxial mesoderm               4 4756  5  9934
## Parietal endoderm               0    0  0 14699
## Pharyngeal mesoderm             2 5082  9  9606
## Rostral neurectoderm            0    0  0 14699
## Somitic mesoderm                7 2948 13 11731
## Spinal cord                     7 4591  7 10094
## Surface ectoderm                9 5556  8  9126
```

**还可以看看哪些上、下调差异基因在各种类型细胞中比较多**

```r
# 上调
up.de <- is.de > 0 & !is.na(is.de)
head(sort(rowMeans(up.de), decreasing=TRUE), 10)
##     Mid1    Erdr1   Impact    Mcts2     Nnat Kcnq1ot1  Slc38a4    Zdbf2 
##   0.7692   0.6538   0.5385   0.5000   0.5000   0.5000   0.3846   0.3462 
##     Hopx     Peg3 
##   0.3462   0.2308

# 下调
down.de <- is.de < 0 & !is.na(is.de)
head(sort(rowMeans(down.de), decreasing=TRUE), 10)
##        Akr1e1          Xist        Cdkn1c        Phlda2           H13 
##       0.61538       0.57692       0.57692       0.57692       0.46154 
##         Wfdc2         Hbb-y         Grb10 B930036N10Rik         Pink1 
##       0.19231       0.11538       0.11538       0.07692       0.07692
```

**还可以看：哪些差异基因是在某个细胞类型中特有的**

也就是这些基因仅仅在某一个类型的细胞中是差异表达的，在其他类型细胞中不是差异表达。

```r
# 首先复制一份差异分析的结果
remotely.de <- decideTestsPerLabel(de.results, threshold=0.5)
# 从中把非差异基因选出来
not.de <- remotely.de==0 | is.na(remotely.de)
# 如果我们只关注Allantois这个细胞类型，那就把剩余细胞类型设为“其他”
other.labels <- setdiff(colnames(not.de), "Allantois")
# 从差异基因中找Allantois的，同时需要排除那些也存在于其他细胞类型的基因：即选择非差异基因中其他细胞类型均为True的，也就是下面rowMeans(not.de[,other.labels])==1
unique.degs <- is.de[,"Allantois"]!=0 & rowMeans(not.de[,other.labels])==1
# 提取基因名
unique.degs <- names(which(unique.degs))

# 看一下差别：如果只看Allantois中存在的是14699个，如果看它特有的是44个
> length(is.de[,"Allantois"]!=0)
[1] 14699
> length(unique.degs)
[1] 44
```

**还可以将这些基因排个序**（例如下面按Pvalue排序）

```r
de.allantois <- de.results$Allantois
de.allantois <- de.allantois[order(de.allantois$PValue),]
de.allantois <- de.allantois[rownames(de.allantois) %in% unique.degs,]

> head(de.allantois)
DataFrame with 6 rows and 5 columns
             logFC    logCPM         F      PValue         FDR
         <numeric> <numeric> <numeric>   <numeric>   <numeric>
Slc22a18 -4.629906   3.87065  118.8863 2.19531e-27 1.42228e-24
Eif2s3y   1.757650   5.84561   74.0124 1.01564e-17 4.04928e-15
Rbp4      1.978988   4.38295   32.7443 1.11016e-08 1.74363e-06
Cfc1     -0.884383   5.66713   23.7376 1.13690e-06 1.33922e-04
Pdgfa    -0.423246   8.70475   23.4935 1.28991e-06 1.48569e-04
H3f3b     0.269312  12.07841   22.8530 1.79717e-06 2.02494e-04
```

如果说某些细胞类型没有重复或没有对照，它们就不好做差异分析，这些“失败”的类型可以提取出来：

```r
metadata(de.results)$failed
## [1] "Caudal epiblast"      "Caudal Mesoderm"      "Def. endoderm"       
## [4] "ExE ectoderm"         "Parietal endoderm"    "Rostral neurectoderm"
```

## 3 差异细胞丰度分析 DA analysis

> 检测不同条件下细胞类型(或状态等)组成的变化 如果说DE analysis重点在基因的表达量变化，那么DA analysis就着眼于细胞数量的变化

差异丰度分析在流式细胞术中应用较多，常被用于检查不同条件对复杂细胞群体组成的影响。我们这里也类比FACS技术，对scRNA的细胞也做一个“标记”，然后看看处理前后标记的丰度变化程度。

还是先将细胞按两个维度（细胞类型+处理）归类

```r
abundances <- table(merged$celltype.mapped, merged$sample) 
> class(abundances)
[1] "table"

abundances <- unclass(abundances) #它的作用显而易见
> class(abundances)
[1] "matrix" "array" 

> dim(abundances)
[1] 34  6
head(abundances)
##                      
##                        5  6   7   8   9  10
##   Allantois           97 15 139 127 318 259
##   Blood progenitors 1  6  3  16   6   8  17
##   Blood progenitors 2 31  8  28  21  43 114
##   Cardiomyocytes      85 21  79  31 174 211
##   Caudal epiblast      2  2   0   0  22  45
##   Caudal Mesoderm     10 10   9   3  10  29
```

这个abundances结果，依然可以类比为原来的差异表达分析，即行为基因列为样本，我们要对样本间进行差异比较（这里也就是对6个处理进行比较）

### **3.1 基于edgeR做DA analysis**

首先为列的样本信息增加一些内容：

```r
extra.info <- colData(merged)[match(colnames(abundances), merged$sample),]
y.ab <- DGEList(abundances, samples=extra.info)
y.ab
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-15-023559.png)

然后也还是按行过滤，只不过这里不是根据基因的表达量了，而是按照细胞数量

```r
keep <- filterByExpr(y.ab, group=y.ab$samples$tomato)
y.ab <- y.ab[keep,]
summary(keep)
##    Mode   FALSE    TRUE 
## logical      10      24
```

> 关于filterByExpr函数是如何过滤的【参考帮助文档】： 宗旨：keeps genes that have at least `min.count` reads in a worthwhile number samples 依据：the filtering keeps genes that have count-per-million (CPM) above k in *n* samples 定义：k is determined by `min.count`and by the sample library sizes； n is determined by the design matrix.

这里不再利用`calcNormFactors()`这么复杂的归一化方法，我们只需要简单矫正一个文库大小即可，具体原因下面再讨论

下面的步骤其实和差异基因分析很像：

```r
# 分组矩阵
design <- model.matrix(~factor(pool) + factor(tomato), y.ab$samples)
# 对每个细胞类型估计负二项分布（由于细胞数量远不如基因数量，点不够就不用设置趋势线）
y.ab <- estimateDisp(y.ab, design, trend="none")
# 再进行一个QL（ quasi-likelihood） 分布
fit.ab <- glmQLFit(y.ab, design, robust=TRUE, abundance.trend=FALSE)
# 检验
res <- glmQLFTest(fit.ab, coef=ncol(design))
summary(decideTests(res))
##        factor(tomato)TRUE
## Down                    1
## NotSig                 22
## Up                      1

# 看结果
topTags(res)
## Coefficient:  factor(tomato)TRUE 
##                                  logFC logCPM      F    PValue       FDR
## ExE ectoderm                   -6.5663  13.02 66.267 1.352e-10 3.245e-09
## Mesenchyme                      1.1652  16.29 11.291 1.535e-03 1.841e-02
## Allantois                       0.8345  15.51  5.312 2.555e-02 1.621e-01
## Cardiomyocytes                  0.8484  14.86  5.204 2.701e-02 1.621e-01
## Neural crest                   -0.7706  14.76  4.106 4.830e-02 2.149e-01
## Endothelium                     0.7519  14.29  3.912 5.371e-02 2.149e-01
## Erythroid3                     -0.6431  17.28  3.604 6.367e-02 2.183e-01
## Haematoendothelial progenitors  0.6581  14.72  3.124 8.351e-02 2.505e-01
## ExE mesoderm                    0.3805  15.68  1.181 2.827e-01 6.258e-01
## Pharyngeal mesoderm             0.3793  15.72  1.169 2.850e-01 6.258e-01
```

**注意：**

这几个步骤里面的**重点其实一直都是`model.matrix`的设置**，其他的统计计算我们并不关心，根据edgeR的官方文档，**设置的前后顺序很重要**：

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-15-033544.png)

也就是说，**这里的6个处理样本同时具有两个属性**，即`pool`和`tomato`，因此要对这6个样本进行差异分析，那就必须把这两个属性都考虑进去，但哪个更重要我们需要掂量一下

```r
# pool是哪两个样本属于重复
> y.ab$samples$pool
[1] 3 3 4 4 5 5
# tomato指处理还是对照
> y.ab$samples$tomato
[1]  TRUE FALSE  TRUE FALSE  TRUE FALSE
```

`pool` 即每个处理的来源是不关心的，我们关心的是是否有tomato处理。因此pool放在了前面

### **3.2 处理细胞组成的影响**

前面说不再利用`calcNormFactors()`这么复杂的归一化方法，因为这种方法的假设是：基本每一行（feature）在各个列都是存在的，也就是说大部分基因在所有细胞中都是存在的。但是对于我们这里的DA分析来讲，行（细胞类型）不一定在所有的列（不同处理）中都有。很多实验中只包含少数几个细胞类型，如果还是按照这种计算方法，会对真实的细胞数量产生较大的干扰。

因此，默认的操作就是基于不同处理样本的细胞总数进行归一化处理。这个在技术和统计上说得过去，但落实到生物意义上又有争议。如果一个处理中某种类型的细胞丰度很大，在归一化后，就会导致其他几个类型的细胞占比下降，从而可能得到错误结论：认为这个处理下的其他几种类型的细胞数量会减少。

下面有几种处理方式：

**方法一：首先假设大部分的细胞类型在各个处理中都是存在的**

这个假设对于野生型/对照组来说是合理的，因为它不需要考虑注射处理后产生的不同结果。 这样就可以利用`calcNormFactors()`去计算

```r
y.ab2 <- calcNormFactors(y.ab)
y.ab2$samples$norm.factors
y.ab2 <- estimateDisp(y.ab2, design, trend="none")
fit.ab2 <- glmQLFit(y.ab2, design, robust=TRUE, abundance.trend=FALSE)
res2 <- glmQLFTest(fit.ab2, coef=ncol(design))
topTags(res2, n=10)
## Coefficient:  factor(tomato)TRUE 
##                                  logFC logCPM      F    PValue       FDR
## ExE ectoderm                   -6.9215  13.17 70.364 5.738e-11 1.377e-09
## Mesenchyme                      0.9513  16.27  6.787 1.219e-02 1.143e-01
## Neural crest                   -1.0032  14.78  6.464 1.429e-02 1.143e-01
## Erythroid3                     -0.8504  17.35  5.517 2.299e-02 1.380e-01
## Cardiomyocytes                  0.6400  14.84  2.735 1.047e-01 4.809e-01
## Allantois                       0.6054  15.51  2.503 1.202e-01 4.809e-01
## Forebrain/Midbrain/Hindbrain   -0.4943  16.55  1.928 1.713e-01 5.178e-01
## Endothelium                     0.5482  14.27  1.917 1.726e-01 5.178e-01
## Erythroid2                     -0.4818  16.00  1.677 2.015e-01 5.373e-01
## Haematoendothelial progenitors  0.4262  14.73  1.185 2.818e-01 6.240e-01
```

**方法二：移除那些细胞数量很多的细胞类型**

采用的常规的归一化处理方法，只是考虑到那些丰度大的细胞类型对整体的影响，于是可以去除它们。

比如我们认为ExE ectoderm这个细胞类型中细胞数量太多

```r
offenders <- "ExE ectoderm"
# keep.lib.sizes=FALSE参数意为重置文库大小：reset the total number of cells for all samples
y.ab3 <- y.ab[setdiff(rownames(y.ab), offenders),, keep.lib.sizes=FALSE]
y.ab3 <- estimateDisp(y.ab3, design, trend="none")
fit.ab3 <- glmQLFit(y.ab3, design, robust=TRUE, abundance.trend=FALSE)
res3 <- glmQLFTest(fit.ab3, coef=ncol(design))
topTags(res3, n=10)
## Coefficient:  factor(tomato)TRUE 
##                                  logFC logCPM      F   PValue     FDR
## Mesenchyme                      1.1274  16.32 11.501 0.001438 0.03308
## Allantois                       0.7950  15.54  5.231 0.026836 0.18284
## Cardiomyocytes                  0.8104  14.90  5.152 0.027956 0.18284
## Neural crest                   -0.8085  14.80  4.903 0.031798 0.18284
## Erythroid3                     -0.6808  17.32  4.387 0.041743 0.19202
## Endothelium                     0.7151  14.32  3.830 0.056443 0.21636
## Haematoendothelial progenitors  0.6189  14.76  2.993 0.090338 0.29683
## Def. endoderm                   0.4911  12.43  1.084 0.303347 0.67818
## ExE mesoderm                    0.3419  15.71  1.036 0.314058 0.67818
## Pharyngeal mesoderm             0.3407  15.76  1.025 0.316623 0.67818
```


# 3.10 检测Doublet

刘小泽写于2020.7.15

## 1 前言

scRNA中，**doublets指的就是一个文库中存在两个细胞的情况**。一般是由于技术误差导致的（比如细胞分选、捕获），尤其在包含几千个细胞的基于液滴的技术中比较突出 ([Zheng et al. 2017](https://pubmed.ncbi.nlm.nih.gov/28091601/))。它干扰了对单个细胞表达量以及细胞形态的判断，比如一个液滴中有两个细胞，会通过误导我们这个细胞可能处于分化的“过渡态”。因此检测和去除这部分的影响至关重要。

一个比较通用的检测方法是：单纯根据表达量([Dahlin et al. 2018](https://pubmed.ncbi.nlm.nih.gov/29588278/))。下面将利用一个10X数据来展示两种检测方法，它们的主要区别是我们是否需要提前知道分群的信息

### **数据准备**

```r
#--- loading ---#
library(scRNAseq)
sce.mam <- BachMammaryData(samples="G_1")

#--- gene-annotation ---#
library(scater)
rownames(sce.mam) <- uniquifyFeatureNames(
    rowData(sce.mam)$Ensembl, rowData(sce.mam)$Symbol)

library(AnnotationHub)
ens.mm.v97 <- AnnotationHub()[["AH73905"]]
rowData(sce.mam)$SEQNAME <- mapIds(ens.mm.v97, keys=rowData(sce.mam)$Ensembl,
    keytype="GENEID", column="SEQNAME")

#--- quality-control ---#
is.mito <- rowData(sce.mam)$SEQNAME == "MT"
stats <- perCellQCMetrics(sce.mam, subsets=list(Mito=which(is.mito)))
qc <- quickPerCellQC(stats, percent_subsets="subsets_Mito_percent")
sce.mam <- sce.mam[,!qc$discard]

#--- normalization ---#
library(scran)
set.seed(101000110)
clusters <- quickCluster(sce.mam)
sce.mam <- computeSumFactors(sce.mam, clusters=clusters)
sce.mam <- logNormCounts(sce.mam)

#--- variance-modelling ---#
set.seed(00010101)
dec.mam <- modelGeneVarByPoisson(sce.mam)
top.mam <- getTopHVGs(dec.mam, prop=0.1)

#--- dimensionality-reduction ---#
library(BiocSingular)
set.seed(101010011)
sce.mam <- denoisePCA(sce.mam, technical=dec.mam, subset.row=top.mam)
sce.mam <- runTSNE(sce.mam, dimred="PCA")

#--- clustering ---#
snn.gr <- buildSNNGraph(sce.mam, use.dimred="PCA", k=25)
colLabels(sce.mam) <- factor(igraph::cluster_walktrap(snn.gr)$membership)

sce.mam
## class: SingleCellExperiment 
## dim: 27998 2772 
## metadata(0):
## assays(2): counts logcounts
## rownames(27998): Xkr4 Gm1992 ... Vmn2r122 CAAA01147332.1
## rowData names(3): Ensembl Symbol SEQNAME
## colnames: NULL
## colData names(5): Barcode Sample Condition sizeFactor label
## reducedDimNames(2): PCA TSNE
## altExpNames(0):
```

或者[直接加载分享的数据](https://share.weiyun.com/ReZZnAMw)

## 2 两种检测方法

### **2.1 基于分群结果的检测**

使用`doubletCluster()` 将任一cluster与其他另外两个clusters的表达量进行比较，即3个cluser为一组，其中一个是query，另外两个是source。基于的原假设是：query cluster的细胞中如果包含doublet，那么它是来自两个source clusters。 ([Bach et al. 2017](https://pubmed.ncbi.nlm.nih.gov/29225342/))

> 参考帮助文档，它的具体做法是： For each “query” cluster, we examine **all possible pairs of “source” clusters,** hypothesizing that the **query consists of doublets formed from the two sources**.
>
> If so, gene expression in the query cluster should be strictly i**ntermediate between the two sources** after library size normalization.

```r
library(scran)
# sce.mam一共分了10群，所以下面的结果也是10行，每一群都做了一次检测
> table(sce.mam$label)

  1   2   3   4   5   6   7   8   9  10 
550 799 716 452  24  84  52  39  32  24 

dbl.out <- doubletCluster(sce.mam)
> dim(dbl.out)
[1] 10  9
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-15-080546.png)

返回的结果包括：

* 基因数量N：query cluster与另外两个source cluster相比特有的差异基因，它的数量多少可以为拒绝原假设提供依据。这个基因数量越少，query越有可能是doublets
* 文库大小比例 lib.size：每个source cluster的各个细胞文库大小中位数 / query cluster中的各个细胞文库大小中位数，因此两个source就对应两个lib.size。我们知道doublets是一个文库包含两个细胞，因此它会比单个细胞的文库更大。这个值越小，query越可能是doublets
* 占全部细胞的百分比 prop：query中的细胞数量占全部细胞的百分比，一般这个值小于5%，不过也取决于10X机器上样的数量

最后主要还是看N的数量，可以将这个结果按照N排个序

```r
library(scater)
chosen.doublet <- rownames(dbl.out)[isOutlier(dbl.out$N, 
    type="lower", log=TRUE)]
chosen.doublet
## [1] "6"
```

挑出来怀疑对象，可以对cluster6进一步检查

比如找到cluster6的marker基因

```r
markers <- findMarkers(sce.mam, direction="up")
dbl.markers <- markers[[chosen.doublet]]
> dim(dbl.markers)
[1] 27998    13
# 然后找Top10基因
library(scater)
chosen <- rownames(dbl.markers)[dbl.markers$Top <= 10]
> length(chosen)
[1] 43
# 最后热图
plotHeatmap(sce.mam, order_columns_by="label", features=chosen, 
    center=TRUE, symmetric=TRUE, zlim=c(-5, 5))
```

看到这个cluster的marker基因，是不是在它的source cluster（即cluster1、2）中也有类似的表达模式？

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-15-083507.png)

另外，基于背景知识，没有细胞会同时表达basal cells (**Acta2**) and alveolar cells (**Csn2**) 这两个基因，但是看到cluster6中这两个基因表达量都较高，再一次证明了我们的假设：cluster6是一个doublet混合体，而不是纯粹的一个细胞类型

```r
plotExpression(sce.mam, features=c("Acta2", "Csn2"), 
    x="label", colour_by="label")
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-15-083828.png)

**注意**

从上面也能看到，`doubletCluster()`的优点就是方便操作，并且结果比较好理解。一旦有怀疑的cluster，就可以立即检查。但缺点是高度依赖分群的质量，如果分群不好，那么这个结果的可信度就会大打折扣。另外，如果真的有某个亚群中细胞数量很少，带来的结果就是：N小，让这个亚群很有可能成为怀疑对象。

不过，随着scRNA的技术改进，这个doublets情况会逐渐好转

### **2.2 基于模拟推断的检测**

将利用来自scran包的`doubletCells()` ，它基于的假设是：模拟的doublets和真实的doublets接近

它的算法是：

* 随机选取两个原始单细胞表达量，加和，当做一个模拟的doublet，这样操作上千次
* 对每一个原始细胞，看看它附近有多少的模拟的doublet，并计算密度
* 对每一个原始细胞，同时计算它附近的其他原始细胞的数量，并计算密度
* 对每一个细胞，计算两个密度的比值，作为“doublet score”

为了加快密度的计算，这个函数会进行一个PCA以及log转换

```r
library(BiocSingular)
set.seed(100)

dbl.dens <- doubletCells(sce.mam, subset.row=top.mam, 
    d=ncol(reducedDim(sce.mam)))
summary(dbl.dens)
##     Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
##     0.00     7.63    21.04   395.42    49.30 39572.52
```

然后把计算结果的doublet score画出来，数值较高的细胞聚成一团

```r
sce.mam$DoubletScore <- log10(dbl.dens+1)
plotTSNE(sce.mam, colour_by="DoubletScore")
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-15-085115.png)

同时，结合之前`doubletCluster()`的结果看一眼：**多么明显的cluster6！**

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-15-085508.png)

**注意**

`doubletCells()` 的优点就是不需要依赖分群结果，降低了分群的影响。缺点是需要对doublets的模拟更精确，真的要保证它和真实的情况接近。

另外简单去掉那些doublet scores较高的细胞有时也是不够的，例如一个潜在的doublet cluster中只有一小部分的分值高，去掉它们剩下的细胞依然会干扰判断。那么问题来了，**怎么样才叫高的doublet scores呢？是不是得设置一个阈值？**&#x5C31;像刚才这种情况，如果把阈值降低会不会就多包括了一些潜在的doublets呢？其实这也是生信中的一个比较头疼的问题，没有一个固定的阈值或者范围，一切都是相对的。

比较推荐的方法是将`doubletCells()` 的结果再用分群展示出来，就像上面的图，可以更直观看到那些细胞影响较大。

#### 小结

* 检测doublet操作必须要求数据来自同一批次，因为doublet也不会来自两次捕获的细胞，它毕竟是一个文库，只是包含两个细胞，因此也不需要担心检测doublet时怎么去除批次效应之类的问题。最好还是在分析前最好先清楚实验设计
* 如果数据中包含了细胞分化轨迹的信息，doublet就不好判断了，因为处于变化状态的细胞就很像doublet，容易被误认


# 3.11 细胞周期推断

刘小泽写于2020.7.16

## 1 前言

有时我们希望从scRNA数据中获得细胞周期的信息，一般来讲，细胞在各个周期的分布可能与真实情况还是存在差距，但是可以用这种方法看看不同亚群之间或者各个处理之间的扩增差异。许多细胞周期相关的关键节点（比如通过检查点）属于转录后调控，在转录组数据中不可见，但通过表达量的变化也可以进行初步的推断。

另外还是先要放一张[细胞周期的图片](https://en.wikipedia.org/wiki/Cell_cycle)

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-16-021255.png)

下面就用416B数据来看看

```r
load('../0-data/clusted.sce.416b.RData')
sce.416b
## class: SingleCellExperiment 
## dim: 46604 185 
## metadata(0):
## assays(3): counts logcounts corrected
## rownames(46604): 4933401J01Rik Gm26206 ... CAAA01147332.1
##   CBFB-MYH11-mcherry
## rowData names(4): Length ENSEMBL SYMBOL SEQNAME
## colnames(185): SLX-9555.N701_S502.C89V9ANXX.s_1.r_1
##   SLX-9555.N701_S503.C89V9ANXX.s_1.r_1 ...
##   SLX-11312.N712_S507.H5H5YBBXX.s_8.r_1
##   SLX-11312.N712_S517.H5H5YBBXX.s_8.r_1
## colData names(11): Source Name cell line ... sizeFactor label
## reducedDimNames(2): PCA TSNE
## altExpNames(2): ERCC SIRV
```

#### 2 使用细胞周期蛋白

**2.1 先找细胞周期蛋白基因，再对照数据集**

细胞周期蛋白（cyclins）控制着整个细胞周期的进展，并在细胞周期各阶段的表达模式具有一定特点。例如：

* cyclin A在S和G2期表达；
* cyclin B在G2后期和M（mitosis有丝分裂期）表达量最高
* cyclin D在整个过程都表达，但在G1期表达最高；
* cyclin E在G1/S过渡期表达最高

根据相关基因在各个cluster的表达可以帮助判断：

```r
cyclin.genes <- grep("^Ccn[abde][0-9]$", rowData(sce.416b)$SYMBOL)
cyclin.genes <- rownames(sce.416b)[cyclin.genes]
cyclin.genes
##  [1] "Ccnb3" "Ccna2" "Ccna1" "Ccne2" "Ccnd2" "Ccne1" "Ccnd1" "Ccnb2" "Ccnb1"
## [10] "Ccnd3"
```

画个热图

```r
library(scater)
plotHeatmap(sce.416b, order_columns_by="label", 
    cluster_rows=FALSE, features=sort(cyclin.genes))
```

图中可以看到：cluster1很明显ccnd系列的基因表达量高，说明它可能是G1期，而其他几个clusters分散在后面的时期

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-16-022843.png)

**2.2 先找marker基因，再对应细胞周期蛋白**

不管怎样，416b数据的对应关系还是很不错的，界限清晰，但并非任何数据都像它一样，尤其是一些异质性较高的数据中，细胞周期可能不是驱动基因表达变化的主力。

不过好在，即使不非常清楚地知道每个cluster的每个细胞所处的周期，也还是能回答一下细胞周期相关的问题。例如：我们可以利用常规的寻找差异基因的方法，找找有没有基因在细胞周期蛋白中上调，来帮助判断某个cluster中是否有更多细胞属于某个细胞周期。

**首先寻找marker基因**

```r
library(scran)
markers <- findMarkers(sce.416b, subset.row=cyclin.genes, 
    test.type="wilcox", direction="up")
# 4个cluster都各自找了细胞周期蛋白基因的排名
> markers
List of length 4
names(4): 1 2 3 4
```

**然后对应细胞周期蛋白**

比如看cluster4，它的Ccnb系列基因AUC指标高于其余的基因，说明在cyclin B中表达量较高，存在更多的细胞处于G2/M期

```r
markers[[4]]
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-16-024423.png)

这种直接通过表达量判断的方法很直观，也很好理解。但需要保证得到每个细胞周期蛋白基因表达量，而且需要一个假设：细胞周期蛋白基因的表达不受其他生物因素的影响，尤其对于那些非常容易混淆正常细胞周期的事件，如恶性癌细胞的扩增。

#### 3 辅以参考数据

就是使用别人已经做好的细胞周期注释数据，来辅助我们自己的数据进行注释【有没有很像之前细胞类型注释的环节？】加入参考注释的目的是：更精确地寻找细胞周期相关的基因

这里使用的参考数据是来自 [Buettner et al. 2015](https://pubmed.ncbi.nlm.nih.gov/25599176/) 的小鼠胚胎干细胞结果

```r
library(scRNAseq)
sce.ref <- BuettnerESCData()
sce.ref
## class: SingleCellExperiment 
## dim: 38293 288 
## metadata(0):
## assays(1): counts
## rownames(38293): ENSMUSG00000000001 ENSMUSG00000000003 ...
##   ENSMUSG00000097934 ENSMUSG00000097935
## rowData names(3): EnsemblTranscriptID AssociatedGeneName GeneLength
## colnames(288): G1_cell1_count G1_cell2_count ... G2M_cell95_count
##   G2M_cell96_count
## colData names(1): phase
## reducedDimNames(0):
## altExpNames(1): ERCC

> table(sce.ref$phase)
 G1 G2M   S 
 96  96  96
```

为了提高判断的精准度，可以除了参考数据集的注释结果外，再加上GO中与细胞周期相关的基因，再与我们自己的基因取个交集

```r
# 细胞周期通路（https://www.ebi.ac.uk/QuickGO/term/GO:0007049）基因
library(org.Mm.eg.db)
cycle.anno <- select(org.Mm.eg.db, keytype="GOALL", keys="GO:0007049", 
    columns="ENSEMBL")[,"ENSEMBL"]
# 再取交集
candidates <- Reduce(intersect, 
    list(rownames(sce.ref), rowData(sce.416b)$ENSEMBL, cycle.anno))
str(candidates)
##  chr [1:1605] "ENSMUSG00000000001" "ENSMUSG00000000028" ...
```

用三者交集的基因，代入到注释好的参考数据集中，继续找marker基因（相当于再过滤一遍）

```r
sce.ref <- logNormCounts(sce.ref)
phase.stats <- pairwiseWilcox(logcounts(sce.ref), sce.ref$phase, 
    direction="up", subset.row=candidates)
cycle.markers <- getTopMarkers(phase.stats[[1]], phase.stats[[2]])
```

将得到的marker基因给到416b数据

```r
# 由于参考数据中的基因ID是Ensembl，所以这里也使用Ensembl
test.data <- logcounts(sce.416b)
rownames(test.data) <- rowData(sce.416b)$ENSEMBL

library(SingleR)
assignments <- SingleR(test.data, ref=sce.ref,
    label=sce.ref$phase, genes=cycle.markers)
tab <- table(assignments$labels, colLabels(sce.416b))
tab
##      
##        1  2  3  4
##   G1  71  7 19  1
##   G2M  2 60  1 13
##   S    5  2  4  0

# 可以再添加一步检验，看看两个cluster之间细胞分布的差异大小
chisq.test(tab[,1:2])
## 
##  Pearson's Chi-squared test
## 
## data:  tab[, 1:2]
## X-squared = 107.91, df = 2, p-value < 2.2e-16
```

可以看到，cluster1大部分的细胞都在G1期，和之前利用细胞周期蛋白得到的结果差不多。但和之前的方法不同的是，这次我们是清楚看到有多少细胞处于哪个时期，而不是通过图去观察

#### 4 用已发表的分类器

例如[Scialdone et al. (2015)](https://pubmed.ncbi.nlm.nih.gov/26142758/)发表了一个`cyclone`的分类器，属于scran包，其中也是包括了小鼠和人的训练数据集

```r
# 加载内置数据
set.seed(100)
library(scran)
mm.pairs <- readRDS(system.file("exdata", "mouse_cycle_markers.rds", 
    package="scran"))

> class(mm.pairs)
[1] "list"
> names(mm.pairs)
[1] "G1"  "S"   "G2M"
> head(mm.pairs$G1)
               first             second
1 ENSMUSG00000000001 ENSMUSG00000001785
2 ENSMUSG00000000001 ENSMUSG00000005470
3 ENSMUSG00000000001 ENSMUSG00000012443
4 ENSMUSG00000000001 ENSMUSG00000015120
5 ENSMUSG00000000001 ENSMUSG00000022033
6 ENSMUSG00000000001 ENSMUSG00000023015

# 进行比较
assignments <- cyclone(sce.416b, mm.pairs, gene.names=rowData(sce.416b)$ENSEMBL)

> class(assignments)
[1] "list"
> names(assignments)
[1] "phases"            "scores"           
[3] "normalized.scores"
> dim(assignments$scores)
[1] 185   3
> head(assignments$scores)
     G1     S   G2M
1 0.663 0.995 0.000
2 0.960 0.456 0.003
3 0.055 0.517 0.144
4 0.000 0.768 1.000
5 0.927 0.473 0.002
6 0.914 0.944 0.000
```

**看这个score结果**

其中对416b数据中的每个细胞都划定了一个分数，分数越高就越倾向于那个周期，比如看看G1和G2/M期

```r
plot(assignments$score$G1, assignments$score$G2M,
    xlab="G1 score", ylab="G2/M score", pch=16)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-16-033614.png)

**判断标准**

如果G1 分数高于0.5并且大于G2/M的分数，那么就判断为G1期；同样的方法可以判断G2/M期；如果二者分数都不大于0.5，那就是S期

```r
table(assignments$phases, colLabels(sce.416b))
##      
##        1  2  3  4
##   G1  74  8 20  0
##   G2M  1 48  0 13
##   S    3 13  4  1
```

这种方法使用简单，就是实际使用过程可能会耗时

#### 5 移除细胞周期导致的差异

有时担心细胞周期带来的数据差异会影响下游分析，如果要去掉这部分影响，可以先按照上面的方法推断出细胞周期，然后将每个周期当成一个独立的批次，再进行批次矫正。最简单的方法是使用线性模型（如`regressBatches()`）来移除这个影响

```r
library(batchelor)
sce.nocycle <- regressBatches(sce.416b, batch=assignments$phases)

# 另外其他支持block的函数也是可以移除这个影响的
dec.nocycle <- modelGeneVarWithSpikes(sce.416b, "ERCC", 
    block=assignments$phases)
marker.nocycle <- findMarkers(sce.416b, block=assignments$phases)
```

不过，这个操作并非常规的分析流程。因为大部分数据中，细胞周期导致的差异是次要的，不会超过细胞真实生物差异的影响。


# 3.12 细胞轨迹推断

刘小泽写于2020.7.16

## 1 前言

接触单细胞数据，相信你一定听过：轨迹推断，英文名词是： Trajectory Analysis。和它相关的另一个名词是：拟时序分析（pseudotime），指的是细胞沿着这个轨迹，并且对潜在的生物活动进行量化。注意这里看字面意思就知道，并不是指真正的时间，而是指细胞与细胞之间的更替、转化的顺序或者是轨迹，可以理解为“一个连续过程的缩影”。

不同的生物过程对应的“拟时序”也是不同的：

![图片来自：https://www.youtube.com/watch?v=XmHDexCtjyw](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-16-070704.png)

许多生物过程都伴随着细胞状态的连续性变化，比如研究发育就会经常使用到。我们可以利用单细胞数据在高维空间画一条线，贯穿于多种细胞状态。最简单是点到点的一条路径，更复杂的还有一个点出发再生成多个分支。

看一下[现在做相关分析的工具](https://broadinstitute.github.io/2019_scWorkshop/pseudotime-cell-trajectories.html)：

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-16-064042.png)

自2014年以后，已经开发出了超50种方法，那么选择何种方法进行分析成为了一个难题，因为我们不可能每一种都试一下，但有评测文章发现：[**Slingshot**](https://bioconductor.org/packages/release/bioc/vignettes/slingshot/inst/doc/vignette.html)**、**[**TSCAN**](https://www.bioconductor.org/packages/release/bioc/vignettes/TSCAN/inst/doc/TSCAN.pdf)**、**[**Monocle DDRTree**](http://cole-trapnell-lab.github.io/monocle-release/docs_mobile/)**这几种方法都不错**

> 当然还有其他的几种方法值得推荐，还做成了一个流程图方便查阅 如果对评测感兴趣，可以看看[dynverse](https://github.com/dynverse/dynverse)

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-16-064932.png)

### 资源整理

* ELIXIR-SE 公开课视频推荐：[Trajectory inference analysis of scRNA-seq data from ELIXIR-SE](https://www.youtube.com/watch?v=XmHDexCtjyw) \
  他们也有[线上课程资料](https://github.com/NBISweden/excelerate-scRNAseq)
* Anthony Gitter整理了一份[轨迹推断工具的清单](https://github.com/agitter/single-cell-pseudotime)

### **做这个分析之前，最好先问几个问题：**

* 确定数据会体现发育轨迹吗？也就是研究的样本是不是和发育相关的？
* 数据中的细胞会体现出中间态吗？
* 是否认为轨迹会出现分支？

### **并且要注意：**

* 任何数据都可以强行画出轨迹，但不一定都有生物学意义！
* 先要保证目前找到的HVGs和降维结果符合我们的预期，才能继续向下分析&#x20;

## 2 学习Slingshot

它需要两个必须的输入文件：降维结果与细胞分群结果

因为它分析的基础假设就是：在低维空间上，细胞的位置是连续的并且是一个接一个的

### **2.1 数据准备**

```r
means <- rbind(
    # non-DE genes
    matrix(rep(rep(c(0.1,0.5,1,2,3), each = 300),100),
        ncol = 300, byrow = TRUE),
    # early deactivation
    matrix(rep(exp(atan( ((300:1)-200)/50 )),50), ncol = 300, byrow = TRUE),
    # late deactivation
    matrix(rep(exp(atan( ((300:1)-100)/50 )),50), ncol = 300, byrow = TRUE),
    # early activation
    matrix(rep(exp(atan( ((1:300)-100)/50 )),50), ncol = 300, byrow = TRUE),
    # late activation
    matrix(rep(exp(atan( ((1:300)-200)/50 )),50), ncol = 300, byrow = TRUE),
    # transient
    matrix(rep(exp(atan( c((1:100)/33, rep(3,100), (100:1)/33) )),50), 
        ncol = 300, byrow = TRUE)
)
counts <- apply(means,2,function(cell_means){
    total <- rnbinom(1, mu = 7500, size = 4)
    rmultinom(1, total, cell_means)
})
rownames(counts) <- paste0('G',1:750)
colnames(counts) <- paste0('c',1:300)
> counts[1:4,1:4]
   c1 c2 c3 c4
G1  0  1  2  0
G2  2  3  2  5
G3  3  8  5  4
G4  5 16  6  8
> dim(counts)
[1] 750 300

# 构建一个sce对象
sim <- SingleCellExperiment(assays = List(counts = counts))
> sim
class: SingleCellExperiment 
dim: 750 300 
metadata(0):
assays(1): counts
rownames(750): G1 G2 ... G749 G750
rowData names(0):
colnames(300): c1 c2 ... c299 c300
colData names(0):
reducedDimNames(0):
altExpNames(0):
```

### **2.2 基因过滤**

```r
geneFilter <- apply(assays(sim)$counts,1,function(x){
    sum(x >= 3) >= 10
})
sim <- sim[geneFilter, ]
# 过滤掉11个基因
> dim(sim)
[1] 739 300
```

### **2.3 归一化**

```r
FQnorm <- function(counts){
    rk <- apply(counts,2,rank,ties.method='min')
    counts.sort <- apply(counts,2,sort)
    refdist <- apply(counts.sort,1,median)
    norm <- apply(rk,2,function(r){ refdist[r] })
    rownames(norm) <- rownames(counts)
    return(norm)
}
assays(sim)$norm <- FQnorm(assays(sim)$counts)
```

### **2.4降维**

**方法一：PCA**

```r
pca <- prcomp(t(log1p(assays(sim)$norm)), scale. = FALSE)
rd1 <- pca$x[,1:2]

plot(rd1, col = rgb(0,0,0,.5), pch=16, asp = 1)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-16-072820.png)

**方法二：diffusion maps**

```r
library(destiny, quietly = TRUE)
dm <- DiffusionMap(t(log1p(assays(sim)$norm)))
rd2 <- cbind(DC1 = dm$DC1, DC2 = dm$DC2)
plot(rd2, col = rgb(0,0,0,.5), pch=16, asp = 1)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-16-073017.png)

**将两种结果都保存起来**

```r
reducedDims(sim) <- SimpleList(PCA = rd1, DiffMap = rd2)
```

### **2.5 聚类**

**方法一： Gaussian mixture modeling**

```r
library(mclust, quietly = TRUE)
#根据PCA结果
cl1 <- Mclust(rd1)$classification
colData(sim)$GMM <- cl1

library(RColorBrewer)
plot(rd1, col = brewer.pal(9,"Set1")[cl1], pch=16, asp = 1)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-16-073137.png)

**方法二：k-means**

```r
cl2 <- kmeans(rd1, centers = 4)$cluster
colData(sim)$kmeans <- cl2

plot(rd1, col = brewer.pal(9,"Set1")[cl2], pch=16, asp = 1)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-16-073234.png)

### **2.6 使用slingshot**

```r
sim <- slingshot(sim, clusterLabels = 'GMM', reducedDim = 'PCA')
summary(sim$slingPseudotime_1)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   0.000   8.633  21.118  21.415  34.367  43.186
```

* 如果要把slingshot的所有结果都提取出来，可以用`SlingshotDataSet`
* 像是SingleCellExperiment这一类对象的结果可以用 `metadata(sim)$slingshot` 获取

**对结果可视化**

```r
colors <- colorRampPalette(brewer.pal(11,'Spectral')[-6])(100)
plotcol <- colors[cut(sim$slingPseudotime_1, breaks=100)]

plot(reducedDims(sim)$PCA, col = plotcol, pch=16, asp = 1)
lines(SlingshotDataSet(sim), lwd=2, col='black')
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-16-073339.png)

## 3 学习TSCAN

> 说明文档在：<https://bioconductor.org/packages/3.11/bioc/vignettes/TSCAN/inst/doc/TSCAN.pdf>

### **3.1 准备数据**

```r
library(TSCAN)
data(lpsdata)
procdata <- preprocess(lpsdata)
```

这个`preprocess`函数做了三件事：

* 对表达量进行了log2(exp+1)
* 去掉了在超过一半细胞中表达量小于1的基因
* 将coefficient  ofcovariance小于1的基因去掉

### **3.2 构建拟时序**

使用`exprmclust`函数，进行了PCA降维以及model-based的聚类

```r
lpsmclust <- exprmclust(procdata)
# 然后看下结果
plotmclust(lpsmclust)
```

这个图上很乱，但不妨碍看到分了3群

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-16-074550.png)

接着获得排序

```r
lpsorder <- TSCANorder(lpsmclust)
```

### **3.3 基于找到的排序检测差异基因**

使用`difftest`函数

```r
diffval <- difftest(procdata,lpsorder)
```

根据q值找差异基因

```r
head(row.names(diffval)[diffval$qval < 0.05])
```

对其中某个差异基因作图

```r
# 以STAT2基因为例
STAT2expr <- log2(lpsdata["STAT2",]+1)
singlegeneplot(STAT2expr, TSCANorder(lpsmclust,flip=TRUE,orderonly=FALSE))
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-16-074954.png)

## 4 关于monocle

monocle2的拟时序分析前期数据准备可以看：[单细胞转录组学习笔记-18-scRNA包学习Monocle2](https://www.jianshu.com/p/356fa97342b3)

> 另外monocle3可以看：[跟着官网学习单细胞Monocle3](https://www.jianshu.com/p/8f57ac63f399)

以版本2为例，基本上还是分三步走：**从差异分析结果选合适基因=》降维=》细胞排序**

### **step1: 选合适基因**

```r
ordering_genes <- row.names (subset(diff_test_res, qval < 0.01))
cds <- setOrderingFilter(cds, ordering_genes)
plot_ordering_genes(cds)
```

### **step2: 降维**

```r
# 默认使用DDRTree的方法 
cds <- reduceDimension(cds, max_components = 2,
                            method = 'DDRTree')
```

### **step3: 细胞排序**

```r
cds <- orderCells(cds)
```

### **最后可视化**

```r
plot_cell_trajectory(cds, color_by = "Biological_Condition")
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-04-100349.png)

这个图就可以看到细胞的发展过程

另外，`plot_genes_in_pseudotime` 可以对基因在不同细胞中的表达量变化进行绘图

```r
plot_genes_in_pseudotime(cds[cg,],
                         color_by = "Biological_Condition")
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-04-100608.png)

## TODO：

* **4 ”关于monocle“章节中的**[单细胞转录组学习笔记-18-scRNA包学习Monocle2](https://www.jianshu.com/p/356fa97342b3)和

  > [跟着官网学习单细胞Monocle3](https://www.jianshu.com/p/8f57ac63f399) 放到补充篇


# 3.13 与蛋白丰度信息结合

刘小泽写于2020.7.17

## 1 前言

CITE-seq（Cellular indexing of transcriptomes and epitopes by sequencing）技术是纽约基因组中心和Satija实验室（即Seurat的开发团队）联合在2017年开发的，能够对数千个单细胞的MRNA进行测定，同时还能够对同一个单细胞中的表面蛋白标记物进行测序。

以往的研究方法是在将细胞放在平板上进行scRNA测序之前，通过流式细胞仪捕获单个细胞的蛋白质信息，但这样通量很低，并且受限于相对少量的蛋白质标记物。而CITE-seq可以不受抗体之间信号干扰的影响，大大增加了表面蛋白标记的数量，从而可以一次性获得多种由抗体蛋白指示的免疫表型的信息。

在这个方法中，重点是设计抗体-寡核苷酸结合物，并预估使用量。细胞首先标记上带有RNA标签的抗体，如果一个细胞预估的目标蛋白丰度越高，那么就应该带更多的抗体，也就应该带更多的antibody-derived tag (ADT)。之后利用液滴法将细胞分离，ADTs和内源转录本分别进行反转录、做成cDNA文库、测序。也就是说，蛋白丰度和基因表达量是受ADTs和内源转录本数量影响。

下面来自：<https://en.wikipedia.org/wiki/CITE-Seq>

* **Step1——ADT preparation**：It involves labeling an antibody directed against a cell surface protein of interest with oligonucleotides for barcoding the antibody.
* **Step2——Bind**：ADT labelled cells are encapsulated within a droplet as single cells with DNA-barcoded microbeads
* **Step3——Release**：Cell lysed to release both bound ADTs as well as mRNA and converted to cDNA. cDNA is prepared from both ADTs and cellular mRNAs.
* **Step4——PCR**：cDNA is PCR-amplified and ADT cDNA and mRNA cDNA are separated based on size（ADT-derived cDNAs are < 180bp and mRNA-derived cDNAs are > 300bp）
* **Step5——Sequence**： The independent libraries are pooled together and sequenced to obtain proteomics and transcriptomics data

![图片来自：https://www.protocols.io/view/cite-seq-protocols-ngzdbx6](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-17-012333.png)

**那么问题来了：怎么才能把ADT数据和常规的表达量数据整合到一起呢？**

首先需要明确一下：

* 这两种数据的本质是不同的，因此也不太可能直接把ADT行也想表达量数据一样当做是一个feature信息；
* 数量差异巨大：大部分实验只选用了少量感兴趣的蛋白（不超过20种），它们是研究者提前指定好的实验设计，而转录组却是整个转录组，包含几万个基因。
* 测序资源不均等：ADTs的测序深度会比较深，因为它们是从转录本中分离出来单独测序，因此更容易使测序资源产生”二八分布“，即ADTs会占用更多的测序资源

### **数据准备**

将会使用10X PBMC的数据，其中就会包括一些感兴趣的表面蛋白定量结果

```r
library(BiocFileCache)
bfc <- BiocFileCache(ask=FALSE)
stuff <- bfcrpath(bfc, file.path("http://cf.10xgenomics.com",
    "samples/cell-exp/3.0.0/pbmc_10k_protein_v3",
    "pbmc_10k_protein_v3_filtered_feature_bc_matrix.tar.gz"))
untar(stuff, exdir=tempdir())

library(DropletUtils)
sce <- read10xCounts(file.path(tempdir(), "filtered_feature_bc_matrix"))
sce
## class: SingleCellExperiment 
## dim: 33555 7865 
## metadata(1): Samples
## assays(1): counts
## rownames(33555): ENSG00000243485 ENSG00000237613 ... IgG1 IgG2b
## rowData names(3): ID Symbol Type
## colnames: NULL
## colData names(2): Sample Barcode
## reducedDimNames(0):
## altExpNames(0):

> names(rowData(sce))
[1] "ID"     "Symbol" "Type"  

# 看到行中就包含了ADT的信息
> table(rowData(sce)$Type)

Antibody Capture  Gene Expression 
              17            33538
```

## 2 预处理

### **2.1 数据分离**

拿到这个sce对象，它里面是整合了转录组和ADT表达信息的，我们首先把ADT分离出来（用`splitAltExps()`），并且之前介绍sce的时候说到，sce有一个模块是alternative Experiment，使用`altExp()`获取，可以存储其他类型的实验结果。于是我们可以将ADT的信息放进去

```r
sce <- splitAltExps(sce, rowData(sce)$Type)
altExpNames(sce)
## [1] "Antibody Capture"

# 现在这里就存储着17个ADT的信息
altExp(sce)
## class: SingleCellExperiment 
## dim: 17 7865 
## metadata(1): Samples
## assays(1): counts
## rownames(17): CD3 CD4 ... IgG1 IgG2b
## rowData names(3): ID Symbol Type
## colnames: NULL
## colData names(0):
## reducedDimNames(0):
## altExpNames(0):
```

如果现在看表达量的话，它是一个稀疏矩阵

```r
> counts(altExp(sce))[1:3,1:3]
3 x 3 sparse Matrix of class "dgCMatrix"

CD3   18  30  18
CD4  138 119 207
CD8a  13  19  10
```

需要先把它变成常规的矩阵，因为它本身的数据并不稀疏，只是因为使用了sce对象，才默认使用稀疏矩阵节省空间和计算资源，而下游分析与稀疏矩阵的兼容性并不好

```r
counts(altExp(sce)) <- as.matrix(counts(altExp(sce)))
> counts(altExp(sce))[1:3,1:3]
     [,1] [,2] [,3]
CD3    18   30   18
CD4   138  119  207
CD8a   13   19   10
```

### **2.2 质控**

大多数情况下，质控还是根据转录组数据，去掉空的液滴和低质量细胞。因为细胞质量不好，又怎么能体现真实的转录本和ADT的数量呢？这一部分，低质量细胞的衡量标准还要依靠线粒体水平，而它还是转录组的范畴。因此ADT数据对于质控这一步的贡献不大

我们这里拿到的数据其实是经过CellRanger过滤了的，已经除去了空的液滴，因此只需要关注线粒体水平去掉低质量细胞即可。

```r
library(scater)
mito <- grep("^MT-", rowData(sce)$Symbol)
df <- perCellQCMetrics(sce, subsets=list(Mito=mito))
mito.discard <- isOutlier(df$subsets_Mito_percent, type="higher")
summary(mito.discard)
##    Mode   FALSE    TRUE 
## logical    7569     296
```

如果只根据转录组过滤感觉不全面的话，也是可以用一下ADT的。基于ADT的特点，它会占用大量的测序资源，它的测序深度一般都很高，因此如果发现某个细胞的ADT表达量很低，那么可以它没有成功加上ADT信息，可以去除

> `isOutlier`使用的是MAD指标（绝对中位差来估计方差,先计算出数据与它们的中位数之间的偏差，然后这些偏差的绝对值的中位数就是MAD，median absolute deviation）。函数认为超过中位数3倍MAD的值就是离群值

```r
# 由于ADT表达量一般很大， 因此需要log2转换
# min_diff含义: minimum difference from the median to consider as an outlier
ab.discard <- isOutlier(df$`altexps_Antibody Capture_detected`,
    log=TRUE, type="lower", min_diff=1)
# 勉强过滤掉一个细胞
summary(ab.discard)
##    Mode   FALSE    TRUE 
## logical    7864       1
```

最后整合一下过滤信息

```r
discard <- ab.discard | mito.discard
> table(discard)
discard
FALSE  TRUE 
 7568   297 

sce <- sce[,!discard]
```

### **2.3 归一化**

> 这一部分，ADT就需要单独进行处理了

由于内源转录本和ADT的生物属性不同，导致它们的细胞捕获效率不同，因此这两种信息的细胞捕获相关的数据偏差也不太可能一致，因此需要各自处理。

另外，ADT的组成差异是很明显的：

* 蛋白的丰度变化更加复杂，蛋白之间是存在互作关系的（试想一下PPI蛋白互作网络），有一点”牵一发动全身“的意思。蛋白丰度的变化，也会直接导致ADT的数量变化
* 靶标蛋白都是提前选好的，因此细胞中也更容易出现丰度的差异

为了减少这些非生物因素差异，**有几种方法可以对每个细胞计算size factor：**

**第一种：基于ADTs的文库大小的计算**

> 首先回顾一下什么是文库大小： We define the library size as the **total sum of counts** across all genes for each cell, the expected value of which is assumed to scale with any cell-specific biases.
>
> 然后看看根据文库大小得到的size factor： The “library size factor” for each cell is then directly proportional to its library size where the **proportionality constant** is defined such that the mean size factor across all cells is **equal to 1**

这也是最直接最简单的方法，对分群是够用的，但它做了一个无偏估计，忽略了细胞间原本要上调或者下调的差异，对于分群结果的解释，我们却正需要差异分析的结果去判断每群的marker基因

```r
sf.lib <- librarySizeFactors(altExp(sce))
summary(sf.lib)
##     Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
##  0.02668  0.52418  0.90485  1.00000  1.26625 22.81740
```

**第二种：基于DESeq的计算**

> 来自DESeq的名词解释： "**ratio**" uses the standard median ratio method introduced in DESeq. The **size factor** is the median ratio of the sample over a **"pseudosample": for each gene, the geometric mean of all samples.**

这里我们也需要指定一个pseudosample，其实最好是来自空液滴的barcode定量结果，但是这里的数据中空液滴被去除，所以只能用其他的均值来代替

```r
# pseudosample
ambient <- rowMeans(counts(altExp(sce)))
# DESeq-like size factors
sf.amb <- medianSizeFactors(altExp(sce), reference=ambient)
summary(sf.amb)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##  0.0030  0.5927  0.8282  1.0000  1.1439 41.7972
```

**第三种：基于对照的计算**

一些实验包含了对照的抗体（例如IgG），这些对照与真实抗体特性相似，但在细胞中缺乏特异性靶标。我们可以做出假设：对象的ADTs在细胞间的丰度无差异。如果发现其中的任何差异，都可以基于这些差异进行归一化。

```r
controls <- grep("^Ig", rownames(altExp(sce)))
> controls
[1] 15 16 17
sf.control <- librarySizeFactors(altExp(sce), subset_row=controls) 
summary(sf.control)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##  0.0000  0.6854  0.8757  1.0000  1.1423 44.0155
```

这里推荐使用第二种基于DESeq的计算方法，最后使用`logNormCounts`会对转录本和ADTs分别计算size factor、进行归一化和log转换

```r
sizeFactors(altExp(sce)) <- sf.amb
sce <- logNormCounts(sce, use_altexps=TRUE)

assayNames(sce)
## [1] "counts"    "logcounts"
assayNames(altExp(sce))
## [1] "counts"    "logcounts"
```

## 3 聚类

与转录本分析不同的是，ADT数据不需要挑选特征信息，因为：

* ADT的特征信息选择早在实验设计阶段就已经完成，我们是先选出感兴趣的蛋白，然后再做的实验得到的数据
* 由于ADTs的数据中行数很少，因此也没有必要去根据这些挑选HVGs或者选PCs
* 每个ADT数据是捕获不同的生物信号，因此这里面的外源噪音并不大，不需要降维也可以轻松去除

综上，**对于ADT数据，可以直接对log归一化的结果进行分群和可视化**

```r
# 设置NA可以跳过PCA，直接分群
g.adt <- buildSNNGraph(altExp(sce), d=NA) 
clusters.adt <- igraph::cluster_walktrap(g.adt)$membership

# 可视化
set.seed(1010010)
altExp(sce) <- runTSNE(altExp(sce))
colLabels(altExp(sce)) <- factor(clusters.adt)
plotTSNE(altExp(sce), colour_by="label", text_by="label", text_col="red")
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-17-052247.png)

因为ADTs数量很少，所以**对每个cluster的检查也变得简单，将每个ADT的log表达量均值画成热图即可**

```r
se.averaged <- sumCountsAcrossCells(altExp(sce), clusters.adt,
    exprs_values="logcounts", average=TRUE)
# 17个ADT，28个cluster
> dim(se.averaged)
[1] 17 28

> assay(se.averaged)[1:3,1:3]
            1        2        3
CD3  4.621559 9.556522 5.673089
CD4  4.348560 9.795961 9.078986
CD8a 4.797436 5.262174 5.306986

# 热图
library(pheatmap)
averaged <- assay(se.averaged)
pheatmap(averaged - rowMeans(averaged),
    breaks=seq(-3, 3, length.out=101))
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-17-052810.png)

## 4 与基因表达数据的结合

### **4.1 继续分亚群**

上面我们对ADT进行了分群，现在根据这个分群结果+转录组数据再分亚群。

> 这个思路就很像scRNA数据分析后，再补充一个FACS实验，都是为了更好地分离检查细胞类型。既然得到了ADT的许多”干净“分群结果（因为ADT每群的表达量都很高并且代表的生物信号很强），那就相当于先得到了一些可靠的细胞类型，那么就可以结合转录组表达量去研究其中更细小的变化。

**下面就对ADT得到的cluster进行循环分亚群**

使用了`quickSubCluster`函数，它会对现在的sce对象中已有的分组，进一步分群

其中的`sce`表示对这个对象进行操作，`clusters.adt`指定分组，`prepFUN`指的是在分群之前的准备工作，`clusterFUN`指的是分群的操作

```r
set.seed(101010)
all.sce <- quickSubCluster(sce, clusters.adt,
    prepFUN=function(x) {
        dec <- modelGeneVar(x)
        top <- getTopHVGs(dec, prop=0.1)
        x <- runPCA(x, subset_row=top, ncomponents=25)
    },
    clusterFUN=function(x) {
        g.trans <- buildSNNGraph(x, use.dimred="PCA")
        igraph::cluster_walktrap(g.trans)$membership
    }
)
# 结果是一个列表
> all.sce
List of length 28
names(28): 1 2 3 4 5 6 7 8 ... 21 22 23 24 25 26 27 28
```

如果对应上面的t-SNE图，看到第3群细胞数量还蛮多的，可以看一下

```r
> all.sce[[3]]
class: SingleCellExperiment 
dim: 33538 1828 
metadata(1): Samples
assays(2): counts logcounts
rownames(33538): ENSG00000243485 ENSG00000237613
  ... ENSG00000277475 ENSG00000268674
rowData names(3): ID Symbol Type
colnames: NULL
colData names(4): Sample Barcode sizeFactor
  subcluster
reducedDimNames(1): PCA
altExpNames(1): Antibody Capture

# 它又可以分8个亚群
> table(all.sce[[3]]$subcluster)

3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 
 90 772  25 178 695  44  19   5
```

**看一下每个ADT分群结果各自对应的亚群**

```r
# 每个ADT群下面有多少细胞
ncells <- sapply(all.sce, ncol,simplify = TRUE)
> ncells
   1    2    3    4    5    6    7    8    9   10   11 
 888   92 1828 1186  512   55   71   85  999  571   70 
  12   13   14   15   16   17   18   19   20   21   22 
 148   73   39  409   21   39   69   74  126   32   50 
  23   24   25   26   27   28 
  13   17   29   38   22   12 

# 每个ADT群下面又分几个亚群
nsubclusters <- sapply(all.sce, function(x) length(unique(x$subcluster)),
                       simplify = TRUE)
> nsubclusters
 1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 
21  3  8  7  3  3  2  3  9  8  3  2  3  1 12  1  1  2 
19 20 21 22 23 24 25 26 27 28 
 3  3  1  2  1  1  1  1  1  1
```

画个图看一下

```r
plot(ncells, nsubclusters, xlab="Number of cells", type="n",
    ylab="Number of subclusters", log="xy")
text(ncells, nsubclusters, names(all.sce))
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-17-055252.png)

**继续分亚群的一个好处是**：我们可以基于之前ADT的分群结果有一个大体的预估，每个cluster大体是什么性质的。假如我们知道了ADT的一个clusterX中包含T细胞，那么就没有必须再对X.1、X.2之类的亚群再进行判断是不是T细胞了。而是可以把更多的精力放在`findMarkers()`后续分析上。比如已经发现cluster12中包含CD8+ T细胞，而且它又分成了两个亚群，就可以继续根据颗粒酶表达量判断亚群

```r
of.interest <- "12"
# 左边是GZMH基因，右边是GZHK
plotExpression(all.sce[[of.interest]], x="subcluster",
    features=c("ENSG00000100450", "ENSG00000113088"))
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-17-061227.png)

**注意**

我们这里的亚群都是根据之前的分群结果继续向下走的，如果说之前的某个分群不对怎么办？那不就误导了后续的分析？

因此可以补充一些检查，看看每个亚群是不是和它们的上一级分群结果有相似的蛋白丰度

```r
of.interest <- "12"
sce.cd8 <- all.sce[[of.interest]]
plotExpression(altExp(sce.cd8), x=I(sce.cd8$subcluster),
    features=c("CD3", "CD8a"))
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-17-061558.png)

### **4.2 利用转录组的分群找蛋白相关的marker基因**

我们选用ADT看蛋白丰度的目的应该不是为了看细胞类型，因为这个事情使用转录组也能完成，并且完成的效果还不错。那么为什么使用蛋白数据呢？因为蛋白可以更直接地反映生物过程活性，可以减少后续手动注释细胞类型的麻烦和不准确性。因此，结合转录组的分群结果，这样会不会更快地帮助判断某群细胞的生物功能呢？

**对转录组数据进行快速分群**

```r
sce <- logNormCounts(sce)
dec <- modelGeneVar(sce)
top <- getTopHVGs(dec, prop=0.1)

set.seed(1001010)
sce <- runPCA(sce, subset_row=top, ncomponents=25)

g <- buildSNNGraph(sce, use.dimred="PCA")
clusters <- igraph::cluster_walktrap(g)$membership
colLabels(sce) <- factor(clusters)

set.seed(1000010)
sce <- runTSNE(sce, dimred="PCA")
plotTSNE(sce, colour_by="label", text_by="label")
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-17-065236.png)

**ADT数据+ 分群结果 =》找marker基因**

```r
markers <- findMarkers(altExp(sce), colLabels(sce))
of.interest <- markers[[16]]
pheatmap(getMarkerEffects(of.interest), breaks=seq(-3, 3, length.out=101))
```

图中可以看到，cluster16中的PD-1表达量升高，如果再把它和某个感兴趣的表型结合起来（例如T细胞衰竭），就可以对数据多一些认识

> 用于标记衰竭程度的一个分子标记是PD-1，以“阻断T细胞杀伤能力”而闻名。PD-1的沉默会通过抑制T细胞的增殖而损害其抗肿瘤功能。
>
> 来自：[PD-1免疫疗法的那些事儿](https://zhuanlan.zhihu.com/p/55295193) Naive T细胞在TCR信号通路激活的时候，会飞快地表达PD-1。而当T细胞被连续不断地刺激时，T细胞会变成exhausted T，会持续性地表达很高的PD-1

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-17-065319.png)

整个过程不需要任何的预判，也不需要对ADT有任何的了解，所有的结果都是基于分群+表达量差异。既然后面是按照ADT的数据推断的marker基因，那么如果有直接针对ADT数据的聚类分群，结果就会更准确，只不过目前数据量还达不到像转录组一样的水平，做降维聚类效果不太好。


# 3.14 处理大型数据

刘小泽写于2020.7.18

## 1 前言

scRNA-seq技术越来越成熟，测的细胞数也在日益增长，越来越多的研究结果发表在GEO数据库中，而且还有大型单细胞项目的开展（例如人类图谱计划HCA）。因此分析方法需要考虑到逐渐庞大的数据集，这次就来看看怎么做可以帮助我们获得更快的处理速度和分析效率。

## 2 快速估算

### **2.1 近似而非精确的近邻搜索**

在高维空间中对近邻细胞进行判断基本上是必备流程，像`buildSNNGraph()`, `doubletCells()` 都是需要经历这一步。默认是利用KNN（k-nearest neighbours）算法找到更准确的近邻数量，而牺牲速度。但是大型数据更需要的是速度，例如这个`BiocNeighbors` R包就可以通过`BNPARAM=`轻松转换近邻搜索方法

以pbmc数据为例，这个数据之前应该分享过

```r
load('clustered.sce.pbmc.RData')
sce.pbmc

## class: SingleCellExperiment 
## dim: 33694 3922 
## metadata(1): Samples
## assays(2): counts logcounts
## rownames(33694): RP11-34P13.3 FAM138A ... AC213203.1 FAM231B
## rowData names(2): ID Symbol
## colnames(3922): AAACCTGAGAAGGCCT-1 AAACCTGAGACAGACC-1 ...
##   TTTGTCACAGGTCCAC-1 TTTGTCATCCCAAGAT-1
## colData names(4): Sample Barcode sizeFactor label
## reducedDimNames(3): PCA TSNE UMAP
## altExpNames(0):
```

看到这里的数据是经过了PCA、t-SNE、UMAP降维操作的，并已经做好了分群，使用的也是默认的精确KNN搜索。

**下面使用近似搜索：**

`AnnoyParam`的解释是：A class to hold parameters for the Annoy algorithm for approximate nearest neighbor identification. 也就是它包装了近似搜索的一套参数，并传递给`buildSNNGraph`

```r
library(scran)
library(BiocNeighbors)
snn.gr <- buildSNNGraph(sce.pbmc, BNPARAM=AnnoyParam(), use.dimred="PCA")
clusters <- igraph::cluster_walktrap(snn.gr)
table(Exact=colLabels(sce.pbmc), Approx=clusters$membership)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-18-005159.png)

这里因为数据量不大，并看不出速度上的优势，只是为了证明二者在准确度上相差无几

### **2.2 奇异值分解**

术语叫做：singular value decomposition (SVD)，在 `denoisePCA()`, `fastMNN()`, `doubletCells()`都有应用。默认使用`base::svd()` 也是进行了精确的计算，同样不适合大型数据集。好在 `irlba`和`rsvd` 两个R包都提供了近似计算方法，具体的参数配置和`AnnoyParam` 一样也是做成了`BiocSingular`包的一个函数

```r
library(scater)
library(BiocSingular)

# 方法一：randomized SVD (RSVD)
set.seed(101000)
r.out <- runPCA(sce.pbmc, ncomponents=20, BSPARAM=RandomParam())
str(reducedDim(r.out))
##  num [1:3922, 1:20] 15.3 13.41 -8.46 -7.86 6.38 ...
##  - attr(*, "dimnames")=List of 2
##   ..$ : chr [1:3922] "AAACCTGAGAAGGCCT-1" "AAACCTGAGACAGACC-1" "AAACCTGAGGCATGGT-1" "AAACCTGCAAGGTTCT-1" ...
##   ..$ : chr [1:20] "PC1" "PC2" "PC3" "PC4" ...
##  - attr(*, "percentVar")= num [1:20] 20.26 10.02 5.36 2.19 1.41 ...
##  - attr(*, "rotation")= num [1:500, 1:20] 0.2015 0.182 0.1764 0.1067 0.0649 ...
##   ..- attr(*, "dimnames")=List of 2
##   .. ..$ : chr [1:500] "LYZ" "S100A9" "S100A8" "HLA-DRA" ...
##   .. ..$ : chr [1:20] "PC1" "PC2" "PC3" "PC4" ...

# 方法二：IRLBA
set.seed(101001)
i.out <- runPCA(sce.pbmc, ncomponents=20, BSPARAM=IrlbaParam())
str(reducedDim(i.out))
##  num [1:3922, 1:20] 15.3 13.41 -8.46 -7.86 6.38 ...
##  - attr(*, "dimnames")=List of 2
##   ..$ : chr [1:3922] "AAACCTGAGAAGGCCT-1" "AAACCTGAGACAGACC-1" "AAACCTGAGGCATGGT-1" "AAACCTGCAAGGTTCT-1" ...
##   ..$ : chr [1:20] "PC1" "PC2" "PC3" "PC4" ...
##  - attr(*, "percentVar")= num [1:20] 20.26 10.02 5.36 2.19 1.41 ...
##  - attr(*, "rotation")= num [1:500, 1:20] 0.2015 0.182 0.1764 0.1067 0.0649 ...
##   ..- attr(*, "dimnames")=List of 2
##   .. ..$ : chr [1:500] "LYZ" "S100A9" "S100A8" "HLA-DRA" ...
##   .. ..$ : chr [1:20] "PC1" "PC2" "PC3" "PC4" ...
```

这两种方法的速度都比准确的SVD方法快，丢失的准确度也微乎其微，也因此被scran和scater包设为了默认的参数，也因此需要设置随机种子（毕竟都是估计的方法，每次运行结果都不一致）。当然，**IRLBA相比RSVD更准确，但速度不如RSVD。**

## 3 并行计算

### **3.1 为什么？**

> 参考：<https://cosx.org/2016/09/r-and-parallel-computing/>

R 采用的是内存计算模式（In-Memory），被处理的数据需要预取到主存（RAM）中。其优点是计算效率高、速度快，但缺点是这样一来能处理的问题规模就非常有限（小于 RAM 的大小）。另一方面，R 的核心（R core）是一个单线程的程序，在多核处理器上，R 无法有效地利用所有的计算内核。即使机器性能很强大，有32个核心，但它也只能使用1/32的计算能力，浪费了31/32。

### **3.2 怎么做？**

**使用BiocParallel包，可以将并行运算覆盖到基于Bioconductor的分析中**

不同的硬件和操作系统，选择的并行方法也不同

> 参考：<https://bioconductor.org/packages/3.11/bioc/vignettes/BiocParallel/inst/doc/Introduction_To_BiocParallel.pdf>

```r
library(BiocParallel)
registered() #可以看看支持哪些类型的加速，最顶上的是默认的
```

一般包括：

* SerialParam：全平台支持
* MulticoreParam：适用于Unix和Mac。在windows上它等同于SerialParam
* SnowParam：全平台支持
* BatchtoolsParam：集群
* DoparParam：全平台支持

如果要更改

```r
# 本来的default是 MulticoreParam
default <- registered()

# 现在改成BatchtoolsParam
register(BatchtoolsParam(workers = 10), default = TRUE)
names(registered())
## [1] "BatchtoolsParam" "MulticoreParam"  "SnowParam"       "SerialParam"

# 要再恢复原来的设置
for (param in rev(default)) register(param)
```

可以这么使用

```r
# 不同的方法
dec.pbmc.mc <- modelGeneVar(sce.pbmc, BPPARAM=MulticoreParam(2))
dec.pbmc.snow <- modelGeneVar(sce.pbmc, BPPARAM=SnowParam(5))
```

在SLURM HPC中，可以使用`BatchtoolsParam`

> 参考：<https://bioconductor.org/packages/3.11/BiocParallel/vignettes/BiocParallel_BatchtoolsParam.pdf>

```r
# 设置每个任务2小时、8G内存、1CPU，总共10个任务
bpp <- BatchtoolsParam(10, cluster="slurm",
    resources=list(walltime=7200, memory=8000, ncpus=1))
```

### **注意**

* 这个并行计算只是加速了CPU的计算速度，但如果任务受限于内存或硬盘的读入读出，它依然是没办法加速的；
* R实现多线程还是很麻烦的，它的操作逻辑是：先设置一个或多个session（对话窗口）；然后加载相关的包；session之间进行数据传递。可能最后还不如单线程运行效果好

## 4 可能会遇到内存不足

想一下，我们处理的核心是不是基于表达矩阵？既然是核心，就需要完全载入内存中，然后才能实现后面的顺利读取、处理。现在单细胞的表达矩阵有两种形式：稀疏矩阵`dgCMatrix`或者普通矩阵`matrix` 。如果我们的数据是10X产生的130万个脑细胞数据，如果是以普通矩阵读入，就需要消耗100G内存，即使使用稀疏矩阵，也需要消耗30G内存左右。因此没有很好的服务器基本上干不了这个事。

**用处理速度换取内存不足**

如果真的面临无法增加内存的困境，还有一个plan B，就是用硬盘空间来存储数据，必要时再调用一部分数据到内存，虽然这一来一回很影响处理速度，但毕竟可以用。

使用这个`HDF5Array`R包可以做到（类似的还有 `bigmemory`, `matter`），它会将底层数据做成HDF5格式

例如，从130万个脑细胞数据中选取了2万个

```r
library(TENxBrainData)
sce.brain <- TENxBrainData20k() 
sce.brain
## class: SingleCellExperiment 
## dim: 27998 20000 
## metadata(0):
## assays(1): counts
## rownames: NULL
## rowData names(2): Ensembl Symbol
## colnames: NULL
## colData names(4): Barcode Sequence Library Mouse
## reducedDimNames(0):
## altExpNames(0):
```

看一下这个表达矩阵，显然是一个HDF5的矩阵

```r
counts(sce.brain)
## <27998 x 20000> matrix of class HDF5Matrix and type "integer":
##              [,1]     [,2]     [,3]     [,4] ... [,19997] [,19998] [,19999]
##     [1,]        0        0        0        0   .        0        0        0
##     [2,]        0        0        0        0   .        0        0        0
##     [3,]        0        0        0        0   .        0        0        0
##     [4,]        0        0        0        0   .        0        0        0
##     [5,]        0        0        0        0   .        0        0        0
##      ...        .        .        .        .   .        .        .        .
## [27994,]        0        0        0        0   .        0        0        0
## [27995,]        0        0        0        1   .        0        2        0
## [27996,]        0        0        0        0   .        0        1        0
## [27997,]        0        0        0        0   .        0        0        0
## [27998,]        0        0        0        0   .        0        0        0
##          [,20000]
##     [1,]        0
##     [2,]        0
##     [3,]        0
##     [4,]        0
##     [5,]        0
##      ...        .
## [27994,]        0
## [27995,]        0
## [27996,]        0
## [27997,]        0
## [27998,]        0
```

看一下这个大小

```r
object.size(counts(sce.brain))
## 2328 bytes
```

但实际上这个底层数据的大小是

```r
file.info(path(counts(sce.brain)))$size
## [1] 76264332
```


# 3.15 不同R包数据的相互转换

刘小泽写于2020.7.18

## 1 前言

> 这部分内容是来自Seurat：<https://satijalab.org/seurat/v3.1/conversion_vignette.html>

单细胞数据格式目前有这么几大派：

* Bioconductor主导的SingleCellExperiment数据格式：例如scran、scater、monocle（尽管它的对象不直接使用SingleCellExperiment，但灵感来源于SingleCellExperiment，并且操作也是类似的）
* Seurat：SeuratObject格式
* scanpy：AnnData格式

这么一来，很多分析流程就被固定在某个包中了，比如使用Seurat会一用到底，也不会去学习scater或其他R包了，但也许就错过了其他R包好用的一些功能（比如我感觉`scater`的`uniquifyFeatureNames`就很好用）

既然有需求，就有开发者添加功能 ，这里Davis McCarthy 和Alex Wolf就为Seurat添加了和其他数据类型转换的函数

## 2 Seurat与SingleCellExperiment的相互转换

```r
library(scater)
# devtools::install_github(repo = "satijalab/seurat", ref = "loom")
library(loomR)
library(Seurat)
library(patchwork)
```

### **2.1 Seurat转SingleCellExperiment**

```r
# 使用Seurat内置数据
data("pbmc_small")
> pbmc_small
An object of class Seurat 
230 features across 80 samples within 1 assay 
Active assay: RNA (230 features)
 2 dimensional reductions calculated: pca, tsne

# 一个函数即可
pbmc.sce <- as.SingleCellExperiment(pbmc_small)
> pbmc.sce
class: SingleCellExperiment 
dim: 230 80 
metadata(0):
assays(2): counts logcounts
rownames(230): MS4A1 CD79B ... SPON2 S100B
rowData names(5): vst.mean vst.variance
  vst.variance.expected
  vst.variance.standardized vst.variable
colnames(80): ATGCCAGAACGACT CATGGCCTGTGCAT ...
  GGAACACTTCAGAC CTTGATTGATCTTC
colData names(8): orig.ident nCount_RNA ...
  RNA_snn_res.1 ident
reducedDimNames(2): PCA TSNE
spikeNames(0):
altExpNames(0):

# 接下来就是scater的操作了
p1 <- plotExpression(pbmc.sce, features = "MS4A1", x = "ident") + theme(axis.text.x = element_text(angle = 45, 
    hjust = 1))
p2 <- plotPCA(pbmc.sce, colour_by = "ident")
p1 + p2
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-18-121906.png)

### **2.2 SingleCellExperiment转Seurat**

```r
# 导入sce对象（https://scrnaseq-public-datasets.s3.amazonaws.com/scater-objects/manno_human.rds）
manno <- readRDS(file = "manno_human.rds")
> manno
class: SingleCellExperiment 
dim: 20560 4029 
metadata(0):
assays(2): counts logcounts
rownames(20560): 'MARC1' 'MARC2' ... ZZEF1 ZZZ3
rowData names(10): feature_symbol
  is_feature_control ... total_counts
  log10_total_counts
colnames(4029): 1772122_301_C02 1772122_180_E05
  ... 1772116-063_G02 1772099-259_H03
colData names(34): Species cell_type1 ...
  pct_counts_ERCC is_cell_control
reducedDimNames(0):
altExpNames(0):

manno <- runPCA(manno)
# 转为seurat对象
manno.seurat <- as.Seurat(manno, counts = "counts", data = "logcounts")

# 看下这个函数
# as.Seurat(
#     x,
#     counts = "counts",
#     data = "logcounts",
#     assay = "RNA",
#     project = "SingleCellExperiment",
#     ...
# )
# 既然有默认参数，因此直接按下面这么写就可以：
manno.seurat <- as.Seurat(manno)

> manno.seurat
An object of class Seurat 
20560 features across 4029 samples within 1 assay 
Active assay: RNA (20560 features)
 1 dimensional reduction calculated: PCA

Idents(manno.seurat) <- "cell_type1"
p1 <- DimPlot(manno.seurat, reduction = "PCA", group.by = "Source") + NoLegend()
p2 <- RidgePlot(manno.seurat, features = "ACTB", group.by = "Source")
p1 + p2
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-18-115930.png)

## 3 Seurat与loom的相互转换

还记得上次在[单细胞交响乐16-处理大型数据](https://www.jianshu.com/p/786752478e08)中说到：处理大型数据遇到内存不足时，可以使用这个`HDF5Array`R包（类似的还有 `bigmemory`, `matter`），它会将底层数据做成HDF5格式，用硬盘空间来存储数据，必要时再调用一部分数据到内存。loom格式就是处理HDF5使用的

### **3.1 Seurat转为loom**

```r
pbmc.loom <- as.loom(pbmc, filename = "pbmc3k.loom", verbose = FALSE)
pbmc.loom
## Class: loom
## Filename: /__w/1/s/output/pbmc3k.loom
## Access type: H5F_ACC_RDWR
## Attributes: version, chunks, LOOM_SPEC_VERSION, assay, last_modified
## Listing:
##        name    obj_type dataset.dims dataset.type_class
##   col_attrs   H5I_GROUP         <NA>               <NA>
##  col_graphs   H5I_GROUP         <NA>               <NA>
##      layers   H5I_GROUP         <NA>               <NA>
##      matrix H5I_DATASET 2638 x 13714          H5T_FLOAT
##   row_attrs   H5I_GROUP         <NA>               <NA>
##  row_graphs   H5I_GROUP         <NA>               <NA>

# 最后使用完要记得关上loom对象
pbmc.loom$close_all()
```

### **3.2 loom转为Seurat**

**首先读取：用 loomR 的connect**

```r
l6.immune <- connect(filename = "../data/l6_r1_immune_cells.loom", mode = "r")
l6.immune
## Class: loom
## Filename: /__w/1/s/data/l6_r1_immune_cells.loom
## Access type: H5F_ACC_RDONLY
## Attributes: CreationDate, last_modified
## Listing:
##        name    obj_type  dataset.dims dataset.type_class
##   col_attrs   H5I_GROUP          <NA>               <NA>
##  col_graphs   H5I_GROUP          <NA>               <NA>
##      layers   H5I_GROUP          <NA>               <NA>
##      matrix H5I_DATASET 14908 x 27998          H5T_FLOAT
##   row_attrs   H5I_GROUP          <NA>               <NA>
##  row_graphs   H5I_GROUP          <NA>               <NA>
```

**然后转换**

```r
l6.seurat <- as.Seurat(l6.immune)
VlnPlot(l6.seurat, features = c("Sparc", "Ftl1", "Junb", "Ccl4"), ncol = 2, pt.size = 0.1)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-18-120927.png)

**最后处理完，记得关闭loom文件**

```r
l6.immune$close_all()
```

### **3.3 补充**

**如果使用Seurat V2**，还有一个自带的函数`Convert`

```r
data("pbmc_small")
pbmc_small
pfile <- Convert(from = pbmc_small, to = "loom", filename = "pbmc_small.loom", 
    display.progress = FALSE)
pfile
## Class: loom
## Filename: /home/paul/Documents/Satija/pbmc_small.loom
## Access type: H5F_ACC_RDWR
## Attributes: version, chunks
## Listing:
##        name    obj_type dataset.dims dataset.type_class
##   col_attrs   H5I_GROUP         <NA>               <NA>
##  col_graphs   H5I_GROUP         <NA>               <NA>
##      layers   H5I_GROUP         <NA>               <NA>
##      matrix H5I_DATASET     80 x 230          H5T_FLOAT
##   row_attrs   H5I_GROUP         <NA>               <NA>
##  row_graphs   H5I_GROUP         <NA>               <NA>
```

## 4 Scanpy转Seurat

Seurat有一个函数`ReadH5AD`可以读取AnnData的H5AD文件

```r
pbmc3k <- ReadH5AD(file = "pbmc3k.h5ad")
# 利用Seurat操作
Idents(pbmc3k) <- "louvain"
p1 <- DimPlot(pbmc3k, label = TRUE)
p2 <- VlnPlot(pbmc3k, features = c("CST3", "NKG7", "PPBP"), combine = FALSE)
wrap_plots(c(list(p1), p2), ncol = 2) & NoLegend()
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-18-121342.png)

目前还不能直接将Seurat写成H5AD文件，因此不能之间将Seurat转为Scanpy；但是可以将loom文件作为桥梁实现Seurat转Scanpy，例如`Scanpy` 有一个函数`scanpy.read_loom()`

> 参考：<https://scanpy.readthedocs.io/en/stable/api/scanpy.read_loom.html>


# 4 实战篇：活学活用

将所学知识应用到各种数据的分析中，关键是掌握一个分析思路


# 4.1 实战一 | Smart-seq2 | 小鼠骨髓

刘小泽写于2020.7.18

## 1 前言

使用的是来自永生化小鼠骨髓祖细胞系的2个96孔板的416B cells（[Lun et al. 2017](https://pubmed.ncbi.nlm.nih.gov/29030468/)），并且在细胞裂解后文库制备前，在每个细胞中加入一定量的外源RNA（ERCC）

> ERCC就是外源RNA对照联盟开发的人工设计好的已知序列和数量的mRNA，高ERCC占比与低质量数据相关，并且通常是排除的标准 。假如看到ERCC在很多样本中占比超过了20%（假如认为20%是一个较高的比例），那就是说这些样本中有超过20%的reads都”浪费“在了这种不相关的外源序列上，而减少了自身内源转录本的量，这对下游分析是不利的

之后再进行高通量测序，得到每个基因的表达量（这个是通过计算比对到外显子区域的reads数得到的，就像featureCounts的操作）；同样，spike-in的含量也是计算有多少reads比对到了spike-in的参考序列

### **先简单了解一下scRNAseq数据集**

早期版本中只是内置了三个数据集Fluidig、Th2、Allen：当时也写了这一篇[单细胞转录组学习笔记-14-学习scRNAseq这个R包](https://www.jianshu.com/p/cc621292d29e)，但后来这个包进行了更新，原来的数据获取方法也发生了变化

```r
# 原来使用
data(fluidigm)
# 现在使用
ReprocessedFluidigmData() #provides 65 cells 
ReprocessedTh2Data() #provides 96 T helper cells 
ReprocessedAllenData() #provides 379 cells from the mouse visual cortex
```

另外新版本的包还加入了其他很多数据集，在[官网帮助文档](https://bioconductor.org/packages/release/data/experiment/vignettes/scRNAseq/inst/doc/scRNAseq.html#available-data-sets)可以看到全部

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-19-020516.png)

## 2 数据加载

我们会使用416b这个数据

```r
library(scRNAseq)
sce.416b <- LunSpikeInData(which="416b") 

> sce.416b
# class: SingleCellExperiment 
# dim: 46604 192 
# metadata(0):
#   assays(1): counts
# rownames(46604): ENSMUSG00000102693
# ENSMUSG00000064842 ... ENSMUSG00000095742
# CBFB-MYH11-mcherry
# rowData names(1): Length
# colnames(192):
#   SLX-9555.N701_S502.C89V9ANXX.s_1.r_1
# SLX-9555.N701_S503.C89V9ANXX.s_1.r_1 ...
# SLX-11312.N712_S508.H5H5YBBXX.s_8.r_1
# SLX-11312.N712_S517.H5H5YBBXX.s_8.r_1
# colData names(9): Source Name cell line ...
# spike-in addition block
# reducedDimNames(0):
#   altExpNames(2): ERCC SIRV

> table(sce.416b$block)
# 20160113 20160325 
# 96       96 

# 设置分组信息（因为来自2个96孔板），也就是为后面的批次处理做铺垫
sce.416b$block <- factor(sce.416b$block)

# 当前关于行（基因）的信息只有基因长度
> rowData(sce.416b)
# DataFrame with 46604 rows and 1 column
# Length
# <integer>
#   ENSMUSG00000102693      1070
# ENSMUSG00000064842       110
# ENSMUSG00000051951      6094
# ENSMUSG00000102851       480
# ENSMUSG00000103377      2819
# ...                      ...
# ENSMUSG00000094621       121
# ENSMUSG00000098647        99
# ENSMUSG00000096730      3077
# ENSMUSG00000095742       243
# CBFB-MYH11-mcherry      2998
```

### **增加基因的信息（symbol ID、染色体位置）**

**首先是基因ID转换**

> 这个函数是我非常喜欢的，方便了基因ID转换

将Ensembl ID转为Symbol，使用`uniquifyFeatureNames`，参数很简单，第一个是ID，第二是names

> This function will attempt to use `names` if it is unique. If not, it will append the `_ID` to any non-unique value of `names`. Missing `names` will be replaced entirely by `ID`.
>
> 含义就是：
>
> * 如果有symbol name的，它会将Ensembl ID替换为symbol name，没有name的仍然使用ID；
> * 如果name相同、ID不同（即两个Ensembl ID对应同一个Symbol name），它会将ID与name组合（`name_id`）保证特异性
>
>   ![image-20200719110335976](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-19-030336.png)

```r
library(AnnotationHub)
ens.mm.v97 <- AnnotationHub()[["AH73905"]]
columns(ens.mm.v97)
# [1] "DESCRIPTION"         "ENTREZID"            "EXONID"              "EXONIDX"             "EXONSEQEND"         
# [6] "EXONSEQSTART"        "GENEBIOTYPE"         "GENEID"              "GENEIDVERSION"       "GENENAME"           
# [11] "GENESEQEND"          "GENESEQSTART"        "INTERPROACCESSION"   "ISCIRCULAR"          "PROTDOMEND"         
# [16] "PROTDOMSTART"        "PROTEINDOMAINID"     "PROTEINDOMAINSOURCE" "PROTEINID"           "PROTEINSEQUENCE"    
# [21] "SEQCOORDSYSTEM"      "SEQLENGTH"           "SEQNAME"             "SEQSTRAND"           "SYMBOL"             
# [26] "TXBIOTYPE"           "TXCDSSEQEND"         "TXCDSSEQSTART"       "TXID"                "TXIDVERSION"        
# [31] "TXNAME"              "TXSEQEND"            "TXSEQSTART"          "TXSUPPORTLEVEL"      "UNIPROTDB"          
# [36] "UNIPROTID"           "UNIPROTMAPPINGTYPE" 

rowData(sce.416b)$ENSEMBL <- rownames(sce.416b)
rowData(sce.416b)$SYMBOL <- mapIds(ens.mm.v97, keys=rownames(sce.416b),
                                   keytype="GENEID", column="SYMBOL")
```

如果需要把这个函数应用在日常使用中，也很简单，它**只需要两个参数：一个ID，一个ID对应的名字**

```r
# 一个ID
head(rowData(sce.416b)$ENSEMBL)
# [1] "ENSMUSG00000102693" "ENSMUSG00000064842"
# [3] "ENSMUSG00000051951" "ENSMUSG00000102851"
# [5] "ENSMUSG00000103377" "ENSMUSG00000104017"
# 一个ID对应的名字(这一步是问题的根源：存在无对应NA或者一对多、多对一的问题)
head(rowData(sce.416b)$SYMBOL)
# ENSMUSG00000102693 ENSMUSG00000064842 
# "4933401J01Rik"          "Gm26206" 
# ENSMUSG00000051951 ENSMUSG00000102851 
# "Xkr4"          "Gm18956" 
# ENSMUSG00000103377 ENSMUSG00000104017 
# "Gm37180"          "Gm37363"
uniquifyFeatureNames(head(rowData(sce.416b)$ENSEMBL), 
                     head(rowData(sce.416b)$SYMBOL))
```

**然后增加基因的染色体编号（方便后面识别线粒体基因）**

```r
rowData(sce.416b)$SEQNAME <- mapIds(ens.mm.v97, keys=rownames(sce.416b),
                                    keytype="GENEID", column="SEQNAME")
> table(rowData(sce.416b)$SEQNAME=='MT')
# FALSE  TRUE 
# 46004    37
```

## 3 质控

### 备份数据

```r
unfiltered <- sce.416b
```

一般来说，有spike-in其实可以不需要线粒体过滤（因为它们的目的一致），但是这里还是加上了，双重保险

```r
mito <- which(rowData(sce.416b)$SEQNAME=="MT")
# 先计算各个细胞的QC指标
stats <- perCellQCMetrics(sce.416b, subsets=list(Mt=mito))
colnames(stats)
# [1] "sum"                   "detected"             
# [3] "percent_top_50"        "percent_top_100"      
# [5] "percent_top_200"       "percent_top_500"      
# [7] "subsets_Mt_sum"        "subsets_Mt_detected"  
# [9] "subsets_Mt_percent"    "altexps_ERCC_sum"     
# [11] "altexps_ERCC_detected" "altexps_ERCC_percent" 
# [13] "altexps_SIRV_sum"      "altexps_SIRV_detected"
# [15] "altexps_SIRV_percent"  "total"  

# 再根据QC指标进行判断，哪些该去除
qc <- quickPerCellQC(stats, percent_subsets=c("subsets_Mt_percent",
    "altexps_ERCC_percent"), batch=sce.416b$block)

sce.416b <- sce.416b[,!qc$discard]

> dim(sce.416b)
[1] 46604   185
> dim(unfiltered)
[1] 46604   192
```

### **根据原来的数据，加上质控标准作图**

```r
colData(unfiltered) <- cbind(colData(unfiltered), stats)
unfiltered$discard <- qc$discard

gridExtra::grid.arrange(
    plotColData(unfiltered, x="block", y="sum", 
        colour_by="discard") + scale_y_log10() + ggtitle("Total count"),
    plotColData(unfiltered, x="block", y="detected", 
        colour_by="discard") + scale_y_log10() + ggtitle("Detected features"),
    plotColData(unfiltered, x="block", y="subsets_Mt_percent", 
        colour_by="discard") + ggtitle("Mito percent"),
    plotColData(unfiltered, x="block", y="altexps_ERCC_percent", 
        colour_by="discard") + ggtitle("ERCC percent"),
    nrow=2,
    ncol=2
)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-19-032210.png)

### **再看下文库大小和ERCC分别和线粒体含量的关系**

```r
gridExtra::grid.arrange(
    plotColData(unfiltered, x="sum", y="subsets_Mt_percent", 
        colour_by="discard") + scale_x_log10(),
    plotColData(unfiltered, x="altexps_ERCC_percent", y="subsets_Mt_percent",
        colour_by="discard"),
    ncol=2
)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-19-032650.png)

### **然后检查一下被过滤的原因**

```r
colSums(as.matrix(qc))
##              low_lib_size            low_n_features   high_subsets_Mt_percent 
##                         5                         0                         2 
## high_altexps_ERCC_percent                   discard 
##                         2                         7
```

## 4 归一化

之前在 [3.2 归一化](https://jieandze1314.osca.top/03/03-2) 中介绍：去卷积方法计算size factor时，操作是这样的

* 先混合：Pool counts from many cells to increase the size of the counts
* 后去卷积：Pool-based size factors are then “deconvolved” into cell-based factors for normalization of each cell’s expression profile.

```r
set.seed(100)
# 这里一看有pre-clustering就知道是利用的去卷积的size factor
clust.zeisel <- quickCluster(sce.zeisel) 
sce.zeisel <- computeSumFactors(sce.zeisel, cluster=clust.zeisel, min.mean=0.1)
sce.zeisel <- logNormCounts(sce.zeisel)
```

但是这里的数据集由于细胞数量较少，并且所有的细胞都是源自一个细胞系，所以不需要提前进行聚类`quickCluster`操作，直接进行去卷积即可。目的只有一个，将技术因素（如测序）导致的文库大小差异降到最低

```r
library(scran)
sce.416b <- computeSumFactors(sce.416b)
sce.416b <- logNormCounts(sce.416b)
summary(sizeFactors(sce.416b))
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   0.347   0.711   0.921   1.000   1.152   3.604
```

### **看看两种归一化方法的差异**

```r
# 常规：最简单的只考虑文库大小
summary(librarySizeFactors(sce.416b))
# Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
# 0.3996  0.7601  0.9406  1.0000  1.1207  3.6720 

plot(librarySizeFactors(sce.416b), sizeFactors(sce.416b), pch=16,
    xlab="Library size factors", ylab="Deconvolution factors",
     # 这是一个不错的主意，可以学习，直接将不同类型赋予不同颜色
     # +1操作是为了将原来的False（0）、True（1）变成1、2
     # 因此得到的black就是uninduced，red就是induced
    col=c("black", "red")[grepl("induced", sce.416b$phenotype)+1],
    log="xy")
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-19-040144.png)

看到去卷积的方法的确能暴露出细胞类型对文库矫正的影响，因为如果只考虑文库大小，那么常规的方法和去卷积方法应该结果一致，也不会出现红点和黑点分离的情况，但现在既然由于细胞类型不同而导致size factor出现了偏差，那么就是去卷积方法还考虑了其他因素，做的更加精细。

当然，使用常规的文库大小归一化方法对细胞分群和鉴定每群的marker基因是足够的。这里使用去卷积得到更准确的结果，虽然对分群改善不大，但对于每个基因的表达量数据估计与解释还是很重要的，因为它考虑了细胞类型组成差异的影响。

## 5 找表达量高变化基因

这里会使用到之前设置的批次信息，即：用spike-in来拟合更准确的技术偏差，同时考虑上批次信息【在之前 [3.3 挑选高变化基因](https://jieandze1314.osca.top/03/03-3) 的2.4 考虑批次效应部分提到】

```r
dec.416b <- modelGeneVarWithSpikes(sce.416b, "ERCC", block=sce.416b$block)
chosen.hvgs <- getTopHVGs(dec.416b, prop=0.1)
```

分别作图

```r
par(mfrow=c(1,2))
blocked.stats <- dec.416b$per.block
for (i in colnames(blocked.stats)) {
    current <- blocked.stats[[i]]
    plot(current$mean, current$total, main=i, pch=16, cex=0.5,
        xlab="Mean of log-expression", ylab="Variance of log-expression")
    curfit <- metadata(current)
    points(curfit$mean, curfit$var, col="red", pch=16)
    curve(curfit$trend(x), col='dodgerblue', add=TRUE, lwd=2)
}
```

黑点是基因，红点是spike-in，蓝线是根据红点画的拟合线。看到这里的两个批次之间差异很小，表示重复效果不错

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-19-041126.png)

#### 6 批次矫正

从上面的效果看，这两个细胞板之间的细胞细胞组成应该是差不多的，因此使用简单的批次处理即可，不需要动用太复杂的方法；如果数据集比较大，需要考虑的批次效应更多，可能要动用 batchelor包的`regressBatches()`

```r
library(limma)
assay(sce.416b, "corrected") <- removeBatchEffect(logcounts(sce.416b), 
    design=model.matrix(~sce.416b$phenotype), batch=sce.416b$block)
```

## 7 降维

这个小数据集我们估计也不会有太大的异质性，因此设置10个主成分即可

然后使用精确的 SVD方法，来避免 irlba包对于处理小数据可能会出现的一些warning信息

```r
sce.416b <- runPCA(sce.416b, ncomponents=10, subset_row=chosen.hvgs,
    exprs_values="corrected", BSPARAM=BiocSingular::ExactParam())

set.seed(1010)
sce.416b <- runTSNE(sce.416b, dimred="PCA", perplexity=10)
```

## 8 聚类

```r
# 看到这里使用了层次聚类的方法
my.dist <- dist(reducedDim(sce.416b, "PCA"))
my.tree <- hclust(my.dist, method="ward.D2")

library(dynamicTreeCut)
my.clusters <- unname(cutreeDynamic(my.tree, distM=as.matrix(my.dist),
    minClusterSize=10, verbose=0))
colLabels(sce.416b) <- factor(my.clusters)
```

**看一下现在的分群和批次之间的联系**

```r
table(Cluster=colLabels(sce.416b), Plate=sce.416b$block)
##        Plate
## Cluster 20160113 20160325
##       1       40       38
##       2       37       32
##       3       10       14
##       4        6        8
```

**再比较一下现在的分群和表型之间的联系**

```r
##        Oncogene
## Cluster induced CBFB-MYH11 oncogene expression wild type phenotype
##       1                                     78                   0
##       2                                      0                  69
##       3                                      1                  23
##       4                                     14                   0
```

**作图**

```r
plotTSNE(sce.416b, colour_by="label")
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-19-062356.png)

### **可以借助”轮廓图“（silhouette width）检查分群的质量**

会对每个细胞都计算一个silhouette width值，如果一个细胞的**width值为正并且越大**，表示相对于其他亚群的细胞，这个细胞和它所在亚群中的细胞更接近，**分群效果越好**；如果width为负，就表示这个亚群的这个细胞和其他亚群的细胞更接近，即分群效果不太理想。

```r
library(cluster)
sil <- silhouette(my.clusters, dist = my.dist)
> colnames(sil)
[1] "cluster"   "neighbor"  "sil_width"

# 设置每个cluster的颜色
clust.col <- scater:::.get_palette("tableau10medium")

# 下面这一句的意思是：如果sil_width>0，就属于和自己接近，否则属于和其他亚群接近
sil.cols <- clust.col[ifelse(sil[,3] > 0, sil[,1], sil[,2])]
sil.cols <- sil.cols[order(-sil[,1], sil[,3])]

plot(sil, main = paste(length(unique(my.clusters)), "clusters"),
    border=sil.cols, col=sil.cols, do.col.sort=FALSE)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-02-092004.png)

图中可以反映的问题：

* 这个图是对上面得到的各个cluster中的细胞做的barplot，每个cluster是一种颜色。
* 这里看到cluster2都是正值并且横坐标width数值还是最大的，因此说明了分群没有问题，之前t-SNE的结果的确是它本身的问题
* 如果发现width值较小，表示分群结果一般，还有可能是分群过度，本来属于一个群的，又被拆分成小群
* 利用这个图，我们就能调整之前的参数，来调整分群效果，不过也不需要太过纠结完美的分群结果。因为即使图上看似合理的分群，可能实际上也不会得到更多的生物信息

## 9 找marker基因并解释结果

```r
markers <- findMarkers(sce.416b, my.clusters, block=sce.416b$block)
# 找cluster1的marker基因
marker.set <- markers[["1"]]
# cluster1 的Top10
head(marker.set, 10)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-19-063547.png)

**使用cluster1的Top10基因（但不一定只是10个）画热图**

看到这些基因在cluster1的分布确实和其他几个clusters不同，表示差异基因找的比较可靠；另外看到，cluster 1含有致癌细胞，而且和DNA复制和细胞周期相关基因有强烈的下调，道理上也说得通，毕竟细胞衰老也是诱导癌细胞的一个因素

```r
top.markers <- rownames(marker.set)[marker.set$Top <= 10]
> length(top.markers)
[1] 29

plotHeatmap(sce.416b, features=top.markers, order_columns_by="label",
    colour_columns_by=c("label", "block", "phenotype"),
    center=TRUE, symmetric=TRUE, zlim=c(-5, 5))
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-19-063741.png)

看着是不是很像pheatmap的结果，其实看一下这个函数，就是基于pheatmap做的

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-19-063840.png)


# 4.2 实战二 | STRT-Seq | 小鼠大脑

刘小泽写于2020.7.19

## 1 前言

使用了一个存在异质性的数据集，是研究小鼠大脑的 ([Zeisel et al. 2015](https://pubmed.ncbi.nlm.nih.gov/25700174/))

其中大约包含3000个细胞，包括少突胶质细胞，小胶质细胞和神经元等，使用的细胞分离平台是Fluidigm C1微流控系统，属于比较早期的系统【[单细胞测序的知识](https://www.jianshu.com/p/9fc521bf82ba)】

![发展历史](https://1260378310-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MCv4XZpjUYDdSrsYVkw%2F-MCzH7Oo07eVDtwMWDAF%2F-MCzH_2G579QltZNn9qp%2Fimage.png?alt=media\&token=c51730af-19e6-479c-9ddd-4a93e4ed976c)

### 文库制备时加入了UMI

> **UMI简单解释：** UMI就是为了去除PCR扩增偏差的。一般一个基因对应多个UMI时，出现多个reads含有同一个UMI时，这里只计数一次。
>
> **UMI英文解释：** Each transcript molecule can only produce one UMI count but can yield many reads after fragmentation
>
> **UMI详细解释：** 不管是bulk RNA还是scRNA，都需要进行PCR扩增，但是不可避免有一些转录本会被扩增太多次，超过了真实表达量。当起始文库很小时（比如单细胞数据），就需要更多次的PCR过程，这个次数越多，引入的误差就越大。UMI就是Unique Molecular Identifier，由4-10个随机核苷酸组成，在mRNA反转录后，进入到文库中，每一个mRNA随机连上一个UMI，根据PCR结果可以计数不同的UMI，最终统计mRNA的数量。
>
> **UMI图片解释：**
>
> ![image-20200719150326484](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-19-070326.png) ![image-20200719150624540](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-19-070625.png)
>
> **UMI有几个要求：**
>
> * 不能是[均聚物](https://zh.wikipedia.org/zh-hans/%E5%9D%87%E8%81%9A%E7%89%A9) ，如AAAAAAAAAA
> * 不能有N碱基
> * 不能包含碱基质量低于10的碱基

## 2 数据准备

```r
# 自己下载
library(scRNAseq)
sce.zeisel <- ZeiselBrainData()
# 或者使用之前分享的RData
load('sce.zeisel.RData')

sce.zeisel
# class: SingleCellExperiment 
# dim: 20006 3005 
# metadata(0):
#   assays(1): counts
# rownames(20006): Tspan12 Tshz1 ... mt-Rnr1
# mt-Nd4l
# rowData names(1): featureType
# colnames(3005): 1772071015_C02 1772071017_G12
# ... 1772066098_A12 1772058148_F03
# colData names(10): tissue group # ...
# level1class level2class
# reducedDimNames(0):
#   altExpNames(2): ERCC repeat
```

看到这么几个信息：2万多基因，3005个样本；只有原始count矩阵；使用了symbol ID；加入了ERCC

```r
# 有57个ERCC
> dim(altExp(sce.zeisel,'ERCC'))
[1]   57 3005

# 而且已经标注了线粒体基因
> table(rowData(sce.zeisel))

endogenous       mito 
     19972         34
```

### **一个重要操作：aggregateAcrossFeatures**

> 英文解释是：Sum together expression values (by default, counts) for each feature set in each cell. 但是只看说明还是不好理解，举个例子：

**可以看到下面会有很多基因具有多个loc**

比如`OTTMUSG00000016609_loc4`、`OTTMUSG00000016609_loc3` 其实可以算作一个基因

```r
head(rownames(sce.zeisel)[grep("_loc[0-9]+$",rownames(sce.zeisel))])
# [1] "Syne1_loc2"              "Hist1h2ap_loc1"         
# [3] "Inadl_loc1"              "OTTMUSG00000016609_loc4"
# [5] "OTTMUSG00000016609_loc3" "Gm5643_loc2" 

# 这样的有300多个
> length(grep("_loc[0-9]+$",rownames(sce.zeisel)))
[1] 330
```

**如果拿一个基因来看**

Syne1有Syne1\_loc1和Syne1\_loc2

```r
> length(grep("Syne1",rownames(sce.zeisel)))
[1] 2

counts(sce.zeisel)[grep("Syne1",rownames(sce.zeisel)),][1:2,1:3]
# 1772071015_C02 1772071017_G12 1772071017_A05
# Syne1_loc2             11              2              4
# Syne1_loc1              0              0              4
```

**如果使用这个函数，会有怎样效果**

```r
test <- aggregateAcrossFeatures(sce.zeisel, 
                                      id=sub("_loc[0-9]+$", "", rownames(sce.zeisel)))
# 只剩一个了，也就是合二为一
> length(grep("Syne1",rownames(test)))
[1] 1

# 看表达量，也是合二为一
> counts(test)[grep("Syne1",rownames(test)),][1:3]
1772071015_C02 1772071017_G12 1772071017_A05 
            11              2              8
```

因此，明白了，**这个函数就是处理相同行：把几个相同的行的值加在一起变为一行**

也就明白了，下面👇为什么要进行`sub`操作，其实就是为了把loc去掉，暴露出相同的基因名，才能执行`aggregateAcrossFeatures`函数

```r
sce.zeisel <- aggregateAcrossFeatures(sce.zeisel, 
                                      id=sub("_loc[0-9]+$", "", rownames(sce.zeisel)))

> dim(sce.zeisel)
[1] 19839  3005
```

### **再添加Ensembl ID**

```r
library(org.Mm.eg.db)
rowData(sce.zeisel)$Ensembl <- mapIds(org.Mm.eg.db, 
    keys=rownames(sce.zeisel), keytype="SYMBOL", column="ENSEMBL")
```

## 3 质控

### 备份数据

还是备份一下，把unfiltered数据主要用在质控的探索上

```r
unfiltered <- sce.zeisel
```

这个公共数据的作者在发表文章时将数据的低质量细胞去掉了，但并不妨碍我们做个质控，也可以看看它去除的怎样

```r
stats <- perCellQCMetrics(sce.zeisel, subsets=list(
    Mt=rowData(sce.zeisel)$featureType=="mito"))
qc <- quickPerCellQC(stats, percent_subsets=c("altexps_ERCC_percent", 
    "subsets_Mt_percent"))
sce.zeisel <- sce.zeisel[,!qc$discard]

> sum(qc$discard)
[1] 189

> dim(sce.zeisel)
[1] 19839  2816
```

### **根据原来的数据，加上质控标准作图**

```r
colData(unfiltered) <- cbind(colData(unfiltered), stats)
unfiltered$discard <- qc$discard
# 做个图
gridExtra::grid.arrange(
    plotColData(unfiltered, y="sum", colour_by="discard") +
        scale_y_log10() + ggtitle("Total count"),
    plotColData(unfiltered, y="detected", colour_by="discard") +
        scale_y_log10() + ggtitle("Detected features"),
    plotColData(unfiltered, y="altexps_ERCC_percent",
        colour_by="discard") + ggtitle("ERCC percent"),
    plotColData(unfiltered, y="subsets_Mt_percent",
        colour_by="discard") + ggtitle("Mito percent"),
    ncol=2
)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-19-085835.png)

### **再看下文库大小和ERCC分别和线粒体含量的关系**

```r
gridExtra::grid.arrange(
    plotColData(unfiltered, x="sum", y="subsets_Mt_percent",
        colour_by="discard") + scale_x_log10(),
    plotColData(unfiltered, x="altexps_ERCC_percent", y="subsets_Mt_percent",
        colour_by="discard"),
    ncol=2
)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-19-085923.png)

### **然后检查一下被过滤的原因**

```r
##              low_lib_size            low_n_features high_altexps_ERCC_percent 
##                         0                         3                        65 
##   high_subsets_Mt_percent                   discard 
##                       128                       189
```

## 4 归一化

这里细胞数量较多，因此需要预先分群+去卷积计算size factor

```r
library(scran)
set.seed(1000)
clusters <- quickCluster(sce.zeisel)
sce.zeisel <- computeSumFactors(sce.zeisel, cluster=clusters) 
sce.zeisel <- logNormCounts(sce.zeisel)
summary(sizeFactors(sce.zeisel))
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   0.119   0.486   0.831   1.000   1.321   4.509
```

**看看两种归一化方法的差异**

```r
# 常规：最简单的只考虑文库大小
summary(librarySizeFactors(sce.zeisel))
# Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
# 0.1757  0.5680  0.8680  1.0000  1.2783  4.0839 

plot(librarySizeFactors(sce.zeisel), sizeFactors(sce.zeisel), pch=16,
    xlab="Library size factors", ylab="Deconvolution factors", log="xy")
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-19-090527.png)

## 5 找表达量高变化基因

理论上，应该对每个细胞都标记批次信息，添加`block`信息。但是这里由于技术不同，每个板子上只有20-40个细胞，并且细胞群体具有高度异质性，不能假设每个板上的细胞类型的分布是相同的，因此这里不使用`block`将批次信息“锁住”是合适的

既然有ERCC，就可以用第三种方法【在之前 [3.3 挑选高变化基因](https://jieandze1314.osca.top/03/03-3) 的2.3 考虑技术噪音】：

```r
dec.zeisel <- modelGeneVarWithSpikes(sce.zeisel, "ERCC")
top.hvgs <- getTopHVGs(dec.zeisel, prop=0.1)
> length(top.hvgs)
[1] 1816
```

同样使用了spike-in，对比一下，看到这里不管总体方差还是技术因素方差都要比之前smart-seq2要小。

> smart-seq2是按read计数，这里由于添加了UMI，是按molecule计数，也就是说，**UMI的加入，确实减少了PCR扩增的偏差影响**

另外图中看到，这里STRT-seq的spike-in方差一直要比内源基因的方差小，也就是说内源基因的变化幅度一直保持高位，体现了数据中包含多种细胞类型而导致的异质性，异质性导致了基因表达极度不均衡

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-19-091219.png)

## 6 降维

```r
library(BiocSingular)
set.seed(101011001)
sce.zeisel <- denoisePCA(sce.zeisel, technical=dec.zeisel, subset.row=top.hvgs)
sce.zeisel <- runTSNE(sce.zeisel, dimred="PCA")
```

看一下得到的PC数

```r
ncol(reducedDim(sce.zeisel, "PCA"))
## [1] 50
```

## 7 聚类

```r
snn.gr <- buildSNNGraph(sce.zeisel, use.dimred="PCA")
colLabels(sce.zeisel) <- factor(igraph::cluster_walktrap(snn.gr)$membership)
```

看一下结果

```r
table(colLabels(sce.zeisel))
## 
##   1   2   3   4   5   6   7   8   9  10  11  12  13  14 
## 283 451 114 143 599 167 191 128 350  70 199  58  39  24
```

画图

```r
plotTSNE(sce.zeisel, colour_by="label")
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-19-092215.png)

## 8 找marker基因并解释结果

> 主要还是关注上调基因，可以帮我们快速判断出异质性群体中各个细胞类型的差异

比如还是针对cluster1看看

```r
markers <- findMarkers(sce.zeisel, direction="up")
marker.set <- markers[["1"]]

> ncol(marker.set)
[1] 17

> colnames(marker.set)
 [1] "Top"           "p.value"       "FDR"           "summary.logFC" "logFC.2"       "logFC.3"       "logFC.4"      
 [8] "logFC.5"       "logFC.6"       "logFC.7"       "logFC.8"       "logFC.9"       "logFC.10"      "logFC.11"     
[15] "logFC.12"      "logFC.13"      "logFC.14"  


head(marker.set[,1:8], 10)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-19-092744.png)

### **方法一：画热图**

**使用cluster1的Top10基因（但不一定只是10个）**

```r
top.markers <- rownames(marker.set)[marker.set$Top <= 10]
> length(top.markers)
[1] 58

plotHeatmap(sce.zeisel, features=top.markers, order_columns_by="label")
```

接下来就是根据背景知识了，比如看到Gad1、Slc6a1表达量都很高，可能表明cluster1属于中间神经元

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-19-093013.png)

### **方法二：基于logFC**

比如可以挑出cluster1的计算结果`marker.set`中前50个基因（这里就是50个，而不是Top50），然后根据cluster1与其他clusters的logFC，对每个基因表达量做热图

```r
library(pheatmap)
logFCs <- getMarkerEffects(marker.set[1:50,])
pheatmap(logFCs, breaks=seq(-5, 5, length.out=101))
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-19-094211.png)

那么**这个函数到底做了什么呢？**&#x770B;图就知道：

> 也就是把每个基因在其他clusters的logFC结果挑出来，汇集成了一个新矩阵，我们自己手动也是可以做到的

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-19-094036.png)


# 4.3 实战三 | 10X | 未过滤的PBMC

刘小泽写于2020.7.19

## 1 前言

相信学习单细胞数据分析的大家对PBMC都不陌生，虽然不是相关背景，但Seurat的PBMC数据深入人心。PBMC全称是peripheral blood mononuclear cell ，外周血单核细胞。

我们这里用的数据集来自[Zheng et al. 2017](https://pubmed.ncbi.nlm.nih.gov/28091601/)，并在[10X Genomics官网也可以获取](https://support.10xgenomics.com/single-cell-gene-expression/datasets/2.1.0/pbmc4k)，数量比Seurat使用的的2700个细胞数据更大

## 2 数据准备

### **下载**

当然也可以跳过这一步，自己下载好之后读入。这里只是学习一下另一种方法

```r
library(BiocFileCache)
bfc <- BiocFileCache("raw_data", ask = FALSE)
raw.path <- bfcrpath(bfc, file.path("http://cf.10xgenomics.com/samples",
    "cell-exp/2.1.0/pbmc4k/pbmc4k_raw_gene_bc_matrices.tar.gz"))
untar(raw.path, exdir=file.path(tempdir(), "pbmc4k"))
# 最后也就是得到这三个文件：barcodes.tsv genes.tsv    matrix.mtx
```

### **读取**

```r
library(DropletUtils)
fname <- file.path(tempdir(), "pbmc4k/raw_gene_bc_matrices/GRCh38")
sce.pbmc <- read10xCounts(fname, col.names=TRUE)
# 看这里的数量惊人，但是后面还需要过滤
sce.pbmc
# class: SingleCellExperiment 
# dim: 33694 737280 
# metadata(1): Samples
# assays(1): counts
# rownames(33694): ENSG00000243485
# ENSG00000237613 ... ENSG00000277475
# ENSG00000268674
# rowData names(2): ID Symbol
# colnames(737280): AAACCTGAGAAACCAT-1
# AAACCTGAGAAACCGC-1 ... TTTGTCATCTTTAGTC-1
# TTTGTCATCTTTCCTC-1
# colData names(2): Sample Barcode
# reducedDimNames(0):
#   altExpNames(0):
```

### **转换ID，添加染色体信息**

```r
library(scater)
rownames(sce.pbmc) <- uniquifyFeatureNames(
    rowData(sce.pbmc)$ID, rowData(sce.pbmc)$Symbol)

library(EnsDb.Hsapiens.v86)
location <- mapIds(EnsDb.Hsapiens.v86, keys=rowData(sce.pbmc)$ID, 
    column="SEQNAME", keytype="GENEID")
```

## 3 质控

10X数据面临的一大问题就是空液滴，因此需要`emptyDrops`检验一下

```r
set.seed(100)
# 对70多万个液滴进行检验
e.out <- emptyDrops(counts(sce.pbmc))
# > e.out
# DataFrame with 737280 rows and 5 columns
# Total   LogProb    PValue   Limited       FDR
# <integer> <numeric> <numeric> <logical> <numeric>
#   AAACCTGAGAAACCAT-1         1        NA        NA        NA        NA
# AAACCTGAGAAACCGC-1         0        NA        NA        NA        NA
# AAACCTGAGAAACCTA-1         1        NA        NA        NA        NA
# AAACCTGAGAAACGAG-1         0        NA        NA        NA        NA
# AAACCTGAGAAACGCC-1         1        NA        NA        NA        NA
# ...                      ...       ...       ...       ...       ...
# TTTGTCATCTTTACAC-1         2        NA        NA        NA        NA
# TTTGTCATCTTTACGT-1        33        NA        NA        NA        NA
# TTTGTCATCTTTAGGG-1         0        NA        NA        NA        NA
# TTTGTCATCTTTAGTC-1         0        NA        NA        NA        NA
# TTTGTCATCTTTCCTC-1         1        NA        NA        NA        NA

# 最后过滤，剩下4000多
sce.pbmc <- sce.pbmc[,which(e.out$FDR <= 0.001)]
sce.pbmc
# class: SingleCellExperiment 
# dim: 33694 4233 
# metadata(1): Samples
# assays(1): counts
# rownames(33694): RP11-34P13.3 FAM138A ...
# AC213203.1 FAM231B
# rowData names(2): ID Symbol
# colnames(4233): AAACCTGAGAAGGCCT-1
# AAACCTGAGACAGACC-1 ... TTTGTCACAGGTCCAC-1
# TTTGTCATCCCAAGAT-1
# colData names(2): Sample Barcode
# reducedDimNames(0):
#   altExpNames(0):
```

### 数据备份

把unfiltered数据主要用在质控的探索上

```r
unfiltered <- sce.pbmc
```

这里过滤的条件不需要太严苛，**只需要去除线粒体含量太高的细胞即可**

> 成熟的mRNA会通过核孔来到细胞质。如果细胞遭到破坏，大量细胞质中mRNA流失，而线粒体体积比较大，流不出去，含量基本不变，最后导致细胞质中捕获的线粒体RNA占比升高

```r
stats <- perCellQCMetrics(sce.pbmc, subsets=list(Mito=which(location=="MT")))
high.mito <- isOutlier(stats$subsets_Mito_percent, type="higher")
sce.pbmc <- sce.pbmc[,!high.mito]

summary(high.mito)
##    Mode   FALSE    TRUE 
## logical    3922     311
```

### **根据原来的数据，加上质控标准作图**

```r
colData(unfiltered) <- cbind(colData(unfiltered), stats)
unfiltered$discard <- high.mito

gridExtra::grid.arrange(
    plotColData(unfiltered, y="sum", colour_by="discard") +
        scale_y_log10() + ggtitle("Total count"),
    plotColData(unfiltered, y="detected", colour_by="discard") +
        scale_y_log10() + ggtitle("Detected features"),
    plotColData(unfiltered, y="subsets_Mito_percent",
        colour_by="discard") + ggtitle("Mito percent"),
    ncol=3
)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-19-100732.png)

### **再看下文库大小分别和线粒体含量的关系**

```r
plotColData(unfiltered, x="sum", y="subsets_Mito_percent",
    colour_by="discard") + scale_x_log10()
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-19-100808.png)

## 4 归一化

也是使用预分群+去卷积计算size factor的方法

```r
library(scran)
set.seed(1000)
clusters <- quickCluster(sce.pbmc)
sce.pbmc <- computeSumFactors(sce.pbmc, cluster=clusters)
sce.pbmc <- logNormCounts(sce.pbmc)

summary(sizeFactors(sce.pbmc))
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   0.009   0.710   0.871   1.000   1.094  13.948
```

**看看两种归一化方法的差异**

```r
plot(librarySizeFactors(sce.pbmc), sizeFactors(sce.pbmc), pch=16,
    xlab="Library size factors", ylab="Deconvolution factors", log="xy")
abline(a=0, b=1, col="red")
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-19-101041.png)

## 5 找表达量高变化基因

既然有UMI，就可以用第三种方法【在之前 [3.3 挑选高变化基因](https://jieandze1314.osca.top/03/03-3) 的 2.3 考虑技术噪音中介绍过，如果没有spike-in】：可以考虑利用数据分布来表示技术噪音，例如只考虑技术噪音的话，UMI counts通常会呈现近似泊松分布

```r
set.seed(1001)
dec.pbmc <- modelGeneVarByPoisson(sce.pbmc)
top.pbmc <- getTopHVGs(dec.pbmc, prop=0.1)

# 作图
plot(dec.pbmc$mean, dec.pbmc$total, pch=16, cex=0.5,
    xlab="Mean of log-expression", ylab="Variance of log-expression")
curfit <- metadata(dec.pbmc)
# 可视化一条线（下图的蓝线），这条线指所有的基因都会存在的一种偏差
curve(curfit$trend(x), col='dodgerblue', add=TRUE, lwd=2)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-19-101244.png)

## 6 降维

```r
set.seed(10000)
sce.pbmc <- denoisePCA(sce.pbmc, subset.row=top.pbmc, technical=dec.pbmc)

set.seed(100000)
sce.pbmc <- runTSNE(sce.pbmc, dimred="PCA")

set.seed(1000000)
sce.pbmc <- runUMAP(sce.pbmc, dimred="PCA")
```

看看保留了几个PC

```r
ncol(reducedDim(sce.pbmc, "PCA"))
## [1] 8
```

## 7 聚类

```r
g <- buildSNNGraph(sce.pbmc, k=10, use.dimred = 'PCA')
clust <- igraph::cluster_walktrap(g)$membership
colLabels(sce.pbmc) <- factor(clust)

table(colLabels(sce.pbmc))
## 
##   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18 
## 585 518 364 458 170 791 295 107  45  46 152  84  40  60 142  16  28  21

plotTSNE(sce.pbmc, colour_by="label")
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-19-101650.png)

## 8 找marker基因并解释结果

```r
markers <- findMarkers(sce.pbmc, pval.type="some", direction="up")
```

这次看看cluster 7的marker基因

```r
marker.set <- markers[["7"]]
as.data.frame(marker.set[1:30,1:3])
# p.value           FDR summary.logFC
# FCN1   4.881588e-137 1.644802e-132      2.715872
# LGALS2 3.729029e-133 6.282295e-129      2.191398
# CSTA   1.426854e-131 1.602548e-127      2.123738
# CFD    1.207067e-102  1.016773e-98      1.503274
# FGL2    8.567117e-93  5.773209e-89      1.358891
# IFI30   7.822561e-80  4.392889e-76      1.276366
# CLEC7A  6.052032e-79  2.913102e-75      1.109366
# MS4A6A  1.958033e-78  8.246744e-75      1.419465
# CFP     8.802407e-73  3.295426e-69      1.312191
# S100A8  6.193215e-70  2.086742e-66      3.431603
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-19-102158.png)

再和其他clusters对比

```r
plotExpression(sce.pbmc, features=c("CD14", "CD68",
    "MNDA", "FCGR3A"), x="label", colour_by="label")
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-19-102238.png)

其实**最后还是考验的背景知识**：根据cluster7中CD14、CD68、MNDA表达量升高，同时又检查了CD16基因下调，推测cluster7是单核细胞


# 4.4 实战四 | 10X | 过滤后的PBMC

刘小泽写于2020.7.19

## 1 前言

这次使用的数据是来自[Zheng et al. 2017](https://pubmed.ncbi.nlm.nih.gov/28091601/) 的三个PBMC数据，而且这个数据是经过前期过滤的

### **准备数据**

```r
library(TENxPBMCData)
all.sce <- list(
    pbmc3k=TENxPBMCData('pbmc3k'),
    pbmc4k=TENxPBMCData('pbmc4k'),
    pbmc8k=TENxPBMCData('pbmc8k')
)

all.sce
# $pbmc3k
# class: SingleCellExperiment 
# dim: 32738 2700 
# metadata(0):
#   assays(1): counts
# rownames(32738): ENSG00000243485 ENSG00000237613 ...
# ENSG00000215616 ENSG00000215611
# rowData names(3): ENSEMBL_ID Symbol_TENx Symbol
# colnames: NULL
# colData names(11): Sample Barcode ... Individual
# Date_published
# reducedDimNames(0):
#   altExpNames(0):
#   
#   $pbmc4k
# class: SingleCellExperiment 
# dim: 33694 4340 
# metadata(0):
#   assays(1): counts
# rownames(33694): ENSG00000243485 ENSG00000237613 ...
# ENSG00000277475 ENSG00000268674
# rowData names(3): ENSEMBL_ID Symbol_TENx Symbol
# colnames: NULL
# colData names(11): Sample Barcode ... Individual
# Date_published
# reducedDimNames(0):
#   altExpNames(0):
#   
#   $pbmc8k
# class: SingleCellExperiment 
# dim: 33694 8381 
# metadata(0):
#   assays(1): counts
# rownames(33694): ENSG00000243485 ENSG00000237613 ...
# ENSG00000277475 ENSG00000268674
# rowData names(3): ENSEMBL_ID Symbol_TENx Symbol
# colnames: NULL
# colData names(11): Sample Barcode ... Individual
# Date_published
# reducedDimNames(0):
#   altExpNames(0):
```

**多个数据集的批量处理，重点就是列表list和for循环的熟练使用，还有相关的apply家族函数**。而且每个结果数据也要对应放在一个新列表中，比如下面质控使用的`stats <- high.mito <- list()` ，就是新建了两个空列表，然后把结果放进去

## 2 批量质控

### 数据备份

把unfiltered数据主要用在质控的探索上

```r
unfiltered <- all.sce
```

还是先通过线粒体含量计算质控结果，然后根据这个结果进行过滤，一个for循环搞定

```r
library(scater)
stats <- high.mito <- list()

for (n in names(all.sce)) {
  current <- all.sce[[n]]
  is.mito <- grep("MT", rowData(current)$Symbol_TENx)
  stats[[n]] <- perCellQCMetrics(current, subsets=list(Mito=is.mito))
  high.mito[[n]] <- isOutlier(stats[[n]]$subsets_Mito_percent, type="higher")
  all.sce[[n]] <- current[,!high.mito[[n]]]
}
```

### **看一下根据线粒体过滤的结果**

```r
> lapply(high.mito, summary)
$pbmc3k
   Mode   FALSE    TRUE 
logical    2609      91 

$pbmc4k
   Mode   FALSE    TRUE 
logical    4182     158 

$pbmc8k
   Mode   FALSE    TRUE 
logical    8157     224
```

### **批量作图（也是把作图结果放进list，方便后期批量导出）**

```r
qcplots <- list()

for (n in names(all.sce)) {
  current <- unfiltered[[n]]
  colData(current) <- cbind(colData(current), stats[[n]])
  current$discard <- high.mito[[n]]
  qcplots[[n]] <- plotColData(current, x="sum", y="subsets_Mito_percent",
                              colour_by="discard") + scale_x_log10()
}
# do.call也是list处理中的常用函数
do.call(gridExtra::grid.arrange, c(qcplots, ncol=3))
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-19-121017.png)

## 3 批量归一化

这里使用的是最简单的方法`logNormCounts()`，就是用某个细胞中每个基因或spike-in转录本的表达量除以这个细胞计算的size factor，最后还进行一个log转换，得到一个新的矩阵：`logcounts` 【不过这个名字并不是很贴切，只是因为拼写简单，真实应该是：log-transformed normalized expression values。而不是单纯字面意思取个log】

```r
all.sce <- lapply(all.sce, logNormCounts)
```

**看到这里，可能会想，为什么没计算size factor就直接进行了logNormCounts？**

前面提到的操作，一般都是：

```r
# 常规方法
lib.sf.zeisel <- librarySizeFactors(sce.zeisel)
lib.sf.zeisel <- logNormCounts(lib.sf.zeisel)
# 去卷积方法
clusters <- quickCluster(sce.pbmc)
sce.pbmc <- computeSumFactors(sce.pbmc, cluster=clusters)
sce.pbmc <- logNormCounts(sce.pbmc)
```

**看一下`logNormCounts`的帮助文档就能明白了，逻辑很清楚：**

* 函数默认的参数是：`size_factors=NULL`，如果没有计算size factor更新给函数，那么函数会执行`normalizeCounts` 的操作
* 再来看`normalizeCounts`会有什么操作：如果没有提供size factor，它会根据数据类型去自己计算size factor
  * 对于count矩阵和SummarizedExperiment数据类型，会通过`librarySizeFactors`计算
  * 对于SingleCellExperiment这种数据类型，它会首先在数据中寻找size factor是否存在，如果找不到，也是会使用`librarySizeFactors`

也就是说，**如果我们不提前计算，就会自动帮我们用最简单的`librarySizeFactors`计算，并添加到我们的数据中**。正是因为我们只需要最简单的方法，所以才可以不提供。如果要使用去卷积方法，还是要自己先计算好

**最后看下结果**

```r
> lapply(all.sce, function(x) summary(sizeFactors(x)))
$pbmc3k
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.2338  0.7478  0.9262  1.0000  1.1571  6.6042 

$pbmc4k
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.3155  0.7109  0.8903  1.0000  1.1272 11.0267 

$pbmc8k
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.2963  0.7043  0.8772  1.0000  1.1177  6.7942
```

## 4 批量找表达量高变化基因

同样也是使用了最简单的计算方法

```r
library(scran)
all.dec <- lapply(all.sce, modelGeneVar)
all.hvgs <- lapply(all.dec, getTopHVGs, prop=0.1)
```

作图

```r
par(mfrow=c(1,3))
for (n in names(all.dec)) {
    curdec <- all.dec[[n]]
    plot(curdec$mean, curdec$total, pch=16, cex=0.5, main=n,
        xlab="Mean of log-expression", ylab="Variance of log-expression")
    curfit <- metadata(curdec)
  # 可视化一条线（下图的蓝线），这条线指所有的基因都会存在的一种偏差
    curve(curfit$trend(x), col='dodgerblue', add=TRUE, lwd=2)
}
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-19-122747.png)

* 每个点表示一个基因
* 图中蓝线指的是：技术因素导致的偏差
* 纵坐标表示总偏差：它等于技术偏差+生物因素偏差

因此，要衡量一个基因的生物因素偏差大小，就看对应的纵坐标减去对应的蓝线的值

## 5 批量降维

这里将每个PBMC数据单独进行降维，而并没有把它们混合起来再分析

关于降维方法，这里选择是与PCA近似的**SVD算法（singular value decomposition，奇异值分解）**，scater或scran都可以直接通过函数计算SVD，利用参数`BSPARAM=`传递一个`BiocSingularParam`对象到`runPCA`中

* [SVD与PCA的解释](https://www.cnblogs.com/bjwu/p/9280492.html)
* SVD是一种矩阵分解方法，相当于因式分解，目的纯粹就是将一个矩阵拆分成多个矩阵相乘的形式
* **对于稀疏矩阵来说，SVD算法更适用**，这样对于大数据来说节省了很大空间

```r
library(BiocSingular)
set.seed(10000)
# 这里的runPCA是一个降维的函数名字，它可以用本身的PCA算法，还可以用SVD算法
all.sce <- mapply(FUN=runPCA, x=all.sce, subset_row=all.hvgs, 
    MoreArgs=list(ncomponents=25, BSPARAM=RandomParam()), 
    SIMPLIFY=FALSE)

set.seed(100000)
all.sce <- lapply(all.sce, runTSNE, dimred="PCA")

set.seed(1000000)
all.sce <- lapply(all.sce, runUMAP, dimred="PCA")
```

这里使用SVD其实还是为了帮助更好地进行PCA，支持4种方式：

* ExactParam: exact SVD with runExactSVD.
* IrlbaParam: approximate SVD with irlba via runIrlbaSVD.
* RandomParam: approximate SVD with rsvd via runRandomSVD.
* FastAutoParam: fast approximate SVD, chosen based on the matrix representation.

## 6 批量聚类

使用基于图形的聚类，最基础的想法是：我们首先构建一个图，其中每个节点都是一个细胞，它与高维空间中最邻近的细胞相连。连线基于细胞之间的相似性计算权重，权重越高，表示细胞间关系更密切。如果一群细胞之间的权重高于另一群细胞，那么这一群细胞就被当做一个群体 “communiity”。

```r
for (n in names(all.sce)) {
    g <- buildSNNGraph(all.sce[[n]], k=10, use.dimred='PCA')
    clust <- igraph::cluster_walktrap(g)$membership
    colLabels(all.sce[[n]])  <- factor(clust)
}
# 看看各自分了多少群
lapply(all.sce, function(x) table(colLabels(x)))
## $pbmc3k
## 
##   1   2   3   4   5   6   7   8   9  10 
## 487 154 603 514  31 150 179 333 147  11 
## 
## $pbmc4k
## 
##    1    2    3    4    5    6    7    8    9   10   11   12   13 
##  497  185  569  786  373  232   44 1023   77  218   88   54   36 
## 
## $pbmc8k
## 
##    1    2    3    4    5    6    7    8    9   10   11   12   13   14   15   16 
## 1004  759 1073 1543  367  150  201 2067   59  154  244   67   76  285   20   15 
##   17   18 
##   64    9
```

作图

```r
# 还是先新建一个空列表，方便保存数据
all.tsne <- list()

for (n in names(all.sce)) {
    all.tsne[[n]] <- plotTSNE(all.sce[[n]], colour_by="label") + ggtitle(n)
}
do.call(gridExtra::grid.arrange, c(all.tsne, list(ncol=3)))
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-19-125523.png)

## 7 数据整合

> 前面只是批量进行了各个数据集的分析，现在要把它们整合起来再分析一下

### **找共有基因**

```r
# 先看看各自多少基因
> lapply(all.sce, function(x) length(rownames(x)))
$pbmc3k
[1] 32738

$pbmc4k
[1] 33694

$pbmc8k
[1] 33694

# 找共同基因
universe <- Reduce(intersect, lapply(all.sce, rownames))
> length(universe)
[1] 31232
```

### **对每个数据批量取子集**

```r
# 这个操作可以记下来，lapply批量取子集
all.sce2 <- lapply(all.sce, "[", i=universe,)

> lapply(all.sce2, dim)
$pbmc3k
[1] 31232  2609

$pbmc4k
[1] 31232  4182

$pbmc8k
[1] 31232  8157

# 同样的，对找高变异基因的结果取子集
all.dec2 <- lapply(all.dec, "[", i=universe,)
```

### **把三个数据当做一个数据的三个批次，重新进行归一化**

```r
library(batchelor)
normed.sce <- do.call(multiBatchNorm, all.sce2)
```

### **根据重新归一化的结果，再次找HVGs**

这次是把3个批次放在一起再找的

```r
combined.dec <- do.call(combineVar, all.dec2)
combined.hvg <- getTopHVGs(combined.dec, n=5000)
```

### **对一个大数据进行降维**

> 结合：[单细胞交响乐10-数据集整合后的批次矫正](https://www.jianshu.com/p/4b10cff43920) 中的【第4部分 MNN矫正】

`fastMNN`先进行PCA降维，以加速下面的聚类环节

```r
set.seed(1000101)
merged.pbmc <- do.call(fastMNN, c(normed.sce, 
    list(subset.row=combined.hvg, BSPARAM=RandomParam())))

merged.pbmc
# class: SingleCellExperiment 
# dim: 5000 14948 
# metadata(2): merge.info pca.info
# assays(1): reconstructed
# rownames(5000): ENSG00000090382 ENSG00000163220 ...
# ENSG00000122068 ENSG00000011132
# rowData names(1): rotation
# colnames: NULL
# colData names(2): batch label
# reducedDimNames(1): corrected
# altExpNames(0):

# 这时就出现了批次信息
> table(merged.pbmc$batch)
pbmc3k pbmc4k pbmc8k 
  2609   4182   8157
```

检查一下结果，使用`lost.var` ，值越大表示丢失的真实生物异质性越多

* It contains a matrix of the **variance lost in each batch** (column) at each merge step (row).
* Large proportions of lost variance **(>10%)** suggest that correction is **removing genuine biological heterogeneity.**&#x20;

```r
metadata(merged.pbmc)$merge.info$lost.var
##         pbmc3k    pbmc4k   pbmc8k
## [1,] 7.003e-03 3.126e-03 0.000000
## [2,] 7.137e-05 5.125e-05 0.003003
```

### **对一个大数据进行聚类**

```r
g <- buildSNNGraph(merged.pbmc, use.dimred="corrected")
colLabels(merged.pbmc) <- factor(igraph::cluster_louvain(g)$membership)
table(colLabels(merged.pbmc), merged.pbmc$batch)
##     
##      pbmc3k pbmc4k pbmc8k
##   1     113    387    825
##   2     507    395    806
##   3     175    344    581
##   4     295    539   1018
##   5     346    638   1210
##   6      11      3      9
##   7      17     27    111
##   8      33    113    185
##   9     423    754   1546
##   10      4     36     67
##   11    197    124    221
##   12    150    180    293
##   13    327    588   1125
##   14     11     54    160
```

### **可视化**

```r
set.seed(10101010)
merged.pbmc <- runTSNE(merged.pbmc, dimred="corrected")
# 查看3个数据混合后的聚类结果，以及有没有批次效应
gridExtra::grid.arrange(
    plotTSNE(merged.pbmc, colour_by="label", text_by="label", text_colour="red"),
    plotTSNE(merged.pbmc, colour_by="batch")
)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-19-132033.png)


# 4.5 实战五 | CEL-seq2 | 人胰腺细胞

刘小泽写于2020.7.20

## 1 前言

第一代CEL-Seq由以色列理工学院研究团队于[2012发表在Cell Reports](https://pubmed.ncbi.nlm.nih.gov/22939981/)

CEL-Seq的全称是：Cell expression by linear amplification and sequencing，采用线性扩增的测序方法，其主要优势在于错误率比较低，但是和PCR一样都存在序列偏向性。另外还具有以下优点：

* 使用barcode允许多种类型的细胞同时分析；
* 每个细胞在一个管中进行处理，避免了样本之间的污染

后来在2016年，CEL-seq2诞生，与早期版本相比具有低成本、高灵敏度，并缩短了实验操作实验时间

### **数据准备**

这里使用的数据是： [Grun et al. (2016)](https://pubmed.ncbi.nlm.nih.gov/27345837/) 中不同人类供体的胰腺细胞

```r
library(scRNAseq)
sce.grun <- GrunPancreasData()

sce.grun
# class: SingleCellExperiment 
# dim: 20064 1728 
# metadata(0):
#   assays(1): counts
# rownames(20064): A1BG-AS1__chr19 A1BG__chr19 ...
# ZZEF1__chr17 ZZZ3__chr1
# rowData names(2): symbol chr
# colnames(1728): D2ex_1 D2ex_2 ... D17TGFB_95
# D17TGFB_96
# colData names(2): donor sample
# reducedDimNames(0):
#   altExpNames(1): ERCC

rowData(sce.grun)
# DataFrame with 20064 rows and 2 columns
# symbol         chr
# <character> <character>
#   A1BG-AS1__chr19    A1BG-AS1       chr19
# A1BG__chr19            A1BG       chr19
# A1CF__chr10            A1CF       chr10
# A2M-AS1__chr12      A2M-AS1       chr12
# A2ML1__chr12          A2ML1       chr12
# ...                     ...         ...
# ZYG11A__chr1         ZYG11A        chr1
# ZYG11B__chr1         ZYG11B        chr1
# ZYX__chr7               ZYX        chr7
# ZZEF1__chr17          ZZEF1       chr17
# ZZZ3__chr1             ZZZ3        chr1
```

### **ID转换**

**先将symbol转为Ensembl ID**

```r
library(org.Hs.eg.db)
gene.ids <- mapIds(org.Hs.eg.db, keys=rowData(sce.grun)$symbol,
    keytype="SYMBOL", column="ENSEMBL")
```

**接下来有两种处理方式：**

第一种：将Ensembl ID加到原来数据中

```r
library(scater)
rowData(sce.grun)$ensembl <- uniquifyFeatureNames(
  rowData(sce.grun)$symbol, gene.ids)
rowData(sce.grun)
# DataFrame with 20064 rows and 3 columns
# symbol         chr         ensembl
# <character> <character>     <character>
#   A1BG-AS1__chr19    A1BG-AS1       chr19 ENSG00000268895
# A1BG__chr19            A1BG       chr19 ENSG00000121410
# A1CF__chr10            A1CF       chr10 ENSG00000148584
# A2M-AS1__chr12      A2M-AS1       chr12 ENSG00000245105
# A2ML1__chr12          A2ML1       chr12 ENSG00000166535
# ...                     ...         ...             ...
# ZYG11A__chr1         ZYG11A        chr1 ENSG00000203995
# ZYG11B__chr1         ZYG11B        chr1 ENSG00000162378
# ZYX__chr7               ZYX        chr7 ENSG00000159840
# ZZEF1__chr17          ZZEF1       chr17 ENSG00000074755
# ZZZ3__chr1             ZZZ3        chr1 ENSG00000036549
```

第二种：将没有匹配的NA去掉，并且去掉重复的行（因为可能存在一个symbol对应多个Ensembl ID的情况）【这里就试一下这种方法】

```r
keep <- !is.na(gene.ids) & !duplicated(gene.ids)
> sum(is.na(gene.ids));sum(duplicated(gene.ids))
[1] 2487
[1] 2515

sce.grun <- sce.grun[keep,]
rownames(sce.grun) <- gene.ids[keep]
> dim(sce.grun) # 过滤掉2000多基因
[1] 17548  1728
```

## 2 质控

> 前几次实战的质控都很顺利，但并不意味着实际操作中这一步就可以跑跑代码解决 **这一次，就遇到了一个常规代码解决不了的问题，需要思考+调整**

### 数据备份

把unfiltered数据主要用在质控的探索上

```r
unfiltered <- sce.grun
```

看到这里的数据中没有线粒体基因

```r
table(rowData(sce.grun)$chr)
# 
# chr1 chr10 chr11 chr12 chr13 chr14 chr15 chr16 chr17 chr18 
# 1766   672  1071   947   328   551   532   740  1050   256 
# chr19  chr2 chr20 chr21 chr22  chr3  chr4  chr5  chr6  chr7 
# 1245  1142   484   188   400  1014   693   791   907   825 
# chr8  chr9  chrX  chrY 
# 604   664   658    20
```

倒是有几个donor的批次信息

```r
table(colData(sce.grun)$donor)
# 
# D10 D17  D2  D3  D7 
# 288 480  96 480 384
```

我们之前进行质控的时候，一般采用的先使用`perCellQCMetrics` 指定线粒体**计算**，然后用 `quickPerCellQC` 指定ERCC+线粒体进行**过滤**的策略。现在既然没有线粒体的信息，那么在第一步的`perCellQCMetrics`就不需要加`subset`参数了

```r
stats <- perCellQCMetrics(sce.grun)
# 参数subsets的含义: used to identify interesting feature subsets such as ERCC spike-in transcripts or mitochondrial genes.
# > colnames(stats)
# [1] "sum"                   "detected"             
# [3] "percent_top_50"        "percent_top_100"      
# [5] "percent_top_200"       "percent_top_500"      
# [7] "altexps_ERCC_sum"      "altexps_ERCC_detected"
# [9] "altexps_ERCC_percent"  "total"
```

### **然后进行过滤，并让函数注意批次信息**

```r
qc <- quickPerCellQC(stats, percent_subsets="altexps_ERCC_percent",
                     batch=sce.grun$donor)
colSums(as.matrix(qc), na.rm=TRUE)
# low_lib_size            low_n_features high_altexps_ERCC_percent 
# 85                        93                        88 
# discard 
# 129
```

### **作图**

```r
colData(unfiltered) <- cbind(colData(unfiltered), stats)
unfiltered$discard <- qc$discard

gridExtra::grid.arrange(
  plotColData(unfiltered, x="donor", y="sum", colour_by="discard") +
    scale_y_log10() + ggtitle("Total count"),
  plotColData(unfiltered, x="donor", y="detected", colour_by="discard") +
    scale_y_log10() + ggtitle("Detected features"),
  plotColData(unfiltered, x="donor", y="altexps_ERCC_percent",
              colour_by="discard") + ggtitle("ERCC percent"),
  ncol=3
)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-20-082948.png)

### **【重点】发现了问题**

> 质控并非是一帆风顺的，遇到问题应该知道如何探索解决

看到ERCC那个图，很明显D10和D3没有被正确过滤，它们中高ERCC的细胞还是没有被去掉

质控过滤有一个前提条件：大部分的细胞都是高质量的，但显然这里的D10、D3不符合这个要求，因此我们需要再次只根据D17、D2、D7这三个“好一点的样本”进行过滤

```r
# 重新计算过滤条件
qc2 <- quickPerCellQC(stats, percent_subsets="altexps_ERCC_percent",
    batch=sce.grun$donor,
    subset=sce.grun$donor %in% c("D17", "D7", "D2"))

colSums(as.matrix(qc2), na.rm=TRUE)
# low_lib_size            low_n_features high_altexps_ERCC_percent                   discard 
# 450                       511                       606                       665
```

这次再作图看看

```r
colData(unfiltered) <- cbind(colData(unfiltered), stats)
unfiltered$discard <- qc2$discard

gridExtra::grid.arrange(
  plotColData(unfiltered, x="donor", y="sum", colour_by="discard") +
    scale_y_log10() + ggtitle("Total count"),
  plotColData(unfiltered, x="donor", y="detected", colour_by="discard") +
    scale_y_log10() + ggtitle("Detected features"),
  plotColData(unfiltered, x="donor", y="altexps_ERCC_percent",
              colour_by="discard") + ggtitle("ERCC percent"),
  ncol=3
)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-20-091059.png)

看到这次对D10、D3的过滤力度大了很多，基本满意

### **最后把过滤条件应用在原数据**

```r
sce.grun <- sce.grun[,!qc$discard]
```

## 3 归一化

也是使用预分群+去卷积计算size factor的方法

```r
library(scran)
set.seed(1000)
clusters <- quickCluster(sce.grun)
sce.grun <- computeSumFactors(sce.grun, cluster=clusters)
sce.grun <- logNormCounts(sce.grun)

summary(sizeFactors(sce.grun))
# Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
# 0.09581  0.50459  0.79505  1.00000  1.22212 12.16491
```

**看看两种归一化方法的差异**

```r
plot(librarySizeFactors(sce.grun), sizeFactors(sce.grun), pch=16,
     col=as.integer(factor(sce.grun$donor)),
     xlab="Library size factors", ylab="Deconvolution factors", log="xy")
abline(a=0, b=1, col="red")
```

也是再一次证明了去卷积方法比常规的方法更加精细，对批次层面也会纳入考量

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-20-091250.png)

## 4 找表达量高变化基因

既然有批次的信息，那就加到构建模型中去，而且有ERCC，就选用`modelGeneVarWithSpikes`这个方法

```r
block <- paste0(sce.grun$sample, "_", sce.grun$donor)
dec.grun <- modelGeneVarWithSpikes(sce.grun, spikes="ERCC", block=block)
top.grun <- getTopHVGs(dec.grun, prop=0.1)
```

## 5 矫正批次效应

> 使用FastMNN方法：Correct for batch effects in single-cell expression data using a fast version of the mutual nearest neighbors (MNN) method.

```r
library(batchelor)
set.seed(1001010)
merged.grun <- fastMNN(sce.grun, subset.row=top.grun, batch=sce.grun$donor)
merged.grun
# class: SingleCellExperiment 
# dim: 1467 1063 
# metadata(2): merge.info pca.info
# assays(1): reconstructed
# rownames(1467): ENSG00000172023 ENSG00000172016 ...
# ENSG00000144867 ENSG00000179820
# rowData names(1): rotation
# colnames(1063): D2ex_1 D2ex_2 ... D17TGFB_94
# D17TGFB_95
# colData names(1): batch
# reducedDimNames(2): corrected
# altExpNames(0):
```

检查一下结果，使用`lost.var` ，值越大表示丢失的真实生物异质性越多

```r
metadata(merged.grun)$merge.info$lost.var
# D10         D17          D2         D3         D7
# [1,] 0.030843209 0.030956515 0.000000000 0.00000000 0.00000000
# [2,] 0.007129994 0.011275730 0.036836491 0.00000000 0.00000000
# [3,] 0.003528589 0.005027722 0.006846244 0.05020422 0.00000000
# [4,] 0.012070814 0.014673302 0.013972521 0.01267586 0.05281354
```

* It contains a matrix of the **variance lost in each batch** (column) at each merge step (row).
* Large proportions of lost variance **(>10%)** suggest that correction is **removing genuine biological heterogeneity.**&#x20;

## 6 降维 + 聚类

### **降维**

```r
set.seed(100111)
merged.grun <- runTSNE(merged.grun, dimred="corrected")
```

### **聚类**

```r
snn.gr <- buildSNNGraph(merged.grun, use.dimred="corrected")
colLabels(merged.grun) <- factor(igraph::cluster_walktrap(snn.gr)$membership)

# 聚类的分群与批次之间的对比
table(Cluster=colLabels(merged.grun), Donor=merged.grun$batch)
##        Donor
## Cluster D10 D17  D2  D3  D7
##      1   32  71  33  80  29
##      2    5   7   3   4   4
##      3    3  44   0   0  13
##      4   11 119   0   0  55
##      5   12  73  30   3  78
##      6   14  29   3   2  64
##      7    1   9   0   0   7
##      8    2   5   2   3   4
##      9    5  13   0   0  10
##      10  15  33  11  10  37
##      11   5  18   0   1  33
##      12   4  13   0   0   1
```

**最后作图看看批次效应矫正如何**

```r
gridExtra::grid.arrange(
    plotTSNE(merged.grun, colour_by="label"),
    plotTSNE(merged.grun, colour_by="batch"),
    ncol=2
)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-20-092141.png)


# 4.6 实战六 | CEL-seq | 人胰腺细胞

刘小泽写于2020.7.20

## 1 前言

这次使用的数据是：[Muraro et al. (2016)](https://pubmed.ncbi.nlm.nih.gov/27693023/) 中的不同人类供体的胰腺细胞，和上一次相比使用的是更早期的CEL-seq。整体操作和上次CEL-seq2类似

### **数据准备**

```r
library(scRNAseq)
sce.muraro <- MuraroPancreasData()
sce.muraro
# class: SingleCellExperiment 
# dim: 19059 3072 
# metadata(0):
#   assays(1): counts
# rownames(19059): A1BG-AS1__chr19 A1BG__chr19 ...
# ZZEF1__chr17 ZZZ3__chr1
# rowData names(2): symbol chr
# colnames(3072): D28-1_1 D28-1_2 ... D30-8_95
# D30-8_96
# colData names(3): label donor plate
# reducedDimNames(0):
#   altExpNames(1): ERCC
```

这次有4个供体

```r
table(sce.muraro$donor)
# 
# D28 D29 D30 D31 
# 768 768 768 768
```

不过这个基因命名很奇怪，它全部加上了染色体编号

```r
> head(rownames(sce.muraro))
[1] "A1BG-AS1__chr19" "A1BG__chr19"     "A1CF__chr10"    
[4] "A2M-AS1__chr12"  "A2ML1__chr12"    "A2M__chr12"
```

### **ID转换**

选择的方式是：将没有匹配的NA去掉，并且去掉重复的行

由于基因名很奇怪，所以需要把`__chr`及后面的去掉

```r
library(AnnotationHub)
edb <- AnnotationHub()[["AH73881"]]
gene.symb <- sub("__chr.*$", "", rownames(sce.muraro))
gene.ids <- mapIds(edb, keys=gene.symb, 
    keytype="SYMBOL", column="GENEID")

keep <- !is.na(gene.ids) & !duplicated(gene.ids)
# 过滤掉2000多基因
> table(keep)
keep
FALSE  TRUE 
 2119 16940 

sce.muraro <- sce.muraro[keep,]
rownames(sce.muraro) <- gene.ids[keep]
```

## 2 质控

**依然是备份一下，把unfiltered数据主要用在质控的探索上**

```r
unfiltered <- sce.muraro
```

和上一次一样，如果只是针对ERCC和全部的批次进行质控，结果是

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-20-094258.png)

很明显，这个D28个捣鬼，钻了我们“大部分细胞都是高质量”的假设漏洞

**因此，在过滤时不能考虑这个D28**

```r
library(scater)
stats <- perCellQCMetrics(sce.muraro)
qc <- quickPerCellQC(stats, percent_subsets="altexps_ERCC_percent",
    batch=sce.muraro$donor, subset=sce.muraro$donor!="D28")
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-20-094502.png)

**看看过滤掉多少**

```r
colSums(as.matrix(qc))
# low_lib_size            low_n_features high_altexps_ERCC_percent                   discard 
# 663                       700                       738                       773
```

**最后把过滤条件应用在原数据**

```r
sce.muraro <- sce.muraro[,!qc$discard]
```

## 3 归一化

继续使用去卷积方法

```r
library(scran)
set.seed(1000)
clusters <- quickCluster(sce.muraro)
sce.muraro <- computeSumFactors(sce.muraro, clusters=clusters)
sce.muraro <- logNormCounts(sce.muraro)

summary(sizeFactors(sce.muraro))
# Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
# 0.08782  0.54109  0.82081  1.00000  1.21079 13.98692
```

## 4 找表达量高变化基因

再看一眼数据，发现其中有plate和donor信息，它们都是与批次相关的

```r
sce.muraro
# class: SingleCellExperiment 
# dim: 16940 2299 
# metadata(0):
#   assays(2): counts logcounts
# rownames(16940): ENSG00000268895 ENSG00000121410 ...
# ENSG00000159840 ENSG00000074755
# rowData names(2): symbol chr
# colnames(2299): D28-1_1 D28-1_2 ... D30-8_93
# D30-8_94
# colData names(4): label donor plate sizeFactor
# reducedDimNames(0):
#   altExpNames(1): ERCC

table(sce.muraro$donor)
# 
# D28 D29 D30 D31 
# 333 601 676 689 
table(sce.muraro$plate)
# 
# 1   2   3   4   5   6   7   8 
# 281 292 292 295 282 285 283 289
```

因此就把这二者结合作为批次信息，依然是使用针对ERCC的构建模型方法

```r
block <- paste0(sce.muraro$plate, "_", sce.muraro$donor)
dec.muraro <- modelGeneVarWithSpikes(sce.muraro, "ERCC", block=block)
top.muraro <- getTopHVGs(dec.muraro, prop=0.1)
```

## 5 矫正批次效应

```r
library(batchelor)
set.seed(1001010)
merged.muraro <- fastMNN(sce.muraro, subset.row=top.muraro, 
    batch=sce.muraro$donor)

metadata(merged.muraro)$merge.info$lost.var
##           D28      D29      D30     D31
## [1,] 0.060847 0.024121 0.000000 0.00000
## [2,] 0.002646 0.003018 0.062421 0.00000
## [3,] 0.003449 0.002641 0.002598 0.08162
```

## 6 降维+聚类

### **降维**

```r
set.seed(100111)
# 前面矫正过批次效应，所以这里指定参数dimred: specifying the existing dimensionality reduction results to use
merged.muraro <- runTSNE(merged.muraro, dimred="corrected")
```

### **聚类**

```r
snn.gr <- buildSNNGraph(merged.muraro, use.dimred="corrected")
colLabels(merged.muraro) <- factor(igraph::cluster_walktrap(snn.gr)$membership)
```

**如果想看一下这里的分群和之前的批次之间的关系：**

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-20-095422.png)

**Tip：如果感觉批次或分群数量太多，看着效果不好，可以用热图的形式展示：**

```r
tab <- table(Cluster=colLabels(merged.muraro), CellType=sce.muraro$label)
library(pheatmap)
pheatmap(log10(tab+10), color=viridis::viridis(100))
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-20-095539.png)

### **最后检查一下供体的批次效应**

```r
gridExtra::grid.arrange(
    plotTSNE(merged.muraro, colour_by="label"),
    plotTSNE(merged.muraro, colour_by="batch"),
    ncol=2
)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-20-095634.png)


# 4.7 实战七 | SMARTer | 人胰腺细胞

刘小泽写于2020.7.20

## 1 前言

这次使用的数据是：[Lawlor et al. (2017) ](https://pubmed.ncbi.nlm.nih.gov/27864352/)中的不同人类供体的胰腺细胞

SMARTer其实不是个像Fluidigm、10X一样的制备系统，它是一个试剂盒。SMART技术是Clontech（Takara旗下全资子公司）的专利技术，**2009年升级为SAMRTer技术后**，采用更灵敏的SMARTer Oligo和高效的SMARTScribe RT进行逆转录，使其灵敏度提高至皮克级。利用SMARTer技术**只需单管、单酶即可完成逆转录，无需接头连接**，减少了样品操作步骤，也极大降低了样品损失，保留了原始信息，为RNA-Seq提供了可靠的基础。

2013年 Clontech公司就为Fluidigm的C1单细胞全自动制备系统推出了SMARTer Ultra Low RNA Kit。当时也是能够从C1捕获的单细胞中产生mRNA-seq文库，方便了研究。Fluidigm在C1平台上检验了多款试剂盒，发现SMARTer cDNA合成是最可靠的方法之一

> 参考：<http://www.ebiotrade.com/newsf/2013-9/20139493414227.htm>

后来很多平台也在使用SMARTer试剂（如WaferGen ICELL8 Single-Cell System），不过后来发现Smart-seq2的扩增效果优于SMARTer试剂盒，而且Smart-seq2技术比传统的SMARTer方法能产生更长和更多的cDNA，在低表达量时，Smart-seq2比SMARTer的基因检出率更高，结果更稳定

### **数据准备**

```r
library(scRNAseq)
sce.lawlor <- LawlorPancreasData()
sce.lawlor
# class: SingleCellExperiment 
# dim: 26616 638 
# metadata(0):
#   assays(1): counts
# rownames(26616): ENSG00000229483 ENSG00000232849 ...
# ENSG00000251576 ENSG00000082898
# rowData names(0):
#   colnames(638): 10th_C10_S104 10th_C11_S96 ...
# 9th-C96_S81 9th-C9_S13
# colData names(8): title age ... race Sex
# reducedDimNames(0):
#   altExpNames(0):
```

### **ID转换**

```r
library(AnnotationHub)
edb <- AnnotationHub()[["AH73881"]]
anno <- select(edb, keys=rownames(sce.lawlor), keytype="GENEID", 
    columns=c("SYMBOL", "SEQNAME"))
rowData(sce.lawlor) <- anno[match(rownames(sce.lawlor), anno[,1]),-1]
rowData(sce.lawlor)
# DataFrame with 26616 rows and 2 columns
# SYMBOL     SEQNAME
# <character> <character>
#   ENSG00000229483   LINC00362          13
# ENSG00000232849   LINC00363          13
# ENSG00000229558    SACS-AS1          13
# ENSG00000232977   LINC00327          13
# ENSG00000227893   LINC00352          13
# ...                     ...         ...
# ENSG00000232746   LINC02022           3
# ENSG00000150867     PIP4K2A          10
# ENSG00000255021  AC093496.1           3
# ENSG00000251576   LINC01267           3
# ENSG00000082898        XPO1           2
```

## 2 质控

**依然是备份一下，把unfiltered数据主要用在质控的探索上**

```r
unfiltered <- sce.lawlor
```

**检查是否有线粒体基因和批次信息**

```r
# 其中有MT基因
table(rowData(sce.lawlor)$SEQNAME=="MT")
# 
# FALSE  TRUE 
# 25269    13 

# 还有一些批次信息
table(sce.lawlor$`islet unos id`)
# 
# ACCG268 ACCR015A ACEK420A  ACEL337  ACHY057  ACIB065  ACIW009  ACJV399 
# 136       57       45      103       39       57       93      108
```

**进行质控**

```r
stats <- perCellQCMetrics(sce.lawlor, 
    subsets=list(Mito=which(rowData(sce.lawlor)$SEQNAME=="MT")))
qc <- quickPerCellQC(stats, percent_subsets="subsets_Mito_percent",
    batch=sce.lawlor$`islet unos id`)
# 过滤了34个细胞
table(qc$discard)
# 
# FALSE  TRUE 
# 604    34 

sce.lawlor <- sce.lawlor[,!qc$discard]
```

**看看过滤掉多少**

```r
colSums(as.matrix(qc))
# low_lib_size            low_n_features high_subsets_Mito_percent                   discard 
# 9                         5                        25                        34
```

**作图看一下**

```r
colData(unfiltered) <- cbind(colData(unfiltered), stats)
unfiltered$discard <- qc$discard

gridExtra::grid.arrange(
    plotColData(unfiltered, x="islet unos id", y="sum", colour_by="discard") +
        scale_y_log10() + ggtitle("Total count") +
        theme(axis.text.x = element_text(angle = 90)),
    plotColData(unfiltered, x="islet unos id", y="detected", 
        colour_by="discard") + scale_y_log10() + ggtitle("Detected features") +
        theme(axis.text.x = element_text(angle = 90)), 
    plotColData(unfiltered, x="islet unos id", y="subsets_Mito_percent",
        colour_by="discard") + ggtitle("Mito percent") +
        theme(axis.text.x = element_text(angle = 90)),
    ncol=2
)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-20-112030.png)

**看一下文库大小和线粒体占比的关系**

```r
plotColData(unfiltered, x="sum", y="subsets_Mito_percent",
    colour_by="discard") + scale_x_log10()
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-20-112119.png)

**最后把过滤条件应用在原数据**

```r
sce.lawlor <- sce.lawlor[,!qc$discard]
```

## 3 归一化

继续使用去卷积方法

```r
library(scran)
set.seed(1000)
clusters <- quickCluster(sce.lawlor)
sce.lawlor <- computeSumFactors(sce.lawlor, clusters=clusters)
sce.lawlor <- logNormCounts(sce.lawlor)

summary(sizeFactors(sce.lawlor))
# Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
# 0.2955  0.7807  0.9633  1.0000  1.1820  2.6287
```

## 4 找表达量高变化基因

这里没有ERCC也没有UMI，所以就用最基础的方法构建模型：`modelGeneVar`

不过还是要指定批次信息

```r
dec.lawlor <- modelGeneVar(sce.lawlor, block=sce.lawlor$`islet unos id`)
chosen.genes <- getTopHVGs(dec.lawlor, n=2000)
```

## 5 【尝试】矫正批次

这里写“尝试”是因为这里有一个问题：**细胞总数不多，才600个，但批次的数量很多**，所以归到单独的批次上细胞数就很少。这时如果继续矫正批次，**不知道会不会抹除一些真实的生物学特性。**

归根结底，还是一个技术噪音与生物因素之间的取舍问题

```r
table(sce.lawlor$`islet unos id`)
# 
# ACCG268 ACCR015A ACEK420A  ACEL337  ACHY057  ACIB065  ACIW009  ACJV399 
# 136       57       45      103       39       57       93      108
```

所以可以尝试一下：

```r
library(batchelor)
set.seed(1001010)
merged.lawlor <- fastMNN(sce.lawlor, subset.row=chosen.genes, 
                         batch=sce.lawlor$`islet unos id`)

metadata(merged.lawlor)$merge.info$lost.var
```

关于这个结果：**`lost.var` ，值越大表示丢失的真实生物异质性越多**

* It contains a matrix of the **variance lost in each batch** (column) at each merge step (row).
* Large proportions of lost variance **(>10%)** suggest that correction is **removing genuine biological heterogeneity.**&#x20;

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-20-113144.png)

看到的确有损失生物异质性的可能性，**那么就先放弃这个计划，直接进行下面的降维**

## 5 降维聚类

### **降维**

```r
library(BiocSingular)
set.seed(101011001)
sce.lawlor <- runPCA(sce.lawlor, subset_row=chosen.genes, ncomponents=25)
sce.lawlor <- runTSNE(sce.lawlor, dimred="PCA")
```

### **聚类**

```r
snn.gr <- buildSNNGraph(sce.lawlor, use.dimred="PCA")
colLabels(sce.lawlor) <- factor(igraph::cluster_walktrap(snn.gr)$membership)
```

**看分群与细胞类型之间关系**

```r
tab <- table(colLabels(sce.lawlor), sce.lawlor$`cell type`)
library(pheatmap)
pheatmap(log10(tab+10), color=viridis::viridis(100))
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-20-113541.png)

**看分群与批次之间**

```r
tab2 <- table(colLabels(sce.lawlor), sce.lawlor$`islet unos id`)
library(pheatmap)
pheatmap(log10(tab2+10), color=viridis::viridis(100))
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-20-113638.png)

### **最后看看批次效应**

```r
gridExtra::grid.arrange(
    plotTSNE(sce.lawlor, colour_by="label"),
    plotTSNE(sce.lawlor, colour_by="islet unos id"),
    ncol=2
)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-20-113811.png)


# 4.8 实战八 | Smart-seq2  | 人胰腺细胞

刘小泽写于2020.7.20

## 1 前言

这是也是来自多个供体的人类胰腺细胞，使用Smart-seq2建库技术，数据来自[Segerstolpe et al. (2016)](https://pubmed.ncbi.nlm.nih.gov/27864352/)

### **数据准备**

```r
library(scRNAseq)
sce.seger <- SegerstolpePancreasData()
sce.seger
# class: SingleCellExperiment 
# dim: 26179 3514 
# metadata(0):
#   assays(1): counts
# rownames(26179): SGIP1 AZIN2 ... BIVM-ERCC5 eGFP
# rowData names(2): symbol refseq
# colnames(3514): HP1502401_N13 HP1502401_D14 ...
# HP1526901T2D_O11 HP1526901T2D_A8
# colData names(8): Source Name individual ... age
# body mass index
# reducedDimNames(0):
#   altExpNames(1): ERCC
```

看到3500多个细胞，包含ERCC，使用Symbol ID

看下样本信息：

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-20-114942.png)

### **ID转换**

选择的方式是：将没有匹配的NA去掉，并且去掉重复的行

```r
# 首先得到symbol ID和对应的Ensembl ID（其中会存在无对应的NA情况）
library(AnnotationHub)
edb <- AnnotationHub()[["AH73881"]]
symbols <- rowData(sce.seger)$symbol
ens.id <- mapIds(edb, keys=symbols, keytype="SYMBOL", column="GENEID")

# 之前见到的方法是：
# keep <- !is.na(gene.ids) & !duplicated(gene.ids)

# 这里使用了另一种方法（不是直接将NA去掉，而且替换成了symbol）
ens.id <- ifelse(is.na(ens.id), symbols, ens.id)
keep <- !duplicated(ens.id)

sce.seger <- sce.seger[keep,]
rownames(sce.seger) <- ens.id[keep]
```

> **小结一下：至此见到了三种ID转换的方式，根据最后保留的基因数量，可以排个序：**
>
> 保留基因最多（保留了NA和重复）：`uniquifyFeatureNames` 中等（保留了NA，去掉重复）：`ifelse(is.na(ens.id), symbols, ens.id)` 最少（去掉了NA以及重复）：`!is.na(gene.ids) & !duplicated(gene.ids)`

### **编辑样本信息**

之前有8列样本的信息，有点冗余了。这里只保留3列关心的，并重新命名

```r
emtab.meta <- colData(sce.seger)[,c("cell type", 
    "individual", "single cell well quality")]
colnames(emtab.meta) <- c("CellType", "Donor", "Quality")
colData(sce.seger) <- emtab.meta
```

另外把细胞类型这一列中的“cell”字符去掉，并把首字母大写

```r
sce.seger$CellType <- gsub(" cell", "", sce.seger$CellType)
sce.seger$CellType <- paste0(
    toupper(substr(sce.seger$CellType, 1, 1)),
    substring(sce.seger$CellType, 2))
```

## 2 质控

**依然是备份一下，把unfiltered数据主要用在质控的探索上**

```r
unfiltered <- sce.seger
```

之前作者在数据中已经标注了细胞质量，可以看到有问题的细胞还是很多的：

```r
table(sce.seger$Quality)
# 
# control, 2-cell well  control, empty well     low quality cell                   OK 
# 32                   96                 1177                 2209
```

因此就要注意了，这里的数据会不会满足“大部分细胞都是高质量的”这个假设？

还是需要试一下，看看结果先

```r
library(scater)
stats <- perCellQCMetrics(sce.seger)
qc1 <- quickPerCellQC(stats, percent_subsets="altexps_ERCC_percent",
                     batch=sce.seger$Donor)


colData(unfiltered) <- cbind(colData(unfiltered), stats)
unfiltered$discard <- qc1$discard

gridExtra::grid.arrange(
  plotColData(unfiltered, x="Donor", y="sum", colour_by="discard") +
    scale_y_log10() + ggtitle("Total count") +
    theme(axis.text.x = element_text(angle = 90)),
  plotColData(unfiltered, x="Donor", y="detected", colour_by="discard") +
    scale_y_log10() + ggtitle("Detected features") +
    theme(axis.text.x = element_text(angle = 90)),
  plotColData(unfiltered, x="Donor", y="altexps_ERCC_percent",
              colour_by="discard") + ggtitle("ERCC percent") +
    theme(axis.text.x = element_text(angle = 90)),
  ncol=3
)
```

看到HP1509101在过滤时存在过滤不完全的情况，HP1504901过滤的ERCC数量太多，推测这两个批次效果可能并不是很好，可能存在大量的低质量细胞

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-20-140234.png)

因此，再次指定`subset` 参数，重新画图

```r
library(scater)
stats <- perCellQCMetrics(sce.seger)
qc <- quickPerCellQC(stats, percent_subsets="altexps_ERCC_percent",
    batch=sce.seger$Donor,
    subset=!sce.seger$Donor %in% c("HP1504901", "HP1509101"))
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-20-140512.png)

**看看过滤掉多少**

```r
colSums(as.matrix(qc))
##              low_lib_size            low_n_features high_altexps_ERCC_percent 
##                       788                      1056                      1031 
##                   discard 
##                      1246
```

**最后将qc过滤的与本来标注低质量的一同过滤**

```r
low.qual <- sce.seger$Quality == "low quality cell"
sce.seger <- sce.seger[,!(qc$discard | low.qual)]

# 过滤了大概1500个细胞
> dim(unfiltered);dim(sce.seger)
[1] 26179  3514
[1] 26179  2090
```

## 3 归一化

> **此处会有一点小问题，值得注意！**

本来有ERCC，操作应该是：

```r
library(scran)
sce.seger  = computeSpikeFactors(sce.seger, "ERCC")
sce.seger <- logNormCounts(sce.seger) 
# Error in .local(x, ...) : size factors should be positive
```

**但由于存在几个细胞中一个ERCC都没有，所以会报错**&#x20;

此时面临两个选择：要么把这几个细胞去掉；要么就不借助ERCC，用另一种去卷积方法

```r
> table(colSums(counts(altExp(sce.seger)))==0)

FALSE  TRUE 
 2087     3
```

如果要去掉这几个细胞：

```r
test=sce.seger[,!colSums(counts(altExp(sce.seger)))==0]
sce.test = computeSpikeFactors(test, "ERCC")
sce.test <- logNormCounts(test)
```

我们这里**选择保守的方法，不去掉细胞，使用另一种去卷积方法：**

```r
clusters <- quickCluster(sce.seger)
sce.seger <- computeSumFactors(sce.seger, clusters=clusters)
sce.seger <- logNormCounts(sce.seger) 

summary(sizeFactors(sce.seger))
# Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
# 0.0000  0.1832  0.4016  1.0000  1.0996 12.9607
```

## 4 找表达量高变化基因

下面构建模型想使用`modelGeneVarWithSpikes`，于是首先应该把那几个没有ERCC的细胞去掉；另外由于AZ这个批次相对其他批次的细胞数量过于少，因此在模型构建中也把它去掉吧

```r
for.hvg <- sce.seger[,librarySizeFactors(altExp(sce.seger)) > 0
    & sce.seger$Donor!="AZ"]
dec.seger <- modelGeneVarWithSpikes(for.hvg, "ERCC", block=for.hvg$Donor)
chosen.hvgs <- getTopHVGs(dec.seger, n=2000)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-20-143533.png)

**如果要批量作图检查的话**

```r
# 批次数量较多，因此设置多行多列显示
par(mfrow=c(3,3))
blocked.stats <- dec.seger$per.block
for (i in colnames(blocked.stats)) {
    current <- blocked.stats[[i]]
    plot(current$mean, current$total, main=i, pch=16, cex=0.5,
        xlab="Mean of log-expression", ylab="Variance of log-expression")
    curfit <- metadata(current)
    points(curfit$mean, curfit$var, col="red", pch=16)
    curve(curfit$trend(x), col='dodgerblue', add=TRUE, lwd=2)
}
```

> 注意，这里在找完HVGs后，没有进行批次矫正，如果继续向下做，会发现什么？

## 5 降维聚类

### **降维**

```r
library(BiocSingular)
set.seed(101011001)
sce.seger <- runPCA(sce.seger, subset_row=chosen.hvgs, ncomponents=25)
sce.seger <- runTSNE(sce.seger, dimred="PCA")
```

### **聚类**

```r
snn.gr <- buildSNNGraph(sce.seger, use.dimred="PCA")
colLabels(sce.seger) <- factor(igraph::cluster_walktrap(snn.gr)$membership)
```

### **检查聚类分群与批次**

```r
tab <- table(Cluster=colLabels(sce.seger), Donor=sce.seger$Donor)
library(pheatmap)
pheatmap(log10(tab+10), color=viridis::viridis(100))
```

结果真的是：批次效应影响了分群，因此最好还是做一遍`fastMNN`操作

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-20-144127.png)

**tSNE图中也是显示出了强烈的批次效应**

```r
gridExtra::grid.arrange(
    plotTSNE(sce.seger, colour_by="label"),
    plotTSNE(sce.seger, colour_by="Donor"),
    ncol=2
)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-20-144300.png)

## 6 补充矫正批次效应

> 上图看到很明显的批次效应，那么**如果处理后，会有什么不同吗？**

### **利用fastMNN矫正**

```r
library(batchelor)
set.seed(1001010)
merged.seger <- fastMNN(sce.seger, subset.row=chosen.hvgs, 
                         batch=sce.seger$Donor)
merged.seger
# class: SingleCellExperiment 
# dim: 2000 2090 
# metadata(2): merge.info pca.info
# assays(1): reconstructed
# rownames(2000): GCG TTR ... MAP6 LCP1
# rowData names(1): rotation
# colnames(2090): HP1502401_H13 HP1502401_J14 ...
# HP1526901T2D_N8 HP1526901T2D_A8
# colData names(2): batch label
# reducedDimNames(2): corrected TSNE
# altExpNames(0):

# metadata(merged.seger)$merge.info$lost.var
# lost.var ：值越大表示丢失的真实生物异质性越多
```

因为fastMNN会包含PCA降维，所以下面继续进行tSNE即可

### **降维聚类**

```r
library(BiocSingular)
set.seed(101011001)
merged.seger <- runTSNE(merged.seger, dimred="corrected")

snn.gr <- buildSNNGraph(merged.seger, use.dimred="corrected")
colLabels(merged.seger) <- factor(igraph::cluster_walktrap(snn.gr)$membership)
```

**再次作图，是不是明显比之前好很多？**

```r
gridExtra::grid.arrange(
  plotTSNE(merged.seger, colour_by="label"),
  plotTSNE(merged.seger, colour_by="batch"),
  ncol=2
)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-20-145143.png)


# 4.9 实战九 | 不同技术数据整合 | 人胰腺细胞

刘小泽写于2020.7.21

## 1 前言

2016年很多研究都对人类胰腺细胞的scRNA很感兴趣，也因此发表了很多文章：([Muraro et al. 2016](https://pubmed.ncbi.nlm.nih.gov/27693023/); [Grun et al. 2016](https://pubmed.ncbi.nlm.nih.gov/27345837/); [Lawlor et al. 2017](https://pubmed.ncbi.nlm.nih.gov/27864352/); [Segerstolpe et al. 2016](https://pubmed.ncbi.nlm.nih.gov/27667667/))。这些不同作者不同技术手段得到的数据，也给数据整合带来了不小的挑战。相比于之前的PBMC数据整合，这里更为复杂，因为包含的建库、测序方法多样，供体的类型、数量更是不一致。

## 2 简单一点的试验

首先拿技术相似的两套数据来做：分别是 [Muraro et al. 2016](https://pubmed.ncbi.nlm.nih.gov/27693023/); [Grun et al. 2016](https://pubmed.ncbi.nlm.nih.gov/27345837/)，采用了CEL-seq 和 CEL-seq2

> [sce.grun分享数据下载](<https://share.weiyun.com/iO5BCEc9 >)\
> [sce.muraro分享数据链接](https://share.weiyun.com/gvnArtAf)

```r
load('final.sce.grun.Rdata')
load('final.sce.muraro.RData')
final.sce.grun
# class: SingleCellExperiment 
# dim: 17548 1063 
# metadata(0):
#   assays(2): counts logcounts
# rownames(17548): ENSG00000268895 ENSG00000121410 ...
# ENSG00000074755 ENSG00000036549
# rowData names(2): symbol chr
# colnames(1063): D2ex_1 D2ex_2 ... D17TGFB_94
# D17TGFB_95
# colData names(3): donor sample sizeFactor
# reducedDimNames(0):
#   altExpNames(1): ERCC

final.sce.muraro
# class: SingleCellExperiment 
# dim: 16940 2299 
# metadata(0):
#   assays(2): counts logcounts
# rownames(16940): ENSG00000268895 ENSG00000121410 ...
# ENSG00000159840 ENSG00000074755
# rowData names(2): symbol chr
# colnames(2299): D28-1_1 D28-1_2 ... D30-8_93
# D30-8_94
# colData names(4): label donor plate sizeFactor
# reducedDimNames(0):
#   altExpNames(1): ERCC
```

### **2.1 取两个数据的交集子集**

**首先获得交集基因**

```r
universe <- intersect(rownames(final.sce.grun), rownames(final.sce.muraro))

> nrow(final.sce.grun);nrow(final.sce.muraro);length(universe)
[1] 17548
[1] 16940
[1] 15974
```

**对数据集取子集**

```r
sce.grun2 <- final.sce.grun[universe,]
sce.muraro2 <- final.sce.muraro[universe,]
```

> 既然是经过处理后的数据，那么就略过了之前介绍的质控步骤

### **2.2 数据整合后的归一化**

首先测序深度导致的文库大小差异是批次效应的一个重要来源，因此可以先对不同的批次进行文库矫正。**会以文库最小的批次为基准**，对其他批次进行文库归一化。最后返回一个列表

> 使用一个归一化函数：`multiBatchNorm` ，它应用的就是最简单的library size normalization归一化方法： Perform scaling normalization within each batch to provide comparable results to the lowest-coverage batch.

既然是要处理文库差异，那就先看看各自原本的文库大小

```r
summary(colSums(logcounts(sce.muraro2)))
# Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
# 4433    8041    8680    8558    9193   10594 
summary(colSums(logcounts(sce.grun2)))
# Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
# 2347    4141    4565    4538    4969    5992
```

然后进行处理，可以看看前后的变化，就明白了这个函数做了什么事情

```r
library(batchelor)
normed.pancreas <- multiBatchNorm(sce.grun2, sce.muraro2)
sce.grun3 <- normed.pancreas[[1]]
sce.muraro3 <- normed.pancreas[[2]]

summary(colSums(logcounts(sce.muraro3)))
# Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
# 3072    4735    4994    4954    5204    5864 
summary(colSums(logcounts(sce.grun3)))
# Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
# 2347    4141    4565    4538    4969    5992
```

看到混合以后的`sce.muraro3`相对于之前独立的`sce.muraro2`的变化了吧

### **2.3 数据整合后**找表达量高变化基因

**首先对表达量变化模型取子集**

```r
## 原来的sce.grun模型
library(scran)
block1 <- paste0(final.sce.grun$sample, "_", final.sce.grun$donor)
dec.grun <- modelGeneVarWithSpikes(final.sce.grun, spikes="ERCC", block=block1)
# 取子集
dec.grun2 <- dec.grun[universe,]

## 原来的sce.muraro模型
block2 <- paste0(final.sce.muraro$plate, "_", final.sce.muraro$donor)
dec.muraro <- modelGeneVarWithSpikes(final.sce.muraro, "ERCC", block=block2)
# 取子集
dec.muraro2 <- dec.muraro[universe,]
```

**之后组合两组的结果**

> 使用`combineVar` ，它的作用是：
>
> Combine the results of multiple variance decompositions, usually generated for the same genes across separate batches of cells.

```r
library(scran)
combined.pan <- combineVar(dec.grun2, dec.muraro2)
# 把更有可能代表生物差异的基因选出来，用于下游的PCA和聚类
chosen.genes <- rownames(combined.pan)[combined.pan$bio > 0]
```

### **2.4 矫正批次效应**

之前在：[3.8 批次效应处理](https://jieandze1314.osca.top/03/03-8) 中介绍过：

> bulk mRNA转录组中常用的矫正批次效应方法就是线性回归，对每个基因表达量拟合一个线性模型。例如limma的`removeBatchEffect()` ([Ritchie et al. 2015](https://pubmed.ncbi.nlm.nih.gov/25605792/)) 、sva的`comBat()` ( [Leek et al. 2012](https://pubmed.ncbi.nlm.nih.gov/22257669/))。如果要使用这类方法，就需要假设：批次间的细胞组成相同。另外的一个假设是：批次效应的累积的，对于任何给定的基因，在不同亚群中经过任何因素诱导的表达变化倍数是相同的。（其实，**从这两个假设就看出来，这个方法不适合我们的单细胞数据**，但还是要继续了解下去）

**先来看看基于线性回归的rescaleBatches()**

它也是对每个基因的log表达量进行了线性回归，并提高了一些运行性能。另外与`removeBatchEffect()`不同的是，`rescaleBatches()`保持了数据的稀疏性，而`removeBatchEffect()`会破坏稀疏性

```r
library(scater)
rescaled.pancreas <- rescaleBatches(sce.grun2, sce.muraro2)

set.seed(100101)
rescaled.pancreas <- runPCA(rescaled.pancreas, subset_row=chosen.genes,
    exprs_values="corrected")

rescaled.pancreas <- runTSNE(rescaled.pancreas, dimred="PCA")
plotTSNE(rescaled.pancreas, colour_by="batch")
```

不过结果并不尽如人意，两个批次还是分得很开，**表明处理有效果但不彻底** 。影响效果的原因是：我们的数据违背了这个方法的假设

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-21-032715.png)

**再来使用fastMNN()**

与线性回归方法相比，MNN方法不会假设细胞群组成相同或者事先已知。MNN会自己学习细胞群的结构并进行估计.

可能之前听过：`mnnCorrect()`这个方法，它是[Haghverdi et al. (2018) ](https://pubmed.ncbi.nlm.nih.gov/29608177/)提出来的，之前也介绍过：[单细胞转录组数据校正批次效应实战](https://www.jianshu.com/p/b7f6a5efed85)

它和`fastMNN()`原理类似，但速度会慢很多，总之它们的不同可以概括为：

> For scRNA-seq data, `fastMNN()` tends to be both **faster and better** at achieving a satisfactory merge. `mnnCorrect()` is mainly provided here for posterity’s sake, though it is more robust than `fastMNN()` to certain violations of the orthogonality assumptions.

```r
set.seed(1011011)
mnn.pancreas <- fastMNN(sce.grun2, sce.muraro2, subset.row=chosen.genes)

snn.gr <- buildSNNGraph(mnn.pancreas, use.dimred="corrected")
clusters <- igraph::cluster_walktrap(snn.gr)$membership
tab <- table(Cluster=clusters, Batch=mnn.pancreas$batch)
tab
##        Batch
## Cluster   1   2
##      1  239 281
##      2  312 257
##      3  200 837
##      4   56 193
##      5   37   1
##      6   24 108
##      7  109 391
##      8   63  80
##      9   18 115
##      10   0  17
##      11   5  19
```

再做个图

```r
mnn.pancreas <- runTSNE(mnn.pancreas, dimred="corrected")
plotTSNE(mnn.pancreas, colour_by="batch")
```

效果有明显改进

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-21-033244.png)

## 3 更具挑战性的操作

前面是将一个CEL-seq和一个CEL-seq2数据整合，总的说来还不是很复杂

但这里，会再加上两个数据，分别来自[Lawlor et al. 2017](https://pubmed.ncbi.nlm.nih.gov/27864352/) 和 [Segerstolpe et al. 2016](https://pubmed.ncbi.nlm.nih.gov/27667667/) 的数据进行整合，这样四个数据就会包括不同的技术、不同的UMI、不同的表达量、更加不同的供体

> [sce.lawlor数据分享链接](<https://share.weiyun.com/mic3m90k >)\
> [sce.seger数据分享链接](https://share.weiyun.com/7w2vBGdC)

```r
rm(list = ls())
load('final.sce.grun.RData')
load('final.sce.muraro.RData')
load('final.sce.lawlor.RData')
load('final.sce.seger.RData')
sce.grun=final.sce.grun
sce.muraro=final.sce.muraro

sce.grun
# class: SingleCellExperiment 
# dim: 17548 1063 
# metadata(0):
#   assays(2): counts logcounts
# rownames(17548): ENSG00000268895 ENSG00000121410 ...
# ENSG00000074755 ENSG00000036549
# rowData names(2): symbol chr
# colnames(1063): D2ex_1 D2ex_2 ... D17TGFB_94
# D17TGFB_95
# colData names(3): donor sample sizeFactor
# reducedDimNames(0):
#   altExpNames(1): ERCC

sce.muraro
# class: SingleCellExperiment 
# dim: 16940 2299 
# metadata(0):
#   assays(2): counts logcounts
# rownames(16940): ENSG00000268895 ENSG00000121410 ...
# ENSG00000159840 ENSG00000074755
# rowData names(2): symbol chr
# colnames(2299): D28-1_1 D28-1_2 ... D30-8_93
# D30-8_94
# colData names(4): label donor plate sizeFactor
# reducedDimNames(0):
#   altExpNames(1): ERCC

sce.lawlor
# class: SingleCellExperiment 
# dim: 26616 604 
# metadata(0):
#   assays(2): counts logcounts
# rownames(26616): ENSG00000229483 ENSG00000232849 ...
# ENSG00000251576 ENSG00000082898
# rowData names(2): SYMBOL SEQNAME
# colnames(604): 10th_C11_S96 10th_C13_S61 ...
# 9th-C96_S81 9th-C9_S13
# colData names(9): title age ... Sex sizeFactor
# reducedDimNames(0):
#   altExpNames(0):

sce.seger
# class: SingleCellExperiment 
# dim: 25454 2090 
# metadata(0):
#   assays(2): counts logcounts
# rownames(25454): ENSG00000118473 ENSG00000142920 ...
# ENSG00000278306 eGFP
# rowData names(2): symbol refseq
# colnames(2090): HP1502401_H13 HP1502401_J14 ...
# HP1526901T2D_N8 HP1526901T2D_A8
# colData names(4): CellType Donor Quality sizeFactor
# reducedDimNames(0):
#   altExpNames(1): ERCC
```

### **3.1 取四个数据的子集**

**首先获得交集基因**

```r
all.sce <- list(Grun=sce.grun, Muraro=sce.muraro, 
    Lawlor=sce.lawlor, Seger=sce.seger)
universe <- Reduce(intersect, lapply(all.sce, rownames))

nrow(sce.grun);nrow(sce.muraro);nrow(sce.lawlor);nrow(sce.seger);length(universe)
# [1] 17548
# [1] 16940
# [1] 26616
# [1] 25454
# [1] 15231 #共有基因
```

**再分别取子集**

```r
all.sce <- lapply(all.sce, "[", i=universe,)
```

### **3.2 数据整合后的归一化**

```r
normed.pancreas <- do.call(multiBatchNorm, all.sce)
```

### **3.3 数据整合后**找表达量高变化基因

**首先获得各个数据集的表达量变化模型**

```r
library(scran)
# sce.grun
block1 <- paste0(sce.grun$sample, "_", sce.grun$donor)
dec.grun <- modelGeneVarWithSpikes(sce.grun, spikes="ERCC", block=block1)

# sce.muraro
block2 <- paste0(sce.muraro$sample, "_", sce.muraro$donor)
dec.muraro <- modelGeneVarWithSpikes(sce.muraro, spikes="ERCC", block=block2)

# sce.lawlor：没有ERCC信息，就用modelGeneVar()
dec.lawlor <- modelGeneVar(sce.lawlor, block=sce.lawlor$`islet unos id`)

# sce.seger
for.hvg <- sce.seger[,librarySizeFactors(altExp(sce.seger)) > 0
                     & sce.seger$Donor!="AZ"]
dec.seger <- modelGeneVarWithSpikes(for.hvg, "ERCC", block=for.hvg$Donor)

# 整合起来
all.dec <- list(Grun=dec.grun, Muraro=dec.muraro, 
                Lawlor=dec.lawlor, Seger=dec.seger)
```

**再取子集**

```r
all.dec <- lapply(all.dec, "[", i=universe,)
```

**再组合四组的结果**

```r
combined.pan <- do.call(combineVar, all.dec)
# 把更有可能代表生物差异的基因选出来，用于下游的PCA和聚类
chosen.genes <- rownames(combined.pan)[combined.pan$bio > 0]
```

### **3.4 矫正批次效应**

`fastMNN`也包含了PCA的操作

```r
set.seed(1011110)
mnn.pancreas <- fastMNN(normed.pancreas)
```

### **3.5 聚类**

```r
snn.gr <- buildSNNGraph(mnn.pancreas, use.dimred="corrected", k=20)
clusters <- igraph::cluster_walktrap(snn.gr)$membership
clusters <- factor(clusters)
tab <- table(Cluster=clusters, Batch=mnn.pancreas$batch)

tab
##        Batch
## Cluster Grun Lawlor Muraro Seger
##      1    77     33    677   211
##      2   311     26    254   383
##      3   104    244    390   180
##      4   225     16    246   138
##      5   125    203    158   108
##      6    56     17    196   109
##      7     0      0      0    43
##      8     0      0      0    42
##      9     0      2     17     4
##      10   69     12     82    16
##      11   24     19    108    55
##      12    0      0      0    50
##      13   27      0      1     0
##      14   22      6     34    49
##      15   18     18    117   157
##      16    0      0      0   208
##      17    0      0      0    26
##      18    0      0      0   108
##      19    0      0      0   186
##      20    5      8     19    17
```

看到一个批次中都包含了很多clusters，说明数据整合的效果还不错，批次效应没有很强；当然**有些clusters只显示在了seger批次中（比如7、8、12、16、17、18、19）**，那究竟这些clusters到底是不是seger数据特有的细胞类型呢？这个还有待考证

### **作图**

> **注意其中使用到了一个很有趣的函数`I()` ，简单的一个字母，它是base包里的函数**

因为我们是根据`mnn.pancreas`进行作图的，但`clusters`这个向量是根据`mnn.pancreas`创建的，但又不直接存在于`mnn.pancreas`（不像`batch`一样存在于`mnn.pancreas`中）。

因此要从外部把它导入到作图函数中，就可以用这个`I()`

```r
mnn.pancreas <- runTSNE(mnn.pancreas, dimred="corrected")
gridExtra::grid.arrange(
    plotTSNE(mnn.pancreas, colour_by="batch", text_by=I(clusters)),
    plotTSNE(mnn.pancreas, colour_by=I(clusters), text_by=I(clusters)),
    ncol=2
)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-21-045902.png)

> 上面我们粗略根据四个数据集看了下批次效应，发现Seger这个数据还有点特殊，因为很多clusters只存在于Seger中

批次效应的来源除了表面上的4个数据集整合，还有一个重点考虑对象是：供体的种类

### **3.6 对批次效应的检查**

**看一下来自各个数据中供体的批次效应**

首先检查一下各个数据的供体信息，看到Seger最多，因此它的风险也最大。但这个怀疑到底对不对，还要做个图看看

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-21-084814.png)

```r
seger.donors <- donors
seger.donors[mnn.pancreas$batch!="Seger"] <- NA

grun.donors <- donors
grun.donors[mnn.pancreas$batch!="Grun"] <- NA

lawlor.donors <- donors
lawlor.donors[mnn.pancreas$batch!="Lawlor"] <- NA

muraro.donors <- donors
muraro.donors[mnn.pancreas$batch!="Muraro"] <- NA

gridExtra::grid.arrange(
  plotTSNE(mnn.pancreas, colour_by=I(muraro.donors))+ ggtitle('muraro.donors'),
  plotTSNE(mnn.pancreas, colour_by=I(lawlor.donors))+ ggtitle('lawlor.donors'),
  plotTSNE(mnn.pancreas, colour_by=I(grun.donors))+ ggtitle('grun.donors'),
  plotTSNE(mnn.pancreas, colour_by=I(seger.donors))+ ggtitle('seger.donors'),
  ncol=2
)
```

看到图中Seger的供体各个细胞最为分散，因此它的供体批次效应是最强的

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-21-085745.png)

虽说供体也是生物信息，但它对于后续的细胞类型注释没有直接的帮助，相反还会产生混淆的作用（比如前面看到很多clusters只存在于Seger中，说不定就是由于Seger的供体批次效应导致的）

因此，**一个更为谨慎的操作是：除了去除数据集之间的批次效应以外，还要将每个数据内部的供体信息作为另一个批次效应，分别处理掉**

### **3.7 进行一次更严格的批次矫正**

将原来的4个分开的数据聚合在一起，使用`noCorrect` 进行简单的聚合

> 它的含义是：This function is effectively **equivalent to cbinding** the matrices together **without any correction.**

```r
combined <- noCorrect(normed.pancreas)
assayNames(combined) <- "logcounts"
combined$donor <- donors
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-21-092211.png)

对这个数据进行批次矫正，但首先要把**数据批次**和**供体批次**分开

```r
donors.per.batch <- split(combined$donor, combined$batch)

# 获得每个数据批次下的供体批次
donors.per.batch <- lapply(donors.per.batch, unique)
donors.per.batch
## $Grun
## [1] "D2"  "D3"  "D7"  "D10" "D17"
## 
## $Lawlor
## [1] "ACIW009"  "ACJV399"  "ACCG268"  "ACCR015A" "ACEK420A" "ACEL337"  "ACHY057" 
## [8] "ACIB065" 
## 
## $Muraro
## [1] "D28" "D29" "D31" "D30"
## 
## $Seger
##  [1] "HP1502401"    "HP1504101T2D" "AZ"           "HP1508501T2D" "HP1506401"   
##  [6] "HP1507101"    "HP1509101"    "HP1504901"    "HP1525301T2D" "HP1526901T2D"
```

**依然是使用fastMNN**

```r
set.seed(1010100)
# batch信息使用全部的供体
# 增加一步指定weights：可以理解为哪些供体属于哪个数据集
multiout <- fastMNN(combined, batch=combined$donor, 
    subset.row=chosen.genes, weights=donors.per.batch)

# 将两大批次信息记录在新的矫正结果multiout中
multiout$dataset <- combined$batch
multiout$donor <- multiout$batch
```

**检查一下聚类结果**

从下面的结果中可以看到单独属于Seger的cluster没有了

```r
library(scater)
g <- buildSNNGraph(multiout, use.dimred=1, k=20)
clusters <- igraph::cluster_walktrap(g)$membership
tab <- table(clusters, multiout$dataset)
tab
##         
## clusters Grun Lawlor Muraro Seger
##       1   246     20    278   187
##       2   200    239    835   862
##       3   171    254    473   294
##       4   315     27    260   387
##       5    57     17    193   108
##       6    24     18    107    55
##       7    26      0      0     0
##       8     5      9     19    17
##       9    19     19    118   176
##       10    0      1     16     4
```

作图结果也发现数据混合更理想了

```r
multiout <- runTSNE(multiout, dimred="corrected")
gridExtra::grid.arrange(
    plotTSNE(multiout, colour_by="dataset", text_by=I(clusters)),
    plotTSNE(multiout, colour_by=I(seger.donors)),
    ncol=2
)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-21-092759.png)

**最后，和已发表的细胞类型做对比**

由于这些数据都已发表，数据集中也包含了最后作者注释的细胞类型（sce.grun除外）

因此，可以将我们自己整合后又矫正的分群，与发表的细胞分群对比，来说明批次处理质量

```r
# 获得已发表的细胞类型信息
proposed <- c(rep(NA, ncol(sce.grun)), 
    sce.muraro$label,
    sce.lawlor$`cell type`,
    sce.seger$CellType)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-21-093233.png)

看到其中大小写参差不齐，可以全变成小写

```r
proposed <- tolower(proposed)
# 并根据原文章修改一下细胞类型名称
proposed[proposed=="gamma/pp"] <- "gamma"
proposed[proposed=="pp"] <- "gamma"
proposed[proposed=="duct"] <- "ductal"
proposed[proposed=="psc"] <- "stellate"
```

最后检查一下

```r
table(proposed, clusters)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-21-093723.png)

看到，我们处理完批次效应后的分群结果可以比较好的匹配到真实的细胞类型，因此说明了这里使用的批次矫正的方法的力度刚刚好


# 4.10 实战十 | CEL-seq | 小鼠造血干细胞

刘小泽写于2020.7.21

## 1 前言

数据来自[Grun et al. 2016](https://pubmed.ncbi.nlm.nih.gov/27345837/)的小鼠造血干细胞 haematopoietic stem cell (HSC) ，使用的技术是CEL-seq

### **数据准备**

```r
library(scRNAseq)
sce.grun.hsc <- GrunHSCData(ensembl=TRUE)
sce.grun.hsc
# class: SingleCellExperiment 
# dim: 21817 1915 
# metadata(0):
#   assays(1): counts
# rownames(21817): ENSMUSG00000109644
# ENSMUSG00000007777 ... ENSMUSG00000055670
# ENSMUSG00000039068
# rowData names(3): symbol chr originalName
# colnames(1915): JC4_349_HSC_FE_S13_
# JC4_350_HSC_FE_S13_ ...
# JC48P6_1203_HSC_FE_S8_
# JC48P6_1204_HSC_FE_S8_
# colData names(2): sample protocol
# reducedDimNames(0):
#   altExpNames(0):

table(sce.grun.hsc$sample)
# 
# JC20   JC21   JC26   JC27   JC28   JC30   JC32 
# 87     96     85     91     80     96     93 
# JC35   JC36   JC37   JC39    JC4   JC40   JC41 
# 96     80     87     93     84     96     94 
# JC43   JC44   JC45   JC46 JC48P4 JC48P6 JC48P7 
# 92     94     90     96     95     96     94
```

### **ID转换**

```r
library(AnnotationHub)
ens.mm.v97 <- AnnotationHub()[["AH73905"]]
anno <- select(ens.mm.v97, keys=rownames(sce.grun.hsc), 
               keytype="GENEID", columns=c("SYMBOL", "SEQNAME"))

# 这里全部对应
> sum(is.na(anno$SYMBOL))
[1] 0
> sum(is.na(anno$SEQNAME))
[1] 0

# 接下来只需要匹配顺序即可
rowData(sce.grun.hsc) <- anno[match(rownames(sce.grun.hsc), anno$GENEID),]

sce.grun.hsc
## class: SingleCellExperiment 
## dim: 21817 1915 
## metadata(0):
## assays(1): counts
## rownames(21817): ENSMUSG00000109644 ENSMUSG00000007777 ...
##   ENSMUSG00000055670 ENSMUSG00000039068
## rowData names(3): GENEID SYMBOL SEQNAME
## colnames(1915): JC4_349_HSC_FE_S13_ JC4_350_HSC_FE_S13_ ...
##   JC48P6_1203_HSC_FE_S8_ JC48P6_1204_HSC_FE_S8_
## colData names(2): sample protocol
## reducedDimNames(0):
## altExpNames(0):
```

## 2 质控

**依然是备份一下，把unfiltered数据主要用在质控的探索上**

```r
unfiltered <- sce.grun.hsc
```

**发现这个数据既没有MT也没有ERCC**

```r
grep('MT',rowData(sce.grun.hsc)$SEQNAME)
# integer(0)
```

能用的数据只有其中的`protocol`了，它表示细胞提取方法

```r
table(sce.grun.hsc$protocol)
# 
# micro-dissected cells 
# 1546 
# sorted hematopoietic stem cells 
# 369 

# 再看一下各个样本与提取方法的对应关系
table(sce.grun.hsc$protocol,sce.grun.hsc$sample)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-21-111940.png)

根据背景知识，**大部分显微操作（micro-dissected）得到的细胞很多质量都较低**，和我们的质控假设相违背，于是这里就不把它们纳入过滤条件

```r
library(scater)
stats <- perCellQCMetrics(sce.grun.hsc)
# 只用sorted hematopoietic stem cells 计算过滤条件
qc <- quickPerCellQC(stats, batch=sce.grun.hsc$protocol,
    subset=grepl("sorted", sce.grun.hsc$protocol))

colSums(as.matrix(qc))
##   low_lib_size low_n_features        discard 
##            465            482            488

sce.grun.hsc <- sce.grun.hsc[,!qc$discard]
```

**做个图看看**

```r
colData(unfiltered) <- cbind(colData(unfiltered), stats)
unfiltered$discard <- qc$discard

gridExtra::grid.arrange(
    plotColData(unfiltered, y="sum", x="sample", colour_by="discard", 
        other_fields="protocol") + scale_y_log10() + ggtitle("Total count") +
        facet_wrap(~protocol),
    plotColData(unfiltered, y="detected", x="sample", colour_by="discard",
        other_fields="protocol") + scale_y_log10() + 
        ggtitle("Detected features") + facet_wrap(~protocol),
    ncol=1
)
```

可以看到，大多数的显微操作技术得到的细胞文库都比较小，相比于细胞分选方法，它在提取过程中对细胞损伤较大

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-21-112248.png)

## 3 归一化

使用去卷积方法

```r
library(scran)
set.seed(101000110)
clusters <- quickCluster(sce.grun.hsc)
sce.grun.hsc <- computeSumFactors(sce.grun.hsc, clusters=clusters)
sce.grun.hsc <- logNormCounts(sce.grun.hsc)
```

## 4 找表达量高变化基因

这里没有指定任何的批次，因为想保留这两种技术产生的任何差异

```r
set.seed(00010101)
dec.grun.hsc <- modelGeneVarByPoisson(sce.grun.hsc) 
top.grun.hsc <- getTopHVGs(dec.grun.hsc, prop=0.1)
```

做个图

```r
plot(dec.grun.hsc$mean, dec.grun.hsc$total, pch=16, cex=0.5,
    xlab="Mean of log-expression", ylab="Variance of log-expression")
curfit <- metadata(dec.grun.hsc)
curve(curfit$trend(x), col='dodgerblue', add=TRUE, lwd=2)
```

**看到这个线有点“太平缓”**，和之前见过的都不一样，感觉“中间少了一个峰”。**这是因为细胞中的基因表达量都比较低**，差别也不大【大家一起贫穷，于是贫富差距很小】，所以大部分细胞在纵坐标（衡量变化的方差）上体现不出来差距，也就导致了拟合的曲线不会有“峰”

> 可能会想，那为什么不是大家表达量都很高呢（大家都很富有，贫富差距不是也很小吗）？因为横坐标可以看到，从0-3.5，这个范围对于表达量来说确实很小，之前做的图有的都大于10、15

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-21-113627.png)

## 5 降维聚类

### **降维就采取最基础的方式：**

```r
set.seed(101010011)
sce.grun.hsc <- denoisePCA(sce.grun.hsc, technical=dec.grun.hsc, subset.row=top.grun.hsc)
sce.grun.hsc <- runTSNE(sce.grun.hsc, dimred="PCA")

# 检查PC的数量
ncol(reducedDim(sce.grun.hsc, "PCA"))
## [1] 9
```

### **聚类**

```r
snn.gr <- buildSNNGraph(sce.grun.hsc, use.dimred="PCA")
colLabels(sce.grun.hsc) <- factor(igraph::cluster_walktrap(snn.gr)$membership)

table(colLabels(sce.grun.hsc))
## 
##   1   2   3   4   5   6   7   8   9  10  11  12 
## 259 148 221 103 177 108  48 122  98  63  62  18
```

### **作图**

```r
short <- ifelse(grepl("micro", sce.grun.hsc$protocol), "micro", "sorted")
gridExtra:::grid.arrange(
    plotTSNE(sce.grun.hsc, colour_by="label"),
    plotTSNE(sce.grun.hsc, colour_by=I(short)),
    ncol=2
)
```

由于没有去除两个技术批次的差异，所以这里分的很开

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-21-114640.png)

## 6 找marker基因

```r
markers <- findMarkers(sce.grun.hsc, test.type="wilcox", direction="up",
    row.data=rowData(sce.grun.hsc)[,"SYMBOL",drop=FALSE])
```

检查一下cluster6的marker基因

```r
chosen <- markers[['6']]
best <- chosen[chosen$Top <= 10,]
length(best)
# [1] 16

# 将cluster6与其他clusters对比的AUC结果提取出来
aucs <- getMarkerEffects(best, prefix="AUC")
rownames(aucs) <- best$SYMBOL

library(pheatmap)
pheatmap(aucs, color=viridis::plasma(100))
```

看到溶菌酶相关基因（LYZ家族）、Camp、 Lcn2、 Ltf 都上调，表明**cluster6可能是神经元起源细胞**

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-21-115323.png)


# 4.11 实战十一 | Smart-seq2 | 小鼠造血干细胞

刘小泽写于2020.7.21

## 1 前言

数据来自 ([Nestorowa et al. 2016](https://pubmed.ncbi.nlm.nih.gov/27365425/)) 的小鼠造血干细胞 haematopoietic stem cell (HSC) ，使用的技术是Smart-seq2

### **准备数据**

```r
library(scRNAseq)
sce.nest <- NestorowaHSCData()

sce.nest
# class: SingleCellExperiment 
# dim: 46078 1920 
# metadata(0):
#   assays(1): counts
# rownames(46078): ENSMUSG00000000001
# ENSMUSG00000000003 ... ENSMUSG00000107391
# ENSMUSG00000107392
# rowData names(0):
#   colnames(1920): HSPC_007 HSPC_013 ... Prog_852
# Prog_810
# colData names(2): cell.type FACS
# reducedDimNames(1): diffusion
# altExpNames(1): ERCC

counts(sce.nest)[1:3,1:3]
# 3 x 3 sparse Matrix of class "dgCMatrix"
# HSPC_007 HSPC_013 HSPC_019
# ENSMUSG00000000001        .        7        1
# ENSMUSG00000000003        .        .        .
# ENSMUSG00000000028        4        1        2
```

看到使用了ERCC、Ensembl ID

### **ID转换**

```r
library(AnnotationHub)
ens.mm.v97 <- AnnotationHub()[["AH73905"]]
anno <- select(ens.mm.v97, keys=rownames(sce.nest), 
    keytype="GENEID", columns=c("SYMBOL", "SEQNAME"))
# 这里全部对应
> sum(is.na(anno$SYMBOL))
[1] 0
> sum(is.na(anno$SEQNAME))
[1] 0
# 接下来只需要匹配顺序即可
rowData(sce.nest) <- anno[match(rownames(sce.nest), anno$GENEID),]

sce.nest
# class: SingleCellExperiment 
# dim: 46078 1920 
# metadata(0):
#   assays(1): counts
# rownames(46078): ENSMUSG00000000001
# ENSMUSG00000000003 ... ENSMUSG00000107391
# ENSMUSG00000107392
# rowData names(3): GENEID SYMBOL SEQNAME
# colnames(1920): HSPC_007 HSPC_013 ... Prog_852
# Prog_810
# colData names(2): cell.type FACS
# reducedDimNames(1): diffusion
# altExpNames(1): ERCC
```

## 2 质控

**依然是备份一下，把unfiltered数据主要用在质控的探索上**

```r
unfiltered <- sce.nest
```

这里没有线粒体基因，因此只能用ERCC计算过滤条件

```r
library(scater)
stats <- perCellQCMetrics(sce.nest)
qc <- quickPerCellQC(stats, percent_subsets="altexps_ERCC_percent")
sce.nest <- sce.nest[,!qc$discard]

# 看下过滤的细胞
colSums(as.matrix(qc))
# low_lib_size            low_n_features 
# 146                        28 
# high_altexps_ERCC_percent                   discard 
# 241                       264
```

做个图

```r
colData(unfiltered) <- cbind(colData(unfiltered), stats)
unfiltered$discard <- qc$discard

gridExtra::grid.arrange(
    plotColData(unfiltered, y="sum", colour_by="discard") +
        scale_y_log10() + ggtitle("Total count"),
    plotColData(unfiltered, y="detected", colour_by="discard") +
        scale_y_log10() + ggtitle("Detected features"),
    plotColData(unfiltered, y="altexps_ERCC_percent",
        colour_by="discard") + ggtitle("ERCC percent"),
    ncol=2
)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-21-125348.png)

**最后对数据进行过滤**

```r
sce.nest <- sce.nest[,!qc$discard]

# 过滤前后
> dim(unfiltered);dim(sce.nest)
[1] 46078  1920
[1] 46078  1656
```

## 3 归一化

```r
library(scran)
set.seed(101000110)
clusters <- quickCluster(sce.nest)
sce.nest <- computeSumFactors(sce.nest, clusters=clusters)
sce.nest <- logNormCounts(sce.nest)
```

## 4 找表达量高变化基因

使用基于ERCC的构建模型方法

```r
set.seed(00010101)
dec.nest <- modelGeneVarWithSpikes(sce.nest, "ERCC")
top.nest <- getTopHVGs(dec.nest, prop=0.1)

class(dec.nest)
# [1] "DFrame"
# attr(,"package")
# [1] "S4Vectors"

# 其中ERCC的信息就存储在dec.nest的metadata中
curfit <- metadata(dec.nest)
class(curfit)
# [1] "list"
names(curfit)
# [1] "mean"    "var"     "trend"   "std.dev"
length(unique(names(curfit$mean))) # 一共92个ERCC spike-in
# [1] 92 

# 其中的mean、var就定义了横纵坐标
head(curfit$mean)
# ERCC-00002  ERCC-00003  ERCC-00004  ERCC-00009  ERCC-00012  ERCC-00013 
# 14.91183375 11.27060119 13.31197197 11.94866319  0.02211546  0.21249156 
head(curfit$var)
# ERCC-00002 ERCC-00003 ERCC-00004 ERCC-00009 ERCC-00012 ERCC-00013 
# 0.02375131 0.29308411 0.05376959 0.41814635 0.14928826 1.08599155
```

**然后把基因（黑点）、ERCC（红点）、根据ERCC拟合的线（蓝线）画出来**

```r
plot(dec.nest$mean, dec.nest$total, pch=16, cex=0.5,
    xlab="Mean of log-expression", ylab="Variance of log-expression")
curve(curfit$trend(x), col='dodgerblue', add=TRUE, lwd=2)
points(curfit$mean, curfit$var, col="red")
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-21-130616.png)

## 5 降维聚类

### **降维**

```r
set.seed(101010011)
sce.nest <- denoisePCA(sce.nest, technical=dec.nest, subset.row=top.nest)
sce.nest <- runTSNE(sce.nest, dimred="PCA")

# 检查PC的数量
ncol(reducedDim(sce.nest, "PCA"))
## [1] 9
```

### **聚类**

```r
snn.gr <- buildSNNGraph(sce.nest, use.dimred="PCA")
colLabels(sce.nest) <- factor(igraph::cluster_walktrap(snn.gr)$membership)

table(colLabels(sce.nest))
## 
##   1   2   3   4   5   6   7   8   9 
## 203 472 258 175 142 229  20  83  74
```

### 作图&#x20;

```r
plotTSNE(sce.nest, colour_by="label")
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-21-130920.png)

## 6 marker基因检测

```r
markers <- findMarkers(sce.nest, colLabels(sce.nest), 
    test.type="wilcox", direction="up", lfc=0.5,
    row.data=rowData(sce.nest)[,"SYMBOL",drop=FALSE])
```

比如检测一下cluster8：

```r
chosen <- markers[['8']]
best <- chosen[chosen$Top <= 10,]
length(best)
# [1] 13

# 将cluster8与其他clusters对比的AUC结果提取出来
aucs <- getMarkerEffects(best, prefix="AUC")
rownames(aucs) <- best$SYMBOL

library(pheatmap)
pheatmap(aucs, color=viridis::plasma(100))
```

看到其中血红蛋白相关基因（Hba1、Hba2、Hbb）、Car2、Hebp1基因上调，说明**clsuter8可能包含红细胞前体细胞**

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-21-131927.png)

## 7 细胞类型注释

> 将会使用内置的参考注释数据，[`SingleR`](https://bioconductor.org/packages/release/bioc/vignettes/SingleR/inst/doc/SingleR.html)中就包含了一些内置数据集，大部分是bulk RNA-Seq或芯片数据中经过筛选的细胞类型。

### **准备参考数据**

```r
library(SingleR)
mm.ref <- MouseRNAseqData()
mm.ref
# class: SummarizedExperiment 
# dim: 21214 358 
# metadata(0):
#   assays(1): logcounts
# rownames(21214): Xkr4 Rp1 ... LOC100039574
# LOC100039753
# rowData names(0):
#   colnames(358): ERR525589Aligned
# ERR525592Aligned ... SRR1044043Aligned
# SRR1044044Aligned
# colData names(3): label.main label.fine
# label.ont
```

### **进行转换**

```r
renamed <- sce.nest
# 参考数据集中使用的是symbol name，这里也转换一下
rownames(renamed) <- uniquifyFeatureNames(rownames(renamed),
                                          rowData(sce.nest)$SYMBOL)
# 然后把我们的细胞在参考数据集中找对应的细胞类型
# 返回的pred结果是一个数据框，每行是我们自己数据的一个细胞
pred <- SingleR(test=renamed, ref=mm.ref, labels=mm.ref$label.fine)
table(pred$labels)
# 
# B cells Endothelial cells      Erythrocytes      Granulocytes       Macrophages         Monocytes          NK cells           T cells 
# 61                 1              1005                 1                 2               500                 1                85
```

这里也看到cluster8与红细胞更相近

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-21-133040.png)


# 4.12 实战十二 | 10X | 小鼠嵌合体胚胎

刘小泽写于2020.7.21

## 1 前言

数据来自[Pijuan-Sala et al. (2019)](https://pubmed.ncbi.nlm.nih.gov/30787436/)，研究的是小鼠E8.5发育阶段的嵌合胚胎

**数据准备**

```r
# 自己下载
library(MouseGastrulationData)
sce.chimera <- WTChimeraData(samples=5:10)

# 或者加载之前分享的数据
load('sce.chimera.RData')
sce.chimera
## class: SingleCellExperiment 
## dim: 29453 20935 
## metadata(0):
## assays(1): counts
## rownames(29453): ENSMUSG00000051951 ENSMUSG00000089699 ...
##   ENSMUSG00000095742 tomato-td
## rowData names(2): ENSEMBL SYMBOL
## colnames(20935): cell_9769 cell_9770 ... cell_30702 cell_30703
## colData names(11): cell barcode ... doub.density sizeFactor
## reducedDimNames(2): pca.corrected.E7.5 pca.corrected.E8.5
## altExpNames(0):

names(colData(sce.chimera))
# [1] "cell"            "barcode"        
# [3] "sample"          "stage"          
# [5] "tomato"          "pool"           
# [7] "stage.mapped"    "celltype.mapped"
# [9] "closest.cell"    "doub.density"
```

**简单看一下colData中的各个信息**

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-21-135013.png)

其中包含了6个样本的信息，总共20935个细胞

```r
table(sce.chimera$sample)
# 
# 5    6    7    8    9   10 
# 2298 1026 2740 2904 4057 6401
```

**整合行名**

```r
library(scater)
rownames(sce.chimera) <- uniquifyFeatureNames(
    rowData(sce.chimera)$ENSEMBL, rowData(sce.chimera)$SYMBOL)
```

## 2 简单质控

之前作者已经对数据进行了质控，并把细胞做了标志，这里只需要把标记“stripped”、“Doublet”的细胞去掉即可

```r
drop <- sce.chimera$celltype.mapped %in% c("stripped", "Doublet")
table(drop)
# drop
# FALSE  TRUE 
# 19426  1509 
sce.chimera <- sce.chimera[,!drop]
```

## 3 归一化

看到原来数据中也计算了size factors，那么这里就不需要计算，直接应用

```r
sce.chimera <- logNormCounts(sce.chimera)
```

## 4 找表达量高变化基因

我们的数据有6个样本，可以说异质性非常高了。把它们当做不同的批次信息，并尽可能多地从中保存基因

```r
library(scran)
dec.chimera <- modelGeneVar(sce.chimera, block=sce.chimera$sample)
chosen.hvgs <- dec.chimera$bio > 0
table(chosen.hvgs)
# chosen.hvgs
# FALSE  TRUE 
# 14754 14699
```

## 5 数据整合并矫正批次效应

使用了一种“层次整合”的方法，就是先将同种表型样本整合起来（比如3个处理和3个对照先内部整合），再将不同表型的样本组合（将处理和对照整合）

> correctExperiments的含义是：Apply a correction to multiple SingleCellExperiment objects,

```r
library(batchelor)
set.seed(01001001)
# 下面的merge.order就设置了整合的顺序
merged <- correctExperiments(sce.chimera, 
    batch=sce.chimera$sample, 
    subset.row=chosen.hvgs,
    PARAM=FastMnnParam(
        merge.order=list(
            list(1,3,5), # WT (3 replicates)
            list(2,4,6)  # td-Tomato (3 replicates)
        )
    )
)
```

看下结果：**`lost.var` 值越大表示丢失的真实生物异质性越多**

```r
metadata(merged)$merge.info$lost.var
##              5         6         7         8        9       10
## [1,] 0.000e+00 0.0204433 0.000e+00 0.0169567 0.000000 0.000000
## [2,] 0.000e+00 0.0007389 0.000e+00 0.0004409 0.000000 0.015474
## [3,] 3.090e-02 0.0000000 2.012e-02 0.0000000 0.000000 0.000000
## [4,] 9.024e-05 0.0000000 8.272e-05 0.0000000 0.018047 0.000000
## [5,] 4.321e-03 0.0072518 4.124e-03 0.0078280 0.003831 0.007786
```

Large proportions of lost variance **(>10%)** suggest that correction is **removing genuine biological heterogeneity.**

## 6 聚类

```r
g <- buildSNNGraph(merged, use.dimred="corrected")
clusters <- igraph::cluster_louvain(g)
colLabels(merged) <- factor(clusters$membership)
```

**看分群与细胞类型之间关系**

```r
tab <- table(Cluster=colLabels(merged), Sample=merged$sample)
library(pheatmap)
pheatmap(log10(tab+10), color=viridis::viridis(100))
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-21-142727.png)

## 7 降维

```r
merged <- runTSNE(merged, dimred="corrected")
merged <- runUMAP(merged, dimred="corrected")

gridExtra::grid.arrange(
  plotTSNE(merged, colour_by="label", text_by="label", text_col="red"),
  plotTSNE(merged, colour_by="batch"),
  ncol=2
)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-21-143149.png)


# 4.13 实战十三 | 10X | 小鼠乳腺上皮细胞

刘小泽写于2020.7.21

## 1 前言

数据来自[Bach et al. (2017)](https://pubmed.ncbi.nlm.nih.gov/29225342/)，使用的是妊娠期小鼠乳腺上皮细胞 + 10X技术建库

### **数据准备**

```r
library(scRNAseq)
sce.mam <- BachMammaryData(samples="G_1")
sce.mam
# class: SingleCellExperiment 
# dim: 27998 2915 
# metadata(0):
#   assays(1): counts
# rownames: NULL
# rowData names(2): Ensembl Symbol
# colnames: NULL
# colData names(3): Barcode Sample Condition
# reducedDimNames(0):
#   altExpNames(0):
```

### **数据初探**

```r
# 样本信息
sapply(names(colData(sce.mam)), function(x) head(colData(sce.mam)[,x]))
# Barcode              Sample Condition  
# [1,] "AAACCTGAGGATGCGT-1" "G_1"  "Gestation"
# [2,] "AAACCTGGTAGTAGTA-1" "G_1"  "Gestation"
# [3,] "AAACCTGTCAGCATGT-1" "G_1"  "Gestation"
# [4,] "AAACCTGTCGTCCGTT-1" "G_1"  "Gestation"
# [5,] "AAACGGGCACGAAATA-1" "G_1"  "Gestation"
# [6,] "AAACGGGCAGACGCTC-1" "G_1"  "Gestation"
```

### **ID转换**

依然是整合行名 + 添加染色体信息

```r
library(scater)
rownames(sce.mam) <- uniquifyFeatureNames(
    rowData(sce.mam)$Ensembl, rowData(sce.mam)$Symbol)

library(AnnotationHub)
ens.mm.v97 <- AnnotationHub()[["AH73905"]]
rowData(sce.mam)$SEQNAME <- mapIds(ens.mm.v97, keys=rowData(sce.mam)$Ensembl,
    keytype="GENEID", column="SEQNAME")

# 总共有13个线粒体基因
sum(grepl("MT",rowData(sce.mam)$SEQNAME))
# [1] 13
```

## 2 质控

**依然是备份一下，把unfiltered数据主要用在质控的探索上**

```r
unfiltered <- sce.mam
```

使用线粒体信息进行过滤

```r
is.mito <- rowData(sce.mam)$SEQNAME == "MT"
stats <- perCellQCMetrics(sce.mam, subsets=list(Mito=which(is.mito)))
qc <- quickPerCellQC(stats, percent_subsets="subsets_Mito_percent")

colSums(as.matrix(qc))
##              low_lib_size            low_n_features high_subsets_Mito_percent 
##                         0                         0                       143 
##                   discard 
##                       143
```

作图

```r
colData(unfiltered) <- cbind(colData(unfiltered), stats)
unfiltered$discard <- qc$discard

gridExtra::grid.arrange(
    plotColData(unfiltered, y="sum", colour_by="discard") + 
        scale_y_log10() + ggtitle("Total count"),
    plotColData(unfiltered, y="detected", colour_by="discard") + 
        scale_y_log10() + ggtitle("Detected features"),
    plotColData(unfiltered, y="subsets_Mito_percent", 
        colour_by="discard") + ggtitle("Mito percent"),
    ncol=3
)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-21-150405.png)

再看看线粒体含量与文库大小的关系

```r
plotColData(unfiltered, x="sum", y="subsets_Mito_percent", 
    colour_by="discard") + scale_x_log10()
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-21-150514.png)

**最后过滤**

```r
dim(unfiltered);dim(sce.mam)
# [1] 27998  2915
# [1] 27998  2772
```

## 3 归一化

使用去卷积的方法

```r
library(scran)
set.seed(101000110)
clusters <- quickCluster(sce.mam)
sce.mam <- computeSumFactors(sce.mam, clusters=clusters)
sce.mam <- logNormCounts(sce.mam)
```

## 4 找高变异基因

这里由于是10X的数据，所以会有UMI信息，因此可以用基于泊松分布的模型构建方法

```r
set.seed(00010101)
dec.mam <- modelGeneVarByPoisson(sce.mam)
top.mam <- getTopHVGs(dec.mam, prop=0.1)
```

最后做个图

```r
plot(dec.mam$mean, dec.mam$total, pch=16, cex=0.5,
    xlab="Mean of log-expression", ylab="Variance of log-expression")
curfit <- metadata(dec.mam)
curve(curfit$trend(x), col='dodgerblue', add=TRUE, lwd=2)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-21-150855.png)

## 5 降维聚类

### **降维**

```r
library(BiocSingular)
set.seed(101010011)
sce.mam <- denoisePCA(sce.mam, technical=dec.mam, subset.row=top.mam)
sce.mam <- runTSNE(sce.mam, dimred="PCA")

# 检查PC的数量
ncol(reducedDim(sce.mam, "PCA"))
## [1] 15
```

### **聚类**

> 有一个很重要的参数是`k` ，含义是：the number of nearest neighbors used to construct the graph。如果k设置越大，得到的图之间联通程度越高，cluster也越大。因此这个参数也是可以不断尝试的

我们这里由于细胞数量比较多，所以设置的k就比较大，得到的cluster就少而大

```r
snn.gr <- buildSNNGraph(sce.mam, use.dimred="PCA", k=25)
colLabels(sce.mam) <- factor(igraph::cluster_walktrap(snn.gr)$membership)

table(colLabels(sce.mam))
## 
##   1   2   3   4   5   6   7   8   9  10 
## 550 799 716 452  24  84  52  39  32  24
```

### 作图

```r
plotTSNE(sce.mam, colour_by="label")
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-21-151128.png)


# 4.14 | 实战十四 | 10X | HCA计划的38万骨髓细胞

刘小泽写于2020.7.22

## 1 前言

> 前面的种种都是作为知识储备，但是不实战还是记不住前面的知识 **这是第十四个实战练习**，不过这次的练习对电脑要求比较高

### **单细胞领域不得不提的HCA计划**

> 参考链接：\
> [官网](<https://data.humancellatlas.org/ >)\
> [美英机构讨论启动国际人类细胞图谱计划](<http://www.casisd.cn/zkcg/ydkb/kjqykb/2016/201612/201707/t20170703_4821952.html >)\
> [人类细胞图谱计划 |为人体40万亿细胞绘制“脸谱”](<https://baike.baidu.com/tashuo/browse/content?id=6937242830be0d08a0384772 >)\
> [儿童细胞图谱计划](http://med.china.com.cn/content/pid/134504/tid/1015)

**细胞类型多种多样**

细胞是生命最基本的单位，一般人至少有37兆2000亿个细胞，但现有的研究一般是组织层面，对细胞内部机制理解的还不是很深入。**人体内的细胞有多少种类？**&#x8FD9;是一个非常基础的问题。美国国立卫生研究院给出了一个答案：200种，包括像神经元细胞、心脏细胞、肌肉细胞等几大类。然而，如果你去问一个免疫学家，他会告诉你光免疫细胞就至少有200种；如果你去问一个专门研究T细胞的免疫学家，他会告诉你光T细胞就至少有200种。**Regev说她自己光讲人体细胞种类就要花15分钟**，并且一个细胞的种类可以不断地往下划分，亚型下面还有亚型，仅视网膜组织就至少包括100种不同种类的神经元。更棘手的是，有些种类的细胞会在一定条件下转化为其他种类的细胞，并且每个亚型的细胞会根据不同环境呈现出不同的状态。

**细胞图谱的重要性**

一个完整的人体细胞图谱将赋予我们每种细胞类型唯一的“身份证”，可以帮助解释不同类型的细胞如何协作并形成组织的。另外与疾病相关的基因在哪些细胞更活跃，不同细胞类型产生的机理又是怎样的呢？

**HCA计划的开启**

2016年Chan Zuckerberg Initiative（CZI）基金会计划在未来十年内投入30亿美元支持科学研究，其中最瞩目的就是“人类细胞图谱计划”（the Human Cell Atlas）【可与“人类基因组计划”相媲美】。**2017年10月，HCA正式公布了首批拟资助的38个项目，将为40万亿个细胞绘制图谱**，其中清华大学教授张学工负责的项目作为其中唯一一个由中国科学家承担的项目，从全球范围内征集的近500个项目中脱颖而出。

2018年3月8日，**Sanger研究所在其官网宣布了人类发育细胞图谱计划（HDCA）的最新成果**研究团队从捐赠的人类发育组织（包括肝脏、皮肤、肾脏、胎盘）中分离出**超25万个细胞**，并利用强大的单细胞基因组测序工具获取了人类早期发育，以及影响健康或者导致疾病的信息。HDCA是HCA的重要一部分，旨在构建参与人类发育的所有重要细胞的基因组参考图谱。该项目聚焦的其他主要领域包括，改善对血细胞如何形成，以及免疫系统如何发挥功能的理解。

另外，**2018年**，美国费城儿童医院和辛辛那提儿童医院领导的国际研究团队建议在HCA联盟中建立一个纵向的儿童研究分支，并在HCA白皮书中概述了**儿童细胞图谱计划（Pediatric Cell Atlas，PCA**），以在儿童健康和人类发展背景下，展开针对儿童独特生物学特征的跨学科研究。

**HCA的主要内容**

* 编目所有人体细胞类型（如免疫细胞、脑细胞）及其子类型；
* 绘制不同细胞类型在组织和人体中的分布图；
* 区分细胞状态（如免疫细胞在被病原体激活前后的状态）；
* 捕捉细胞转换过程的关键特征（如干细胞细胞激活和分化）；
* 追踪细胞谱系历史（如骨髓前体干细胞到功能性的红细胞）

### **数据准备**

我们这里使用的数据是：10X技术得到的人类约**380,000**个骨髓细胞

> [分享数据下载](https://share.weiyun.com/JLvQvpHS)

```r
# 如果要自己下载
# library(HCAData)
# sce.bone <- HCAData('ica_bone_marrow')
load('sce.bone.new.RData')
sce.bone
# class: SingleCellExperiment 
# dim: 33694 378000 
# metadata(0):
#   assays(1): counts
# rownames(33694): ENSG00000243485
# ENSG00000237613 ... ENSG00000277475
# ENSG00000268674
# rowData names(2): ID Symbol
# colnames(378000):
#   MantonBM1_HiSeq_1-AAACCTGAGCAGGTCA-1
# MantonBM1_HiSeq_1-AAACCTGCACACTGCG-1 ...
# MantonBM8_HiSeq_8-TTTGTCATCTGCCAGG-1
# MantonBM8_HiSeq_8-TTTGTCATCTTGAGAC-1
# colData names(1): Barcode
# reducedDimNames(0):
#   altExpNames(0):
```

**数据初探**

```r
# 这里的数据大小41M
object.size(counts(sce.bone))
# 41741200 bytes

# 真实文件大小700多M
file.info(path(counts(sce.bone)))$size
# [1] 769046295

# 其中包含供体信息
head(sce.bone$Barcode)
# [1] "MantonBM1_HiSeq_1-AAACCTGAGCAGGTCA-1"
# [2] "MantonBM1_HiSeq_1-AAACCTGCACACTGCG-1"
# [3] "MantonBM1_HiSeq_1-AAACCTGCACCGGAAA-1"
# [4] "MantonBM1_HiSeq_1-AAACCTGCATAGACTC-1"
# [5] "MantonBM1_HiSeq_1-AAACCTGCATCGATGT-1"
# [6] "MantonBM1_HiSeq_1-AAACCTGCATTCCTGC-1"

# 提取供体信息
sce.bone$Donor <- sub("_.*", "", sce.bone$Barcode)
table(sce.bone$Donor)
# 
# MantonBM1 MantonBM2 MantonBM3 MantonBM4 MantonBM5 
# 48000     48000     48000     48000     48000 
# MantonBM6 MantonBM7 MantonBM8 
# 42000     48000     48000
```

**ID转换**

还是要获得染色体信息

```r
library(EnsDb.Hsapiens.v86)
rowData(sce.bone)$Chr <- mapIds(EnsDb.Hsapiens.v86, keys=rownames(sce.bone),
    column="SEQNAME", keytype="GENEID")

# 其中包括13个线粒体信息
table(grepl('MT',rowData(sce.bone)$Chr))
# 
# FALSE  TRUE 
# 33681    13
```

整合行名

```r
library(scater)
rownames(sce.bone) <- uniquifyFeatureNames(rowData(sce.bone)$ID,
    names = rowData(sce.bone)$Symbol)
```

## 2 质控

使用线粒体信息进行过滤，并且因为数据量很大，可以**调用多线程**

```r
library(BiocParallel)
# 调用8线程
bpp <- MulticoreParam(8)

start=Sys.time()
sce.bone <- unfiltered <- addPerCellQC(sce.bone, BPPARAM=bpp,
                                     subsets=list(Mito=which(rowData(sce.bone)$Chr=="MT")))
end=Sys.time()
(end-start)
# Time difference of 2.167978 mins
```

过滤掉了6万多个细胞

```r
colSums(as.matrix(qc))
# low_lib_size            low_n_features 
# 33997                     42756 
# high_subsets_Mito_percent                   discard 
# 44105                     61275
```

作图

```r
unfiltered$discard <- qc$discard

gridExtra::grid.arrange(
    plotColData(unfiltered, x="Donor", y="sum", colour_by="discard") +
        scale_y_log10() + ggtitle("Total count"),
    plotColData(unfiltered, x="Donor", y="detected", colour_by="discard") +
        scale_y_log10() + ggtitle("Detected features"),
    plotColData(unfiltered, x="Donor", y="subsets_Mito_percent",
        colour_by="discard") + ggtitle("Mito percent"),
    ncol=2
)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-22-035024.png)

再看看线粒体含量与文库大小的关系

```r
plotColData(unfiltered, x="sum", y="subsets_Mito_percent", 
    colour_by="discard") + scale_x_log10()
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-22-035105.png)

## 3 归一化

这里为了减少计算量，使用原来计算好的文库size factor，不需要重复计算

```r
sce.bone <- logNormCounts(sce.bone, size_factors = sce.bone$sum)
summary(sizeFactors(sce.bone))
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    0.05    0.47    0.65    1.00    0.89   42.38
```

## 4 找高变异基因

将供体作为批次信息加入到构建模型中，这一步依然需要多线程处理

```r
library(scran)
start=Sys.time()
dec.bone <- modelGeneVar(sce.bone, block=sce.bone$Donor, BPPARAM=bpp)
end=Sys.time()
(end-start) # Time difference of 10.58185 mins

top.bone <- getTopHVGs(dec.bone, n=5000)
```

## 5 矫正批次效应

```r
library(batchelor)
library(BiocNeighbors)

set.seed(1010001)
merged.bone <- fastMNN(sce.bone, batch = sce.bone$Donor, subset.row = top.bone,
     BSPARAM=BiocSingular::RandomParam(deferred = TRUE), 
     BNPARAM=AnnoyParam(),
     BPPARAM=bpp)

reducedDim(sce.bone, 'MNN') <- reducedDim(merged.bone, 'corrected')
```

看下结果：`lost.var` 值越大表示丢失的真实生物异质性越多

```r
##      MantonBM1 MantonBM2 MantonBM3 MantonBM4 MantonBM5 MantonBM6 MantonBM7
## [1,]  0.010616  0.008241  0.000000  0.000000  0.000000  0.000000  0.000000
## [2,]  0.007894  0.007741  0.023662  0.000000  0.000000  0.000000  0.000000
## [3,]  0.005834  0.003823  0.005423  0.025272  0.000000  0.000000  0.000000
## [4,]  0.003482  0.002868  0.002581  0.003067  0.027117  0.000000  0.000000
## [5,]  0.005287  0.003544  0.003173  0.005702  0.006551  0.031890  0.000000
## [6,]  0.004601  0.004535  0.004533  0.004067  0.004934  0.005547  0.034452
## [7,]  0.002610  0.002238  0.003135  0.002799  0.001963  0.002697  0.002404
##      MantonBM8
## [1,]   0.00000
## [2,]   0.00000
## [3,]   0.00000
## [4,]   0.00000
## [5,]   0.00000
## [6,]   0.00000
## [7,]   0.04033
```

## 6 降维聚类

### **降维**

这么大的细胞数据，一般会使用UMAP

```r
set.seed(01010100)
sce.bone <- runUMAP(sce.bone, dimred="MNN",
    BNPARAM=AnnoyParam(),
    BPPARAM=bpp,
    n_threads=bpnworkers(bpp))
```

> 另外关于[tSNE和UMAP的对比](https://towardsdatascience.com/how-exactly-umap-works-13e3040e1668)
>
> * **SNE does not preserve global data structure**, meaning that only within cluster distances are meaningful while between cluster similarities are not guaranteed. But **UMAP Can Preserve Global Structure**
> * **tSNE does not scale** well for rapidly increasing sample sizes in scRNAseq.
> * **UMAP is faster than tSNE** when it concerns a) large number of data points, b) number of embedding dimensions greater than 2 or 3, c) large number of ambient dimensions in the data set
> * UMAP的改进：**UMAP** overall follows the philosophy of tSNE, but introduces a number of improvements such as **another cost function** and the **absence of normalization** of high- and low-dimensional probabilities.
> * 总结：Despite **tSNE served** **the Single Cell** research area for years, it has **too many disadvantages** such as **speed** and the **lack of global distance preservation**

### **聚类**

因为这里的细胞数量实在太大，因此可以先借用kmeans方法对它们聚类，比如先聚成1000个小类，然后再对每个小类进行graph-based聚类

```r
set.seed(1000)
clust.bone <- clusterSNNGraph(sce.bone, use.dimred="MNN", 
    use.kmeans=TRUE, kmeans.centers=1000)
colLabels(sce.bone) <- factor(clust.bone)
table(colLabels(sce.bone))
## 
##     1     2     3     4     5     6     7     8     9    10    11    12 
## 21938 42850 19861 38064 38024 71978 19237 24627  7583 16361  3179 13023
```

做个热图

```r
tab <- table(Cluster=colLabels(sce.bone), Donor=sce.bone$Donor)
library(pheatmap)
pheatmap(log10(tab+10), color=viridis::viridis(100))
```

看到每个cluster中都会有几个不同的样本，这也符合预期，因为所有的样本都是来自不同供体的重复，它们实际都是骨髓细胞

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-22-080655.png)

## 7 找marker基因

```r
markers.bone <- findMarkers(sce.bone, block = sce.bone$Donor, 
    direction = 'up', lfc = 1, BPPARAM=bpp)
```

然后检查第5群细胞

```r
top.markers <- markers.bone[["5"]]
best <- top.markers[top.markers$Top <= 10,]
lfcs <- getMarkerEffects(best)

library(pheatmap)
pheatmap(lfcs, breaks=seq(-5, 5, length.out=101))
```

看到LYZ、S100A8、VCAN基因高表达，说明cluster5可能是单核细胞（Monocyte）

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-07-22-081432.png)

## 8 细胞类型注释

使用参考数据集帮助注释，只不过这里并不是对每个细胞单独操作，因为数量太太。**而是基于12个已经分好的cluster的总体表达量进行注释**，提高了注释**速度**，但同样牺牲了研究cluster**内部细胞异质性**的机会。适用于快速注释细胞类群

```r
se.aggregated <- sumCountsAcrossCells(sce.bone, id=colLabels(sce.bone))

library(SingleR)
hpc <- HumanPrimaryCellAtlasData()
anno.single <- SingleR(se.aggregated, ref = hpc, labels = hpc$label.main,
    assay.type.test="sum")
```

看一下结果，包含12行也就是12个分群结果，看到利用参考注释的方法，cluster5也是被注释到了Monocyte这个细胞类型

```r
anno.single
## DataFrame with 12 rows and 5 columns
##                             scores     first.labels      tuning.scores
##                           <matrix>      <character>        <DataFrame>
## 1   0.388013:0.636789:0.754052:...              CMP 0.554195:0.4238259
## 2   0.326563:0.692156:0.661226:...          NK_cell 0.579208:0.4459558
## 3   0.323785:0.668599:0.743138:... Pre-B_cell_CD34- 0.488346:0.0719803
## 4   0.346204:0.637353:0.615021:...          T_cells 0.628161:0.3531725
## 5   0.297369:0.638962:0.745831:... Pre-B_cell_CD34- 0.548995:0.2633979
## ...                            ...              ...                ...
## 8   0.313537:0.773897:0.672663:...           B_cell 0.722126:-0.288616
## 9   0.346503:0.703506:0.670777:... Pro-B_cell_CD34+ 0.774848: 0.710191
## 10  0.313591:0.774162:0.668809:...           B_cell 0.719013:-0.281128
## 11  0.369873:0.720021:0.654892:...           B_cell 0.523979: 0.326187
## 12  0.403994:0.603811:0.697425:...              MEP 0.430493: 0.286268
##               labels    pruned.labels
##          <character>      <character>
## 1                CMP              CMP
## 2            T_cells          T_cells
## 3           Monocyte         Monocyte
## 4            T_cells          T_cells
## 5           Monocyte         Monocyte
## ...              ...              ...
## 8             B_cell           B_cell
## 9   Pro-B_cell_CD34+ Pro-B_cell_CD34+
## 10            B_cell           B_cell
## 11            B_cell               NA
## 12        BM & Prog.       BM & Prog.
```


# 5 补充篇：开拓思路

将会加入上游CellRanger分析流程、Smart-seq2上游分析、流行的Seurat、Monocle的分析流程


# 5.1 10X Genomics概述

## 这一部分将会介绍什么?

目前单细胞领域比较火热的公司就包括10X Genomics公司，来看看与它相关的一些基础知识吧


# 5.1.1 10X Genomics 问题集锦

刘小泽写于19.5.13

> 使用一个新的工具肯定会遇到各种问题，但是一个好的工具一定会有详细的问题解决办法，比如10X官方就做了一个问题集锦，他们将询问邮件搜集整理出来，然后以Q\&A的形式放在官网<https://kb.10xgenomics.com/hc/en-us>
>
> 一般做单细胞还主要是想看表达量，因此我**先翻译整理一下关于Single Cell Gene Expression 的问题**<https://kb.10xgenomics.com/hc/en-us/categories/360000149952-Single-Cell-3-Gene-Expression>

### Q1：3' and 5’ Gene Expression文库的差别

> 来自：<https://kb.10xgenomics.com/hc/en-us/articles/360000939852-What-is-the-difference-between-Single-Cell-3-and-5-Gene-Expression-libraries->

Both solutions use polydT primer for reverse transcription, although in the 3' assay the polydT sequence is located on the gel bead oligo, while in the 5' assay the polydT is supplied as an RT primer.

### Q2：到底怎么建库测序的？机器的工作原理是怎样的？

**先看个图片，有个大体印象**

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-05-051807.png)

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-05-051812.png)

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-05-051817.png)

**10X 机器做了什么？**

10X公司把微珠加DNA标签、微滴反应、酶反应以及高通量测序后的数据分析整合到了一起，做了一个基于油包水乳浊液酶反应原理的分析系统。

看到的10X机器就是**制备油包水的乳浊液**，它搭配芯片一起工作，这个机器制备一次乳浊液大约耗时7-9分钟，能产生500-8万个细胞

再看看芯片(上图二)，芯片有4排8列，也就是32个孔。每一列孔对应一个样本，也就是说，一张芯片一次可以处理8个样本。图中1️⃣的孔是用来放样本的(sample well)，编号2️⃣是用来放预制微珠（gel beads），编号3️⃣是用来放油的，最上面那一排是做好乳浊液以后，回收乳浊液的孔

目前单细胞测序主要还是基于对一群细胞中每个细胞的表达量分析

**工作原理第一步：微珠上DNA引物设计**

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-05-051821.png)

先预制凝胶微珠，也就是所说的gel beads，然后每个凝胶微珠"种上"特定的DNA片段，每个DNA序列分成几段：

* 第一段是barcode，16bp碱基，大约350万种barcodes，一个微珠对应一个barcode，利用这么多barcode可以区分各个凝胶微珠。=》每个凝胶微珠的ID号

  > 其中任意两个barcode之间至少差两个或者两个以上的碱基，因为测序存在对碱基的误读，这样设计可以避免将两个barcode搞混(可以试想，如果两个barcode之差一个碱基，那么就有16分之一的概率将两个判断成一个)
* 第二段是UMI序列，即unique molecular index，它是一段随机序列，也就是说每个DNA分子都有自己的UMI序列，UMI长为10bp，那么就有`4^10=1,048,576`也就是100多万种变化。它的作用就是经过了PCR+深度测序后，找到reads与原始cDNA的对应关系 =》 每个DNA标签分子的ID号

  > 它考虑到了这样一种情况：一个基因片段经过PCR扩增产生多个reads，但是不加标记我们是不知道的，并且不同基因的PCR扩增效率可能不同，因此一个基因最后得到的reads数就可能由于PCR扩增效率高而超过了另一个基因(而这两个基因的真实表达量可能差不多)。也就是排除"PCR bias"
* 第三段是Poly(dT)序列，它起到的作用是与mRNA的poly(A)尾巴结合，作为逆转录的引物，将cDNA逆转录出来

  ![image-20190515175231589](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-05-051825.png)

**工作原理第二步：芯片的液流管路**

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-05-051829.png)

细胞混悬液在第一个十字交叉口，与凝胶微珠混合；接着进入第二个交叉口，这时加上油滴，把凝胶微珠+细胞混悬液包裹起来=》油包水的小液滴=》这些油包水的小微滴就组成了乳浊液

乳浊液中，有的是包含一个细胞的(红圈部分)，也有的不包含细胞，还有的有两个以上细胞(这个叫"Doublets") ，一个小液滴中包含几个细胞是符合"泊松分布"的。

> 大部分细胞会匹配到一个小液滴中(细胞混悬液中大约有65%的细胞可以被成功包到有微珠的小液滴中=》也叫做细胞的捕获效率\~65%)，后续分析的reads就是从它们这里来的

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-05-051833.png)

**工作原理第三步：测序文库构建**

得到乳浊液后，就要脱掉细胞膜，让其中的**mRNA游离**出来=》

游离出来的**mRNA与小液滴中的水相**混合，水相中包括凝胶微珠上连着的核酸引物、逆转录酶、dNTP底物，发生**逆转录**反应=》

通过mRNA的polyA与凝胶微珠上的polyT互补，mRNA与凝胶微珠上带有标签的DNA分子结合起来，然后在逆转录酶作用下，逆转录出cDNA=》

这样得到的**cDNA分子是带有微珠特定的barcode标签**的，并且每个cDNA分子带有特定的UMI标签，有了这两个标签，就可以区分这个特定的cDNA与其他的cDNA=》

然后将乳浊液中所有的**水相抽出来**，也就是把带有标签的cDNA分子抽出来=》

cDNA分子**加接头，PCR扩增**，得到illumina文库

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-05-051837.png)

**数据构成**

一个样本一般就测几百或几千细胞，barcode种类却有3百多万，所以很少出现一个barcode对应两个细胞的情况。因此得到的数据可以通过**barcode拆分**，将测序reads回溯到每个细胞

当然，是有可能出现一个barcode对应两个甚至多个细胞的情况，这时如果按照barcode去拆分，就会将这两个或者多个细胞的reads组合成一&#x4E2A;**"pool**"。因此，为了减少pool的出现，就要在细胞混悬液制备阶段，控制原始的细胞数量

> 所以这里看到，并不是制备的细胞数越多越好。原始细胞数越少，最后的混合pool就越少，这也是符合泊松分布的。**一般来说一个样本混悬液的细胞数在1万以下比较好**

**利用UMI对reads进行简并**，就可以看到细胞reads与基因数量之间的关系，比如这样：横坐标是细胞reads数，纵坐标是基因数，reads数越多能得到的基因也就越多。一般来说一个细胞读到**30万条reads后**，基因数量随reads数增加的速度会变慢=》基因数量"**平台期"**

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-05-51843.png)

一般**一个细胞可以得到4万-8万个有效的UMI**，平均一个细胞的**一个基因有10个**UMI；

**一个细胞的一个基因**的表达量是衡量这个细胞的一个**维度**，于是几千个被测基因的表达量形成了几千个维度。如果将成千上万个细胞放在一起分析，经过降维、聚类，放到一个三维空间并加上颜色，就形成了这样的分布形式

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-05-051846.png)

然后如果将三维空间的一团细胞拿出来，放大，继续细分，就能得到这团细胞的亚型

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-05-051851.png)

### Q3：什么是barcode whitelist？

<https://kb.10xgenomics.com/hc/en-us/articles/115004506263>

就是10X试剂盒中包含的全部已知的barcode序列，在构建文库时发挥作用。例如：在Single Cell 3' and V(D)J的应用中，有约737,000 cell barcodes在whitelist中，whitelist相当于一个"标准"

例如前10个：

```
AAACCTGAGAAACCAT
AAACCTGAGAAACCGC
AAACCTGAGAAACCTA
AAACCTGAGAAACGAG
AAACCTGAGAAACGCC
AAACCTGAGAAAGTGG
AAACCTGAGAACAACT
AAACCTGAGAACAATC
AAACCTGAGAACTCGG
AAACCTGAGAACTGTA
```

可以从软件安装包中找到：`cellranger-2.1.0/cellranger-cs/2.1.0/lib/python/cellranger/barcodes/737K-august-2016.txt`

### Q4：cellranger怎样校正barcode测序错误的？

<https://kb.10xgenomics.com/hc/en-us/articles/115003822406-How-does-Cell-Ranger-correct-barcode-sequencing-errors->

1. 如果数据中的barcode也存在于whitelist中，就计算它们在数据中出现的频率
2. 如果数据中的barcode不存在于whitelist中，就找与whitelist相差**1-Hamming-distance**的barcode：首先看看现在的barcode和whitelist中的barcode的碱基质量值，对它们不同的碱基计算后验概率；如果后验概率超过0.975，就找到后验概率值最大的那个barcode，将这个barcode替换成whitelist中的barcode&#x20;

> By definition from Wikipedia, the Hamming distance between two strings of **equal length** is the number of positions at which the corresponding symbols are different. In other words, it is the **number of substitutions required** to transform one string into another. 前提是两个字符串长度相等，然后hamming的距离就是这两个字符串之间的差异数量。这里说的1-Hamming-distance应该就是数据的barcode和标准的barcode之间差一个碱基

结果产生的BAM文件中，原始未校正的barcode被打上`CR`标签，校正后的是`CB`

### Q5：利用cellranger分析SRA数据

<https://kb.10xgenomics.com/hc/en-us/articles/115003802691-How-do-I-prepare-Sequence-Read-Archive-SRA-data-from-NCBI-for-Cell-Ranger->

利用`fastq-dump`的`--split-files`参数，可以生成两个fq文件，但是命名要注意

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-05-05-051856.png)

### Q6：利用seurat检验10X Aggr整合的数据

<https://kb.10xgenomics.com/hc/en-us/articles/360004524851-How-can-I-identify-the-10x-library-sources-when-analyzing-an-aggr-matrix-using-Seurat->

另外还可以用seurat来整合10X数据：<https://satijalab.org/seurat/merge_vignette.html>


# 5.2 CellRanger篇

## 这一部分将会介绍什么?

自己从下载数据开始，实战上手CellRanger的使用

##


# 5.2.1 CellRanger实战(一)数据下载

刘小泽写于19.5.3

数据来自2018年9月的NC文章[Acquired cancer resistance to combination immunotherapy from transcriptional loss of class I HLA](https://www.nature.com/articles/s41467-018-06300-3)

文章解读在：<https://www.jianshu.com/p/b818e38f7e9c>

#### 实验设计

共有两名患者：

* 患者2586-4：

  * The **primary** patient (2586-4) received hypofractionated radiation for HLA upregulation to some but not all disease sites
  * 利用10X 3' Chromium v2.0平台建库 + Hiseq2500 "rapid run"模式 [GSE117988](https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE117988)
  * **discovery** tumor部分：After sequence alignment and filtering, **7431** tumor cells (**2243 cells before** and **5188 cells after** T cell therapy）
  * **discovery** PBMC部分：After sequence alignment and filtering, a total of **12,874** cells were analyzed \[其中包含了**四个时间点**：治疗前(Pre)，治疗后早期day +27(Early)，治疗后反应期day+37(Resp)，治疗后复发+614 (AR)]

  |                                      ID                                     |   Description   |
  | :-------------------------------------------------------------------------: | :-------------: |
  | [GSM3330559](https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSM3330559) |  Tumor Disc Pre |
  | [GSM3330560](https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSM3330560) |  Tumor Disc AR  |
  | [GSM3330561](https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSM3330561) |     PBMC Pre    |
  | [GSM3330562](https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSM3330562) | PBMC Disc Early |
  | [GSM3330563](https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSM3330563) |  PBMC Disc Resp |
  | [GSM3330564](https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSM3330564) |   PBMC Disc AR  |
* 患者9245-3：

  * The second **validation patient** (9245-3) is a 59-year-old man with metastatic MCC that had initially presented as stage IIIB disease, now metastatic at multiple sites
  * 利用10X 5' V(D)J 进行cell washing, barcoding and library prep+ NovaSeq 6000(gene expression) + Hiseq4000 (V(D)J)  [GSE118056](https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE118056)&#x20;

  |                                      ID                                     |      Description     |
  | :-------------------------------------------------------------------------: | :------------------: |
  | [GSM3317833](https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSM3317833) |  PBMC Relapse - L001 |
  | [GSM3317834](https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSM3317834) |  PBMC Relapse - L002 |
  | [GSM3317835](https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSM3317835) | Tumor Relapse - L001 |
  | [GSM3317836](https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSM3317836) | Tumor Relapse - L002 |

#### 软件环境

原始数据一般是以SRR格式存放，这个文件一般都要几个G，于是下载器首选ascp，但是直接使用ascp下载又需要配置一些参数，对于新手来说，最好是能提供一个ID，然后直接就下载，这个就需要用到**prefetch 与 ascp的组合**了

prefetch是sratools中的一个小工具，因此直接用conda下载就好

```
conda install -c daler sratoolkit
prefetch -h # 可以显示帮助文档就说明安装成功
# 如果要下载数据比如SRR文件，直接加ID号，指定输出目录就好
prefetch SRRxxxxxxx -O PATH
```

**默认情况下，prefetch是利用https方式去下载原始数据**，这个就像直接从网页下载一样，速度有一定的限制。因此我们需要先安装一款叫做"aspera"的下载工具，它是IBM旗下的商业高速文件传输软件，与NCBI和EBI有协作合同

```
wget http://download.asperasoft.com/download/sw/connect/3.7.4/aspera-connect-3.7.4.147727-linux-64.tar.gz
tar zxvf aspera-connect-3.7.4.147727-linux-64.tar.gz
#安装
bash aspera-connect-3.7.4.147727-linux-64.sh
# 然后cd到根目录下看看是不是存在了.aspera文件夹，有的话表示安装成功
cd && ls -a
# 将aspera软件加入环境变量，并激活
echo 'export PATH=~/.aspera/connect/bin:$PATH' >> ~/.bashrc
source ~/.bashrc
# 最后检查ascp是不是能用了
ascp --help
```

ascp安装成功后，prefetch就会默认将下载方式从https转移到fasp，说明开启加速模式

一般ascp没有什么问题，出问题主要是：

```
ascp: Failed to open TCP connection for SSH, exiting.

Session Stop  (Error: Failed to open TCP connection for SSH)

# 官网给出的解决办法是：https://support.asperasoft.com/hc/en-us/articles/216126918-Error-44-UDP-session-initiation-fatal-error
On many Linux systems the default firewall can be configured with iptables. You will have to allow all incoming and outgoing traffic on UDP port 33001 (or whatever your Aspera UDP port is), which you can do with the following commands:
# 使用下面这两个命令(但需要管理员权限)
# iptables -I INPUT -p tcp --dport 33001 -j ACCEPT
# iptables -I OUTPUT -p tcp --dport 33001 -j ACCEPT
```

#### 数据下载

以患者2586-4为例，所有数据都存放在GEO中

1. 打开<https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE117988> (这里注意链接是有规律的，只需要改变最后的ID号就能获取其他的GEO数据)
2. 点击SRA这里的`SRP155988` ![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-19-071912.png)
3. `send to` => `Run Selector` => `Go` ![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-19-071927.png)
4. 下载Accession List，然后就得到了一个文本文件，列出了6个SRR ID号 ![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-19-071939.png)
5. 下载代码

   ```
   wkd=/home/project/single-cell/MCC

   cd $wkd/raw
   # for patient 2586-4
   cat >SRR_Acc_List-2586-4.txt
   SRR7722937
   SRR7722938
   SRR7722939
   SRR7722940
   SRR7722941
   SRR7722942

   cat SRR_Acc_List-2586-4.txt |while read i
   do prefetch $i -O `pwd` && echo "** ${i}.sra done **"
   done
   # 一般2.6G文件下载2分钟左右
   ```
6. 下载成功会有提示

   ```
   2019-xxxxxxxx prefetch.2.9.1:  fasp download succeed
   2019-xxxxxxxx prefetch.2.9.1: 1) 'SRR7722937' was downloaded successfully
   2019-xxxxxxxx prefetch.2.9.1: 'SRR7722937' has 0 unresolved dependencies
   ** SRR7722937.sra done **
   ```

两个患者的十个样本数据下载结束后发现，SRR7722939和SRR7722942下载失败，看了一下数据源，这两个数据在`sra-sos.public`这个位置，而不是在`ncbi`

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-19-071954.png)

> 于是，可以选择**另一个途径EBI下载**

1. 进入官网<https://www.ebi.ac.uk/ena> ，搜索想下载的SRA号

   ![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-19-072006.png)
2. 选择SRR这里\[或者直接通过<https://www.ebi.ac.uk/ena/data/view/SRR7722939>修改ID]

   ![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-19-072017.png)
3. EBI有个好处就是可以直接下载fastq格式文件（左边方框），如果要下载sra就复制右边红色方框中链接 ![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-19-072027.png)
4. 然后利用这个代码下载

```
ascp -QT -l 300m -P33001 -i ~/.aspera/connect/etc/asperaweb_id_dsa.openssh era-fasp@fasp.sra.ebi.ac.uk:vol1/srr/SRR772/009/SRR7722939 ./
```


# 5.2.2 CellRanger实战(二) 使用前注意事项

刘小泽写于19.5.4-5.5

> 上次拿到了sra原始数据，接下来就要使用10X官方的软件cell ranger进行操作了，但使用前需要注意几个地方

#### 将SRA转为fastq

我们使用`fastq-dump`这款软件，它是sra-tool中的一个工具，使用conda安装即可

```
conda install -c bioconda sra-tools
```

关于这个软件，参数设置一般比较简单(虽然帮助文档写的不是那么好理解)

```
wkd=/home/project/single-cell/MCC
cd $wkd/raw/P2586-4
cat SRR_Acc_List-2586-4.txt |while read i
do
time fastq-dump --gzip --split-3 -A $i ${i}.sra && echo "** ${i}.sra to fastq done **"
done
```

其中主要使用了三个参数：

* `--gzip`将生成的结果fastq文件进行压缩
* `--split-3`其实有点复杂：首先它是分割的意思，`-3`实际上指的是分成3个文件，它诞生的时间比较早，是在1000 Genome的Phase1阶段产生的。

  <https://www.biostars.org/p/186741/>

  如果结果发现只有一个文件，说明数据不是双端(第三个文件太大会覆盖前两个)；

  如果结果有两个文件，说明是双端文件并且数据质量比较高(没有低质量的reads或者长度小于20bp的reads)；

  如果结果有三个文件，说明是双端文件，但是有的数据质量不高，存在trim的结果，第三个文件的名字一般是：`<srr_id>.fastq`， 而且文件也不大，基本可以忽略
* -A指定输出的文件名

**如果使用上面的参数`--split-3`，结果只有一个fq文件**

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-030127.png)

利用cell ranger软件分析，一般需要两个输入文件，其中一个是测序reads，另一个是UMI+Barcode文件，那么只生成一个文件是不够的，因此可以换个参数

**使用另外一个参数`--split-files`来替代`--split-3` ，就可以生成三个文件**，其中第一个文件的所有序列都是8bp，第二个文件都是26bp，第三个文件都是91bp，初步判断，第三个文件是测序reads![image-20190920110138765](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-030139.png)

> 这里可以对比下这两个参数输出的数据 ![image-20190920110146815](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-030147.png)

但是前两个呢？哪一个是UMI+Barcode？

#### 如何解释生成的这三个fastq文件

单细胞转录组数据和普通的bulk转录组还是不太一样，bulk结果一般就是R1、R2，很容易区分；10X单细胞数据比较特殊，它的测序文库中包括index、barcode、UMI和测序reads。

文章使用的是10X Genomics 3' Chromium v2.0 平台，那么就看一下它的帮助手册（<https://assets.ctfassets.net/an68im79xiti/1CnKSfa7taoQwIEe0WaA4m/8635b2c9ee86c022e731b6fb2e13fed2/CG000080_10x_Technical_Note_Base_Composition_SC3_v2_RevB.pdf> ）

**先大概了解一下10X文库组成：**

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-030154.png)

其中Read2：98那里的星号表示这个长度不是固定的，可以调整，比如文章中患者P2586-4的Read2长度就是98，而患者9245-3的Read2长度是91

**然后看看测序时每个run cycle做了什么事：**

> 利用illumina边合成变测序（sequencing by synthesis ,SBS），每一个cycle都是一个碱基，因此用cycle数可以表示测序长度

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-030203.png)

首先，1-26个cycle就是测序得到了26个碱基，先是16个Barcode碱基，然后是10个UMI碱基；

然后，27-34这8个cycle得到了8个碱基，就是i7的sample index；

最后35-132个cycle得到了98个碱基，就是转录本reads

**看下Read1、i7 index、Read2的碱基分布：**

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-030209.png)

可以看到转录本read前端有20多bp质量是存在波动的，因为5’端的前几个碱基为随机引物序列，存在一定的偏好性

**另外，index和barcode有什么区别，为什么用两个fq文件进行区分？**

找到10X官方给出的一个解答：<https://kb.10xgenomics.com/hc/en-us/articles/115002777072-How-do-I-demultiplex-by-sample-index-and-barcode->

* **i7 sample index（library barcode）**&#x662F;加到Illumina测序接头上的，保证多个测序文库可以在同一个flow-cell上或者同一个lane上进行混合测序(multiplexed)。当然可以自己指定index，但更多情况下会使用10X公司提供的index序列(bundled index sets)，针对不同项目使用的index也是不同的。不过共性就是：96孔板的每个孔中都加入了4种不同的index oligos混合(详见：<https://kb.10xgenomics.com/hc/en-us/articles/218168503-What-oligos-are-in-my-sample-index->)。![image-20190920110219008](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-030219.png) 它的作用就是在CellRanger的`mkfastq` 功能中体现出来的，它自动识别样本index名称（例如：SA-GA-A1），将具有相同4种oligo的fq文件组合在一起，表示同一个样本。它保证了一个测序lane上可以容纳多个样本

  > 然后回过头，又看了看GEO的数据记录，发现的确记录了index信息，
  >
  > ![image-20190920110226731](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-030227.png)
  >
  > 但是作者没有上传最原始的BCL文件，因此无法体验mkfastq的功能，但是官网有测试数据。这里了解大体的拆分流程就好
  >
  > ![image-20190920110232992](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-030233.png)
* **10X Barcode（Cell barcode）** 是10X特有的，用来区分GEMs，也就是对细胞做了一个标记。一般在拆分混养测序数据（demultiplexing）这个过程后进行操作，当然这也很符合原文的操作

  ![image-20190920110239553](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-030239.png)
* 在实验建库的过程中，barcoding过程(也就是GEM生成的过程)是早于indexing过程(它是PCR的最后阶段)，但是生信分析过程把这两个顺序颠倒过来了，我们需要先根据样本index，利用`mkfastq` 将reads进行demultiplex，分到各自的测序文库中去，然后对每个文库再处理barcodes信息

**UMI的作用呢？**

> 它是为处理PCR 扩增偏差而生

首先，不管是bulk RNA还是scRNA，都需要进行PCR扩增，但是不可避免有一些转录本会被扩增太多次，超过了真实表达量。当起始文库大小很小时（比如单细胞数据），就需要更多次的PCR过程，这个次数越多，引入的误差就越大

UMI就是Unique Molecular Identifier，由4-10个随机核苷酸组成，在mRNA反转录后，进入到文库中，每一个mRNA随机连上一个UMI，根据PCR结果可以计数不同的UMI，最终统计mRNA的数量。

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-030257.png)

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-030306.png)

UMI有几个要求：

* 不能是[均聚物](https://zh.wikipedia.org/zh-hans/%E5%9D%87%E8%81%9A%E7%89%A9) ，如AAAAAAAAAA
* 不能有N碱基
* 不能包含碱基质量低于10的碱基

**综上所述：raw fastq中包含以下信息**

> * **Library Barcode (Sample Index)** - Used to pool multiple samples on one sequencing lane
> * **Cell Barcode (10x Barcode)** – Used to identify the cell the read came from
> * **Unique Molecular Index (UMI)** – Used to identify reads that arise during PCR replication
> * **Sequencing Reads** – Used to identify the gene a read came from

**那么这三个文件的名称需要修改吗？**

> 我认为是需要修改的，因为命名太模糊，不容易指定文件进行下游分析。 然后看到官网给出的解答：<https://support.10xgenomics.com/single-cell-gene-expression/software/pipelines/2.0/using/fastq-input#wrongname> ，也的确说需要修改

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-030315.png)

**那么怎么改？**

肯定要批量处理，就利用下载SRA的SRR ID好了

```
# 比如，将原来的SRR7692286_1.fastq.gz改成SRR7692286_S1_L001_I1_001.fastq.gz
# 依次类推，将原来_2的改成R1，将_3改成R2
cat  SRR_Acc_List-9245-3.txt | while read i ;do (mv ${i}_1*.gz ${i}_S1_L001_I1_001.fastq.gz;mv ${i}_2*.gz ${i}_S1_L001_R1_001.fastq.gz;mv ${i}_3*.gz ${i}_S1_L001_R2_001.fastq.gz);done
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-030323.png)


# 5.2.3 CellRanger实战(三) 使用初探

刘小泽写于19.5.6

### 首先对上次改好名称的fastq数据进行质控

```
# 以P2586-4为例
mkdir -p $wkd/qc
cd $wkd/qc
find $wkd/raw/P2586-4 -name '*R1*.gz'>P2586-4-id-1.txt
find $wkd/raw/P2586-4 -name '*R2*.gz'>P2586-4-id-2.txt
cat P2586-4-id-1.txt P2586-4-id-2.txt >P2586-4-id-all.txt

cat P2586-4-id-all.txt| xargs fastqc -t 20 -o ./
```

![图1](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-030611.png)

然后利用Filezilla下载其中**SRR7722937**的R1、R2的html，打开看下

首先是R1的，这个就是16bp Barcode+10bp UMI，可以看到Phred值是比较稳定的

![图2](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-030627.png)

然后看下真实的测序数据：它的Phred值开始质量较差，中间较好，测序结束位置质量有下降，这是和测序仪的工作原理有关的

> 测序仪在刚开始进行合成反应的时候也会由于反应还不够稳定，会带来质量值的波动；碱基的合成利用的的是聚合酶化学反应，使得碱基可以从5’端向3’端合成并延伸。但合成的过程中随着链的增长，DNA聚合酶的效率会不断下降，特异性也开始变差，越到后面碱基合成的错误率就会越高）

总体上还是在Q30以上的，数据质量不错，并且没有接头序列

![图3](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-030641.png)

如果要对Fastqc结果进行详细的了解，可以去官网的帮助信息，非常的简明扼要：[\<https://www.bioinformatics.babraham.ac.uk/projects/fastqc/Help/3%20Analysis%20Modules/](https://app.gitbook.com/s/-MCv4XZpjUYDdSrsYVkw/05/5.1-cellranger-pian/%3Chttps://www.bioinformatics.babraham.ac.uk/projects/fastqc/Help/3%20Analysis%20Modules/)

### Cell Ranger的介绍

**什么是Cell Ranger？**

> 官网的说明最原汁原味： Cell Ranger is **a set of analysis pipelines** that process **Chromium single cell 3’ RNA-seq output** to **align** reads, generate gene-cell **matrices** and perform **clustering** and gene **expression** analysis.

到目前为止，它有1.0，1.1，1.2，1.3，2.0，2.1，2.2，3.0这8个版本，其中1.2版本之后的可以支持Chromium Single Cell 3' v1 and v2 试剂，最新的V3试剂需要用到3.0版本

要分析的文章中使用了两个不同的版本（2.0和2.1）去分析两个患者的数据：

![图4](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-030656.png)

**那么这个软件能干什么事？**

它主要包括四个主要基因表达分析流程：

* [**cellranger mkfastq**](https://support.10xgenomics.com/single-cell-gene-expression/software/pipelines/2.0/using/mkfastq) ： 它借鉴了Illumina的`bcl2fastq` ，可以将一个或多个lane中的混样测序样本按照index标签生成样本对应的fastq文件
* [**cellranger count**](https://support.10xgenomics.com/single-cell-gene-expression/software/pipelines/2.0/using/count) ：利用`mkfastq`生成的fq文件，进行比对(基于STAR)、过滤、UMI计数。利用细胞的barcode生成gene-barcode矩阵，然后进行样本分群、基因表达分析
* [**cellranger aggr**](https://support.10xgenomics.com/single-cell-gene-expression/software/pipelines/2.0/using/aggregate) ：接受`cellranger count`的输出数据，将同一组的不同测序样本的表达矩阵整合在一起，比如tumor组原来有4个样本，PBMC组有两个样本，现在可以使用`aggr`生成最后的tumor和PBMC两个矩阵，并且进行标准化去掉测序深度的影响
* [**cellranger reanalyze**](https://support.10xgenomics.com/single-cell-gene-expression/software/pipelines/2.0/using/reanalyze) ：接受`cellranger count`或`cellranger aggr`生成的gene-barcode矩阵，使用不同的参数进行降维、聚类

它的结果主要是包含有细胞信息的`BAM, MEX, CSV, HDF5 and HTML`文件

**相关的术语**

* **Sample：**（样本）从单一来源(比如血液、组织等)提取的细胞悬液
* **Library：**（文库）单个样本制备的10X barcode 测序文库，对应10X Chromium Controller一个run（即运行一次）的单个芯片通道
* **Sequencing Run / Flowcell**: 测序仪主要依靠flowcell（例如Hiseq4000就有2个flowcell），每运行一次就是run一次，数据会在flowcell上产出，然后这些数据又会根据flowcell上的不同lane以及lane上不同样本的index进行区分
* **以上术语的错综复杂：**&#x20;
  * 单独一个样本可以制备成多个文库，这样可以一次run就得到更多的细胞数量，而不用在单次run的单个文库中使用全部的样品，造成"过载"现象("过犹不及")
  * 单独一个样本可以跨多个flowcell测序，如果它们是在一次测序过程中产出，就可以将产出的reads合并
  * 上面说一个文库可以用多个flowcell，那么一个flowcell也可以包含多个文库，使用不同的lane上或者样本的index进行区分
  * 同一个10x 芯片的两个channels可以说是两个文库，但是两个不同芯片的同一个channel不能说是两个文库

**大体流程**

> 主要根据sample、library、flowcell的数量来定义分析的复杂程度(由浅入深)

* 一个sample、一个library、一个flowcell

  ![图5](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-030710.png)

  这是最基本的模式：只有一个生物样本，只需要制备一个文库，在一个flowcell上测序就好，通过`mkfastq`得到fastq文件后直接运行`count` 就好(具体见： [Single-Library Analysis](https://support.10xgenomics.com/single-cell-gene-expression/software/pipelines/2.0/using/count))
* 一个library，多个flowcell

  ![图6](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-030720.png)

  如果有个文库在多个flowcell上测序，可以利用 [Running Multi-Library Samples](https://support.10xgenomics.com/single-cell-gene-expression/software/pipelines/2.0/advanced/multi-library-samples) 将不同测序run的reads混合
* 一个sample，多个library

  ![图7](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-030730.png)

  如果一个样本有多个文库(比如做了技术重复)，那么每一个文库的数据应该单独跑`cellranger count` 流程，然后可以利用`aggr`整合起来， [Multi-Library Aggregation](https://support.10xgenomics.com/single-cell-gene-expression/software/pipelines/2.0/using/aggregate) ； 当然先将一个样本的多个文库组合在一起再分析也是可以的，就需要利用到 [MRO syntax](https://support.10xgenomics.com/single-cell-gene-expression/software/pipelines/2.0/advanced/multi-library-samples) (这里先做了解)
* 多个samples

  ![图8](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-030742.png)

  必须每个样本的每个文库单独跑`cellranger count` ，比如说：实验中有4个样本，每个样本由两个文库(或者两个技术重复)，那么就需要跑`count` 流程8次，最后利用`aggr`汇总在一起

### Cell Ranger的安装与配置

**系统要求**

* Linux 至少8核CPU(推荐16核)
* 64GB RAM(推荐128GB)，最低配的64G允许`cellranger aggr`合并最多250k个细胞
* 1T硬盘
* 64-bit CentOS/RedHat 6.0 or Ubuntu 12.04

**软件依赖**

大多软件是和Cell Ranger打包在一起的，但是`cellranger mkfastq` 依然需要[Illumina bcl2fastq](http://support.illumina.com/downloads/bcl2fastq_conversion_software.html) (要求版本2.17以上；如果使用Novaseq，最好用版本2.20或者更高)

**使用资源限制**

Cell Ranger默认在本地运行(或者使用`--jobmode=local`指定)，它会占用90%的空余内存以及所有空余的CPU。如果要进行资源限制，可以使用`—localmem`或者`--localcores`

> 使用`localcores`之前，应该先检查`ulimit -u`，看看服务器最大支持多少用户同时在线，因为cellranger使用一个核就会产生64个用户队列，不能超过这个限定值。例如检查`ulimit -u`为4096，那么最多设置64个核心，也就是`64 * 64 = 4096` ，才不会因为这个报错

**下载软件**

为了复现文章，我们需要用到Cell Ranger 2.0与2.1，其中2.0版本是2017年9月发布的，2.1版本是2018年2月发布的

```
# 2.0版本下载(732M)
curl -o cellranger-2.0.2.tar.gz "http://cf.10xgenomics.com/releases/cell-exp/cellranger-2.0.2.tar.gz?Expires=1557256518&Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cDovL2NmLjEweGdlbm9taWNzLmNvbS9yZWxlYXNlcy9jZWxsLWV4cC9jZWxscmFuZ2VyLTIuMC4yLnRhci5neiIsIkNvbmRpdGlvbiI6eyJEYXRlTGVzc1RoYW4iOnsiQVdTOkVwb2NoVGltZSI6MTU1NzI1NjUxOH19fV19&Signature=HoJUuPo4iTFdQgzFU1GH7uKf3uGitQxTjB6WOA9qGPlejf7tNcBPjO65WuSUZ~w8WWdeAvky-oV7XGfheY-bUr2b7QHr7jQEqc84cyU~PLvT~fYjkgC7cG7nlpbJOT~b7U~YH9amvR~SCLlyynp7scPDIA~9~keCYrIPgevTf2QyktybuSyjNTwugefOic~~XFkc9lrS~WQ9MNA1CLl4ExlQKsxWS77PEB6mwrMZXX65obDnZW9fIs3dIny6H5YoadbkgmsT52jmLien6PsG1g2jpAO90pPuHoru8LL64Q9gmB3I0nJAqi3EmrO3GKnUpHUhGb6doKmjSN6XccpmsA__&Key-Pair-Id=APKAI7S6A5RYOXBWRPDA"
# 2.1版本下载
curl -o cellranger-2.1.1.tar.gz "http://cf.10xgenomics.com/releases/cell-exp/cellranger-2.1.1.tar.gz?Expires=1557260110&Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cDovL2NmLjEweGdlbm9taWNzLmNvbS9yZWxlYXNlcy9jZWxsLWV4cC9jZWxscmFuZ2VyLTIuMS4xLnRhci5neiIsIkNvbmRpdGlvbiI6eyJEYXRlTGVzc1RoYW4iOnsiQVdTOkVwb2NoVGltZSI6MTU1NzI2MDExMH19fV19&Signature=RNQd-gTASTQhtnUSBfQWrnqo6Pyy2wDXtV5tlxkG97727GvoRhMqFXbEsz4gJl2BMckdVvW3S1tZRwRo5pmxPzmhq-8RKxf99pGqlzo84HYqhbIRkxXlIbLbj-u3PUJqo8cesWpbSVSKkS2TCNS-9GMFNieQswqMS2-DN4BqoBOJnWr7T4wlOMd9hypXWwOsW2P2fqaM-WP2ooMyo-oIxm3y9gDghXdDEP5lvHU7GCQcFGGexkdIrD6S5p8JPJ1DB5XieGrtEuP1YVp6tLMGXFoRWXS8dQLI1egWDYlOuRaiQgLIb3o5ZxBg5NpzLPP5kDHMAVzJFdBpf~~rkyNYTA__&Key-Pair-Id=APKAI7S6A5RYOXBWRPDA"
```

软件下载完，需要解压缩一段时间，因为它打包了太多的软件和资源

```
tar zxvf cellranger-2.0.2.tar.gz
```

然后添加环境变量(注意：如果之前安装过其他版本，比如我之前装过3.0，但是当前你只想用这个2.0版本，那么就**需要在`~/.bashrc`中将新安装的2.0版本路径放在3.0的下方**，因为linux是根据`$PATH`自上而下调用软件的，将新安装的路径放在`.bashrc`下方，那么在`$PATH`中显示的就是新路径在上方，**它们的顺序是相反的**)

```
# 举个例子
# 原来我的~/.bashrc中有个cell ranger 3.0
echo 'export PATH=/home/biosoft/cellranger-2.2.0:$PATH' >> ~/.bashrc
# 现在我想输入cellranger时先调用2.0版本，那就在3.0的下方写上
export PATH=/home/biosoft/cellranger-3.0:$PATH
export PATH=/home/biosoft/cellranger-2.0.2:$PATH
# 然后保存退出，激活环境变量
# 这时查看环境变量
echo $PATH
/home/biosoft/cellranger-2.0.2:/home/biosoft/cellranger-3.0
# 于是输入cellranger，给出的帮助文档就是
cellranger  (2.0.2)
Copyright (c) 2017 10x Genomics, Inc.  All rights reserved.
-------------------------------------------------------------------------------

Usage:
    cellranger mkfastq

    cellranger count
    cellranger aggr
    cellranger reanalyze
    cellranger mkloupe
    cellranger mat2csv

    cellranger mkgtf
    cellranger mkref

    cellranger vdj

    cellranger mkvdjref

    cellranger testrun
    cellranger upload
    cellranger sitecheck
# 成功切换了版本！
```

然后安装好以后，cellranger还提供了一个小工具(我认为是个有意思的地方)，让你全面了解你的linux性能，不用自己找代码。另外这些代码我们也可以借鉴，以后再用

```
$ cellranger sitecheck > sitecheck.txt
```

![图9](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-030805.png)

> 为了确保软件所有的自带流程都成功安装，可以进行一个软件自检
>
> ```
> cellranger testrun --id=tiny
> # 我使用了12个CPU，大约需要20分钟检查完
> # 如果成功完整地安装的话，最后会给出这样一个报告：
> cellranger testrun (2.0.2)
> Copyright (c) 2017 10x Genomics, Inc.  All rights reserved.
> -------------------------------------------------------------------------------
>
> Running Cell Ranger in test mode...
>
> Martian Runtime - 2.0.2-2.2.2
>
> Running preflight checks (please wait)...
> [runtime] (ready)           ID.tiny.SC_RNA_COUNTER_CS.SC_RNA_COUNTER.SETUP_CHUNKS
> [runtime] (split_complete)  ID.tiny.SC_RNA_COUNTER_CS.SC_RNA_COUNTER.SETUP_CHUNKS
> ...
>
> Pipestance completed successfully!
> ```

**参考序列下载(1.2.0版本，2016年12月发布)**

> 文章比对到了hg38(基于ensembl数据库)，不能直接使用网站下载的基因组与注释文件，需要过滤一下

如果直接下载的话一共11GB，当然这里面包含了基因组、注释源文件，以及cell ranger自己利用`mkgtf`构建的注释和`mkref`构建的基因组

```
curl -O http://cf.10xgenomics.com/supp/cell-exp/refdata-cellranger-GRCh38-1.2.0.tar.gz
# 然后解压
tar -xzvf refdata-cellranger-GRCh38-1.2.0.tar.gz
```

![图10](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-030835.png)

如果要自己尝试构建，可以使用

```
# 下载基因组
wget ftp://ftp.ensembl.org/pub/release-84/fasta/homo_sapiens/dna/Homo_sapiens.GRCh38.dna.primary_assembly.fa.gz
gunzip Homo_sapiens.GRCh38.dna.primary_assembly.fa.gz
# 下载注释
wget ftp://ftp.ensembl.org/pub/release-84/gtf/homo_sapiens/Homo_sapiens.GRCh38.84.gtf.gz
gunzip Homo_sapiens.GRCh38.84.gtf.gz
# 软件构建注释(可以自选attribute)
# mkgtf <input_gtf> <output_gtf> [--attribute=KEY:VALUE...]
cellranger mkgtf Homo_sapiens.GRCh38.84.gtf Homo_sapiens.GRCh38.84.filtered.gtf \
                 --attribute=gene_biotype:protein_coding \
                 --attribute=gene_biotype:lincRNA \
                 --attribute=gene_biotype:antisense \
                 --attribute=gene_biotype:IG_LV_gene \
                 --attribute=gene_biotype:IG_V_gene \
                 --attribute=gene_biotype:IG_V_pseudogene \
                 --attribute=gene_biotype:IG_D_gene \
                 --attribute=gene_biotype:IG_J_gene \
                 --attribute=gene_biotype:IG_J_pseudogene \
                 --attribute=gene_biotype:IG_C_gene \
                 --attribute=gene_biotype:IG_C_pseudogene \
                 --attribute=gene_biotype:TR_V_gene \
                 --attribute=gene_biotype:TR_V_pseudogene \
                 --attribute=gene_biotype:TR_D_gene \
                 --attribute=gene_biotype:TR_J_gene \
                 --attribute=gene_biotype:TR_J_pseudogene \
                 --attribute=gene_biotype:TR_C_gene

# 我看到这里写了这么多gene_biotype（也就是基因的生物类型）的键值对，不禁好奇，GTF中存在多少种基因类型以及对应的数目？
$ cat Homo_sapiens.GRCh38.84.filtered.gtf  |grep -v "#" |awk -v FS='gene_biotype ' 'NF>1{print $2}'|awk -F ";" '{print $1}'|sort | uniq -c

    213 "IG_C_gene"
     33 "IG_C_pseudogene"
    152 "IG_D_gene"
     76 "IG_J_gene"
      9 "IG_J_pseudogene"
   1209 "IG_V_gene"
    646 "IG_V_pseudogene"
    125 "TR_C_gene"
     16 "TR_D_gene"
    316 "TR_J_gene"
     12 "TR_J_pseudogene"
    848 "TR_V_gene"
    110 "TR_V_pseudogene"
  45662 "antisense"
  58181 "lincRNA"
2337766 "protein_coding"


# 软件利用构建好的注释，去构建需要的基因组
cellranger mkref --genome=GRCh38 \
                 --fasta=Homo_sapiens.GRCh38.dna.primary_assembly.fa \
                 --genes=Homo_sapiens.GRCh38.84.filtered.gtf \
                 --ref-version=1.2.0
```


# 5.2.4 CellRanger实战(四)流程概览

刘小泽写于19.5.7

> 这一篇主要看流程，不涉及真实数据展示

总的来说，Cell Ranger主要的流程有：拆分数据 mkfastq、细胞定量 count、定量组合 aggr、调参reanalyze，还有一些小工具比如mkref、mkgtf、upload、sitecheck、mat2csv、vdj、mkvdjref、testrun

### 首先是mkfastq 拆分数据

> 虽然这里用不到(因为我们下载的就是fastq数据)，但是为了流程的完整还是要学习一下

**目的：**&#x5C06;每个flowcell 的Illumina sequencer's base call files (BCLs)转为fastq文件

**特色：** 它借鉴了Illumina出品的`bcl2fastq`，另外增加了：

* 将10X 样本index名称与四种寡核苷酸对应起来，比如A1孔是样本`SI-GA-A1`，然后对应的寡核苷酸是`GGTTTACT, CTAAACGG, TCGGCGTC, and AACCGTAA` ，那么程序就会去index文件中将存在这四种寡核苷酸的fastq组合到A1这个样本
* 提供质控结果，包括barcode 质量、总体测序质量如Q30、R1和R2的Q30碱基占比、测序reads数等
* 可以使用10X简化版的样本信息表

它的示意流程：

![图1](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-031018.png)

**两种使用方式：**

```
# 第一种
$ cellranger mkfastq --id=bcl \
                     --run=/path/to/bcl \
                     --samplesheet=samplesheet-1.2.0.csv
# 第二种
$ cellranger mkfastq --id=bcl \
                     --run=/path/to/bcl \
                     --csv=simple-1.2.0.csv
# 其中id指定输出目录的名称，run指的是下机的原始BCL文件目录
# 重要的就是测序lane、样本名称、index等信息
```

**samplesheet.csv文件**就是illumina常规使用的，类似下面这种。它除了需要指定各种ID、name之外，**还要根据不同的试剂盒版本调整`[Reads]`长度**

> V2试剂盒R1序列长度为26bp(包括16bp的barcode+10bp的UMI)，R2为98bp； V3试剂盒R1序列长度为28bp(包括16bp的barcode+12bp的UMI)，R2为91bp

![图2](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-031027.png)

还有一种10X定制的简单化的csv文件，例如：

```
Lane,Sample,Index
1,test_sample,SI-GA-A3
# 其中第一列指定lane ID，第二列是样本名称，第三列是index名称
```

使用**简化版的这个文件，可以识别使用的试剂盒版本**，然后自行调整reads的长度信息

> 最后的结果就是三个文件：I1序列文件以及两个测序文件R1、R2

目录结构如下：

```
- tiny-bcl/outs/fastq_path/bcl/
  - Sample1
    - Sample1_S1_L001_I1_001.fastq.gz
    - Sample1_S1_L001_R1_001.fastq.gz
    - Sample1_S1_L001_R2_001.fastq.gz
```

自己分析的数据也要改成这种结构存放，方便后续分析

### 小Tip--指定fastq文件位置

> 后续分析需要指定fastq位置，但是这些fastq文件可以由`cellranger mkfastq`得到，也可以利用s [Illumina's](https://support.10xgenomics.com/single-cell-gene-expression/software/pipelines/2.0/using/bcl2fastq-direct) `bcl2fastq` 、公共数据、[10X](https://support.10xgenomics.com/docs/bamtofastq)的`bamtofastq` ,每种情况可能得到的fastq存放位置是不同的，那么如何根据不同情况进行指定呢？

**第一种情况：**

利用`mkfastq`或者`bcl2fastq`生成的文件，大概长这样

![图3](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-031037.png)

```
# 会有这几种选择方式[注意几种参数的设置]
# 1.所有mkfastq生成的样本
--fastqs=MKFASTQ_ID/outs/fastq_path
# 2. 多个flowcell生成的所有样本
--fastqs=MKFASTQ_ID/outs/fastq_path1,MKFASTQ_ID/outs/fastq_path2
# 3.所有bcl2fastq 生成的样本
--fastqs=/PATH/TO/bcl2fastq_output
# 4. 所有lanes上的test_sample1样本
--fastqs=MKFASTQ_ID/outs/fastq_path \
--sample=test_sample1
# 5. lane1上的test_sample1样本
--fastqs=MKFASTQ_ID/outs/fastq_path \
--sample=test_sample1 \
--lanes=1
# 6. 将test_sample1和test_sample2各自进行操作
--fastqs=MKFASTQ_ID/outs/fastq_path \
--sample=test_sample1,test_sample2
```

其实从上面的各种设置也能看出来，一开始的样本命名规则是非常重要的

**第二种情况：**

也是利用`mkfastq`或者`bcl2fastq`生成的文件，但是同一个样本的数据放在不同的目录

![图4](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-031046.png)

```
# 1. 将所有SI-GA-A1样本的reads组合
--fastqs=MKFASTQ_ID/outs/fastq_path \
--sample=SI-GA-A1_1,SI-GA-A1_2,SI-GA-A1_3,SI-GA-A1_4
# 2. 只处理SI-GA-A1样本的第一个index样本
--fastqs=MKFASTQ_ID/outs/fastq_path \
--sample=SI-GA-A1_1
```

**第三种情况：**

也是利用`mkfastq`或者`bcl2fastq`生成的文件，但和Reports、Stats在同一个目录

![图5](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-031054.png)

```
# 1. mkfastq得到的所有样本
--fastqs=MKFASTQ_ID/outs/fastq_path
# 2. bcl2fastq得到的所有样本
--fastqs=/PATH/TO/bcl2fastq_output
# 3. test_sample样本的所有lanes
--fastqs=MKFASTQ_ID/outs/fastq_path \
--sample=test_sample
# 4. test_sample样本的lane1
--fastqs=MKFASTQ_ID/outs/fastq_path \
--sample=test_sample \
--lanes=1
```

**第四种情况：**

使用 `mkfastq` or `bcl2fastq` 得到的fastq文件和Report、Stats不在同一个目录，但命名方式与之前一样，这个目录中只能看到fastq文件

![图6](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-031102.png)

```
# 1.处理所有样本
--fastqs=/PATH/TO/PROJECT_FOLDER
# 2. 处理Mysample样本的所有lanes的数据
--fastqs=/PATH/TO/PROJECT_FOLDER \
--sample=MySample
# 3. 只处理Mysample样本的lane1数据
--fastqs=/PATH/TO/PROJECT_FOLDER \
--sample=MySample \
--lanes=1
```

**第五种情况：**

fastq命名方式变了，类似于这样：

![图7](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-031114.png)

它一般是从`demux`流程中拆分出来的数据，但是目前被`mkfastq`取代，没有好的方法，需要知道样本相关的index或者oligos

```
# 1.所有样本
--fastqs=/PATH/TO/PROJECT_FOLDER
# 2.所有SI-GA-A1样本
--fastqs=/PATH/TO/PROJECT_FOLDER \
--indices=SI-GA-A1
# 3.所有SI-GA-A1样本的lane1数据
--fastqs=/PATH/TO/PROJECT_FOLDER \
--indices=SI-GA-A1 \
--lanes=1
# 4.利用oligo
--fastqs=/PATH/TO/PROJECT_FOLDER \
--indices=AACCGTAA,CTAAACGG,GGTTTACT,TCGGCGTC
```

**第六种情况：**

数据命名与上面完全不同，因此需要自己重命名，方式就是

```
# 这个在单细胞实战(二)中介绍过
[Sample Name]_S1_L00[Lane Number]_[Read Type]_001.fastq.gz
# 其中Read Type
# I1: Sample index read (optional)
# R1: Read 1
# R2: Read 2
```

![图8](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-031123.png)

分析时就可以直接调用了

```
# 1.所有样本
--fastqs=/PATH/TO/PROJECT_FOLDER
# 2. 某个样本的所有lanes数据
--fastqs=/PATH/TO/PROJECT_FOLDER \
--sample=SAMPLENAME
# 3. 某个样本的某个lane
--sample=SAMPLENAME \
--fastqs=/PATH/TO/PROJECT_FOLDER \
--lanes=1
```

### 然后是count 细胞定量

这个过程是最重要的，它完成细胞与基因的定量，它将比对、质控、定量都包装了起来，内部流程很多，但使用很简单

**先学会使用**

每个版本要求的参数是不同的，尤其是V2与V3版本存在较大差异，这里先对V2进行了解

基本上自己需要输入的参数是：

```
# 这是示例，不是真实数据 #
cellranger count --id=sample345 \
                   --transcriptome=/opt/refdata-cellranger-GRCh38-1.2.0 \
                   --fastqs=/home/scRNA/runs/HAWT7ADXX/outs/fastq_path \
                   --sample=mysample \
                   --expect-cells=1000 \
                   --nosecondary
# id指定输出文件存放目录名
# transcriptome指定与CellRanger兼容的参考基因组
# fastqs指定mkfastq或者自定义的测序文件
# sample要和fastq文件的前缀中的sample保持一致，作为软件识别的标志
# expect-cells指定复现的细胞数量，这个要和实验设计结合起来
# nosecondary 只获得表达矩阵，不进行后续的降维、聚类和可视化分析(因为后期会自行用R包去做)
```

**它的输出文件有很多**

```
Outputs:
- Run summary HTML:                      /opt/sample345/outs/web_summary.html
- Run summary CSV:                       /opt/sample345/outs/metrics_summary.csv
- BAM:                                   /opt/sample345/outs/possorted_genome_bam.bam
- BAM index:                             /opt/sample345/outs/possorted_genome_bam.bam.bai
- Filtered gene-barcode matrices MEX:    /opt/sample345/outs/filtered_gene_bc_matrices
- Filtered gene-barcode matrices HDF5:   /opt/sample345/outs/filtered_gene_bc_matrices_h5.h5
- Unfiltered gene-barcode matrices MEX:  /opt/sample345/outs/raw_gene_bc_matrices
- Unfiltered gene-barcode matrices HDF5: /opt/sample345/outs/raw_gene_bc_matrices_h5.h5
- Secondary analysis output CSV:         /opt/sample345/outs/analysis
- Per-molecule read information:         /opt/sample345/outs/molecule_info.h5
- Loupe Cell Browser file:               /opt/sample345/outs/cloupe.cloupe

Pipestance completed successfully!
```

从上到下依次来看：

* web\_summary.html：官方说明 [summary HTML file](https://support.10xgenomics.com/single-cell-gene-expression/software/pipelines/2.0/output/summary)&#x20;
* metrics\_summary.csv：CSV格式数据摘要
* possorted\_genome\_bam.bam：比对文件
* possorted\_genome\_bam.bam.bai：索引文件
* filtered\_gene\_bc\_matrices：**是重要的一个目录**，下面又包含了 barcodes.tsv.gz、features.tsv.gz、matrix.mtx.gz，是**下游Seurat、Scater、Monocle等分析的输入文件**
* filtered\_feature\_bc\_matrix.h5：过滤掉的barcode信息HDF5 format
* raw\_feature\_bc\_matrix：原始barcode信息
* raw\_feature\_bc\_matrix.h5：原始barcode信息HDF5 format
* analysis：数据分析目录，下面又包含聚类clustering（有graph-based & k-means）、差异分析diffexp、主成分线性降维分析pca、非线性降维tsne
* molecule\_info.h5：下面进行**aggregate使用的文件**
* cloupe.cloupe：官方可视化工具Loupe Cell Browser 输入文件

### **一些内置软件和算法**

**基因组比对—是否在外显子？**

利用了 [STAR](https://github.com/alexdobin/STAR)比对工具，这款比对工具比对速度快，灵敏度高，是ENCODE、GATK推荐使用的工具，允许基因的可变剪切。比对完之后，利用GTF文件将reads溯源回外显子区、内含子区、基因间区：如果一条read的50%以上与外显子有交集，那么就认为它在外显区；如果不在外显子区，与内含子有交集，那么就认为它在内含子区；与外显子、内含子都没有交集，那么就认为在基因间区

**MAPQ 辅助判断—在外显子的正确率有多少？**

如果reads比对到了一个外显子区，同时也比对到了1个或多个的非外显子区，更相信它在外显子区，然后看MAPQ值，值越大越可信，如果MAPQ的值为255的话，那么就可以非常确定它比对到了外显子区

> MAPQ即mapping quality，告诉我们这个read比对到参考基因组上某个位置的可信度，它的公式是：`-10logP(error)`，如果这个值大于30就认为比对发生错误的概率是千分之一

**转录组比对—是否特异比对？**

如果上面得到的外显子区域reads同时比对上有注释转录本上的外显子，并且在同一条链上，那么认为这个reads也比对到了转录组；如果只比对到单个基因的注释信息，那么认为它是特异比对到转录组的(uniquely /confidently mapped )，这样的reads才会拿来做接下来的UMI 计数

**重点和难点在于自主构建参考信息**

Cell Ranger为比对和定量提供了参考基因组及注释 [pre-built human (hg19, GRCh38), mouse (mm10), and ercc92 reference packages](https://support.10xgenomics.com/single-cell-gene-expression/software/downloads/latest)

但是很多时候，我们需要根据自己的需要，自定义一套参考信息，但需要注意以下问题：

* 参考序列只能有很少的 overlapping gene annotations，因为reads比对到多个基因会导致流程检测的分子数更少(它只要uniquely mapped的结果)
* FASTA与GTF比对和STAR兼容，GTF文件的第三列（feature type）必须有exon，过滤后的GTF只包含有注释的基因类型

**首先利用mkgtf过滤GTF文件**

先从 ENSEMBL或UCSC上下载，然后使用`mkgtf`

```
cellranger mkgtf input.gtf output.gtf --attribute=key:allowable_value
# 其中键值对可以指定多个，比如
$ cellranger mkgtf Homo_sapiens.GRCh38.ensembl.gtf Homo_sapiens.GRCh38.ensembl.filtered.gtf \
                   --attribute=gene_biotype:protein_coding \
                   --attribute=gene_biotype:lincRNA \
                   --attribute=gene_biotype:antisense \
                   --attribute=gene_biotype:IG_LV_gene \
                   --attribute=gene_biotype:IG_V_gene \
                   --attribute=gene_biotype:IG_V_pseudogene \
                   --attribute=gene_biotype:IG_D_gene \
                   --attribute=gene_biotype:IG_J_gene \
                   --attribute=gene_biotype:IG_J_pseudogene \
                   --attribute=gene_biotype:IG_C_gene \
                   --attribute=gene_biotype:IG_C_pseudogene \
                   --attribute=gene_biotype:TR_V_gene \
                   --attribute=gene_biotype:TR_V_pseudogene \
                   --attribute=gene_biotype:TR_D_gene \
                   --attribute=gene_biotype:TR_J_gene \
                   --attribute=gene_biotype:TR_J_pseudogene \
                   --attribute=gene_biotype:TR_C_gene
# 这样得到的Homo_sapiens.GRCh38.ensembl.filtered.gtf结果中就不包含gene_biotype:pseudogene这部分
```

**然后利用mkref构建参考索引**

```
# 基本使用(单个物种)
cellranger mkref --genome=hg19 --fasta=hg19.fa --genes=hg19-filtered-ensembl.gtf
# 可以使用--nthreads使用多线程加速
# 得到的输出结果(保存在--genome名称的目录中)
ls hg19
fasta/  genes/  pickle/  reference.json  star/

# 如果对于多个物种组合(本文的数据其实就应该这样组合起来)
cellranger mkref --genome=hg19 --fasta=hg19.fa --genes=hg19-filtered-ensembl.gtf \
                   --genome=mm10 --fasta=mm10.fa --genes=mm10-filtered-ensembl.gtf
# 得到的结果
 ls hg19_and_mm10
 fasta/  genes/  pickle/  reference.json  star/
```

### **如果要增加基因信息**

参考链接：<https://kb.10xgenomics.com/hc/en-us/articles/115003327112-How-can-we-add-genes-to-a-reference-package-for-Cell-Ranger->

第一步，在`fasta/genome.fa`的FASTA基础上增加序列信息；

第二步，在`genes/genes.gtf`的GTF基础上增加注释信息，注意格式

```
# 每一行有9列tab分隔信息
# 第一列：Chromosome 指定基因组上染色体或contig位置
# 第二列：Source 这个用处不大
# 第三列：Feature CellRanger软件只取exon的部分
# 第四列：Start 起始位点(1-based)
# 第五列：End 终止位点(1-based)
# 第六列：Score 这个用处不大，建议用"."表示
# 第七列：Strand feature信息在基因组的+或-链
# 第八列：Frame 用处不大，建议“.”
# 第九列：分号分隔的键值对，重点是transcript_id 和gene_id。gene_name可选
例如：
mylocus    annotation    exon    100    200    .    +    .    gene_id "mygene"; transcript_id "mygene";
```

第三步，使用`cellranger mkref`运行更新一下

**P.S. 最后得到的参考信息(包括参考基因组、注释信息)文件结构如下：**

```
# 这是官网下载的hg38数据
refdata-cellranger-GRCh38-1.2.0
|-- [ 222]  README.BEFORE.MODIFYING
|-- [4.0K]  fasta
|   `-- [2.9G]  genome.fa
|-- [4.0K]  genes
|   `-- [1.3G]  genes.gtf
|-- [4.0K]  pickle
|   `-- [ 58M]  genes.pickle
|-- [ 424]  reference.json
|-- [4.0K]  star
|   |-- [3.0G]  Genome
|   |-- [8.0G]  SA
|   |-- [1.5G]  SAindex
|   |-- [1.2K]  chrLength.txt
|   |-- [1.9K]  chrName.txt
|   |-- [3.0K]  chrNameLength.txt
|   |-- [2.1K]  chrStart.txt
|   |-- [ 37M]  exonGeTrInfo.tab
|   |-- [ 15M]  exonInfo.tab
|   |-- [526K]  geneInfo.tab
|   |-- [ 909]  genomeParameters.txt
|   |-- [9.1M]  sjdbInfo.txt
|   |-- [7.1M]  sjdbList.fromGTF.out.tab
|   |-- [7.1M]  sjdbList.out.tab
|   `-- [9.4M]  transcriptInfo.tab
`-- [   6]  version

4 directories, 21 files
```

### 多个文库的整合 aggr

当处理多个生物学样本或者一个样本存在多个重复/文库时，最好的操作就是先分别对每个文库进行单独的count定量，然后将定量结果利用`aggr`组合起来

**第一步 得到count结果**

例如现在分别进行3个定量流程

```
$ cellranger count --id=LV123 ...
... wait for pipeline to finish ...
$ cellranger count --id=LB456 ...
... wait for pipeline to finish ...
$ cellranger count --id=LP789 ...
... wait for pipeline to finish ...
```

**第二步 构建Aggregation CSV**

就像这样：

```
# AGG123_libraries.csv
library_id,molecule_h5
LV123,/opt/runs/LV123/outs/molecule_info.h5
LB456,/opt/runs/LB456/outs/molecule_info.h5
LP789,/opt/runs/LP789/outs/molecule_info.h5
# 其中
# molecule_h5：文件molecule_info.h5 file的路径
```

**第三步 运行aggr**

```
cellranger aggr --id=AGG123 \
                  --csv=AGG123_libraries.csv \
                  --normalize=mapped
# 结果输出到AGG123这个目录中
```

至于最后的 **reanalyze** ，这个属于定制化分析，这里暂时不做探讨，日后待标准化流程构建起来，再补充这一部分


# 5.2.5 CellRanger实战(五) 理解count输出的结果

刘小泽写于19.5.13

> CellRanger count出来的结果，怎么看？这次是作为了解的内容

Cellranger的结果力求与常规分析的结果格式相同，比如它生成的 [barcoded BAM files](https://support.10xgenomics.com/single-cell-gene-expression/software/pipelines/2.0/output/bam) 也可以放到IGV中查看比对质量

count结果一般放在`out`目录下，主要有summary和analysis两大类，包含以下几项：

![图1](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-031236.png)

#### Summary

> 这是一个html格式的文件，直接下载到本地打开

打开时就能对数据进行一个判断，**网页顶端颜色**显示为黄色或者红色说明数据存在异常

![图2](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-031243.png)

然后**点击Details**，可以看到为何数据出错：

![图3](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-031250.png)

结果部分包括实验捕获的细胞数目、检测到的基因数目、测序数据的产出与质量统计、参考基因组的比对情况

(第一张为V2试剂的结果)

![图4](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-031257.png)

(第二张为V3试剂的结果)

![图5](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-031304.png)

**几个指标可以关注一下：**

* 左上部分中，包括了reads数、barcodes数、UMI、index、Q30等统计值
* 左下是reads比对的比例，包括基因间区、外显子、内含子，如果比对率太低(一般认为外显子的比对率要在60%以上)
* 右上图是利用barcodes上的UMI标签分布来估计细胞数，绿/蓝色表示细胞，灰色表示背景，其中Y轴表示每个barcode对应的UMI数量，X轴是一定数量的UMI序列所对应barcode的数量，比如上图中有1000个barcodes含有10k个UMI，**细胞过滤**就是通过这个图来展示的。

  首先明确，barcode用来区分细胞，UMI用来区分转录本；其次，barcodes数量时要大于细胞数量的（以保证每个细胞都会有barcode来进行区分）如果图线陡降说明

> 下面👇是在对原文数据进行count时遇到的一个问题，记录下来

#### 原文的小坑

其实有了参考信息与原始序列，跑一个count命令并不难，但是对于这篇文章来讲，有一个小坑需要注意：看原文的方法描述

![图6](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-031313.png)

原文是需要将**hg38和Merkel cell polyomavirus, MCPyV(默克尔细胞多瘤病毒)**&#x7684;基因组共同作为参考序列去比对，基本操作流程类似这样：

![图7](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-031534.png)

其中，hg38的fasta与gtf获取比较容易，直接下载即可(具体见"单细胞实战—四")，但是作者还用到了这个MCPyV，它的基因组也是比较容易获得<https://www.ncbi.nlm.nih.gov/nuccore/NC_010277，但是**gtf怎么获得**是个问题，作者给出的解答是：需要用基因组fasta自己生成gtf文件>

![图8](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-031323.png)

这个思路很重要，但是**如何将基因组fasta转为gtf**，还是个问题，于是本着"自己的锅自己背"的原则，又去问了作者

![图9](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-031330.png)

本以为人家不会给回信息，因为问的问题好像有点和他们文章没什么关系了，但他真的回信了并且很负责告诉我："他也不会！"，解决办法就是：**手动构建一个**

> 其实这个事情可以告诉我们，复现文章中遇到问题时，直接和原作者沟通是最有效的方式（这是我第一次和一作直接进行交流）。之前的时间都是自己在摸不着头脑地进行各种尝试，然后找代码，结果都不可以成功

**先把基因组序列下载下来**

![图10](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-031337.png)

**然后根据genbank的序列信息，造出5个CDS的注释信息**

![图11](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-031344.png)

**基本就按照这个样子来就好，不需要很复杂**

需要注意的是，cellranger只能识别exon，所以我们也要这样设计

![图12](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-09-20-031350.png)

```
# 每一行有9列tab分隔信息
# 第一列：Chromosome 指定基因组上染色体或contig位置
# 第二列：Source 这个用处不大
# 第三列：Feature CellRanger软件只取exon的部分
# 第四列：Start 起始位点(1-based)
# 第五列：End 终止位点(1-based)
# 第六列：Score 这个用处不大，建议用"."表示
# 第七列：Strand feature信息在基因组的+或-链
# 第八列：Frame 用处不大，建议“.”
# 第九列：分号分隔的键值对，重点是transcript_id 和gene_id。gene_name可选
ADE45414.1_1     Gnomon  exon    465    1190    .       -       .       gene_id "1"; transcript_id "1.1";
ADE45415.1_2     Gnomon  exon    1156   2427    .       -       .       gene_id "1"; transcript_id "1.1";
ADE45416.1_3     Gnomon  exon    2503   4722    .       -       .       gene_id "1"; transcript_id "1.1";
ADE45416.1_3     Gnomon  exon    5154   5387    .       -       .       gene_id "1"; transcript_id "1.1";
ADE45417.1_4     Gnomon  exon    4827   5387    .       -       .       gene_id "1"; transcript_id "1.1";
```

但是自己构建时，**一定要注意使用tab分隔**，即使看上去像也不可信，检查的方法有两个：

一个是直接运行`cellranger mkgtf` 看是否报错，可能会提示：GTF的行数不对；

另一个是直接检查：`awk -F '\t' '{print NF}' mcv.gtf`，如果显示1，那么说明没有tab分隔，而是用的多个空格，利用sed可以把这些空格替换成tab：

`sed 's/ \+ /\t/g' inputfile > outputfile`


# 5.3 Seurat的使用

## 这一部分将会介绍什么?

目前主流的还是Seurat 3版本（2019-04-16 release），不过还是会介绍一下版本2、3之间的差异；

另外于2020-10-20更新了 Seurat 4.0 Beta版本，相信未来4.0的功能会更加强大


# 5.3.1 Seurat V3 | 实战之2700 PBMCs分析

刘小泽写于19.7.18、28

> &#x20;官方总共有7个数据集，涵盖了新版本的不同亮点 这一次跟着教程<https://satijalab.org/seurat/v3.0/pbmc3k_tutorial.html>\
> 走一遍最简单的教程，记录自己的学习轨迹&#x20;

#### 前言

这个数据集是10X放出的2700个外周血单核细胞(PBMC，Peripheral Blood Mononuclear Cells )公共数据，使用 Illumina NextSeq 500测序，原始数据在：<https://s3-us-west-2.amazonaws.com/10x.files/samples/cell/pbmc3k/pbmc3k_filtered_gene_bc_matrices.tar.gz>

这个教程更新于2019-6-24，它会做这么几件事：QC and data filtration, calculation of high-variance genes, dimensional reduction, graph-based clustering, and the identification of cluster markers.

#### 从读取数据开始

10X的数据可以使用`Read10X`这个函数，会返回一个UMI count矩阵，其中的每个值表示每个基因(行表示)在每个细胞(列表示)的分子数量

**加载PBMC数据**

```r
pbmc.data <- Read10X(data.dir = "filtered_gene_bc_matrices/hg19/")
```

**这个矩阵长什么样？和我们平常见到的bulk转录组矩阵一样吗？**

找几个基因，看看前30个细胞的情况

```r
> pbmc.data[c("CD3D", "TCL1A", "MS4A1"), 1:30]

3 x 30 sparse Matrix of class "dgCMatrix"
   [[ suppressing 30 column names ‘AAACATACAACCAC’, ‘AAACATTGAGCTAC’, ‘AAACATTGATCAGC’ ... ]]

CD3D  4 . 10 . . 1 2 3 1 . . 2 7 1 . . 1 3 . 2  3 . . . . . 3 4 1 5
TCL1A . .  . . . . . . 1 . . . . . . . . . . .  . 1 . . . . . . . .
MS4A1 . 6  . . . . . . 1 1 1 . . . . . . . . . 36 1 2 . . 2 . . . .
```

从这个结果可以得出两个结论：它是`sparse Matrix`；它很杂乱

但其实仔细看，这个`.`就表示空着，数值为0，也就是没有检测到该分子。因为单细胞数据和bulk转录组数据还不太一样，scRNA的大部分数值都是0，原因一是由于建库时细胞的起始反应模板浓度就很低；二是测序存在dropout现象(本来基因在这个细胞有表达量，但没有测到)。

这里Seurat采用了一种聪明的再现方式，比原来用0表示的矩阵**大大减小了空间占用**，可以对比一下：

```r
dense.size <- object.size(as.matrix(pbmc.data))
dense.size # 709548272 bytes

sparse.size <- object.size(pbmc.data)
sparse.size # 29861992 bytes

dense.size/sparse.size #空间缩小23.8倍
```

**然后利用这个矩阵构建Seurat对象**

这个对象是一个容器，里面装了数据(比如表达矩阵)和分析结果(比如PCA、聚类)。另外关于这个对象的详细解释，见：<https://github.com/satijalab/seurat/wiki/Seurat#slots，包装了许多的接口>

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-08-21-050347.png)

```r
# 用原始数据（非标准化）先构建一个对象
pbmc <- CreateSeuratObject(counts = pbmc.data, project = "pbmc3k", min.cells = 3, min.features = 200)

> pbmc
An object of class Seurat 
13714 features across 2700 samples within 1 assay 
Active assay: RNA (13714 features)
```

发现这里有个`active assay`，它其中存的就是表达矩阵，用`pbmc[["RNA"]]@counts`就可以访问

> 使用两个中括号是对assay的快速访问，例如访问metadata：`pbmc[[]]`就如同`object@meta.data`

另外看上面的图，seurat对象还有很多信息：

```r
# 可以利用str来查看
> str(pbmc)
Formal class 'Seurat' [package "Seurat"] with 12 slots
  ..@ assays      :List of 1
  .. ..$ RNA:Formal class 'Assay' [package "Seurat"] with 7 slots
  .. .. .. ..@ counts       :Formal class 'dgCMatrix' [package "Matrix"] with 6 slots
  .. .. .. 
# 后面还有很多
# 如果要查看其他的信息，可以用@，例如
> pbmc@version
[1] ‘3.0.0.9000’
```

#### 预处理阶段

标准流程包括了根据QC 结果进行的细胞选择和过滤，标准化过程，检测显著差异基因

**质控（QC）及筛选细胞**

这篇文章：<https://www.ncbi.nlm.nih.gov/pmc/articles/PMC4758103/介绍了一些QC的标准>

例如：

* 检测每个细胞中检测到的unique genes数量(这种情况，一般低质量的细胞或空的液滴"droplet"中基因数量也会较少；如果一个液滴中有两个细胞"doublets"或者存在多个细胞"multiplets"，这样会导致检测到的基因数量出奇的高)
* 利用细胞中检测到的molecules总量也可以(它的结果和unique genes方法高度相关)
* 比对到线粒体基因组的reads数量，因为一般低质量或死亡的细胞中会广泛存在线粒体基因组污染；可以利用`PercentageFeatureSet`函数计算，顾名思义这个函数会计算来自某一个feature的reads count数量，需要做的就是挑出来以`MT-`开头的feature对应的所有基因。

  ```r
  # 质控筛选, [[符号可以在pbmc这个对象中添加metadata，利用这个方法还可以挑出其他的feature
  pbmc[["percent.mt"]] <- PercentageFeatureSet(pbmc, pattern = "^MT-")
  # 检查下metadata
  > head(pbmc@meta.data, 3)
                 orig.ident nCount_RNA nFeature_RNA percent.mito percent.mt
  AAACATACAACCAC   10X_PBMC       2419          779  0.030177759  3.0177759
  AAACATTGAGCTAC   10X_PBMC       4903         1352  0.037935958  3.7935958
  AAACATTGATCAGC   10X_PBMC       3147         1129  0.008897363  0.8897363
  # 绘制多个feature指标
  VlnPlot(pbmc, features = c("nFeature_RNA", "nCount_RNA", "percent.mt"), ncol = 3)
  ```

  <img src="https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-08-21-050355.png" alt="" data-size="original">

  这张图可以给出的提示就是**过滤信息**：过滤的时候可以过滤掉feature大于2500和小于200的(就是避免前面所说基因数过多/过少的情况)；然后线粒体占比图显示大部分都集中在5%以下，因此超过5%的可以过滤掉

  ```r
  # 另外可以用一个散点图（FeatureScatter）来绘制两组feature指标的相关性，最后组合在一起（CombinePlots）
  plot1 <- FeatureScatter(pbmc, feature1 = "nCount_RNA", feature2 = "percent.mt")
  plot2 <- FeatureScatter(pbmc, feature1 = "nCount_RNA", feature2 = "nFeature_RNA")
  CombinePlots(plots = list(plot1, plot2))
  ```

  ![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-08-21-050359.png)

  这个图给出的意思是：(左边👈)有表达量的reads和在线粒体的reads数量一般成反比，线粒体reads占比很高的情况一般有表达量的很少；相反，如果是真正表达的基因reads，很少会来自线粒体基因组；(右边👉)就是刚才所说的比对结果中的基因数量(feature)一般会和测序得到的reads count值成正比

  一切就绪，最后进行一个**过滤操作**

  ```r
  # 最后进行一个过滤操作
  pbmc <- subset(pbmc, subset = nFeature_RNA > 200 & nFeature_RNA < 2500 & percent.mt < 5)
  ```

**数据归一化（normalize）**

> **为何normalize要叫“归一化”？** 这个名称不必纠结，看它使用的方法应该就好理解：因为我们经常关心的就是看差异表达基因，也就是找**一个基因在不同样本的差异**，而这种差异，往往不能直接进行比较，因为不同样本的测序深度和文库大小可能不能，那么基因表达量差异就存在一部分因素来自这里。为了更真实反映基因差异的生物学意义，就要将数据进行一个转换（例如RPKM、TPM等），可以让同一基因在不同样本中具有可比性。 **“归一”归的就是这个文库大小。** 另外呢，原始表达量是一个离散程度很高的值，有的高表达基因表达量可能成千上万，可有的却只有几十。我们即使采用了一些归一化的算法消除了一些技术性误差，但**真实存在的表达量极差往往会影响之后绘制热图、箱线图的美观性**，于是可以另外采用`log` 进行一个**区间缩放（却不会影响真实值）**，比如原来表达量为1的，用`log2(1+1)`转换后结果依然为1；原来表达量为1000的，`log2(1000+1)`转换后约为10。那么原来相差1000倍的变化，现在只差10倍，在不破坏原有数据差异的同时，使数据更加集中。

关于Seurat归一化原理，可以看这一篇：<https://www.biorxiv.org/content/biorxiv/early/2019/03/18/576827.full.pdf>

当过滤掉一部分细胞以后，就要会数据进行归一化。默认是进行一个全局的`LogNormalize`操作，具体方法是：

> normalizes the feature expression measurements for each cell by the **total expression**, multiplies this by a **scale factor** (10,000 by default), and **log-transforms** the result 翻译成公式就是：`log1p(value/colSums[cell-idx] *scale_factor)` ，其中`log1p指的是log(x + 1)`，(Tip:只看到`log`就表示`以e为底的log值`，用`log1p(exp(1)-1)`测试一下，看结果是`1` )
>
> 这里也有解释：<https://github.com/satijalab/seurat/issues/833>
>
> 当然，除了这一种默认的算法，还有：
>
> * **CLR:** Applies a centered log ratio transformation
> * **RC:** Relative counts. Feature counts for each cell are divided by the total counts for that cell and multiplied by the scale.factor. No log-transformation is applied. For counts per million (CPM) set `scale.factor = 1e6`

得到的结果存储在：`pbmc[["RNA"]]@data`

```r
pbmc <- NormalizeData(pbmc, normalization.method = "LogNormalize", scale.factor = 10000)
# 当然其中都是默认参数，于是直接写这种也是可以的
pbmc <- NormalizeData(pbmc)
```

这一步要因人而异，可以看：<https://bioinformatics.stackexchange.com/questions/5115/seurat-with-normalized-count-matrix> 假入有一个TPM的count矩阵，那么就可以不需要使用`Seurat::NormalizeData()`操作了，因为TPM已经根据测序深度进行了归一化，只需要进行log降一下维度即可。如果后续进行`ScaleData`操作，程序会检测是否使用了Seurat提供的标准化方法，如果我们使用自己的的归一化数据，那么就可能出现一个warning提醒，不过到时候不想被提醒，可以设置`check.for.norm =F`

**鉴定差异基因HVGs(highly variable features)**

意思就是：在细胞与细胞间进行比较，选择表达量差别最大的(也即是同一个基因在有的细胞中表达量很高，同时在部分细胞中表达很少)，利用`FindVariableFeatures`函数，会计算一个`mean-variance`结果，也就是给出表达量均值和方差的关系并且得到`top variable features`。

那么关于如何计算这些`top variable features`，这个函数是有三种算法的，分别是：

* (默认) vst：对方差(variance)和均值(mean)都取log值+loess线性拟合
* `mean.var.plot`方法：average expression & dispersion (for each feature) + z-scores
* `dispersion`方法： the highest dispersion values

```r
pbmc <- FindVariableFeatures(pbmc, selection.method = "vst", nfeatures = 2000)
top10 <- head(VariableFeatures(pbmc), 10)

# 分别绘制带基因名和不带基因名的
plot1 <- VariableFeaturePlot(pbmc)
plot2 <- LabelPoints(plot = plot1, points = top10, repel = TRUE)
CombinePlots(plots = list(plot1, plot2))
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-08-21-050405.png)

#### 数据标准化（scale）

它的目的是实现数据的线性转换(scaling)，这也是降维处理(如PCA)之前的标准预处理。 主要利用了`ScaleData`函数，回忆一下，之前归一化（normalize）这里做的操作是log处理，它是对所有基因的表达量统一对待的，最后放在了一个起跑线上。但是为了真正找到差异，我们还要基于这个起跑线，考虑不同样本对表达量的影响

它做了：

* 将每个基因在所有细胞的表达量进行调整，使得每个基因在所有细胞的表达量**均值为0**
* 将每个基因的表达量进行标准化，让每个基因在所有细胞中的表达量**方差为1**

```r
# 先使用全部基因
all.genes <- rownames(pbmc)
> length(all.genes)
[1] 13714
pbmc <- ScaleData(pbmc, features = all.genes)
# 结果存储在pbmc[["RNA"]]@scale.data中
```

**感觉这一步太慢，能不能加速一点？**

这一步主要目的就是下一步的降维，使用全部基因是比较慢。如果想提高速度，可以只对鉴定的HVGs（之前`FindVariableFeatures`设置的是2000个）进行操作，其实这个函数**默认就是对部分差异基因进行操作**，：`pbmc <- ScaleData(pbmc)` 。这样操作的结果中**降维和聚类不会被影响**，但是只对HVGs基因进行归一化，下面画的热图依然会受到全部基因中某些极值的影响。所以**为了热图的结果，还是对所有基因进行归一化比较好。**

**ScaleData的操作过程是怎样的?**

看一下帮助文档就能了解： `ScaleData`这个函数有两个默认参数：`do.scale = TRUE`和`do.center = TRUE`，然后需要输入进行`scale/center`的基因名（默认是取前面`FindVariableFeatures`的结果）。 `scale`和`center`这两个默认参数应该不陌生了，`center`就是对每个基因在不同细胞的表达量都减去均值； `scale`就是对每个进行center后的值再除以标准差（就是进行了一个**z-score的操作**）

**再看一个来自BioStar的讨论：<https://www.biostars.org/p/349824/>**

**问题1：为什么在NormalizeData()之后，还需要进行ScaleData操作？**

前面`NormalizeData`进行的归一化主要考虑了测序深度/文库大小的影响（reads*10000 divide by total reads, and then log），但实际上归一化的结果还是存在基因表达量分布区间不同的问题；而`ScaleData`做的就是在归一化的基础上再添`zero-centres` （mean/sd =》 z-score），将各个表达量\*放在了同一个范围中，真正实现了”表达量的同一起跑线“。*

另外，新版的`ScaleData`函数还支持设定回归参数，如（nUMI、percent.mito），来校正一些技术噪音

**问题2： FindVariableGenes() or RunPCA() or FindCluster()这些参数是基于归一化Normalized\_Data还是标准化Scaled\_Data ？**

所有操作都是基于标准化的数据`Scaled_Data` ，因为这个数据是针对基因间比较的。例如有两组基因表达量如下:

```
g1 10 20 30 40 50
g2 20 40 60 80 100
```

虽然它们看起来是g2的表达量是g1的两倍，但真要降维聚类时，就要看`scale`的结果：

```r
> scale(c(10,20,30,40,50))
           [,1]
[1,] -1.2649111
[2,] -0.6324555
[3,]  0.0000000
[4,]  0.6324555
[5,]  1.2649111
attr(,"scaled:center")
[1] 30
attr(,"scaled:scale")
[1] 15.81139
> scale(c(20,40,60,80,100))
           [,1]
[1,] -1.2649111
[2,] -0.6324555
[3,]  0.0000000
[4,]  0.6324555
[5,]  1.2649111
attr(,"scaled:center")
[1] 60
attr(,"scaled:scale")
[1] 31.62278
```

二者标准化后的分布形态是一样的（只是中位数不同），而我们后来聚类也是看数据的分布，分布相似聚在一起。所以归一化差两倍的两个基因，根据标准化的结果依然聚在了一起

**问题3： ScaleData()在scRNA分析中一定要用吗？**

实际上标准化这个过程不是单细胞分析固有的，在很多机器学习、降维算法中比较不同feature的距离时经常使用。当不进行标准化时，有时feature之间巨大的差异（就像👆问题2中差两倍的基因表达量，实际上可能差的倍数会更多）会影响分析结果，让我们误以为它们的数据分布相差很远。

很多时候，我们会混淆`normalization`和`scale`：那么看一句英文解释：

> **Normalization** "normalizes" within the cell for the difference in sequenicng depth / mRNA throughput. 主要着眼于样本的文库大小差异 **Scaling** "normalizes" across the sample for differences in *range* of variation of expression of genes . 主要着眼于基因的表达分布差异

另外，看`scale()`函数的帮助文档就会发现，它实际上是**对列进行操作：** ![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2020-08-21-050413.png)

那么具体操作中是要先对表达矩阵转置，然后scale，最后转回来

```r
t(scale(t(dat[genes,])))
```

**问题4：RunCCA()函数需要归一化数据还是标准化数据？**

通过看这个函数的帮助文档：

```r
RunCCA(object, object2, group1, group2, group.by, num.cc = 20, genes.use,
  scale.data = TRUE, rescale.groups = FALSE, ...)
```

显示`scale.data = TRUE`，那么就需要标准化后的数据

> 怎样移除不想要的差异来源？

在进行降维聚类时，主要就是考虑数据差异对分布产生的影响。由于技术误差导致的差异是我们不感兴趣的，排除这一部分其实也在上面的问题1中提到了，如果说不想线粒体污染导致的差异影响整体差异分布：

```r
pbmc <- ScaleData(pbmc, vars.to.regress = "percent.mt")
```

这个函数仅针对初级用户，如果想深入探索技术误差的排除，官方给出了另一个专业版函数：`SCTransform`，它也包含`vars.to.regress`这个选项。详细的说明在：<https://satijalab.org/seurat/v3.0/sctransform_vignette.html>

#### 进行线性降维

最常用的线性降维就是PCA方法，**使用标准化的数据**

它默认选择之前鉴定的差异基因（2000个）作为input，但可以使用`features`进行设置；默认分析的主成分数量是50个

```r
# 这里就使用默认值
pbmc <- RunPCA(pbmc, features = VariableFeatures(object = pbmc))
# 结果保存在reductions 这个接口中
```

**检查下PCA结果：**

```r
> print(pbmc[["pca"]], dims = 1:3, nfeatures = 5)
# 或者用 print(pbmc@reductions$pca, dims = 1:3, nfeatures = 5)
PC_ 1 
Positive:  CST3, TYROBP, LST1, AIF1, FTL 
Negative:  MALAT1, LTB, IL32, IL7R, CD2 
PC_ 2 
Positive:  CD79A, MS4A1, TCL1A, HLA-DQA1, HLA-DQB1 
Negative:  NKG7, PRF1, CST7, GZMB, GZMA 
PC_ 3 
Positive:  HLA-DQA1, CD79A, CD79B, HLA-DQB1, HLA-DPB1 
Negative:  PPBP, PF4, SDPR, SPARC, GNG11
```

**用VizDimLoadings对print的结果可视化：**

```r
VizDimLoadings(pbmc, dims = 1:3, reduction = "pca")
```

![对print的结果可视化](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-19-035810.png)

**用DimPlot进行降维后的可视化**

提取两个主成分（默认前两个，当然可以修改`dims`选项）绘制散点图

```r
DimPlot(pbmc, reduction = "pca")
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-19-040130.png)

这个图中每个点就是一个样本，它根据PCA的结果坐标进行画图，这个坐标保存在了`cell.embeddings`中：

```r
> head(pbmc[['pca']]@cell.embeddings)[1:2,1:2]
                     PC_1       PC_2
AAACATACAACCAC -4.7296855 -0.5184265
AAACATTGAGCTAC -0.5174029  4.5918957
```

其实还支持定义分组：根据`pbmc@meta.data`中的`ident`分组：

```r
> table(pbmc@meta.data$orig.ident)

pbmc3k 
  2638 
# 当然这里只有一个分组
```

**探索异质性来源—DimHeatmap**

每个细胞和基因都根据PCA结果得分进行了排序，默认画前30个基因（`nfeatures`设置），1个主成分(`dims`设置)，细胞数没有默认值（`cells`设置）

```r
DimHeatmap(pbmc, dims = 1:15, cells = 500, balanced = TRUE)
# 其中balanced表示正负得分的基因各占一半
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-19-063348.png)

> 降维的真实目的是尽可能减少具有相关性的变量数目，例如原来有700个样本，也就是700个维度/变量，但实际上根据样本中的基因表达量来归类，它们或多或少都会有一些关联，这样有关联的一些样本就会聚成一小撮，表示它们的”特征距离“相近。最后直接分析这些”小撮“，就用最少的变量代表了实际最多的特征

既然每个主成分都表示具有相关性的一小撮变量（称之为”metafeature“，元特征集），那么问题来了：

**降维后怎么选择合适数量的主成分来代表整个数据集？**

选10个？20个？50个？最简单的方法就是：

```r
ElbowPlot(pbmc)
```

它是根据每个主成分对总体变异水平的贡献百分比排序得到的图，我们主要关注”肘部“的PC，它是一个转折点（也即是这里的PC9-10），说明取前10个主成分可以比较好地代表总体变化

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-19-071354.png)

但官方依然建议我们，下游分析时多用几个成分试试（10个、15个甚至50个），如果起初选择的合适，结果不会有太大变化；另外推荐**开始不确定时要多选一些主成分**，也不要直接就定下5个成分进行后续分析，那样很有可能会出错

#### 细胞聚类

Seurat 3版本提供了`graph-based` 聚类算法，参考两篇文章：[SNN-Cliq, Xu and Su, Bioinformatics, 2015\]](http://bioinformatics.oxfordjournals.org/content/early/2015/02/10/bioinformatics.btv088.abstract) 和 [PhenoGraph, Levine *et al*., Cell, 2015\]](http://www.ncbi.nlm.nih.gov/pubmed/26095251). 简言之，这些`graph`的方法都是将细胞嵌入一个算法图层中，例如：`K-nearest neighbor (KNN) graph` 中，每个细胞之间的连线就表示相似的基因表达模式，然后按照这个相似性将图分隔成一个个的高度相关的小群体（名叫`‘quasi-cliques’ or ‘communities’`）；

![KNN解释（https://slideplayer.com/slide/7087250/）](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-19-073441.png)

又比如，在`PhenoGraph`中，首先根据PCA的欧氏距离结果，构建了KNN图，找到了各个近邻组成的小社区；然后根据近邻中两两住户（细胞）之间的交往次数（`shared overlap`）计算每条线的权重（术语叫`Jaccard similarity`） 。这些计算都是用包装好的函数`FindNeighbors()` 得到的，它的输入就是前面降维最终确定的主成分

```r
# 因为我们前面挑选了10个PCs，所以这里dims定义为10个
pbmc <- FindNeighbors(pbmc, dims = 1:10)
```

> 以上是凭个人理解发挥，大概就是这么个意思，不涉及算法层面推导

得到权重后，为了对细胞进行聚类，使用了计算模块的算法（默认使用`Louvain`，另外还有包括SLM的其他三种），使用`FindClusters()` 进行聚类。其中包含了一个`resolution`的选项，它会设置一个”间隔“值，该值越大，间隔越大，得到的cluster数量越多

> **怎么理解这个`resolution`参数？** 可以想象成配眼镜的时候，镜片度数越高，分辨率越高，看的越清楚，看到的细节越丰富（cluster越多）；反之，如果分辨率调的很低，结果就看的模模糊糊一大坨

一般来说，这个值在细胞数量为3000左右时设为`0.4-1.2` 会有比较好的结果

```r
pbmc <- FindClusters(pbmc, resolution = 0.5)
# 结果聚成几类，用Idents查看
> head(Idents(pbmc), 5)
AAACATACAACCAC AAACATTGAGCTAC AAACATTGATCAGC AAACCGTGCTTCCG 
             1              3              1              2 
AAACCGTGTATGCG 
             6 
Levels: 0 1 2 3 4 5 6 7 8
# 发现3000细胞，设resolution=0.5时，得到Number of communities: 9
```

#### 另一种降维方法—非线性降维（UMAP/tSNE）

> **线性降维PCA（1933）**&#x4E00;个特点就是：它将高维中不相似的数据点放在较低维度时，会尽可能多地保留原始数据的差异，因此导致这些不相似的数据相距甚远，大多的数据重叠放置 **tSNE（2008）兼顾了高维降维+低维**展示，降维后的那些不相似的数据点依然可以放得靠近一些，保证了点既在局部分散，又在全局聚集 后来开发的**UMAP（2018）**&#x76F8;比tSNE又能展示更多的维度(参考文献：<https://sci-hub.tw/https://doi.org/10.1038/nbt.4314>)

做个对比：

![https://www.nature.com/articles/nbt.4314](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-19-082039.png)

```r
# 使用UMAP
pbmc <- RunUMAP(pbmc, dims = 1:10)
DimPlot(pbmc, reduction = "umap") # 还可以设置label = TRUE让数字显示在每个cluster上

# 对计算过程很费时的结果保存一下
save(pbmc, file = 'pbmc_umap.Rdata')
```

**需要注意的点：**

注意1：tSNE算法具有随机性，为了保证重复性，要固定一个随机种子

**注意2：如何在R中使用UMAP？**

> 以下测试是在个人电脑上，需要管理员权限

因为UMAP是基于Python的，如果要用它，必须先保证有一个python的安装环境，先试验一下：

```r
> reticulate::py_install(packages ='umap-learn')
# 结果报错，说让我升级一下virtualenv(mac和linux默认使用virtualenv，而Windows只能使用conda)
Error: Prerequisites for installing Python packages not available.

Execute the following at a terminal to install the prerequisites:

$ sudo /usr/local/bin/pip install --upgrade virtualenv
```

然后直接在Rstudio中打开`Terminal`，输入：`sudo /usr/local/bin/pip install --upgrade virtualenv`

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-19-082728.png)

再运行一遍上面的R代码（成功与否取决于个人的网络，因为它要通过外链去下载，wlan有限制的话就用个人热点吧）：

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-19-082834.png)

#### 找差异表达基因（cluster biomarker）

> 目的是根据表达量不同这个特征而分出不同细胞群的基因们（就是找具有生物学意义的HVGs=》即biomarkers） marker可以理解成标记，就是一看到有这些基因，就说明是这个细胞群体

主要利用`FindAllMarkers()`函数，它默认会对单独的一个细胞群与其他几群细胞进行比较找到**正、负表达biomarker（这里的正负有点上调、下调基因的意思**；正marker表示在我这个cluster中表达量高，而在其他的cluster中低）

```r
# 找到cluster1中的marker基因
cluster1.markers <- FindMarkers(pbmc, ident.1 = 1, min.pct = 0.25)
head(cluster1.markers, n = 5)
```

上面这个代码中`min.pct`的意思是：一个基因在任何两群细胞中的占比最低不能低于多少，当然这个可以设为0，但会大大加大计算量

另外，如果**要比较cluster5和cluster0、cluster3的marker基因：**

```r
cluster5.markers <- FindMarkers(pbmc, ident.1 = 5, ident.2 = c(0, 3), min.pct = 0.25)
head(cluster5.markers, n = 5)
```

**一步到位的办法`FindAllMarkers`**（对所有cluster都比较一下，并只挑出来正表达marker）：

```r
pbmc.markers <- FindAllMarkers(pbmc, only.pos = TRUE, min.pct = 0.25, logfc.threshold = 0.25)
# 这一步过滤好好理解(进行了分类、排序、挑前2个)
pbmc.markers %>% group_by(cluster) %>% top_n(n = 2, wt = avg_logFC)
```

**Seurat提供了多种差异分析的检验方法**，在`test.use`选项中设置（详见：[DE vignette](https://satijalab.org/seurat/v3.0/de_vignette.html) ）例如：

```r
cluster1.markers <- FindMarkers(pbmc, ident.1 = 0, logfc.threshold = 0.25, test.use = "roc", only.pos = TRUE)
```

**多种可视化方法：**

* `VlnPlot`：用小提琴图对某些基因在全部cluster中表达量进行绘制

  ```r
  VlnPlot(pbmc, features = c("MS4A1", "CD79A"))
  ```

  ![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-19-095336.png)
* `FeaturePlot`：最常用的可视化=》将基因表达量投射到降维聚类结果中

  ```r
  FeaturePlot(pbmc, features = c("MS4A1", "GNLY", "CD3E", "CD14", "FCER1A", "FCGR3A", "LYZ", "PPBP", "CD8A"))
  ```

  ![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-19-095641.png)

另外还有：`RidgePlot`, `CellScatter`, and `DotPlot`

* `DoHeatmap` 对给定细胞和基因绘制热图，例如对每个cluster绘制前20个marker

  ```r
  top10 <- pbmc.markers %>% group_by(cluster) %>% top_n(n = 10, wt = avg_logFC)
  DoHeatmap(pbmc, features = top10$gene) + NoLegend()
  ```

#### 赋予每个cluster细胞类型

重点在于背景知识的理解，能够将marker与细胞类型对应起来，例如这里从各个cluster中找到的marker对应到了不同的细胞（这一过程是比较考验课题理解程度的）：

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-19-100319.png)

有了这个，绘图就很简单：

```r
new.cluster.ids <- c("Naive CD4 T", "Memory CD4 T", "CD14+ Mono", "B", "CD8 T", "FCGR3A+ Mono", "NK", "DC", "Platelet")
names(new.cluster.ids) <- levels(pbmc)
pbmc <- RenameIdents(pbmc, new.cluster.ids)
DimPlot(pbmc, reduction = "umap", label = TRUE, pt.size = 0.5) + NoLegend()
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-19-100435.png)


# 5.3.2 Seurat V3 | 如何改造Seurat包的DoHeatmap函数？

刘小泽写于19.12.4

> 分析过单细胞数据的小伙伴应该都使用过Seurat包，其中有个函数叫`DoHeatmap`，具体操作可以看： [单细胞转录组学习笔记-17-用Seurat包分析文章数据](https://www.jianshu.com/p/f6f54ce92e24)

## 前言

走完Seurat流程，会得到分群结果`FindClusters()`，并找到marker基因`FindAllMarkers()`，然后想要对每群的前10个marker基因进行热图可视化

```r
rm(list = ls()) 
options(warn=-1) 
options(stringsAsFactors = F)

install.packages('Seurat')
library(Seurat)
library(stringr)   
library(dplyr)  

load('sce_out_for_heatmap_all.Rdata')
top10 <- sce.markers %>% group_by(cluster) %>% top_n(10, avg_logFC)
DoHeatmap(sce,top10$gene,size=3)
```

![plot\_zoom\_png](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-12-04-071009.png)

但是这个图在后期调整时会遇到很多障碍，因此最好用pheatmap重新画一下

## 如何用Pheatmap画这个结果？

其实，**画一个热图最重要的是表达矩阵和分组信息**

### **第1步：得到表达矩阵**

那么现在有了sce对象，从中提取表达矩阵也不难

```r
# 提取原始表达矩阵
cts <- GetAssayData(sce, slot = "counts")
> cts[1:4,1:4]
4 x 4 sparse Matrix of class "dgCMatrix"
               A1_01 A1_10 A1_11 A1_12
00R-AC107638.2      .      2      .      2
0610005C13Rik       .      .      .      .
0610007P14Rik       1     49      3    328
0610009B22Rik       .     12      .     78
# 然后对这个矩阵取log
cts <- log10(cts + 1)
```

### **第2步：得到小的top10表达矩阵**

当然不能使用整个表达矩阵进行处理，可以直接使用Seurat得到的`top10`结果，它帮我们得到了每个cluster的marker基因，而我们**只需要取出这个小表达矩阵即可**

Seurat得到的top10计算结果是这样，那么**矩阵的行就按基因名取：**

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-12-04-072247.png)

因为这个热图结果是按照cluster进行排序展示的，因此我们也要**将小表达矩阵的列按cluster从小到大排序：**

```r
# 原来的cluster分组信息存储在 sce$seurat_clusters 中
> head(sce$seurat_clusters)
A1_01 A1_10 A1_11 A1_12 A1_13 A1_14 
     4      4      4      4      4      4 
Levels: 0 1 2 3 4 5 6 7 8
# 可见并不是从第0个cluster开始的

# 排序之后
new_cluster <- sort(sce$seurat_clusters)
> head(new_cluster)
A10_09 A10_16 A10_18 A10_33 A10_36 A10_42 
      0       0       0       0       0       0 
Levels: 0 1 2 3 4 5 6 7 8
```

👌有了行和列的规定，我们就能很轻松地提取出整个小的表达矩阵：

```r
cts <- as.matrix(cts[top10$gene, names(new_cluster)])
```

### **第3步：做一个列的注释**

因为要做出来DoHeatmap的顶部0-8 cluster的展示，需要使用pheatmap的一个参数：`annotation_col`

这个参数接收一个数据框作为输入。因为是对列进行注释，所以这个数据框的行是矩阵的列名，而它的列在这里对应的就是cluster分群信息

```r
ac=data.frame(cluster=new_cluster)
rownames(ac)=colnames(mat)
> head(ac)
        cluster
A10_09       0
A10_16       0
A10_18       0
A10_33       0
A10_36       0
A10_42       0
```

最后，就可以画图了：

```r
library(pheatmap)
pheatmap(cts,show_colnames =F,show_rownames = T,
         cluster_rows = F,
         cluster_cols = F,
         annotation_col=ac)
```

![plot\_zoom\_png-2](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-12-04-073859.png)

当然，这个pheatmap有很多参数可以调整，看它的参数就知道：

比如想加上每个cluster的分隔，需要用到参数`gaps_col =` ，不过需要提供每个cluster最后一个细胞的序号

【当然这个是后话了，最重要的是知道如何进行数据的转换】

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-12-04-073958.png)

### 除了pheatmap，当然还能用其他的包

画热图的包有很多，其中一个比较常用的就是`ComplexHeatmap`

例如：

```r
BiocManager::install("ComplexHeatmap")
library(ComplexHeatmap)

# 列标题的颜色框
color <- rainbow(9)
names(color) <- levels(new_cluster)
top_color <- HeatmapAnnotation(
  cluster = anno_block(gp = gpar(fill = color), 
                       labels = levels(new_cluster), 
                       labels_gp = gpar(cex = 0.5, col = "white"))) 

Heatmap(mat,
        cluster_rows = FALSE,
        cluster_columns = FALSE,
        show_column_names = FALSE,
        show_row_names = TRUE,
        column_split = new_cluster,
        heatmap_legend_param = list(
          title = "log10(count+1)",
          title_position = "leftcenter-rot"
        ),
        top_annotation = top_anno,
        column_title = NULL)
```

也可以实现列的注释，并且将每个cluster的列都进行分隔，和`DoHeatmap`的结果更像

![plot\_zoom\_png-3](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-12-04-075333.png)


# 5.3.3 scRNA的3大R包对比

刘小泽写于19.9.3

> 将主要介绍Seurat V2、V3、Scater、Monocle V2、Monocle V3之间的差异\
> **表格可以左右滑动哦！**

|           用法          |                                Seurat 2.x                               |                                     Seurat 3.x                                     | Scater                                                                                                                     | Monocle2.x                                                                                                                                                                    | Monocle3.x                                                                                                                    |
| :-------------------: | :---------------------------------------------------------------------: | :--------------------------------------------------------------------------------: | -------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
|       创建R包要求的对象       |                           CreateSeuratObject()                          |                    函数不变，参数取消了raw\.data，min.genes更改为min.features                    | SingleCellExperiment()                                                                                                     | newCellDataSet()，其中的phenoData、featureData参数都是用new()建立的AnnotatedDataFrame对象                                                                                                    | new\_cell\_data\_set()，其中的cell\_metadata、gene\_metadata参数都是数据框                                                                |
|         添加注释信息        |                              AddMetaData()                              |                        AddMetaData()或者直接通过object$meta\_name                        | 可以直接使用sce$meta\_name                                                                                                       | addCellType()添加细胞类型                                                                                                                                                           | 可以用基础R函数                                                                                                                      |
| QC and selecting cell |                              <sce@raw.data>                             |                                   GetAssayData()                                   | calculateQCMetrics(),其中的feature\_controls参数可以指定过滤指标，然后有一系列的可视化函数。过滤用filter()或isOutlier()                                   | 用基础R函数进行初步过滤，还可以用detectGenes()函数加上subset()过滤                                                                                                                                  | 用基础R函数进行初步过滤                                                                                                                  |
|      表达量的标准化或者归一化     |                     NormalizeData()，归一化后检测用sce\@data                    |                        NormalizeData()，归一化后检测用sce\[\['RNA']]                       | 计算CPM：calculateCPM()、归一化：normalize()                                                                                       | estimateSizeFactors()还有estimateDispersions                                                                                                                                    | preprocess\_cds()                                                                                                             |
|        寻找重要的基因        |                           FindVariableGenes()                           |                           FindVariableFeatures()，其中算法有变动                           | 没有专门函数                                                                                                                     | differentialGeneTest()函数                                                                                                                                                      | 版本3和版本2的差异分析可以说是完全不同，版本3取代了2中的differentialGeneTest() and BEAM()。它利用fit\_models()或graph\_test()                                |
|         去除干扰因素        |                    ScaleData()，结果存储在<sce@scale.data>中                   |                     ScaleData()，结果存储在sce\[\["RNA"]]@scale.data中                    | limma的removeBatchEffect()、scran的mnnCorrect()                                                                               | 去除干扰因素的功能被包装在降维函数中                                                                                                                                                            | preprocess\_cds()中指定参数residual\_model\_formula\_str                                                                           |
|           降维          | PCA：RunPCA()，参数pc.genes，结果存储在sce\@dr$<pca@gene.loadings> tSNE：RunTSNE() | PCA：RunPCA()，参数features，结果存储在sce\@reductions$<pca@feature.loadings> tSNE：RunTSNE() | PCA：runPCA()，结果在reducedDims中； tSNE：runTSNE()                                                                               | reduceDimension函数，可以选择多种参数                                                                                                                                                    | reduce\_dimension()，算法包括UMAP", "tSNE", "PCA" and "LSI"                                                                        |
|         降维后可视化        |                   VizPCA和PCElbowPlot;PCAPlot或者TSNEPlot                  |                 VizDimLoadings()、DimPlot()、DimHeatmap()、ElbowPlot()                | plotReducedDim()、plotPCA()                                                                                                 | plot\_cell\_clusters()                                                                                                                                                        | plot\_cells()                                                                                                                 |
|          细胞聚类         |                              FindClusters()                             |                          FindNeighbors() + FindClusters()                          | 没有包装聚类函数，可以辅助其它R包，或者R基础函数                                                                                                  | clusterCells()                                                                                                                                                                | cluster\_cells()，依赖一个Python模块louvain                                                                                          |
|       找marker基因       |                      FindMarkers()或FindAllMarkers()                     |              FindMarkers()或FindAllMarkers()，VlnPlot()、FeaturePlot()可视化             | 借助SC3包                                                                                                                     | newCellTypeHierarchy()、 classifyCells()                                                                                                                                       | top\_markers()                                                                                                                |
|          绘图相关         |       基因相关性绘图：GenePlot()；细胞相关性绘图：CellPlot()，选择细胞用<sce@cell.names>       |          基因相关性绘图：FeatureScatter()；细胞相关性绘图：CellScatter()，选择细胞用colnames(sce)         | 基因相关性绘图：绘制基因表达相关plotExpression()；检测高表达基因plotHighestExprs()、表达频率plotExprsFreqVsMean()、细胞质控plotColData()、表达量累计贡献plotScater() | plot\_cell\_trajectory()、plot\_genes\_in\_pseudotime()、plot\_genes\_jitter()、plot\_pseudotime\_heatmap()、plot\_genes\_branched\_heatmap()、plot\_genes\_branched\_pseudotime() | plot\_pc\_variance\_explained()、对每组的marker基因可视化： plot\_genes\_by\_group()、3D发育轨迹plot\_cells\_3d()、画小提琴图：plot\_genes\_violin() |


# 5.3.4 Seurat两种数据比较：integrated vs RNA assay

刘小泽写于2020.11.16

> 内容来自：<https://www.biostars.org/p/399789/>

主要探讨多个数据利用Seurat整合后，会出现integrated 和 RNA assay

![](https://1260378310-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MCv4XZpjUYDdSrsYVkw%2F-MN2OiutUio80lq_NCBd%2F-MN2P9NH8JafvVbZU0Sz%2Fimage.png?alt=media\&token=8538375f-24f8-43ea-8148-c5b2681d4e63)

那么它们的**区别在哪里呢？**

### Q1：What is this integrated assay and how/why is it different than "RNA".

* This is the data after integration. You can think of it as **batch-adjusted** data. 也就是说integrated assay是校正完批次效应后的数据
* data integration process will return a matrix with "corrected" value. Set DefaultAssay to "integrated" means your following analysis will on the "corrected" value.&#x20;
* Set DefaultAssay to "RNA" means your following analysis will on the original value.
* You can use integrated data to do clustering. But it is not proper to use integrated for DE, and most tools only accept raw counts for DE.

### Q2：What are the consequences of NOT changing it and leaving RNA as the default

* If you do not switch to `integrated` assay, you will not be working with integrated data.&#x20;
* If you are not interested in the integrated data, then you don't need to perform integration. If you just want to combine two Seurat objects without any additional adjustments, there a `merge` function and [a vignette for that workflow](https://satijalab.org/seurat/v3.1/merge_vignette.html).


# 5.3.5 seurat 的几种findmaker比较

> from : <https://www.biostars.org/p/409790/>

* **FindMarkers** will find markers between two different identity groups - you have to specify both identity groups. This is useful for comparing the differences between two specific groups.<br>
* **FindAllMarkers** will find markers differentially expressed in each identity group by comparing it to all of the others - you don't have to manually define anything. Note that markers may bleed over between closely-related groups - they are not forced to be specific to **only** one group. This is what most people use (and likely what you want).<br>
* **FindConservedMarkers** will find markers that are conserved between two groups - this can be useful if you want to find markers that are conserved between a treated and untreated condition for **a specific cell type or group of cells**. It means they are differentially expressed compared to other groups, but have similar expression between the two groups you're actually comparing.<br>

### 另外findmarker也可以按照metadata的某一列进行寻找：

> 来自：<https://github.com/satijalab/seurat/issues/252>

```
Idents(object = sce) <- sce@meta.data$'celltype'
# 然后再往下走find流程
sce.markers <- FindAllMarkers(object = sce, only.pos = TRUE, min.pct = 0.25, thresh.use = 0.25)
```


# 5.4 Monocle的使用

## 这一部分将会介绍什么?

主打Monocle 3版本，后期可能会继续补充Monocle V2


# 5.4.1 Monocle V3实战

刘小泽写于19.8.21+28

> **学习感想：** \
> 这次花费的时间较长，个人感觉这个包比Seurat、Scater难学，说明文档写的实在是太长了！

### 前言

> 依旧主打全人工理解（**不是翻译**）

对这款软件的了解主要是因为它做的发育轨迹、拟时序分析很漂亮，这也是它的主打

Monocle的官网在：

* 版本2：<https://cole-trapnell-lab.github.io/monocle-release/docs/#installing-monocle>
* 版本3：<https://cole-trapnell-lab.github.io/monocle3/monocle3_docs/>

首先看新版本的特性和升级之处：

* 处理的细胞数增加很多（millions of cells）
* 针对发育生物学领域，做了一些重大改进：
  * 发育轨迹的研究流程优化
  * 支持[UMAP](https://github.com/lmcinnes/umap)推断发育轨迹，无缝衔接到`reduceDimension`函数，或者使用独立的函数`UMAP`&#x20;
  * 支持多个祖源（toots）的发育推断
  * 利用这个算法[approximate graph abstraction](https://www.biorxiv.org/content/early/2017/10/25/208819) 理解各条发育轨迹的分离及平行发育的轨迹
  * 3D构建发育轨迹
* 除了发育轨迹以外，官方还介绍它在亚型发现、差异分析方面表现不错
* 另外，版本3在持续不断优化，官方称每几个周就会增加一些新功能
* 其中的算法细节看2019年发表的[Cao & Spielmann et al](http://dx.doi.org/10.1038/s41586-019-0969-x).&#x20;

它的主要分析流程是：

![Monocle主要分析流程](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-22-015711.png)

> **注意：以下是介绍版本3（版本2的使用和3存在一些不同之处，会在”跟着官网学习“这一部分的最后标注）**

### 安装版本3

> 要求R版本3.5以上，Bioconductor版本3.5以上

```r
if (!requireNamespace("BiocManager", quietly = TRUE))
    install.packages("BiocManager")
# 一些依赖包
bioc_pkgs <- c('BiocGenerics', 'DelayedArray', 'DelayedMatrixStats',
                       'limma', 'S4Vectors', 'SingleCellExperiment',
                       'SummarizedExperiment')
for (bpkg in bioc_pkgs){
  if (! require(bpkg,character.only=T) ) {
    BiocManager::install(bpkg,ask = F,update = F)
    require(bpkg,character.only=T)
    }
}

# (可选)如果要在细胞聚类时设定分辨率参数(resolution)，就要安装一个python包
install.packages("reticulate")
reticulate::py_install("louvain")
# 现在安装3版本，还是要通过github
devtools::install_github('cole-trapnell-lab/monocle3')
# 重启Rstudio，检测是否成功
library(monocle3)
```

### Monocle示例1-细胞聚类及鉴定亚群

**创建对象**

整体代码就是这样（先了解概况然后下面有实际练习）：

```r
cds <- new_cell_data_set(expression_matrix,
                         cell_metadata = cell_metadata,
                         gene_metadata = gene_annotation)
```

但是这个对象`cell_data_set`的设置需要好好理解：

* 这个对象的灵感来自[SingleCellExperiment](http://bioconductor.org/packages/release/bioc/html/SingleCellExperiment.html)，因此它的操作也是类似的
* `expression_matrix` 是一个行为基因，列为细胞样本的表达矩阵
* `cell_metadata` 是一个数据框，行为细胞，列是细胞的属性（比如细胞类型、培养环境、培养时间等）
* `gene_metadata`是一个数据框，**行为feature信息（比如基因），列是基因属性（比如GC含量）**【这个很新鲜，因为其他的基于SingleCellExperiment的包如`Scater` 只需要表达矩阵和样本信息即可】。更重要的是，这个数据框中**必须有这么一列：`gene_short_name`，其中保存基因名**（简称或Symbol标准名均可，用于作图）
* 并且要满足：`expression_matrix`的行与`gene_metadata`的行对应；`expression_matrix`的列与`cell_metadata`的行对应

然后测试一下（**下载速度会很慢！**）

> 小提示：使用服务器`axel`下载，速度会加快（不过这要看服务器的网络配置），如果给力的话，下载均速能到120kb/s，一两分钟就下载好 如果网络真的不给力也没关系，我帮大家下载下来了，在豆豆和花花的公众号”生信星球“后台回复”monocle“就可以得到这三个文件啦：
>
> ![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-22-024036.png)
>
> ![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-22-032551.png)

```r
# 下载数据(如果已经有了，可以跳过)
expression_matrix <- readRDS(url("http://staff.washington.edu/hpliner/data/cao_l2_expression.rds"))
cell_metadata <- readRDS(url("http://staff.washington.edu/hpliner/data/cao_l2_colData.rds"))
gene_annotation <- readRDS(url("http://staff.washington.edu/hpliner/data/cao_l2_rowData.rds"))

# 加载、探索数据
expression_matrix <- readRDS(file = 'cao_l2_expression.rds')
cell_metadata <- readRDS(file = 'cao_l2_colData.rds')
gene_annotation <- readRDS(file = 'cao_l2_rowData.rds')

dim(expression_matrix)
expression_matrix[1:3,1:3]
cell_metadata[1:3,1:3]
head(gene_annotation)

# 创建CDS(Cell Data Set)对象
cds <- new_cell_data_set(expression_matrix,
                                   cell_metadata = cell_metadata,
                                   gene_metadata = gene_annotation)
cds
```

**数据预处理**

这一步就是告诉Monocle，你想怎么对数据进行归一化、标准化，初步降维是使用PCA（针对标准的RNA-seq，需要指定计算的主成分数）还是LSA（Latent semantic analysis，针对ATAC-seq）。这些都是为后面的聚类操作（tSNE、UMAP做准备）

```r
# 比较耗时
cds <- preprocess_cds(cds, num_dim = 100) #大约2 mins
# 这里设置这么多主成分，可能是因为细胞数太多(4万多个)，成分太少不足以代表整体
```

这个`preprocess_cds`看下帮助文档就能得知：

* 降维方法`method = c("PCA", "LSI")`，默认是PCA
* 默认维度`num_dim`是50
* 初步降维前归一化`norm_method` 默认是log处理
* 设置降维方法是PCA时，自动进行标准化处理

检查一下使用的主成分（PCs）是否能够抓取最主要的基因表达变化信息

```r
plot_pc_variance_explained(cds)
# 很像Seurat的ElbowPlot()
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-22-034739.png)

可以看到，超过100个主成分后对整体变化的贡献值就没有太多了，而且每多一个PC就会对下游数据分析造成压力

**继续降维及可视化**

**关于维度有必要好好再了解下：**

> （详见`?reduce_dimension`） **那我就编一个”降维“小故事吧** \
> 每个细胞中都有2万多基因，可以被当做高维空间上的一个点，而这里说的**每个维度就是就不同基因的表达量**，也就是说，**我们现在有2万个维度上的4万个点**；处理的维度越多，后面的轨迹推断也就是越难 \
> 好在基因之间不是毫无瓜葛的，各种基因之间总会有错综复杂的联系，通过排除这种”冥冥中的相关性“就会**提取出它们之间最不同的地方**，更能说明整体的一个特征。于是线性降维就能将2万个维度降到这里的100个，那么之后再处理的话，可能线性降维也有压力。\
> **”专人专事**：“\~于是找来了更专业的非线性降维工具tSNE、UMAP，它们最终能实现再将这100个成分继续压缩，最终得到我们能理解的二维或者3维空间（那么其中包含的样本特征一定是**精华中的精华**） 英文的解释就是：Each cell can be viewed as a point in a high-dimensional space, where each dimension describes the expression of a different gene.

这里主要采用非线性降维的方法：一种是目前很流行的tSNE，另一种是2018年发布的基于Python的UMAP（UMAP处理大量细胞的算法更优秀；另外与t-SNE相比，UMAP可以保留更多的细胞亚群连续性）

```r
cds <- reduce_dimension(cds, preprocess_method = "PCA",reduction_method = c("UMAP"))
# 大约运行1.5 min
```

关于这个函数：

* 多种继续降维的算法：如`"UMAP", "tSNE", "PCA" and "LSI"`，不过函数默认使用UMAP
* 关于运行加速： 基于`BiocParallel`支持多线程运行，使用`cores`定义；或者使用`umap.fast_sgd=TRUE`参数可以加速降维 但是自己加速的话，它会友情提示一下：

  ```r
  # Note: reduce_dimension will produce slightly different output each time you run it unless you set 'umap.fast_sgd = FALSE' and 'cores = 1'
  ```
* 默认得到两个维度
* 需要定义上一步`preprocess_method`使用的算法，默认是`LSI`

进行可视化：

```r
plot_cells(cds)
```

![默认的umap结果](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-22-041227.png)

图中的每个点都代表`cds`对象中的不同细胞，可以看到不同的细胞分成了不同的群，有的群有几千个细胞，有的群只有少数几个，我们这里直接使用测试数据中做好的细胞标记

```r
# 数据中一共提供了30类细胞(细胞类型及数量见：)
table(colData(cds)$"cao_cell_type")
# 根据这个上色
plot_cells(cds, color_cells_by="cao_cell_type")
# (另外除了用cao_cell_type细胞类型，还能用下面的任意一个)
> colnames(colData(cds))
[1] "plate"         "cao_cluster"   "cao_cell_type" "cao_tissue"    "Size_Factor"
```

可以看到许多细胞类型再UMAP结果中靠的很近

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-22-041537.png)

根据基因表达量进行映射：

```r
plot_cells(cds, genes=c("cpna-2", "egl-21", "ram-2", "inos-1"))
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-23-012402.png)

如果想使用tSNE方法降维的话：

```r
cds <- reduce_dimension(cds, reduction_method="tSNE")
# 然后对tSNE结果可视化
plot_cells(cds, reduction_method="tSNE", color_cells_by="cao_cell_type")
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-23-013132.png)

其实可以看到，tSNE结果的各个细胞亚群内部的关联性不如UMAP（比如这个`stem cells`）

**检查、移除批次效应 => residual\_model\_formula\_str参数**

为了更加真实地反映差异基因的来源是生物学因素，而不是技术因素（比如细胞板批次、细胞培养差别、测序差别等），就首先要检查有没有所谓的”技术批次“对数据差异产生影响。

当进行降维分析时，应该经常检查批次效应，最好在`colData(cds)` 添加一列，其中注明细胞的批次信息（比如来自哪个细胞板），然后就能根据这个对细胞的批次进行可视化，**结果最好是各个细胞亚群的不同批次混在一起**，这样我们就能认为：降维所采用的”特异性“的特征并不来自批次

```r
plot_cells(cds, color_cells_by="plate", label_cell_groups=FALSE)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-23-014500.png)

这里看到还不错，但是如果真的担心不同批次会产生其他的影响，可以在降维之前对批次进行校正，保证后面的各个细胞群只来自一个批次，那么它们之间的分群差异就更可能是由于生物因素导致

```r
# 假入要对批次进行校正
cds = preprocess_cds(cds, num_dim = 100, residual_model_formula_str = "~ plate")
# 校正后再降维
cds = reduce_dimension(cds)
# 再次检查批次效应
plot_cells(cds, color_cells_by="plate", label_cell_groups=FALSE)
```

**细胞聚类 => cluster\_cells()**

这是细胞分群过程中非常重要的一步。Monocle利用[Louvain community detection](https://en.wikipedia.org/wiki/Louvain_Modularity) 这个非监督聚类方法（它是`phenoGraph`算法的一部分），它和我们常见的Kmeans、hclust层次聚类等还不太一样，这个方法**更倾向于找到一个网络中联系紧密的部**分，而经常忽略节点的特性；而我们**常见的聚类**呢，它**一般会忽视整体中各个部分的联系，**&#x800C;通过**计算两个节点(目标)之间的距离（如欧式距离、曼哈顿距离、余弦相似度等）** 找到相似的特性

这一步依赖一个Python模块`louvain`

> **关于Python模块在R中的安装：** 可以看我写过的[Linux/Mac/Windows的Rstudio安装Python模块总报错，怎么破？](https://www.jianshu.com/p/40cc441cd8f5)

做的话很简单，一个函数，结果保存在了`cds@clusters$UMAP$clusters`中：

```r
cds = cluster_cells(cds, resolution=c(10^seq(-6,-1)))
plot_cells(cds)
# 这个可视化函数如果不加任何参数，它默认对不同的cluster上色
# 那么有哪些cluster呢？用下面函数查看：
cds@clusters$UMAP$clusters #或者
clusters(cds) 
#一共有85个cluster
```

![按照cluster可视化](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-23-030049.png)

不过这样细分看的太混杂，于是 \[Alex Wolf et al]\(<https://genomebiology.biomedcentral.com/articles/10.1186/s13059-019-1663-x>") 利用他们开发的 [PAGA](https://github.com/theislab/paga) 算法将一些**小的cluster聚合**成一个大的partition，结果也是保存在了`cluster_cells`的计算结果中`cds@clusters$UMAP$partitions`

```r
plot_cells(cds, color_cells_by="partition", group_cells_by="partition")
# 检查有多少partition
cds@clusters$UMAP$partitions
# 整合成了36个大的partition
```

![按照partition进行可视化](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-25-120805.png)

除了默认按照cluster编号进行标记不同的亚群，还可以**按照每个cluster对应的细胞类型**进行可视化：

```r
plot_cells(cds, color_cells_by="cao_cell_type")
```

![按照每个cluster对应的细胞类型进行可视化](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-27-020039.png)

不过这样又很多重复的细胞类型，我们可以**对label进行简化：**

```r
plot_cells(cds, color_cells_by="cao_cell_type", label_groups_by_cluster=FALSE)
```

![对label进行简化](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-27-022233.png)

> **归根结底，都是对细胞的类型、坐标、颜色映射**。就是说：`cds@colData$cao_cell_type`中存储了每个细胞对应的细胞类型；`cds@reducedDims$UMAP`中存储了每个细胞UMAP降维后的坐标；`cds@clusters$UMAP$partitions`或`cds@clusters$UMAP$clusters`中存储了每个细胞聚类后对应的细胞分群；接下来就是根据坐标对每个细胞进行分群上色、标记类型

**找marker基因 => top\_markers()**

确定了各种亚群以后，我们就想知道什么导致了它们的差异，这就是寻找marker基因的过程，**利用`top_markers()`函数**

```r
marker_test_res = top_markers(cds, group_cells_by="partition", reference_cells=1000, cores=8)
# 设置reference_cells是随机挑出来这些数量的细胞作为参照，然后让top_markers和参照集中的基因进行显著性检验；另外reference_cells还可以是来自colnames(cds)的细胞名
marker_test_res[1:4,1:4]
```

结果得到一个数据框，其中包含了每个`partition` （因为上面我们设置了`group_cells_by`）的特异表达基因。

然后我们可以**自行过滤：**

```r
top_specific_markers = marker_test_res %>%
    filter(fraction_expressing >= 0.10) %>%
    group_by(cell_group) %>%
    top_n(1, pseudo_R2)
# 基因id去重
top_specific_marker_ids = unique(top_specific_markers %>% pull(gene_id))
```

然后可以**对每组的marker基因可视化=> plot\_genes\_by\_group()：**

```r
plot_genes_by_group(cds,
                    top_specific_marker_ids,
                    group_cells_by="partition",
                    ordering_type="maximal_on_diag",
                    max.size=3)
```

![对每组的marker基因可视化](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-27-034950.png)

如果要多看几个marker，只需要修改一下`top_n`

```r
top_specific_markers = marker_test_res %>%
    filter(fraction_expressing >= 0.10) %>%
    group_by(cell_group) %>%
    top_n(3, pseudo_R2)

top_specific_marker_ids = unique(top_specific_markers %>% pull(gene_id))

plot_genes_by_group(cds,
                    top_specific_marker_ids,
                    group_cells_by="partition",
                    ordering_type="cluster_row_col",
                    max.size=3)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-27-035540.png)

**对细胞类型进行注释**

因为我们上面的测试数据中给出了细胞类型，所以可以进行聚类后的可视化，但实际上，我们通常只能看到不同的cluster数字和它们的分布（也就是类似于`as.character(partitions(cds))`的结果），因此**利用marker基因去重新定义细胞类型是非常关键的一步。**

```r
# 先将partitions的分组由因子型转为字符型
colData(cds)$assigned_cell_type = as.character(partitions(cds))
# 再对字符型重新定义
colData(cds)$assigned_cell_type = dplyr::recode(colData(cds)$assigned_cell_type,
                                                "1"="Body wall muscle",
                                                "2"="Germline",
                                                "3"="Unclassified neurons",
                                                "4"="Seam cells",
                                                "5"="Coelomocytes",
                                                "6"="Pharyngeal epithelia",
                                                "7"="Vulval precursors",
                                                "8"="Non-seam hypodermis",
                                                "9"="Intestinal/rectal muscle",
                                                "10"="Touch receptor neurons",
                                                "11"="Pharyngeal neurons",
                                                "12"="Am/PH sheath cells",
                                                "13"="NA",
                                                "14"="Unclassified neurons",
                                                "15"="flp-1(+) interneurons",
                                                "16"="Canal associated neurons",
                                                "17"="Pharyngeal gland",
                                                "18"="Other interneurons",
                                                "19"="Ciliated sensory neurons",
                                                "20"="Ciliated sensory neurons",
                                                "21"="Ciliated sensory neurons",
                                                "22"="Ciliated sensory neurons",
                                                "23"="Ciliated sensory neurons",
                                                "24"="Ciliated sensory neurons",
                                                "25"="Oxygen sensory neurons",
                                                "26"="Ciliated sensory neurons",
                                                "27"="Unclassified neurons",
                                                "28"="Pharyngeal gland",
                                                "29"="Ciliated sensory neurons",
                                                "30"="Ciliated sensory neurons",
                                                "31"="Ciliated sensory neurons",
                                                "32"="Ciliated sensory neurons",
                                                "33"="Pharyngeal muscle",
                                                "34"="Failed QC")
plot_cells(cds, group_cells_by="partition", color_cells_by="assigned_cell_type")
# Seurat中用RenameIdents 进行细胞类型重定义

# 另外，想从中取子集、过滤的话
cds[,colData(cds)$assigned_cell_type != "Failed QC"]
```

![细胞类型进行注释](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-27-041026.png)

**基于Garnett的自动化注释**

> 下面这部分先了解下方法就行，日后有需要再细学

前面对细胞类型进行手动注释有点麻烦，而且cluster编号一旦更改，又要手动去注释一遍。

利用Monocle团队开发的 [Garnett](https://cole-trapnell-lab.github.io/garnett/) ，它可以根据marker基因对细胞进行分类

**预处理**

```r
# step-1：首先根据上面得到的细胞类型assigned_cell_type，找top_marker
assigned_type_marker_test_res = top_markers(cds,
                                            group_cells_by="assigned_cell_type",
                                            reference_cells=1000,
                                            cores=8)
# step-2：过滤（阈值自定义）
garnett_markers = assigned_type_marker_test_res %>%
  filter(marker_test_q_value < 0.01 & specificity >= 0.5) %>%
  group_by(cell_group) %>%
  top_n(5, marker_score)
# step-3：去重复
garnett_markers = garnett_markers %>% group_by(gene_short_name) %>%
  filter(n() == 1)
# step-4：生成marker文件
generate_garnett_marker_file(garnett_markers, file="./marker_file.txt")
```

![generate\_garnett\_marker\_file生成marker文件](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-27-081221.png)

得到这个文件，其实也只是自动化实现的第一步，我们自己可以根据已有的知识在其中增加或删除一些marker信息；另外如果发现多个细胞类型中的marker基因有大量重合，就要考虑这几种细胞类型可能都是某一种的细胞的亚型。还有很多关于marker文件修改的帮助信息：Garnett [documentation](https://cole-trapnell-lab.github.io/garnett/docs/)

**方法一：自己使用Garnett**

```r
# 安装Garnett
devtools::install_github("cole-trapnell-lab/garnett", ref="monocle3")
library(garnett)
# 安装物种注释库(这里的物种是C. elegans秀丽隐杆线虫)，目的是为了做基因ID转换
BiocManager::install("org.Ce.eg.db")
colData(cds)$garnett_cluster = clusters(cds)
worm_classifier <- train_cell_classifier(cds = cds,
                                         marker_file = "./marker_file.txt",
                                         db=org.Ce.eg.db::org.Ce.eg.db,
                                         cds_gene_id_type = "ENSEMBL",
                                         num_unknown = 50,
                                         marker_file_gene_id_type = "SYMBOL",
                                         cores=8)
# 这样自己就做了一个分组数据集，官方也建议将高质量的训练集汇总到：https://github.com/cole-trapnell-lab/garnett/issues
# 
cds = classify_cells(cds, worm_classifier,
                           db = org.Ce.eg.db::org.Ce.eg.db,
                           cluster_extend = TRUE,
                           cds_gene_id_type = "ENSEMBL")
plot_cells(cds,
           group_cells_by="partition",
           color_cells_by="cluster_ext_type")
```

**方法二：使用别人提供的分组数据集**

例如作者提供了已经做好的数据集（<https://cole-trapnell-lab.github.io/garnett/classifiers/ceWhole），我们直接下载就能使用：>

```r
load(ceWhole)
cds = classify_cells(cds, ceWhole,
                           db = org.Ce.eg.db,
                           cluster_extend = TRUE,
                           cds_gene_id_type = "ENSEMBL")
```

### Monocle示例2-构建发育轨迹

**意义：** 在发育生物学中，细胞整个发育阶段中对刺激做出的不同相应导致了细胞功能状态的转化。不同状态的细胞表达不同的基因，从而产生不同的蛋白、代谢物参与不同的生命过程。伴随着细胞状态的转化，转录信息也在不断更换，有的基因起初沉默后来激活。但是这个事情很难去探索，因为要纯化出不同状态并且稳定的细胞基本是不现实的，而scRNA可以不用通过实验方法的纯化细胞，就能研究细胞生命的各个状态。

**这也是Monocle最大的亮点**—利用算法去学习每个细胞基因表达量的变化，从而对细胞状态进行推断，推断出整体的一个表达轨迹，那么就可以将每个细胞放在特定位置上；另外有可能一个过程有多个结果，那么在轨迹图上就会有多个分支，代表了细胞分化

> **上面使用的数据集到此结束**，下面发育轨迹会用到一套新的数据集（来自[Packer & Zhu et al](http://dx.doi.org/10.1101/565549). 的线虫整个胚胎发育的时间线数据集，这里主要节选了神经元的数据） **需要注意的是，下面做出的图和官网给出的图是不同的**

**载入数据，构建对象 => new\_cell\_data\_set()**

```r
expression_matrix = readRDS(url("http://staff.washington.edu/hpliner/data/packer_embryo_expression.rds"))
cell_metadata = readRDS(url("http://staff.washington.edu/hpliner/data/packer_embryo_colData.rds"))
gene_annotation = readRDS(url("http://staff.washington.edu/hpliner/data/packer_embryo_rowData.rds"))

# 文件大小(同样可以公众号后台回复"monocle-2"获取)
#|-- [497K]  packer_embryo_colData.rds
#|-- [10.0M]  packer_embryo_expression.rds
#`-- [226K]  packer_embryo_rowData.rds

cds <- new_cell_data_set(expression_matrix,
                         cell_metadata = cell_metadata,
                         gene_metadata = gene_annotation)
```

**预处理 => preprocess\_cds()**

和示例一的操作差不多，只是这里根据原作者（Packer & Zhu et al ）的方法处理了一下批次效应

```r
cds <- preprocess_cds(cds, num_dim = 100, residual_model_formula_str = "~ bg.300.loading + bg.400.loading + bg.500.1.loading + bg.500.2.loading + bg.r17.loading + bg.b01.loading + bg.b02.loading")
```

**降维可视化 => reduce\_dimension()**

强烈建议用UMAP

```r
cds <- reduce_dimension(cds)
plot_cells(cds, label_groups_by_cluster=FALSE,  color_cells_by = "cell.type")
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-27-101608.png)

看不同基因在不同分支的表达量:

```r
ciliated_genes = c("che-1",
                   "hlh-17",
                   "nhr-6",
                   "dmd-6",
                   "ceh-36",
                   "ham-1")

plot_cells(cds,
           genes=ciliated_genes,
           label_cell_groups=FALSE,
           show_trajectory_graph=FALSE)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-28-013917.png)

**细胞聚类 => cluster\_cells()**

Monocle不认为一组数据中的所有细胞都来自同一个”祖先“，很多实验中，它们会有多个发育轨迹。Monocle会通过聚类来判断细胞是否应该归属同一个发育轨迹。之前介绍的`cluster_cells()`中，细胞可以按cluster细分，还可以按partition归为大类。

```r
# 这里就用partition聚类
cds <- cluster_cells(cds)
plot_cells(cds, color_cells_by = "partition")
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-28-014614.png)

**利用learn graph在每个partition中寻找主路径 => learn\_graph()**

> 理论上，任何的UMAP或者tSNE降维结果都能找到这样的主路径，选用UMAP是考虑了它能更多展示细胞之间的关联，另外最重要是对数据的了解

```r
cds <- learn_graph(cds)
plot_cells(cds,
           color_cells_by = "cell.type",
           label_groups_by_cluster=FALSE,
           label_leaves=FALSE,
           label_branch_points=FALSE)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-28-021732.png)

**有了初步的轨迹线，接下来就是对细胞出现的先后进行排序 => order\_cells()**

> 也就是pseudotime（拟时序分析），推断哪个细胞先出来，哪个细胞后出来。它会对每个细胞在特定的过程（比如分化过程）中的参与度进行评估

很多生命活动中，细胞并非同步发育的，例如在细胞分化阶段，实验捕获到的细胞可能会分布到各个发育过程。因此做单细胞测序时得到的那一群细胞，其中可能包括已经完成某个生命过程的细胞，还可能包括没有开始这个过程的细胞。这种非同步的特性会阻碍我们理解细胞过渡状态究竟发生了怎样的调控。

Monocle的这个算法将基因表达量的变化定义为发育轨迹上**生命过程的变化**（体现为不同的细胞类型）**，而不是真正的时间变化**（因此我们叫它”拟时序“，而不是”真时间分析“）。

之前得到的轨迹总长度就是细胞从一个阶段的起始到终止所产生的转录水平变化总量；拟时序分析会沿着最短的路径，计算每个细胞和轨迹起始位点的距离

```r
# 先将每个细胞根据”胚胎发育时间区间“进行上色，然后根据胚胎发育过程前后绘制节点
# 在cds@colData中提供了细胞对应的类型以及胚胎发育时间，这个才是真正的关键信息。有了这个信息才能作图。工具不是万能的，它不可能帮助我们去完成生物学中的推断，一定是我们自己先定义好，再交给它进行可视化而已
plot_cells(cds,
           color_cells_by = "embryo.time.bin",
           label_cell_groups=FALSE,
           label_leaves=TRUE,
           label_branch_points=TRUE,
           graph_label_size=1.5)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-28-025246.png)

* 图中的**黑线**就是整个架构（注意到整个图并非完全连接的，毕竟是按照partition聚类，每个partition中的细胞差异有点大）；
* **浅灰色的圆圈**表示”叶片“表示发育轨迹中的不同结局（可以认为拟时序分析的图是一个”根-茎-叶“结构），用参数`label_leaves`控制；
* **黑色的圆圈**表示分支节点，预示着其中的细胞会有不同发展方向，用参数`label_branch_points`控制；
* 圈中数字大小表示出现时间的先后

从上面的图中我们就能知道出现时间比较早的细胞位置，我们需要对全部的细胞都排个先后顺序，因此要用到`order_cells()` ，不过这个函数**需要一个”根节点“位置**，一般是一个partition分配一个根节点。

不过，**怎么简单高效挑选根节点呢？**=> get\_earliest\_principal\_node()

首先根据轨迹图中的节点将与它们最相近的细胞分成组，然后计算来自最早时间点的细胞所占组分，最后挑出包含早期细胞数量最多的节点，认为它是就是根节点

```r
# 官方给出了一个函数，这里定义了一个time_bin，选择了最早的时间点区间。
get_earliest_principal_node <- function(cds, time_bin="130-170"){
  # 首先找到出现在最早时间区间的细胞ID
  cell_ids <- which(colData(cds)[, "embryo.time.bin"] == time_bin)

  closest_vertex <-
    cds@principal_graph_aux[["UMAP"]]$pr_graph_cell_proj_closest_vertex
  closest_vertex <- as.matrix(closest_vertex[colnames(cds), ])
  root_pr_nodes <-
    igraph::V(principal_graph(cds)[["UMAP"]])$name[as.numeric(names
      (which.max(table(closest_vertex[cell_ids,]))))]

  root_pr_nodes
}
cds = order_cells(cds, root_pr_nodes=get_earliest_principal_node(cds))
```

然后进行可视化：

```r
plot_cells(cds,
           color_cells_by = "pseudotime",
           label_cell_groups=FALSE,
           label_leaves=FALSE,
           label_branch_points=FALSE,
           graph_label_size=1.5)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-28-063403.png)

**利用3D的发育轨迹对上述内容做个概述**

3D轨迹实际上就是降维时选前3个主成分 => `max_components = 3`，后续都和2D保持类似

> 需要注意的是，这个3D图需要等待一段时间加载，加载出来的图可以用鼠标拖动

```r
# 3D trajectories（4步走）
cds_3d = reduce_dimension(cds, max_components = 3)
cds_3d = cluster_cells(cds_3d)
cds_3d = learn_graph(cds_3d)
cds_3d = order_cells(cds_3d, root_pr_nodes=get_earliest_principal_node(cds))

cds_3d_plot_obj = plot_cells_3d(cds_3d, color_cells_by="partition")
cds_3d_plot_obj
```

![3D发育轨迹](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-28-063935.png)

### Monocle差异分析

> **版本3和版本2的差异分析可以说是完全不同**，版本3取代了2中的`differentialGeneTest()` and `BEAM()`

Monocle3提供了不同细胞类型之间寻找差异基因的方法，主要有两种：

* Regression analysis：利用`fit_models()`，用来评价基因表达是否会受到诸如时间、处理等的影响
* Graph-autocorrelation analysis：利用`graph_test()`，用来寻找一条轨迹或不同cluster中基因的差异

**方法一：Regression analysis => fit\_models()**

> 这一部分将会根据几种不同的标准进行差异分析，并且不同复杂程度的分析用时也有较大差别（几分钟甚至几小时）。教程只会针对一小部分基因进行最简单的分析，但实际上它可以处理上千基因

测试小基因集为：

```r
ciliated_genes = c("che-1",
                   "hlh-17",
                   "nhr-6",
                   "dmd-6",
                   "ceh-36",
                   "ham-1")
cds_subset = cds[rowData(cds)$gene_short_name %in% ciliated_genes,]
```

接下来，Monocle会对每个基因拟合一个回归模型，其中会加入实验中的一些因素（比如时间、处理等）。例如，在胚胎相关的单细胞数据中，细胞会在不同的时间点进行收集，这里就可以检测是否有基因在这段时间内的表达量发生了变化，利用`fit_models`

```r
gene_fits = fit_models(cds_subset, model_formula_str = "~embryo.time")
# 其中model_formula_str就是要比较的分组对象，如果相获得不同的cluster或者partition的差异基因，就用model_formula_str = "~cluster"或者model_formula_str = "~partition"；另外还支持添加多个变量，比如考虑到批次效应 model_formula_str = "~embryo.time + batch"
```

然后看看哪些基因是与时间因素相关的：

```r
fit_coefs = coefficient_table(gene_fits)
# 挑出时间相关的组分
emb_time_terms = fit_coefs %>% filter(term == "embryo.time")
# coefficient_table()默认使用 Benjamini and Hochberg（BH）方法进行了p值的校正，得到了q值
emb_time_terms %>% filter (q_value < 0.05) %>%
  select(gene_short_name, term, q_value, estimate)

# 结果数据集中的6个基因有5个是时间相关的差异基因
```

除此以外，还能**画小提琴图： => plot\_genes\_violin()**

```r
plot_genes_violin(cds_subset, group_cells_by="embryo.time.bin", ncol=2) +
    theme(axis.text.x=element_text(angle=45, hjust=1))
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-28-095900.png)

**如果要考虑批次效应**

```r
gene_fits = fit_models(cds_subset, model_formula_str = "~embryo.time + batch")
fit_coefs = coefficient_table(gene_fits)
fit_coefs %>% filter(term != "(Intercept)") %>%
  select(gene_short_name, term, q_value, estimate)
```

**模型做出来了，效果到底如何？ => evaluate\_fits()**

对模型进行评价，使用`evaluate_fits()`

```r
gene_fits = fit_models(cds_subset, model_formula_str = "~embryo.time")
evaluate_fits(gene_fits)
```

那么模型中到底要不要考虑批次呢？使用`compare_models()`比较判断。结果会返回一个likelihood ratio test结果，

```r
time_batch_models = fit_models(cds_subset,
                               model_formula_str = "~embryo.time + batch",
                               expression_family="negbinomial")
time_models = fit_models(cds_subset,
                        model_formula_str = "~embryo.time",
                        expression_family="negbinomial")
compare_models(time_batch_models, time_models) %>% select(gene_short_name, q_value)
```

其中第一个`time_batch_models`叫做`full model`，这个模型知道细胞的收集时间和批次，来预测其中每个基因的表达；第二个`time_models`叫做`reduced model`，这个模型只知道细胞的收集时间。因为第一个模型中信息更丰富，所以它对每个细胞中基因表达预测也更准。而**Monocle要回答的问题是：第一个模型相比于第二个，究竟做了多少提升？** **如果提升的部分大多来自第一个模型的批次效应，那么得到的likelihood ratio test结果就越显著**

```r
gene_short_name  q_value
  <chr>              <dbl>
1 ham-1           3.74e-20
2 dmd-6           7.11e-37
3 hlh-17          1.04e- 5
4 nhr-6           5.95e- 3
5 ceh-36          6.23e-10
6 che-1           5.06e-6
```

结果看到，**所有基因的likelihood ratio tests结果都是极显著，说明了数据中的确存在显著的批次效应**，也验证了添加批次信息的可靠性。

> 上面👆所使用的**线性模型方法要指定一个基因表达量的分布**，大多数研究使用负二项分布，这种分布往往是符合测序reads或UMI count值的，这个方法也被应用在RNA-seq分析（如DESeq2）中

`fit_models()`函数支持多种分布，默认是：`quasipoisson` ，它和负二项分布相似，只不过比负二项分布准确性低一点、速度会更快，对于细胞数量多更适用。

关于其他的分布（使用`expression_family`参数设置）：

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-28-102243.png)

**方法二：Graph-autocorrelation analysis => graph\_test()**

> 注意：下面的代码和图片看看就好（因为不能运行）

```r
# 选一部分神经元细胞
neurons_cds = cds[,colData(cds)$assigned_cell_type == "Neurons"]
plot_cells(neurons_cds, color_cells_by="partition")
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-28-133349.png)

从图中可以看到，存在不同类型的神经元细胞，它们也许来自不同亚群。为了探索这些不同类型的细胞之间是哪些基因差异表达，一种方法就是上面的线性回归。不过对于UMAP或tSNE的图，**`graph_test()`这个函数可以进行一个叫做\[Moran's I]\(**<https://en.wikipedia.org/wiki/Moran's_I>**) 的spatial autocorrelation分析方法**

> `Moran's I`这个指标叫：莫兰指数，澳大利亚统计学家莫兰于1950年提出。它是用来**度量空间相关性**的一个指标，数据经过方差归一化之后，会落在-1.0和1.0之间。这个值大于0表示空间正相关，值越大空间相关性越强；等于0时空间是随机性分布；小于0时表示空间负相关，值越小空间差异越大

```r
pr_graph_test_res = graph_test(neurons_cds, neighbor_graph="knn", cores=8)
pr_deg_ids = row.names(subset(pr_graph_test_res, q_value < 0.05))
```

`pr_graph_test_res`结果是一个数据框，存储了cds对象中每个基因的`Moran's I`检验结果。正值表示基因分布在UMAP空间的一小块”焦点“区域（例如一个或几个cluster）。

![pr\_graph\_test\_res结果](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-28-124940.png)

现在有了和”焦点“区域和基因，那么怎么把二者结合起来？

**将共同作用的基因合并成模块 => find\_gene\_modules()**

> 根据这些共同作用基因的表达量对所有细胞进行UMAP，然后利用Louvain community analysis将它们聚集成小模块，然后将小模块和大cluster甚至更大的partition联系起来

```r
gene_module_df = find_gene_modules(neurons_cds[pr_deg_ids,], resolution=1e-2)
```

得到的结果`gene_module_df`中有一行记录了每个基因和module的对应关系

要看哪个module和哪个cluster/partition相关，有**两种可视化方法：**

* 第一种：

  ```r
  cell_group_df = tibble::tibble(cell=row.names(colData(neurons_cds)), cell_group=partitions(cds)[colnames(neurons_cds)])
  agg_mat = aggregate_gene_expression(neurons_cds, gene_module_df, cell_group_df)
  row.names(agg_mat) = stringr::str_c("Module ", row.names(agg_mat))
  colnames(agg_mat) = stringr::str_c("Partition ", colnames(agg_mat))

  pheatmap::pheatmap(agg_mat, cluster_rows=TRUE, cluster_cols=TRUE,
                     scale="column", clustering_method="ward.D2",
                     fontsize=6)
  ```

  结果大体是这样：

  ![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-28-140341.png)
* 第二种：针对少数量的module可以看得比较清楚，这里选了4个

  ```r
  plot_cells(neurons_cds,
             genes=gene_module_df %>% filter(module %in% c(16,38,33,42)),
             group_cells_by="partition",
             color_cells_by="partition",
             show_trajectory_graph=FALSE)
  ```

  ![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-28-140805.png)

**找到影响发育轨迹的基因**

> 这里很重要，但学完后并没有恍然大明白的感觉，感觉跟不上作者的思维了。还要继续摸索这部分

```r
ciliated_cds_pr_test_res = graph_test(cds, neighbor_graph="principal_graph", cores=4)
# 使用neighbor_graph="principal_graph"来检验轨迹相邻的细胞的表达是否相关
pr_deg_ids = row.names(subset(ciliated_cds_pr_test_res, q_value < 0.05))
# 从pr_deg_ids中挑选几个基因，然后可视化
plot_cells(cds, genes=c("hlh-4", "gcy-8", "dac-1", "oig-8"),
           show_trajectory_graph=FALSE,
           label_cell_groups=FALSE,
           label_leaves=FALSE)

# 可以将这些在轨迹上变化的pr_deg_ids继续分为小模块
gene_module_df = monocle3:::find_gene_modules(cds[pr_deg_ids,], resolution=c(0,10^seq(-6,-1)))

cell_group_df = tibble::tibble(cell=row.names(colData(cds)), cell_group=colData(cds)$cell.type)
agg_mat = aggregate_gene_expression(cds, gene_module_df, cell_group_df)
row.names(agg_mat) = stringr::str_c("Module ", row.names(agg_mat))
pheatmap::pheatmap(agg_mat,
                    scale="column", clustering_method="ward.D2")

plot_cells(cds,
           genes=gene_module_df %>% filter(module %in% c(29,20, 11,22)),
           label_cell_groups=FALSE,
           show_trajectory_graph=FALSE)
```

**另外还有一种方法：**

先画完整的轨迹图：

```r
plot_cells(cds, show_trajectory_graph=FALSE)
```

然后挑某一个细胞亚群对应的一段轨迹：

```r
# 假设这里AFD细胞对应一段22、28、35组成的轨迹线
AFD_genes = c("gcy-8", "dac-1", "oig-8")
AFD_lineage_cds = cds[rowData(cds)$gene_short_name %in% AFD_genes,
                      clusters(cds) %in% c(22, 28, 35)]

plot_genes_in_pseudotime(AFD_lineage_cds,
                         color_cells_by="embryo.time.bin",
                         min_expr=0.5)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-08-28-143429.png)

就知道dac-1基因的激活发生在其他两个基因之前

### 如果使用版本2...

> 版本2的部分应该是要新写一篇的（后期粘上版本2的链接）

首先创建对象：

* 需要指定`exprs`、`phenoData`、`featureData`，后两个都要是`AnnotatedDataFrame`对象（目的是不让用户随便修改，看来还是版本3比较人性化），然后依然是要与表达矩阵行、列对应

```r
#do not run
HSMM_expr_matrix <- read.table("fpkm_matrix.txt")
HSMM_sample_sheet <- read.delim("cell_sample_sheet.txt")
HSMM_gene_annotation <- read.delim("gene_annotations.txt")

pd <- new("AnnotatedDataFrame", data = HSMM_sample_sheet)
fd <- new("AnnotatedDataFrame", data = HSMM_gene_annotation)
HSMM <- newCellDataSet(as.matrix(HSMM_expr_matrix),
    phenoData = pd, featureData = fd)
```

### 写在最后

#### 体验

一句话就是：满怀期待，收获满满，看得费劲

#### **优点**

* 创建对象变得方便许多，从原来的`new()`函数创建对象到现在的数据框，减少了函数的记忆
* 据说是差异分析算法进行了更新，具体新不新有待探索

#### **缺点**

* Monocle3的示例数据做的不是很走心，数据下载速度受限。这一点不如Seurat做的清晰明了
* 另外很多地方不能重复出来（比如目前加载的shinny功能还不完善，会出现无响应的状态；文档还有一些小错误）
* 许多代码操作过程复杂
* 版本2、3之间代沟很大，像是完全不同的两个版本，而不是像Seurat2、3几个参数、函数的改变
* 可视化的结果好像比版本2少了一些，不知道这部分是没有改变还是被整合了


# 5.5 多个数据集的整合


# 5.5.1 使用Seurat的merge功能进行整合

刘小泽写于19.10.8

### 前言

单细胞数据未来会朝着多样本发展，因此数据整合是一项必备技能。cellranger中自带了aggr的整合功能，而这篇文章（Differentiation dynamics of mammary epithelial cells revealed by single-cell RNA-sequencing）的作者也正是这么做得到的组合后的表达矩阵，然后用`Read10X`读入

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-10-08-055124.png)

**关于文章**

这是发表在2017年10月的NC文章。

作者的论点是：乳腺上皮细胞对研究乳腺癌的发展很重要，但目前只有很少的marker可以追踪这群细胞，因此有必要探索乳腺发育不同阶段的乳腺上皮细胞变化。

实验涉及了四个时期：8 weeks virgin =》nulliparous (NP) 未怀孕时期、14.5d gestation (G) 妊娠期第14.5天、6d lactation (L) 哺乳期第6天、11d post involution (PI) 完全退化第11天。其中每个时期都采集两只老鼠的组织细胞，所以一共8个样本，然后使用10X建库，那么最后的测序文件就是`8*3 = 24`个

如果要对这24个文件分别去整合，使用seurat的`merge`函数即可，不过**问题的关键是：如何用代码将这些样本区分开，然后分别构建对象，最后merge这些对象**

### 开始操作

### **第一步：准备原始测序数据**

我们下载第一个：GSE106273\_RAW\.tar（183.5 Mb） <https://www.ncbi.nlm.nih.gov/geo/download/?acc=GSE106273&format=file>

> 感觉手机下载速度就是比电脑快，这个文件在手机上下载3分钟，电脑预计时间1小时

下载后解压，整个过程直接在Rstudio中的Terminal直接完成

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-10-08-062817.png)

### **第二步：整理数据**

思路：根据中间的分组信息（NP、G）将包含的文件放到相应的文件夹中

**方法一：shell脚本**

```
# 将同一组数据放在同一目录下
ls GSM* | awk -F '_' '{print $2"_"$3}'| uniq | while read i;do mkdir $i;mv *$i*gz $i;done
# 各自重命名
find -name "*barcodes.tsv.gz" | while read i;do mv $i $(dirname $i)/barcodes.tsv.gz;done
find -name "*genes.tsv.gz" | while read i;do mv $i $(dirname $i)/genes.tsv.gz;done
find -name "*matrix.mtx.gz" | while read i;do mv $i $(dirname $i)/matrix.mtx.gz;done
```

**方法二：R脚本**

```r
# 列出当前目录下所有开头是GSM的文件
fs=list.files('./','^GSM')
# 然后获取四个样本信息
library(stringr)
samples=str_split(fs,'_',simplify = T)[,1]
# 设置一个循环，对每个样本信息做同样的事：
#（1）找到包含这个样本的文件(用grepl)
# (2)设置对应的目录名（str_split+paste）然后创建目录（用dir.create）
# (3)将文件放到对应目录(采用的是file.rename)并重命名文件
lapply(unique(samples),function(x){
  y=fs[grepl(x,fs)]
  folder=paste(str_split(y[1],'_',simplify = T)[,2:3],
               collapse = '')
  dir.create(folder,recursive = T)
  file.rename(y[1],file.path(folder,"barcodes.tsv.gz"))
  file.rename(y[2],file.path(folder,"genes.tsv.gz"))
  file.rename(y[3],file.path(folder,"matrix.mtx.gz"))
})
```

需要注意的是，`Read10X`函数需要读取解压后的文件，于是还要对所有的数据文件进行解压

```
find ./ -name "*gz" |xargs  gunzip
```

> **常见错误：**
>
> * 说找不到Barcode文件，但明明存在Barcode：
>
> ```r
> Error in Read10X() : Barcode file missing
> ```
>
> 那很有可能是因为三个10X数据的命名出了问题，一定要命名成"barcodes.tsv" "genes.tsv""matrix.mtx"
>
> 【补充：
>
> **cellranger的V2版本**得到的结果分别是：barcodes.tsv、genes.tsv、matrix.mtx；
>
> **V3版本**得到的结果分别是：matrix.mtx.gz、features.tsv.gz、barcodes.tsv.gz】
>
> * 说找不到基因文件，那么就要看看测序数据是不是解压后的
>
> ```r
> Error in Read10X() : Gene name or features file missing
> ```

### **第三步：批量读取成10X对象**

**Read10X() + CreateSeuratObject()**

```r
# 因为Read10X函数需要对目录进行操作，所以先把目录名提取出来
folders=list.files('./',pattern='[12]$')
> folders
[1] "G_1"  "G_2"  "L_1"  "L_2" 
[5] "NP_1" "NP_2" "PI_1" "PI_2"

# 然后使用lapply进行循环(看下lapply的帮助文档就知道，它是对列表或向量进行循环，而apply是对数据框或矩阵操作)
library(Seurat)
sceList = lapply(folders,function(folder){ 
  CreateSeuratObject(counts = Read10X(folder), 
                     project = folder )
})
# 此时的sceList仅仅是一个堆砌了8个10X对象的集合，下一步就要真正合并起来
> sceList
[[1]]
An object of class Seurat 
27998 features across 2915 samples within 1 assay 
Active assay: RNA (27998 features)

[[2]]
An object of class Seurat 
27998 features across 3106 samples within 1 assay 
Active assay: RNA (27998 features)
```

### **第四步：组合**

```r
sce.big <- merge(sceList[[1]], 
                 y = c(sceList[[2]],sceList[[3]],sceList[[4]],
                       sceList[[5]],sceList[[6]],
                       sceList[[7]],sceList[[8]]), 
                 add.cell.ids = folders, 
                 project = "mouse8")

> table(sce.big$orig.ident)
 G_1  G_2  L_1  L_2 NP_1 NP_2 PI_1 PI_2 
2915 3106 5906 3697 2249 2127 1500 4306 

save(sce.big,file = 'sce.big.merge.mouse8.Rdata') # 保存的数据是1.4G
```

### 补充

> 官网的merge教程在：<https://satijalab.org/seurat/v3.1/merge_vignette.html>

描述了三种情况

**第一种： merge两个seurat对象（原始数据）**

需要注意的是，组合数据时需要注明每个数据的名称，使用`add.cell.ids`参数指定

```r
pbmc.combined <- merge(pbmc4k, y = pbmc8k, add.cell.ids = c("4K", "8K"), project = "PBMC12K")
pbmc.combined
## An object of class Seurat 
## 33694 features across 12721 samples within 1 assay 
## Active assay: RNA (33694 features)

# 之后的组合数据就会出现列名的标识
head(colnames(pbmc.combined))
## [1] "4K_AAACCTGAGAAGGCCT" "4K_AAACCTGAGACAGACC" "4K_AAACCTGAGATAGTCA"
## [4] "4K_AAACCTGAGCGCCTCA" "4K_AAACCTGAGGCATGGT" "4K_AAACCTGCAAGGTTCT"
table(pbmc.combined$orig.ident)
## 
## PBMC4K PBMC8K 
##   4340   8381
```

**第二种：merge两个以上（原始数据）**

将参数`y` 设成一个向量，就可以指定其他的数据

```r
pbmc.big <- merge(pbmc3k, 
                  y = c(pbmc4k, pbmc8k), 
                  add.cell.ids = c("3K", "4K", "8K"), 
                  project = "PBMC15K")
unique(sapply(X = strsplit(colnames(pbmc.big), split = "_"), FUN = "[", 1))
## [1] "3K" "4K" "8K"
table(pbmc.big$orig.ident)
## 
## pbmc3k PBMC4K PBMC8K 
##   2638   4340   8381
```

**第三种：merge归一化、标准化数据**

默认情况，只会组合原始数据，但如果有的数据时标准化之后的呢？

其实可以通过一个参数`merge.data = TRUE`指定

```r
pbmc4k <- NormalizeData(pbmc4k)
pbmc8k <- NormalizeData(pbmc8k)
pbmc.normalized <- merge(pbmc4k, 
                         y = pbmc8k, 
                         add.cell.ids = c("4K", "8K"), 
                         project = "PBMC12K", 
                         merge.data = TRUE)
```

看看第一种组合raw data和第三种组合normalized data对比：

```r
#################
# raw data
#################
GetAssayData(pbmc.combined)[1:10, 1:15]
## 10 x 15 sparse Matrix of class "dgCMatrix"
##                                            
## RP11-34P13.3  . . . . . . . . . . . . . . .
## FAM138A       . . . . . . . . . . . . . . .
## OR4F5         . . . . . . . . . . . . . . .
## RP11-34P13.7  . . . . . . . . . . . . . . .
## RP11-34P13.8  . . . . . . . . . . . . . . .
## RP11-34P13.14 . . . . . . . . . . . . . . .
## RP11-34P13.9  . . . . . . . . . . . . . . .
## FO538757.3    . . . . . . . . . . . . . . .
## FO538757.2    . . . . . . . . . 1 . . . . .
## AP006222.2    . . . . . . . . . . . 1 . . .

#################
# normalized data
#################
GetAssayData(pbmc.normalized)[1:10, 1:15]
## 10 x 15 sparse Matrix of class "dgCMatrix"
##                                                           
## RP11-34P13.3  . . . . . . . . . .         . .        . . .
## FAM138A       . . . . . . . . . .         . .        . . .
## OR4F5         . . . . . . . . . .         . .        . . .
## RP11-34P13.7  . . . . . . . . . .         . .        . . .
## RP11-34P13.8  . . . . . . . . . .         . .        . . .
## RP11-34P13.14 . . . . . . . . . .         . .        . . .
## RP11-34P13.9  . . . . . . . . . .         . .        . . .
## FO538757.3    . . . . . . . . . .         . .        . . .
## FO538757.2    . . . . . . . . . 0.7721503 . .        . . .
## AP006222.2    . . . . . . . . . .         . 1.087928 . . .
```

> 上面的组合多个数据就结束了，接下来是检查组合后的分群结果

**首先检查原样本分群结果**

```r
# 归一化+标准化（移除了不想要的差异来源nCount_RNA）
sce.big <- NormalizeData(sce.big)
sce.big <- ScaleData(sce.big, vars.to.regress = c('nCount_RNA'),
                     model.use = 'linear', use.umi = FALSE)
# 默认选2000个HVGs
sce.big <- FindVariableFeatures(object = sce.big, 
                              mean.function = ExpMean, 
                              dispersion.function = LogVMR, 
                              mean.cutoff = c(0.0125,4), 
                              dispersion.cutoff = c(0.5,Inf))
# 降维（PCA+tSNE）
sce.big <- RunPCA(object = sce.big, pc.genes = VariableFeatures(sce.big))
sce.big <- RunTSNE(object = sce.big, dims.use = 1:10)
DimPlot(object = sce.big, reduction = "tsne")
# 当然也有ICA的选择 
# sce.big <- RunICA(sce.big )
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-10-08-130236.png)

**然后鉴定亚群，看看它们的分群结果**

```r
ElbowPlot(sce.big)
# 官方建议，下游分析时可以多用几个PCs试试
sce.big <- FindNeighbors(sce.big, dims = 1:20)
# 保持和原文一样的15个亚群
sce.big <- FindClusters(sce.big, resolution = 0.23)
head(Idents(sce.big), 5)
# 新的亚群结果
DimPlot(object = sce.big, reduction = "tsne",
        group.by = 'RNA_snn_res.0.23')
# 原样本分群结果
DimPlot(object = sce.big, reduction = "tsne",
        group.by = 'orig.ident')
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-10-08-131058.png)

```r
table(sce.big$orig.ident,sce.big@meta.data$RNA_snn_res.0.23)
```

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-10-08-131654.png)

看到，NP有三个主群、G有三个主群、L有三个主群、PI有两个主群

**在之前**[**单细胞天地的推送**](https://mp.weixin.qq.com/s?__biz=MzI1Njk4ODE0MQ==\&mid=2247485216\&idx=1\&sn=ad88d057acfc5e0fefd5ab69ffb46ee8\&scene=21#wechat_redirect)**中，得到了：**

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-10-08-143621.png)

一点小差别就是：第一张图的L中0组在第二张图中拆分成了0、1组

> **为了保持参数一致，设置tsne的resolution=0.3再进行测试** 但下面第三张图中L组得到的也是一大群，而且PI得到了3个主群 ![image-20191008224728646](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-10-08-144729.png)

**对比原文的数据**

它得到了3个NP、3个G、2个L、3个PI，其余的分给了Basal 4群

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-10-08-132826.png)


# 5.5.2 如何使用sctransform去除批次效应

刘小泽写于19.10.10

> &#x20;上一次介绍了[使用Seurat 的merge函数来合并4个样本（各有2个生物学重复）的8组数据](https://jieandze1314.osca.top/05/5.5-duo-ge-shu-ju-ji-de-zheng-he/5.5.1-shi-yong-seurat-de-merge-gong-neng-jin-hang-zheng-he)，但是merge只是将原始数据简单混合起来，谁也不知道混合后的结果是不是引入了批次效应

关于去除多组数据中的批次效应，有多种算法，如Seurat包的CCA(canonical correlation analysis)、LIGER的NMF(non-negative matrix factorization)、[Scran包的mnnCorrect](https://www.jianshu.com/p/b7f6a5efed85)、Seurat包的sctransform

这次就来看看sctransform是如何使用的

### 前言

> 目前教程更新于**2020-04-17**

<https://satijalab.org/seurat/v3.1/sctransform_vignette.html>

它的开发者曾说：

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-10-10-000422.png)

**还支持管道单行命令：**

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-10-10-000533.png)

[之前利用常规流程处理](https://jieandze1314.osca.top/05/5.3-seurat-de-shi-yong/5.3.1-seurat-v3-shi-zhan-zhi-2700-pbmcs-fen-xi)，得到的UMAP结果是：

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-10-10-005933.png)

### 现在再使用sctransform看看结果

**第一步：加载10X原始数据，创建对象**

```r
# 原始数据在：https://s3-us-west-2.amazonaws.com/10x.files/samples/cell/pbmc3k/pbmc3k_filtered_gene_bc_matrices.tar.gz
pbmc_data <- Read10X(data.dir = "./filtered_gene_bc_matrices/hg19/")
pbmc <- CreateSeuratObject(counts = pbmc_data)
```

**第二步：记录线粒体信息，一会进行校正**

```r
pbmc <- PercentageFeatureSet(pbmc, pattern = "^MT-", col.name = "percent.mt")

pbmc <- SCTransform(pbmc, vars.to.regress = "percent.mt", verbose = FALSE)
```

* 别看SCTransform只有一个单独的函数，其实它做了：`NormalizeData` 、`ScaleData`、`FindVariableFeatures` 的事情，并且也支持`ScaleData的vars.to.regress`
* 运行的结果存储在：`pbmc@assays$SCT)` 或者`pbmc[["SCT"]]`

  ```r
  > dim(pbmc@assays$RNA)
  [1] 32738  2700
  > dim(pbmc@assays$SCT)
  [1] 12572  2700
  > pbmc@assays$SCT
  Assay data with 12572 features for 2700 cells
  Top 10 variable features:
   S100A9, GNLY, LYZ, S100A8, NKG7, FTL, GZMB, IGLL5, CCL5, FTH1
  ```

**第三步：PCA+UMAP降维，然后聚类**

```r
pbmc <- RunPCA(pbmc, verbose = FALSE)
pbmc <- RunUMAP(pbmc, dims = 1:30, verbose = FALSE)

pbmc <- FindNeighbors(pbmc, dims = 1:30, verbose = FALSE)
pbmc <- FindClusters(pbmc, verbose = FALSE)
DimPlot(pbmc, label = TRUE) + NoLegend()
```

得到的结果是：

![](https://jieandze1314-1255603621.cos.ap-guangzhou.myqcloud.com/blog/2019-10-10-011358.png)

**注意到：这里的`FindNeighbors`使用了更多的主成分（30个）**，而之前常规分析中根据`ElbowPlot(pbmc)`结果仅使用了10个主成分就得了不错的结果。

这是因为：

> * 在常规分析中，使用少量的PC既能关注到关键的生物学差异，又能够不引入更多的技术差异，相当于一种保守性的做法。是的，它会失去一些生物差异信息，但是同时又在常规手段中比较安全。
> * 这里使用的`sctransform`，显然更“自信”一些，它认为：我很厉害，我的归一化、标准化都做得不错，多给我一些PCs吧，我能提取更多的生物差异，并且兼顾不引入技术误差

另外，常规分析中的`FindVariableFeatures`默认得到2000个高变异基因（HVGs），而**这里的`sctransform`因为使用了更多的PCs，算法也更优化，所以默认会得到3000个HVGs**。sctransform认为：新增加的这1000个基因就包含了之前没有检测到的微弱的生物学差异。而且，即使使用全部的全部的基因去做下游分析，得到的结果也是和`sctransform`这3000个基因的结果相似

**综合：单行代码实现分析**

因为SCTransform对参数的要求不是很多，一般默认参数就能应付大多数情况，因此作者也给出了单行从创建对象到最后分群的结果

```r
pbmc <- CreateSeuratObject(pbmc_data) %>% PercentageFeatureSet(pattern = "^MT-", col.name = "percent.mt") %>% 
    SCTransform(vars.to.regress = "percent.mt") %>% RunPCA() %>% FindNeighbors(dims = 1:30) %>% 
    RunUMAP(dims = 1:30) %>% FindClusters()
```

### **关于SCTransform得到的结果**

作为了解即可：它利用了正则化负二项分布（regularized negative binomial regression）计算了技术噪音模型，得到的残差是归一化值，有正有负。正值表示：考虑到细胞群体中基因的平均表达量和细胞测序深度，某个细胞的某个基因所包含的UMIs比预测值要高。

**它的结果有以下几种，不过都包含在pbmc\[\["SCT"]]**

* `pbmc[["SCT"]]@scale.data` ：包含了残差数据，用作PCA的输入。这个数据不是稀疏矩阵，因此会占用大量内存。不过SCTransform函数计算的时候，为了节省内存，默认使用了`return.only.var.genes = TRUE` ，只保留差异基因的结果
* `pbmc[["SCT"]]@counts` ：包含了校正后的UMI count值
* `pbmc[["SCT"]]@data`：包含了上面count值的log-normalized结果，有利于后面可视化
* 目前可以使用`pbmc[["SCT"]]@data`结果进行差异分析，但实际上，官方更推荐直接使用残差值`pbmc[["SCT"]]@scale.data` 【这个功能目前还不支持，会在后面的Seurat版本中更新】

**第四步：使用一些权威的marker对细胞群体注释**

这些marker的选择：

* CD8 T cell populations (naive, memory, effector), based on CD8A, GZMK, CCL5, GZMK expression
* CD4 T cell populations (naive, memory, IFN-activated) based on S100A4, CCR7, IL32, and ISG15
* Additional developmental sub-structure in B cell cluster, based on TCL1A, FCER2
* Additional separation of NK cells into CD56dim vs. bright clusters, based on XCL1 and FCGR3A

```r
VlnPlot(pbmc, features = c("CD8A", "GZMK", "CCL5", "S100A4", "ANXA1", "CCR7", "ISG15", "CD3D"), 
    pt.size = 0.2, ncol = 4)

FeaturePlot(pbmc, features = c("CD8A", "GZMK", "CCL5", "S100A4", "ANXA1", "CCR7"), pt.size = 0.2, 
    ncol = 3)

FeaturePlot(pbmc, features = c("CD3D", "ISG15", "TCL1A", "FCER2", "XCL1", "FCGR3A"), pt.size = 0.2, 
    ncol = 3)
```


