泛基因组分析器
GenomeAnalyzer
00 Remarks
整个分析过程被重构为:
- 索引化:在分析开始之前,一次性把所有的字符串主键(基因ID、基因组名、家族ID) 映射为连续的整数下标,后续的热循环全部在并行数组(
Int32)上进行, 避免上百万次的字符串哈希查找; - 去二次化:直系同源分组(例如cd-hit的cluster)直接做 k-1 次并查集合并, 不再生成 O(k^2) 的两两配对中间对象;共线性分析不再对每个基因组对做全基因表扫描; 泛基因组曲线由 O(迭代数 x 基因组数 x 家族数) 的全表扫描改为 O(迭代数 x 基因数) 的增量计数;
- 并行化:家族聚类之后相互独立的计算单元(基因家族、基因组对、蒙特卡洛迭代) 全部通过
Parallel做数据并行。
01 Syntax
02 Methods
| Name | Overloads | Summary |
|---|---|---|
| .ctor | 1 | |
| BuildIndex | 1 | 一次性构建好所有的整数索引,后续的分析过程将不再需要针对上百万个基因 反复做字符串哈希查找 |
| CompareGeneLocus | 1 | 基因组内的基因排序比较函数:先按照染色体,再按照起始位点 |
| MakeFamilyMapping | 1 | 通过BBH两两比对结果建立基因家族(并查集合并) |
| BuildFamilyIndex | 1 | 从并查集之中提取出基因家族,并且建立家族的整数索引 |
| AnalyzePanGenome | 2 | 执行泛基因组分析的主函数 |
| RunAnalysis | 1 | 家族聚类完成之后的所有分析步骤 |
| BuildPAVAndClassify | 1 | 并行构建PAV矩阵、同时对基因家族做分类, 并且顺带统计四个类别在各个基因组之内的占比(两种口径) |
| BuildCategoryPercent | 1 | 计算四个基因家族类别在各个基因组之内的占比(两种口径)以及全部基因组的平均值 |
| CalculateCopyNumber | 1 | 计算特定基因家族在特定基因组中的拷贝数 |
| CalculatePangenomeCurve | 1 | 计算泛基因组曲线(基于蒙特卡洛模拟) |
| BuildFamilyToGeneMap | 1 | 建立 基因组 -> (家族 -> 基因) 的映射,用于快速的查找某个家族在某个基因组内的唯一同源基因 |
| CalculateCollinearity | 1 | 计算基因组间的共线性区块(并行版) |
| CalculatePairCollinearity | 1 | 计算一对基因组之间的共线性区块 |
| MakeCollinearBlock | 1 | 生成共线性区块;在不保留逐基因配对数据的模式下只生成统计摘要 |
| GetBlockRange | 1 | 计算共线性区块在两个基因组之上所覆盖的坐标范围 |
| DetectInversion | 1 | 检测基因顺序反向的区块 |
| DetectStructuralVariations | 1 | 基于泛基因组聚类结果和共线性分析结构变异(并行版) |
| GenesOfGenome | 1 | 取出基因家族之内属于某一个基因组的基因ID |
| OrderKey | 1 | 辅助函数:生成唯一的基因组对Key(无论顺序) |
| CalculatePanGenomeJaccardDistance | 1 | 基于泛基因组基因家族计算基因组间的 Jaccard 遗传距离矩阵(位图 + 并行) |
| CalculateGeneticDistance | 1 | 基于直系同源比对计算基因组间的遗传距离矩阵 |
03 Properties
| Name | Overloads | Summary |
|---|---|---|
| RetainOrthologyLinks | 1 | 是否在共线性结果之中保留逐基因的同源配对数据? |
| CurveIterations | 1 | 泛基因组曲线的蒙特卡洛模拟迭代次数 |
| SVClusterCount | 1 | SV结构变异的信息熵散点图所使用的KMeans簇数(默认4,对应四类进化模式象限) |
| CoreThreshold | 1 | |
| SoftCoreThreshold | 1 | |
| ShellThreshold | 1 | |
| CNV_Gain_Factor | 1 | |
| CNV_Loss_Factor | 1 | |
| MinCollinearGenes | 1 |
04 Fields
| Name | Overloads | Summary |
|---|---|---|
| MaxRetainLinkGenomes | 1 | 当基因组的数量超过这个阈值的时候,共线性结果中将只会保留区块的统计信息, 不再保留逐基因的同源配对数据,以避免占用过大的内存 |
| CategoryCore | 1 | 家族类别:核心基因(在所有基因组之中都存在) |
| CategorySoftCore | 1 | 家族类别:软核心基因(出现比例不低于软核心阈值) |
| CategoryShell | 1 | 家族类别:壳基因 |
| CategoryCloud | 1 | 家族类别:云基因 |
| CategoryCount | 1 | 基因家族的类别数量 |
| CategoryNames | 1 | 四个家族类别的名称,顺序与 GenomeAnalyzer.CategoryCore 等类别下标一致 |
| CategoryColors | 1 | 四个家族类别的配色,与报告页面上的饼图/条形图保持一致 |
| geneAnnotations | 1 | 全局基因注释字典(用于查询基因所属基因组) |
| geneIds | 1 | 下标 -> 基因ID |
| geneIndex | 1 | 基因ID -> 下标 |
| geneGenome | 1 | 下标 -> 所属基因组下标 |
| genomeList | 1 | 有序的基因组名称列表 |
| genomeGenes | 1 | 每个基因组内按照(染色体, 起始位点)排序好的基因下标,只排序一次供所有算法复用 |
| familyIds | 1 | 下标 -> 家族ID |
| familyMembers | 1 | 家族下标 -> 该家族内的基因下标 |
| familyGeneIds | 1 | 家族下标 -> 该家族内的基因ID |
| geneFamily | 1 | 基因下标 -> 家族下标(-1表示未聚类) |
| familyToGene | 1 | 基因组下标 -> (家族下标 -> 该基因组内的唯一基因下标,多拷贝或者不存在为-1) |
05 Members
Dictionary(Of String, GeneInfo), UnionFind)| Name | Type | Description |
|---|---|---|
geneAnnotations | Dictionary(Of String, GeneInfo) | 所有基因的详细信息字典,Key为GeneID |
一次性构建好所有的整数索引,后续的分析过程将不再需要针对上百万个基因 反复做字符串哈希查找
Int32, Int32)基因组内的基因排序比较函数:先按照染色体,再按照起始位点
Dictionary(Of String, BiDirectionalBesthit()))通过BBH两两比对结果建立基因家族(并查集合并)
从并查集之中提取出基因家族,并且建立家族的整数索引
Dictionary(Of String, BiDirectionalBesthit()))执行泛基因组分析的主函数
| Name | Type | Description |
|---|---|---|
orthologDict | Dictionary(Of String, BiDirectionalBesthit()) | 直系同源比对结果(BBH) |
分析结果对象
Dictionary(Of String, String()), Dictionary(Of String, String()))执行泛基因组分析的主函数(直接基于直系同源分组,例如cd-hit的聚类结果)
| Name | Type | Description |
|---|---|---|
orthoGroups | Dictionary(Of String, String()) | Key为家族/聚类ID,Value为该家族之内的基因ID集合。 一个包含k个基因的家族只需要 k-1 次并查集合并操作, 不需要先展开为 O(k^2) 个两两配对关系。 |
orthoSources | Dictionary(Of String, String()) | 与orthoGroups一一对应的基因来源(一般是replicon编号), 用于保持与BBH路径一致的行为:同一个来源之内的基因不会被合并到同一个家族之中。 |
分析结果对象
Dictionary(Of String, BiDirectionalBesthit()))家族聚类完成之后的所有分析步骤
Int32, Int32)并行构建PAV矩阵、同时对基因家族做分类, 并且顺带统计四个类别在各个基因组之内的占比(两种口径)
Int32()(), Int32)计算四个基因家族类别在各个基因组之内的占比(两种口径)以及全部基因组的平均值
当基因组的数量非常多的时候,壳基因与云基因会因为基因组数量的累加而在绝对数量上面 远远超过核心基因与软核心基因;但是它们在单个基因组内部的占比其实并不高。 这里通过"单个基因组内部的占比"这个口径来消除基因组数量对绝对数量的放大效应。
| Name | Type | Description |
|---|---|---|
totals | Int32()() | 类别占比累加器:行 0..3 是四个类别的拷贝数之和,行 4..7 是四个类别出现的家族个数 |
N | Int32 | 基因组数量 |
List(Of String), String)计算特定基因家族在特定基因组中的拷贝数
| Name | Type | Description |
|---|---|---|
familyGenes | List(Of String) | 该基因家族包含的所有基因ID列表 |
targetGenomeName | String | 目标基因组名称 |
拷贝数
Int32)计算泛基因组曲线(基于蒙特卡洛模拟)
原来的实现在每一次迭代的每一个基因组上面都要对全部基因家族做一次LINQ全表扫描, 复杂度为 O(迭代数 x 基因组数 x 家族数)。
这里改为增量计数:为每一次迭代维护一个家族计数器,加入第 i 个基因组的时候, 该基因组的家族集合 S 之内:
- 计数为0的家族 -> 泛基因组大小 +1;
- 计数为i的家族 -> 说明该家族在已经加入的i个基因组之中都存在, 加入当前基因组之后仍然为核心基因,核心基因数量就是这类家族的数量;
- 计数加一之后不低于 ceil(软核心阈值 x (i+1)) 的家族 -> 说明该家族在目前 已经加入的 i+1 个基因组之中的出现比例不低于软核心阈值,依然属于软核心基因。
核心基因的判定条件(S=100%)非常严格,在基因组数量增加的时候核心基因的数量 会迅速衰减到零;软核心基因只要求出现比例不低于阈值(GenomeAnalyzer.SoftCoreThreshold, 默认为95%,可以通过R#脚本的build_context接口调整),因此软核心曲线在基因组数量 增加的时候依然能够保持在一个可观的水平上面。
复杂度降低为 O(迭代数 x 基因总数)。
建立 基因组 -> (家族 -> 基因) 的映射,用于快速的查找某个家族在某个基因组内的唯一同源基因
Dictionary(Of String, BiDirectionalBesthit()))计算基因组间的共线性区块(并行版)
原来的实现对每一个基因组对都做了一次全基因表的扫描与排序, 复杂度为 O(N^2 x 基因总数),在几百个基因组的数据集上面是不可用的。 这里改为:基因按照基因组预分组并且只排序一次;同源关系通过预建的 家族索引做 O(1) 查找;基因组对之间并行处理,处理完之后立即丢弃中间结果。
| Name | Type | Description |
|---|---|---|
orthologDict | Dictionary(Of String, BiDirectionalBesthit()) | BBH两两比对结果;如果这个参数为空,则直接基于基因家族索引推导同源关系 |
Int32, Int32, Dictionary(Of String, List(Of BiDirectionalBesthit)))计算一对基因组之间的共线性区块
生成共线性区块;在不保留逐基因配对数据的模式下只生成统计摘要
区块在两个基因组之上所覆盖的坐标范围由区块内所有基因的最小起始位点与最大终止位点决定, 不论是否保留逐基因的同源配对数据,坐标范围都会被写入结果之中, 这样子后续就可以直接使用这些坐标来绘制共线性点图或者带状图了。
| Name | Type | Description |
|---|---|---|
source | CollinearBlock | 包含基因组名称与染色体信息的区块模板 |
links | List(Of OrthologyLink) | 区块之内的同源基因对 |
queryIdx | List(Of Int32) | 与links一一对应的基因组1侧基因下标 |
hitIdx | List(Of Int32) | 与links一一对应的基因组2侧基因下标 |
List(Of Int32), List(Of Int32))计算共线性区块在两个基因组之上所覆盖的坐标范围
| Name | Type | Description |
|---|---|---|
queryIdx | List(Of Int32) | 区块之内的基因组1侧基因下标 |
hitIdx | List(Of Int32) | 区块之内的基因组2侧基因下标 |
检测基因顺序反向的区块
| Name | Type | Description |
|---|---|---|
block | CollinearBlock | - |
基于泛基因组聚类结果和共线性分析结构变异(并行版)
这个函数要求在调用前需要完成共线性检测计算
Int32(), Int32)取出基因家族之内属于某一个基因组的基因ID
String, String)辅助函数:生成唯一的基因组对Key(无论顺序)
| Name | Type | Description |
|---|---|---|
g1 | String | - |
g2 | String | - |
基于泛基因组基因家族计算基因组间的 Jaccard 遗传距离矩阵(位图 + 并行)
使用位图(bitmap)来表示每一个基因组的基因家族集合, 两个基因组之间的交集大小可以通过位图的 AND + popcount 在 O(家族数/64) 之内得到, 相比原来的 HashSet 遍历要快一个数量级,并且内存占用也很小。
Dictionary(Of String, BiDirectionalBesthit()), List(Of String))基于直系同源比对计算基因组间的遗传距离矩阵
是否在共线性结果之中保留逐基因的同源配对数据?
在基因组数量非常多的时候(例如几百个基因组的两两比较),同源配对的数据量是 O(N^2 x 平均每基因组基因数),会占用非常巨大的内存。默认情况下当基因组数量 大于 GenomeAnalyzer.MaxRetainLinkGenomes 的时候这个开关会被自动关闭, 只保留 CollinearBlock.LinkCount 统计信息。
泛基因组曲线的蒙特卡洛模拟迭代次数
SV结构变异的信息熵散点图所使用的KMeans簇数(默认4,对应四类进化模式象限)
当基因组的数量超过这个阈值的时候,共线性结果中将只会保留区块的统计信息, 不再保留逐基因的同源配对数据,以避免占用过大的内存
家族类别:核心基因(在所有基因组之中都存在)
家族类别:软核心基因(出现比例不低于软核心阈值)
家族类别:壳基因
家族类别:云基因
基因家族的类别数量
四个家族类别的名称,顺序与 GenomeAnalyzer.CategoryCore 等类别下标一致
四个家族类别的配色,与报告页面上的饼图/条形图保持一致
全局基因注释字典(用于查询基因所属基因组)
下标 -> 基因ID
基因ID -> 下标
下标 -> 所属基因组下标
有序的基因组名称列表
每个基因组内按照(染色体, 起始位点)排序好的基因下标,只排序一次供所有算法复用
下标 -> 家族ID
家族下标 -> 该家族内的基因下标
家族下标 -> 该家族内的基因ID
基因下标 -> 家族下标(-1表示未聚类)
基因组下标 -> (家族下标 -> 该基因组内的唯一基因下标,多拷贝或者不存在为-1)