轨迹分析系列:
速率分析系列:
官网: https://scvelo.readthedocs.io/getting_started/
1. 简介
测量单个细胞中的基因活性需要破坏这些细胞以读取其内容,这使得研究动态过程和了解细胞命运决定具有挑战性。 La Manno et al. (Nature, 2018)引入了 RNA velocity 的概念,利用新转录的未剪接的前体 mRNA 和成熟的剪接 mRNA 可以在常见的单细胞 RNA-seq 流程中区分的事实,可以恢复定向动态信息,前者可通过内含子的存在检测。这种不仅测量基因活性,而且测量它们在单个细胞中的变化(RNA 速率)的概念,开辟了研究细胞分化的新方法。最初提出的框架将速率作为观察到的剪接和未剪接 mRNA 的比率与推断的稳态的偏差。如果违反了共同剪接速率的中心假设和对具有稳态 mRNA 水平的完整剪接动力学的观察,则会出现速率估计错误。
Bergen et al. (Nature Biotechnology, 2020)开发了 scVelo,通过使用基于似然的动力学模型求解剪接动力学的完整转录动力解决了这些局限。这将 RNA 速率推广到包括瞬态细胞状态的各种系统,这些系统在发育和对扰动的响应中很常见。此外,scVelo 推断转录、剪接和降解的基因特异性速率,并恢复在细胞过程的潜在时间。这个仅基于其转录动力学的潜在时间代表细胞的内部时钟,并近似于细胞在分化时经历的真实时间。此外,scVelo 识别调节变化的机制,例如细胞命运决定的阶段,并在其中系统地检测推定的驱动基因。(参考:单细胞测序的轨迹推断)
目前的scVelo的主要应用:
计算RNA velocity
确定可能和细胞动态变化过程相关的驱动基因
推定转录事件的时序
估算RNA相关的转录剪切降解速率
做统计检验
核心步骤:
scv.pp.filter_and_normalize(adata)
选择基因,然后标准化
scv.pp.moments(adata)
计算一阶和二阶矩
scv.tl.velocity(adata)
计算velocity,默认mode='stochastic',也可以设置成'dynamics' (Dynamical Modeling)
scv.tl.velocity_graph(adata)
将velocity投影到降维之后的封装(t-SNE/umap)之中
可视化:
scv.pl.velocity_embedding(adata, basis='umap')
scv.pl.velocity_embedding_grid(adata, basis='umap')
scv.pl.velocity_embedding_stream(adata, basis='umap')
scv.pl.velocity(adata, ['Cpe', 'Gnao1', 'Ins2', 'Adk'], ncols=2)
scv.pl.scatter(adata, 'Cpe', color=['clusters', 'velocity'],add_outline='Ngn3 high EP, Pre-endocrine, Beta')
scv.pl.velocity_graph(adata, threshold=.1)
2. 安装
scVelo 需要 Python 3.6 或更高版本。建议使用Miniconda。
- PyPI
使用以下命令从PyPI安装 scVelo :
pip install -U scvelo
-U 是--upgrade 的缩写。如果出现Permission denied错误,请改用pip install -U scvelo --user
- 开发版
要使用最新的开发版本,请使用以下命令从GitHub安装:
pip install git+https://github.com/theislab/scvelo
或者
git clone https://github.com/theislab/scvelo
pip install -e scvelo
-e是--editable的缩写,将包链接到原始克隆位置,这样拉取的更改也会反映在环境中。
- 依赖包
anndata - 带注释的数据对象。
scanpy - 用于单细胞分析的工具包。
numpy、scipy、pandas、scikit-learn、matplotlib。
部分scVelo(定向 PAGA 和 Louvain 模块化)需要安装(可选):
pip install python-igraph louvain
通过hnswlib使用快速邻近搜索进一步需要安装(可选):
pip install pybind11 hnswlib
3. Getting Started
3.1 导入数据
import scvelo as scv
scv.logging.print_version()
scv.settings.verbosity = 3 # show errors(0), warnings(1), info(2), hints(3)
scv.settings.presenter_view = True # set max width size for presenter view
scv.set_figure_params('scvelo') # for beautified visualization
分析基于内置胰腺数据。
如果要对自己的数据进行速率分析,可以通过adata = scv.read('path/file.loom', cache=True)
将文件(loom, h5ad, csv …)读取到AnnData数据对象。
如果想将loom文件合并到已存在的 AnnData 对象中,可以使用scv.utils.merge(adata, adata_loom)
。
adata = scv.datasets.pancreas()
adata
# 查看细胞类型
adata.obs['clusters'].unique()
scVelo 是基于adata
。这个对象存储了数据矩阵adata.X
、观测注释adata.obs
、变量adata.var
和非结构化注释的对象adata.uns
。观测和变量的名称可分别通过adata.obs_names
和adata.var_names
访问。AnnData可以像数据框一样进行取切片操作。例如:adata_subset = adata[:, list_of_gene_names]
。更多细节可参考anndata docs。
# 8类细胞剪切和未剪切的比例
scv.pl.proportions(adata)
这个图展示了剪切/未剪切count的比例。根据使用单细胞技术的不同(Drop-Seq,Smart-Seq),数据中通常有10%-25%的未剪切分子含有内含子序列。作者还建议检查cluster水平的变化,以验证剪切效率的一致性。这个图显示,如预期的变化那样,在循环导管细胞中未剪切的比例略低,在许多基因开始转录的Ngn3高表达的和内分泌前细胞中的比例更高。
3.2 数据预处理
预处理包括:
- 通过检测(以最少计数检测)和高变异性(离散度)进行基因选择,也就是过滤。
- 按每个细胞的初始大小和logX使每个细胞标准化。
过滤是同时对spliced/unspliced counts和X进行的,Logarithmizing则仅仅对X进行。如果X在先前的操作中已经被标准化,这里则不会再次标准化。
上述的操作在scVelo中都被包装在一个函数中:scv.pp.filter_and_normalize
,但实际上它包含了如下四步:
# scv.pp.filter_genes(adata, min_shared_counts=20)
# scv.pp.normalize_per_cell(adata)
# scv.pp.filter_genes_dispersion(adata, n_top_genes=2000)
# scv.pp.log1p(adata)
此外,我们需要在PCA空间中最近的邻居之间计算的一阶矩和二阶矩 (first and second order moments) (平均值和去中心化方差),这些内容封装在scv.pp.moments
。这个函数实际上计算了scv.pp.pca
和scv.pp.neighbors
。确定速率估计需要一阶矩,而随机估计需要二阶矩。
scv.pp.filter_and_normalize(adata, min_shared_counts=20, n_top_genes=2000)
scv.pp.moments(adata, n_pcs=30, n_neighbors=30)
进一步的预处理(如批次校正)可用于消除不必要的变异源。更多详细信息参考最佳实践。注意,任何额外的预处理步骤仅影响count data X,不应用于spliced/unspliced counts。
3.3 估计RNA速率
速率是基因表达空间中的载体,代表单个细胞运动的方向和速率。无论是随机模型(默认)还是确定性模型(通过设置mode='deterministic'
),速率都是通过对拼接动力学的转录动力学进行建模获得。对于每个基因,预成熟(未剪切)和成熟(剪切)mRNA计数的稳定状态比,构成一个恒定的转录状态。然后,从此比率中获取速率作为残差。正速率表示基因被上调,这发生在细胞显示该基因的未剪切mRNA的丰度高于预期的稳定状态。相反,负速表示基因被下调。
完整的动力学模型的解决方案是通过设置mode='dynamical'
获得的,这需要事先运行scv.tl.recover_dynamics(adata)
。我们将在下一个教程中详细阐述动态模型。
scv.tl.velocity(adata)
computing velocities
finished (0:00:01) --> added
'velocity', velocity vectors for each individual cell (adata.layers)
计算的速率存储在计数矩阵adata.layers中。
查看一下这个函数
help(scv.tl.velocity)
adata
然后,基因间速率的组合可用于估计单个细胞的未来状态。为了将速率投射到低维嵌入中,我们估计了细胞-细胞间的转换概率 (transition probabilities) 。也就是说,对于每个速率矢量,我们寻找了和矢量方向一致的可能的细胞转换。转换概率是使用潜在细胞到细胞的过渡和速率矢量之间的余弦相关性 (cosine correlation) 计算的,并存储在表示速率图的矩阵中。由此产生的速率图具有维度nobs×nobs,并总结了通过速率矢量可以很好地解释的可能的细胞状态变化(对于运行时速率,也可以通过设置approx=True在减少的 PCA 空间上计算)。
scv.tl.velocity_graph(adata)
对于多种应用来说,通过应用高斯函数将余弦值相关性转换为实际的过渡概率,速率图可以转换为transition matrix。我们可以通过scv.utils.get_transition_matrix
访问这个马尔可夫转移矩阵。
如前所述,这个函数内部通过对scv.tl.velocity_embedding
获得的平均概率过渡概率进行平均转换,将速率投射到低维嵌入中。此外,我们可以通过scv.tl.terminal_states
沿着马尔可夫转移矩阵追踪细胞的起源和潜在命运,从而获得根细胞和终点的轨迹。
3.4 绘制结果图
最后,速率被投影到any embedding中 (通过basis指定),并以以下方式之一可视化:
- 细胞水平:
scv.pl.velocity_embedding
- 网格线:
scv.pl.velocity_embedding_grid
- 流线型:
scv.pl.velocity_embedding_stream
请注意,数据已预计算 UMAP 嵌入,并注释了细胞群。在应用到自己的数据时,可以使用scv.tl.umap
和scv.tl.louvain
。详细信息参考scanpy tutorial。此外,所有绘图功能都默认使用basis='umap'
和color='clusters'
,这些设置可以根据需要进行更改。
scv.pl.velocity_embedding_stream(adata, basis='umap')
流线显示的速率矢量可对发育过程进行详细分析。它准确地勾画了导管细胞和内分泌祖细胞的循环。此外,它阐明了细胞的谱系命运、细胞周期回归和内分泌细胞分化状态。
图中的箭头显示了单个细胞运动的方向和速率,从未剪切指向剪切。这个结果提示了 Ngn3 细胞(黄色)的早期内分泌命运,以及近端α细胞(蓝色)和瞬态β细胞(绿色)之间的明显分化差异。
scv.pl.velocity_embedding(adata, arrow_length=3, arrow_size=2, dpi=120)
scv.pl.velocity_embedding_grid(adata, basis='umap')
3.5 解释速率
这可能是最重要的部分,因为我们建议用户不要将生物结论限制在预测速率上,而是通过图像来检查个体基因动力学,以了解特定基因如何支持推断方向。
查看GIF,了解如何解释剪切与未剪切的图像。基因活动是由转录调节的。
特定基因的转录诱导导致(新转录的)前体未剪切 mRNA 增加,而相反,抑制或没有转录会导致未转录 mRNA 的减少。拼接的 mRNA 由未剪切的 mRNA 生成,并遵循相同的趋势,并具有时滞。时间是一个隐藏/潜在的变量。因此,需要从实际测量中推断出动态:phase portrait中展示的剪切和未剪切的 mRNA。
现在,让我们来检查一些标记基因的图像,通过scv.pl.velocity(adata, gene_names)
或scv.pl.scatter(adata, gene_names)
可视化
scv.pl.velocity(adata, ['Cpe', 'Gnao1', 'Ins2', 'Adk'], ncols=2)
对角黑线对应着估计的"稳定状态"的未剪切/剪切比率,即unspliced和spliced的 mRNA 丰度比值。spliced的 mRNA 丰度处于恒定的转录状态。特定基因的RNA速率被确定为残差(residual),也就是观察值与稳定状态线的偏差程度 (见上面链接的动图) 。正速率表示基因被向上调节,这发生在细胞显示该基因的未剪切mRNA的丰度高于预期的稳定状态。相反,负速率表示基因表达降低。
例如,Cpe可以解释上调的Ngn3 high EP(黄色)--Pre endocrine -(橙色)--β细胞(绿色)的分化方向,而Adk则解释了下调的Ductal(深绿色)-- Ngn3 high EP(黄色)到remaining endocrine细胞的方向。
scv.pl.scatter(adata, 'Cpe', color=['clusters', 'velocity'],add_outline='Ngn3 high EP, Pre-endocrine, Beta')
3.6 识别重要基因
我们需要一种系统的方法来识别基因,这可能有助于解释由此产生的向量场和推断的谱系。为此,我们可以测试哪些基因具有群体特异性微分速率表达,与剩余群相比,其比例要高得多/更低。模块scv.tl.rank_velocity_genes
运行差异速率 t-test,并生成每个cluster的基因排名。可以设置阈值(例如min_corr
),以限制对基因候选者选择的test。
scv.tl.rank_velocity_genes(adata, groupby='clusters', min_corr=.3)
df = scv.DataFrame(adata.uns['rank_velocity_genes']['names'])
df.head()
kwargs = dict(frameon=False, size=10, linewidth=1.5,
add_outline='Ngn3 high EP, Pre-endocrine, Beta')
scv.pl.scatter(adata, df['Ngn3 high EP'][:5], ylabel='Ngn3 high EP', **kwargs)
scv.pl.scatter(adata, df['Pre-endocrine'][:5], ylabel='Pre-endocrine', **kwargs)
例如, Ptprs, Pclo, Pam, Abcc8, Gnas等基因支持从Ngn3 高表达的 EP(黄色)到内分泌前(橙色)到Beta(绿色)的方向性。
3.7 循环祖细胞的速率(可选,了解一下)
RNA速率检测到的细胞周期,通过细胞周期评分(相标记基因平均表达水平的标准化分数)在生物学上得到了证实。
scv.tl.score_genes_cell_cycle(adata)
scv.pl.scatter(adata, color_gradients=['S_score', 'G2M_score'], smooth=True, perc=[5, 95])
对于循环导管细胞,我们可以筛选S和G2M期的marker。前一个模块还计算了一个spearmans 相关分数,我们可以用它来对phase marker基因进行排名/排序,然后显示它们的phase portraits。
s_genes, g2m_genes = scv.utils.get_phase_marker_genes(adata)
s_genes = scv.get_df(adata[:, s_genes], 'spearmans_score', sort_values=True).index
g2m_genes = scv.get_df(adata[:, g2m_genes], 'spearmans_score', sort_values=True).index
kwargs = dict(frameon=False, ylabel='cell cycle genes')
scv.pl.scatter(adata, list(s_genes[:2]) + list(g2m_genes[:3]), **kwargs)
特别是Hells 和Top2a非常适合解释循环祖细胞的矢量场。Top2a 在 G2M期实际达到峰值前不久被分配了高速。在这里,负的速率和随后的下调完美匹配。
scv.pl.velocity(adata, ['Hells', 'Top2a'], ncols=2, add_outline=True)
3.8 速率和连贯性
还有两个有用的统计数据:
- 分化的速度/速率由速率矢量的长度给出。
- 矢量场的一致性(即速率矢量如何与其邻近速率相关)提供了置信度的衡量标准。
scv.tl.velocity_confidence(adata)
keys = 'velocity_length', 'velocity_confidence'
scv.pl.scatter(adata, c=keys, cmap='coolwarm', perc=[5, 95])
These provide insights where cells differentiate at a slower/faster pace, and where the direction is un-/determined.
On cluster-level, we find that differentiation substantially speeds up after cell cycle exit (Ngn3 low EP), keeping the pace during Beta cell production while slowing down during Alpha cell production.
df = adata.obs.groupby('clusters')[keys].mean().T
df.style.background_gradient(cmap='coolwarm', axis=1)
3.9 速率图和拟时间
我们可以可视化速率图,以描绘所有速率推断的细胞-细胞连接/过渡。它可以通过设置threshold
限制在高概率转换。例如,该图指示来自早期和晚期内分泌细胞的 Epsilon 细胞产生的两个阶段。
scv.pl.velocity_graph(adata, threshold=.1)
此外,该图可用于绘制来自特定细胞的后代/祖先。在这里,内分泌前细胞可以追溯到其潜在的命运。
x, y = scv.utils.get_cell_transitions(adata, basis='umap', starting_cell=70)
ax = scv.pl.velocity_graph(adata, c='lightgrey', edge_width=.05, show=False)
ax = scv.pl.scatter(adata, x=x, y=y, s=120, c='ascending', cmap='gnuplot', ax=ax)
最后,根据速率图,可以计算速率伪时间。在从图形中推断出 root cells 的分布后,它可以计算从 root cells 开始沿着图形到达一个细胞所需的平均步数。
与diffusion pseudotime相反, it implicitly infers the root cells。而且它是基于directed velocity graph,而不是基于相似性的diffusion kernel。
scv.tl.velocity_pseudotime(adata)
scv.pl.scatter(adata, color='velocity_pseudotime', cmap='gnuplot')
3.10 PAGA速率图
PAGA graph abstraction has benchmarked as top-performing method for trajectory inference. It provides a graph-like map of the data topology with weighted edges corresponding to the connectivity between two clusters. Here, PAGA is extended by velocity-inferred directionality.
# PAGA requires to install igraph, if not done yet.
!pip install python-igraph --upgrade --quiet
# this is needed due to a current bug - bugfix is coming soon.
adata.uns['neighbors']['distances'] = adata.obsp['distances']
adata.uns['neighbors']['connectivities'] = adata.obsp['connectivities']
scv.tl.paga(adata, groups='clusters')
df = scv.get_df(adata, 'paga/transitions_confidence', precision=2).T
df.style.background_gradient(cmap='Blues').format('{:.2g}')
reads从左/行到右/列读取,例如分配了从Ductal到Ngn3 low EP的可信过渡。
此表可以通过叠加在 UMAP 嵌入上的定向图进行汇总展示。
scv.pl.paga(adata, basis='umap', size=50, alpha=.1,
min_edge_width=2, node_size_scale=1.5)
4. Dynamical Modeling
4.1 数据准备
和前面一样
import scvelo as scv
scv.logging.print_version()
scv.settings.verbosity = 3 # show errors(0), warnings(1), info(2), hints(3)
scv.settings.presenter_view = True # set max width size for presenter view
scv.settings.set_figure_params('scvelo') # for beautified visualization
adata = scv.datasets.pancreas()
scv.pp.filter_and_normalize(adata, min_shared_counts=20, n_top_genes=2000)
scv.pp.moments(adata, n_pcs=30, n_neighbors=30)
4.2 动力学模型
我们使用动力学模型来学习splicing kinetics的全部转录动态
It is solved in a likelihood-based expectation-maximization framework, by iteratively estimating the parameters of reaction rates and latent cell-specific variables, i.e. transcriptional state and cell-internal latent time. It thereby aims to learn the unspliced/spliced phase trajectory for each gene.
scv.tl.recover_dynamics(adata)
scv.tl.velocity(adata, mode='dynamical')
scv.tl.velocity_graph(adata)
# 运行动力学模型可能需要一段时间。因此可以存储结果以供重复使用
#adata.write('data/pancreas.h5ad', compression='gzip')
#adata = scv.read('data/pancreas.h5ad')
scv.pl.velocity_embedding_stream(adata, basis='umap')
4.3 Kinetic rate paramters
无需实验即可估算RNA转录、剪切和降解的速率。
它们有助于更好地了解细胞身份和表型异质性。
df = adata.var
df = df[(df['fit_likelihood'] > .1) & df['velocity_genes'] == True]
kwargs = dict(xscale='log', fontsize=16)
with scv.GridSpec(ncols=3) as pl:
pl.hist(df['fit_alpha'], xlabel='transcription rate', **kwargs)
pl.hist(df['fit_beta'] * df['fit_scaling'], xlabel='splicing rate', xticks=[.1, .4, 1], **kwargs)
pl.hist(df['fit_gamma'], xlabel='degradation rate', xticks=[.1, .4, 1], **kwargs)
scv.get_df(adata, 'fit*', dropna=True).head()
4.4 Latent time(相当于轨迹分析)
The dynamical model recovers the latent time of the underlying cellular processes. This latent time represents the cell’s internal clock and approximates the real time experienced by cells as they differentiate, based only on its transcriptional dynamics.
scv.tl.latent_time(adata)
scv.pl.scatter(adata, color='latent_time', color_map='gnuplot', size=80)
top_genes = adata.var['fit_likelihood'].sort_values(ascending=False).index[:300]
scv.pl.heatmap(adata, var_names=top_genes, sortby='latent_time', col_color='clusters', n_convolve=100)
4.5 Top-likelihood genes
Driver genes display pronounced dynamic behavior and are systematically detected via their characterization by high likelihoods in the dynamic model.
# 查看top15的基因
top_genes = adata.var['fit_likelihood'].sort_values(ascending=False).index
scv.pl.scatter(adata, basis=top_genes[:15], ncols=5, frameon=False)
# 查看特定基因
var_names = ['Actn4', 'Ppp3ca', 'Cpe', 'Nnat']
scv.pl.scatter(adata, var_names, frameon=False)
scv.pl.scatter(adata, x='latent_time', y=var_names, frameon=False)
4.6 Cluster-specific top-likelihood genes
scv.tl.rank_dynamical_genes(adata, groupby='clusters')
df = scv.get_df(adata, 'rank_dynamical_genes/names')
df.head(5)
for cluster in ['Ductal', 'Ngn3 high EP', 'Pre-endocrine', 'Beta']:
scv.pl.scatter(adata, df[cluster][:5], ylabel=cluster, frameon=False)
5. Differential Kinetics
One important concern is dealing with systems that represent multiple lineages and processes, where genes are likely to show different kinetic regimes across subpopulations. Distinct cell states and lineages are typically governed by different variations in the gene regulatory networks and may hence exhibit different splicing kinetics. This gives rise to genes that display multiple trajectories in phase space.(这个是说有多个谱系存在的情况,也是我们分析自己数据的时候最常见到的情况)
为了解决这个问题,可以使用动力学模型来执行差分动力学的似然比测试。通过这种方式,我们可以检测到无法通过整体动力学的单个模型很好解释的动力学行为的集群。将细胞聚类到其不同的动力学模型中,然后允许分别拟合每个机制。
这一部分的演示数据集使用的是包含了多种异质性亚群的dentate gyrus neurogenesis。
5.1 预处理和数据准备
和前面一样
import scvelo as scv
scv.logging.print_version()
scv.settings.verbosity = 3 # show errors(0), warnings(1), info(2), hints(3)
scv.settings.presenter_view = True # set max width size for presenter view
scv.settings.set_figure_params('scvelo') # for beautified visualization
adata = scv.datasets.dentategyrus()
scv.pp.filter_and_normalize(adata, min_shared_counts=30, n_top_genes=2000)
scv.pp.moments(adata, n_pcs=30, n_neighbors=30)
5.2 Basic Velocity Estimation
scv.tl.velocity(adata)
scv.tl.velocity_graph(adata)
scv.pl.velocity_embedding_stream(adata, basis='umap')
5.3 Differential Kinetic Test
不同的细胞类型和谱系可能表现出不同的动力学机制,因为它们可以由不同的网络结构控制。即使细胞类型与谱系相关,由于可变剪切、可变多聚腺苷酸环化和降解调节,动力也会有所不同。
动力学模型允许我们通过差分动力学的似然比测试来解决这个问题,以检测显示动力学行为的集群/谱系,而整体动力学的的单个模型无法充分解释这些行为。测试每种细胞类型是否独立拟合产生显著改善的可能性。
遵循渐进卡方分布的似然比可以检验显著性。请注意,出于效率原因,默认情况下使用正交回归而不是完整的相轨迹来测试集群是否能被整体动力学很好的解释或表现出不同的动力学。
var_names = ['Tmsb10', 'Fam155a', 'Hn1', 'Rpl6']
scv.tl.differential_kinetic_test(adata, var_names=var_names, groupby='clusters')
scv.get_df(adata[:, var_names], ['fit_diff_kinetics', 'fit_pval_kinetics'], precision=2)
kwargs = dict(linewidth=2, add_linfit=True, frameon=False)
scv.pl.scatter(adata, basis=var_names, add_outline='fit_diff_kinetics', **kwargs)
diff_clusters=list(adata[:, var_names].var['fit_diff_kinetics'])
scv.pl.scatter(adata, legend_loc='right', size=60, title='diff kinetics',
add_outline=diff_clusters, outline_width=(.8, .2))
5.4 Testing top-likelihood genes
通过最高似然基因进行筛选,我们发现了一些显示多种动力学机制的基因动态。
scv.tl.recover_dynamics(adata)
#adata.write('data/pancreas.h5ad', compression='gzip')
#adata = scv.read('data/pancreas.h5ad')
top_genes = adata.var['fit_likelihood'].sort_values(ascending=False).index[:100]
scv.tl.differential_kinetic_test(adata, var_names=top_genes, groupby='clusters')
scv.pl.scatter(adata, basis=top_genes[:15], ncols=5, add_outline='fit_diff_kinetics', **kwargs)
scv.pl.scatter(adata, basis=top_genes[15:30], ncols=5, add_outline='fit_diff_kinetics', **kwargs)
5.5 Recompute velocities
最后,可以利用多个相互竞争的动力学机制的信息重新计算速度。
scv.tl.velocity(adata, diff_kinetics=True)
scv.tl.velocity_graph(adata)
scv.pl.velocity_embedding(adata, dpi=120, arrow_size=2, arrow_length=2)