#!/usr/bin/env python
# coding: utf-8
# # Celltype auto annotation with SCSA
# Single-cell transcriptomics allows the analysis of thousands of cells in a single experiment and the identification of novel cell types, states and dynamics in a variety of tissues and organisms. Standard experimental protocols and analytical workflows have been developed to create single-cell transcriptomic maps from tissues.
#
# This tutorial focuses on how to interpret this data to identify cell types, states, and other biologically relevant patterns with the goal of creating annotated cell maps.
#
# Paper: [SCSA: A Cell Type Annotation Tool for Single-Cell RNA-seq Data](https://doi.org/10.3389/fgene.2020.00490)
#
# Code: https://github.com/bioinfo-ibms-pumc/SCSA
#
# Colab_Reproducibility:https://colab.research.google.com/drive/1BC6hPS0CyBhNu0BYk8evu57-ua1bAS0T?usp=sharing
#
#
#
Note
#
# The annotation with SCSA can't be used in rare celltype annotations
#
#
#
# ![scsa](https://www.frontiersin.org/files/Articles/524690/fgene-11-00490-HTML/image_m/fgene-11-00490-g001.jpg)
# In[1]:
import omicverse as ov
print(f'omicverse version:{ov.__version__}')
import scanpy as sc
print(f'scanpy version:{sc.__version__}')
ov.ov_plot_set()
# ## Loading data
#
# The data consist of 3k PBMCs from a Healthy Donor and are freely available from 10x Genomics ([here](http://cf.10xgenomics.com/samples/cell-exp/1.1.0/pbmc3k/pbmc3k_filtered_gene_bc_matrices.tar.gz) from this [webpage](https://support.10xgenomics.com/single-cell-gene-expression/datasets/1.1.0/pbmc3k)). On a unix system, you can uncomment and run the following to download and unpack the data. The last line creates a directory for writing processed data.
#
# In[2]:
# !mkdir data
# !wget http://cf.10xgenomics.com/samples/cell-exp/1.1.0/pbmc3k/pbmc3k_filtered_gene_bc_matrices.tar.gz -O data/pbmc3k_filtered_gene_bc_matrices.tar.gz
# !cd data; tar -xzf pbmc3k_filtered_gene_bc_matrices.tar.gz
# !mkdir write
# Read in the count matrix into an AnnData object, which holds many slots for annotations and different representations of the data. It also comes with its own HDF5-based file format: `.h5ad`.
# In[3]:
adata = sc.read_10x_mtx(
'data/filtered_gene_bc_matrices/hg19/', # the directory with the `.mtx` file
var_names='gene_symbols', # use gene symbols for the variable names (variables-axis index)
cache=True) # write a cache file for faster subsequent reading
# ## Data preprocessing
#
# Here, we use `ov.single.scanpy_lazy` to preprocess the raw data of scRNA-seq, it included filter the doublets cells, normalizing counts per cell, log1p, extracting highly variable genes, and cluster of cells calculation.
#
# But if you want to experience step-by-step preprocessing, we also provide more detailed preprocessing steps here, please refer to our [preprocess chapter](https://omicverse.readthedocs.io/en/latest/Tutorials-single/t_preprocess/) for a detailed explanation.
#
# We stored the raw counts in `count` layers, and the raw data in `adata.raw.to_adata()`.
# In[4]:
#adata=ov.single.scanpy_lazy(adata)
#quantity control
adata=ov.pp.qc(adata,
tresh={'mito_perc': 0.05, 'nUMIs': 500, 'detected_genes': 250})
#normalize and high variable genes (HVGs) calculated
adata=ov.pp.preprocess(adata,mode='shiftlog|pearson',n_HVGs=2000,)
#save the whole genes and filter the non-HVGs
adata.raw = adata
adata = adata[:, adata.var.highly_variable_features]
#scale the adata.X
ov.pp.scale(adata)
#Dimensionality Reduction
ov.pp.pca(adata,layer='scaled',n_pcs=50)
#Neighbourhood graph construction
sc.pp.neighbors(adata, n_neighbors=15, n_pcs=50,
use_rep='scaled|original|X_pca')
#clusters
sc.tl.leiden(adata)
#Dimensionality Reduction for visualization(X_mde=X_umap+GPU)
adata.obsm["X_mde"] = ov.utils.mde(adata.obsm["scaled|original|X_pca"])
adata
# ## Cell annotate automatically
#
# We create a pySCSA object from the `adata`, and we need to set some parameter to annotate correctly.
#
# In normal annotate, we set `celltype`=`'normal'` and `target`=`'cellmarker'` or `'panglaodb'` to perform the cell annotate.
#
# But in cancer annotate, we need to set the `celltype`=`'cancer'` and `target`=`'cancersea'` to perform the cell annotate.
#
#
#
Note
#
# The annotation with SCSA need to download the database at first. It can be downloaded automatically. But sometimes you will have problems with network errors.
#
#
#
# The database can be downloaded from [figshare](https://figshare.com/ndownloader/files/41369037) or [Google Drive](https://drive.google.com/drive/folders/1pqyuCp8mTXDFRGUkX8iDdPAg45JHvheF?usp=sharing). And you need to set parameter `model_path`=`'path'`
# In[5]:
scsa=ov.single.pySCSA(adata=adata,
foldchange=1.5,
pvalue=0.01,
celltype='normal',
target='cellmarker',
tissue='All',
model_path='temp/pySCSA_2023_v2_plus.db'
)
# In the previous cell clustering we used the leiden algorithm, so here we specify that the type is set to leiden. if you are using louvain, please change it. And, we will annotate all clusters, if you only want to annotate a few of the classes, please follow `'[1]'`, `'[1,2,3]'`, `'[...]'` Enter in the format.
#
# `rank_rep` means the `sc.tl.rank_genes_groups(adata, clustertype, method='wilcoxon')`, if we provided the `rank_genes_groups` in adata.uns, `rank_rep` can be set as `False`
# In[6]:
anno=scsa.cell_anno(clustertype='leiden',
cluster='all',rank_rep=True)
# We can query only the better annotated results
# In[7]:
scsa.cell_auto_anno(adata,key='scsa_celltype_cellmarker')
# We can also use `panglaodb` as target to annotate the celltype
# In[8]:
scsa=ov.single.pySCSA(adata=adata,
foldchange=1.5,
pvalue=0.01,
celltype='normal',
target='panglaodb',
tissue='All',
model_path='temp/pySCSA_2023_v2_plus.db'
)
# In[9]:
res=scsa.cell_anno(clustertype='leiden',
cluster='all',rank_rep=True)
# We can query only the better annotated results
# In[10]:
scsa.cell_anno_print()
# In[11]:
scsa.cell_auto_anno(adata,key='scsa_celltype_panglaodb')
# Here, we introduce the dimensionality reduction visualisation function `ov.utils.embedding`, which is similar to `scanpy.pl.embedding`, except that when we set `frameon='small'`, we scale the axes to the bottom-left corner and scale the colourbar to the bottom-right corner.
#
# - adata: the anndata object
# - basis: the visualized embedding stored in adata.obsm
# - color: the visualized obs/var
# - legend_loc: the location of legend, if you set None, it will be visualized in right.
# - frameon: it can be set `small`, False or None
# - legend_fontoutline: the outline in the text of legend.
# - palette: Different categories of colours, we have a number of different colours preset in omicverse, including `ov.utils.palette()`, `ov.utils.red_color`, `ov.utils.blue_color`, `ov.utils.green_color`, `ov. utils.orange_color`. The preset colours can help you achieve a more beautiful visualisation.
# In[12]:
ov.utils.embedding(adata,
basis='X_mde',
color=['leiden','scsa_celltype_cellmarker','scsa_celltype_panglaodb'],
legend_loc='on data',
frameon='small',
legend_fontoutline=2,
palette=ov.utils.palette()[14:],
)
# If you want to draw stacked histograms of cell type proportions, you first need to colour the groups you intend to draw using `ov.utils.embedding`. Then use `ov.utils.plot_cellproportion` to specify the groups you want to plot, and you can see a plot of cell proportions in the different groups
# In[13]:
#Randomly designate the first 1000 cells as group B and the rest as group A
adata.obs['group']='A'
adata.obs.loc[adata.obs.index[:1000],'group']='B'
#Colored
ov.utils.embedding(adata,
basis='X_mde',
color=['group'],
frameon='small',legend_fontoutline=2,
palette=ov.utils.red_color,
)
# In[14]:
ov.utils.plot_cellproportion(adata=adata,celltype_clusters='scsa_celltype_cellmarker',
visual_clusters='group',
visual_name='group',figsize=(2,4))
# Of course, we also provide another downscaled visualisation of the graph using `ov.utils.plot_embedding_celltype`
# In[15]:
ov.utils.plot_embedding_celltype(adata,figsize=None,basis='X_mde',
celltype_key='scsa_celltype_cellmarker',
title=' Cell type',
celltype_range=(2,6),
embedding_range=(4,10),)
# We calculated the ratio of observed to expected cell numbers (Ro/e) for each cluster in different tissues to quantify the tissue preference of each cluster (Guo et al., 2018; Zhang et al., 2018). The expected cell num- bers for each combination of cell clusters and tissues were obtained from the chi-square test. One cluster was identified as being enriched in a specific tissue if Ro/e>1.
#
# The Ro/e function was wrote by `Haihao Zhang`.
# In[16]:
roe=ov.utils.roe(adata,sample_key='group',cell_type_key='scsa_celltype_cellmarker')
# In[40]:
import seaborn as sns
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(2,4))
transformed_roe = roe.copy()
transformed_roe = transformed_roe.applymap(
lambda x: '+++' if x >= 2 else ('++' if x >= 1.5 else ('+' if x >= 1 else '+/-')))
sns.heatmap(roe, annot=transformed_roe, cmap='RdBu_r', fmt='',
cbar=True, ax=ax,vmin=0.5,vmax=1.5,cbar_kws={'shrink':0.5})
plt.xticks(fontsize=12)
plt.yticks(fontsize=12)
plt.xlabel('Group',fontsize=13)
plt.ylabel('Cell type',fontsize=13)
plt.title('Ro/e',fontsize=13)
# ## Cell annotate manually
#
# In order to compare the accuracy of our automatic annotations, we will here use marker genes to manually annotate the cluster and compare the accuracy of the pySCSA and manual.
#
# We need to prepare a marker's dict at first
# In[38]:
res_marker_dict={
'Megakaryocyte':['ITGA2B','ITGB3'],
'Dendritic cell':['CLEC10A','IDO1'],
'Monocyte' :['S100A8','S100A9','LST1',],
'Macrophage':['CSF1R','CD68'],
'B cell':['MS4A1','CD79A','MZB1',],
'NK/NKT cell':['GNLY','KLRD1'],
'CD8+T cell':['CD8A','CD8B'],
'Treg':['CD4','CD40LG','IL7R','FOXP3','IL2RA'],
'CD4+T cell':['PTPRC','CD3D','CD3E'],
}
# We then calculated the expression of marker genes in each cluster and the fraction
# In[39]:
sc.tl.dendrogram(adata,'leiden')
sc.pl.dotplot(adata, res_marker_dict, 'leiden',
dendrogram=True,standard_scale='var')
# Based on the dotplot, we name each cluster according `ov.single.scanpy_cellanno_from_dict`
# In[40]:
# create a dictionary to map cluster to annotation label
cluster2annotation = {
'0': 'T cell',
'1': 'T cell',
'2': 'Monocyte',#Germ-cell(Oid)
'3': 'B cell',#Germ-cell(Oid)
'4': 'T cell',
'5': 'Macrophage',
'6': 'NKT cells',
'7': 'T cell',
'8':'Monocyte',
'9':'Dendritic cell',
'10':'Megakaryocyte',
}
ov.single.scanpy_cellanno_from_dict(adata,anno_dict=cluster2annotation,
clustertype='leiden')
# ## Compare the pySCSA and Manual
#
# We can see that the auto-annotation results are almost identical to the manual annotation, the only difference is between monocyte and macrophages, but in the previous auto-annotation results, pySCSA gives the option of `monocyte|macrophage`, so it can be assumed that pySCSA performs better on the pbmc3k data
# In[52]:
ov.utils.embedding(adata,
basis='X_mde',
color=['major_celltype','scsa_celltype_cellmarker'],
legend_loc='on data', frameon='small',legend_fontoutline=2,
palette=ov.utils.palette()[14:],
)
# We can use `get_celltype_marker` to obtain the marker of each celltype
# In[42]:
marker_dict=ov.single.get_celltype_marker(adata,clustertype='scsa_celltype_cellmarker')
marker_dict.keys()
# In[43]:
marker_dict['B cell']
# ## The tissue name in database
#
# For annotation of cell types in specific tissues, we can query the tissues available in the database using `get_model_tissue`.
# In[44]:
scsa.get_model_tissue()
# In[ ]: