Manage a pathway registry#
Pathways represent interconnected molecular networks of signaling cascades that govern critical cellular processes. They provide understandings cellular behavior mechanisms, insights of disease progression and treatment responses. In an R&D organization, managing pathways across different datasets are crucial for gaining insights of potential therapeutic targets and intervention strategies.
In this notebook we manage a pathway registry based on β2023 GO Biological Processβ ontology. Weβll walk you through the following steps:
Register pathways and link them to genes.
Perform a pathway enrichment analysis on an interferon-beta treated dataset and track the dataset with LaminDB.
Demonstrate how datasets are queryable by pathways and genes using LaminDB.
Setup#
Warning
Please ensure that you have created or loaded a LaminDB instance before running the remaining part of this notebook!
# A lamindb instance containing bionty schema (skip if you already loaded your own instance)
!lamin init --storage ./enrichr --schema bionty
Show code cell output
π‘ creating schemas: core==0.47.5 bionty==0.30.4
β
saved: User(id='DzTjkKse', handle='testuser1', email='testuser1@lamin.ai', name='Test User1', updated_at=2023-09-06 17:22:15)
β
saved: Storage(id='u0MPvpBF', root='/home/runner/work/lamin-usecases/lamin-usecases/docs/enrichr', type='local', updated_at=2023-09-06 17:22:15, created_by_id='DzTjkKse')
β
loaded instance: testuser1/enrichr
π‘ did not register local instance on hub (if you want, call `lamin register`)
import lamindb as ln
import lnschema_bionty as lb
from lamin_usecases import datasets as ds
import gseapy as gp
import scanpy as sc
import matplotlib.pyplot as plt
lb.settings.species = "human" # globally set species
β
loaded instance: testuser1/enrichr (lamindb 0.52.2)
Fetch GO pathways annotated with human genes using Enrichr#
First we fetch the βGO_Biological_Process_2023β pathways for humans using GSEApy which wraps GSEA and Enrichr.
go_bp = gp.get_library(name="GO_Biological_Process_2023", organism="Human")
print(f"Number of pathways {len(go_bp)}")
Number of pathways 5406
go_bp["ATF6-mediated Unfolded Protein Response (GO:0036500)"]
['MBTPS1', 'MBTPS2', 'XBP1', 'ATF6B', 'DDIT3', 'CREBZF']
Parse out the ontology_id from keys, convert into the format of {ontology_id: (name, genes)}
def parse_ontology_id_from_keys(key):
"""Parse out the ontology id.
"ATF6-mediated Unfolded Protein Response (GO:0036500)" -> ("GO:0036500", "ATF6-mediated Unfolded Protein Response")
"""
id = key.split(" ")[-1].replace("(", "").replace(")", "")
name = key.replace(f" ({id})", "")
return (id, name)
go_bp_parsed = {}
for key, genes in go_bp.items():
id, name = parse_ontology_id_from_keys(key)
go_bp_parsed[id] = (name, genes)
go_bp_parsed["GO:0036500"]
('ATF6-mediated Unfolded Protein Response',
['MBTPS1', 'MBTPS2', 'XBP1', 'ATF6B', 'DDIT3', 'CREBZF'])
Register pathway ontology in LaminDB#
pathway_bionty = lb.Pathway.bionty() # equals to bionty.Pathway()
Show code cell output
pathway_bionty
Pathway
Species: all
Source: go, 2023-05-10
#terms: 47514
π Pathway.df(): ontology reference table
π Pathway.lookup(): autocompletion of terms
π― Pathway.search(): free text search of terms
β
Pathway.validate(): strictly validate values
π§ Pathway.inspect(): full inspection of values
π½ Pathway.standardize(): convert to standardized names
πͺ Pathway.diff(): difference between two versions
π Pathway.ontology: Pronto.Ontology object
Next, we register all the pathways and genes in LaminDB to finally link pathways to genes.
Register pathway terms#
To register the pathways we make use of .from_values
to directly parse the annotated GO pathway ontology IDs into LaminDB.
pathway_records = lb.Pathway.from_values(go_bp_parsed.keys(), lb.Pathway.ontology_id)
β
created 5406 Pathway records from Bionty matching ontology_id: 'GO:0044208', 'GO:0051084', 'GO:0006103', 'GO:0061158', 'GO:0070935', 'GO:0050427', 'GO:0042791', 'GO:0009452', 'GO:0036261', 'GO:0006370', 'GO:0015866', 'GO:0006167', 'GO:0046033', 'GO:0036500', 'GO:0006754', 'GO:0046034', 'GO:0042773', 'GO:0015867', 'GO:0086016', 'GO:0086067', ...
lb.Pathway.from_bionty(ontology_id="GO:0015868")
β
created 1 Pathway record from Bionty matching ontology_id: 'GO:0015868'
Pathway(id='SMqshx3Y', name='purine ribonucleotide transport', ontology_id='GO:0015868', description='The Directed Movement Of A Purine Ribonucleotide, Any Compound Consisting Of A Purine Ribonucleoside (A Purine Organic Base Attached To A Ribose Sugar) Esterified With (Ortho)Phosphate, Into, Out Of Or Within A Cell.', bionty_source_id='DKg7', created_by_id='DzTjkKse')
ln.save(pathway_records, parents=False) # not recursing through parents
Register gene symbols#
Similarly, we use .from_values
for all Pathway associated genes to register them with LaminDB.
all_genes = {g for genes in go_bp.values() for g in genes}
gene_records = lb.Gene.from_values(all_genes, lb.Gene.symbol)
Show code cell output
β
created 14620 Gene records from Bionty matching symbol: 'ATL2', 'FAM53C', 'INSYN1', 'SH3GLB1', 'ZC3H4', 'LRWD1', 'DPM2', 'ZNF550', 'BBS1', 'SNW1', 'F9', 'CORO7', 'CD34', 'CHORDC1', 'RABGAP1', 'XPO1', 'MNDA', 'LEPR', 'ADAMTS10', 'RAP1BL', ...
β
created 40 Gene records from Bionty matching synonyms: 'C3ORF33', 'C11ORF80', 'C15ORF62', 'C12ORF29', 'C11ORF65', 'C3ORF38', 'C1ORF131', 'C2ORF69', 'C12ORF50', 'C20ORF173', 'C3ORF70', 'C18ORF32', 'C18ORF25', 'PDZD3', 'C12ORF57', 'C19ORF12', 'C17ORF97', 'C1ORF68', 'C1ORF109', 'C1ORF56', ...
β ambiguous validation in Bionty for 1082 records: 'CORO7', 'CHORDC1', 'PKLR', 'OR9G4', 'BTG3', 'GBA3', 'TBC1D3H', 'PSME1', 'CEP72', 'WDR81', 'HLA-C', 'OR2A14', 'UBE2NL', 'CLIC1', 'SERPINA1', 'BDH1', 'MARVELD2', 'CYP21A2', 'HSPA1A', 'CTTN', ...
β did not create Gene records for 37 non-validated symbols: 'AFD1', 'AZF1', 'CCL4L1', 'DGS2', 'DUX3', 'DUX5', 'FOXL3-OT1', 'IGL', 'LOC100653049', 'LOC102723475', 'LOC102723996', 'LOC102724159', 'LOC107984156', 'LOC112268384', 'LOC122319436', 'LOC122513141', 'LOC122539214', 'LOC344967', 'MDRV', 'MTRNR2L1', ...
gene_records[:3]
[Gene(id='7ksbbHE6TJxO', symbol='ATL2', ensembl_gene_id='ENSG00000119787', ncbi_gene_ids='64225', biotype='protein_coding', description='atlastin GTPase 2 [Source:HGNC Symbol;Acc:HGNC:24047]', synonyms='ARL6IP2|ATLASTIN2', species_id='uHJU', bionty_source_id='AM1N', created_by_id='DzTjkKse'),
Gene(id='zvhfLSmLRxIo', symbol='FAM53C', ensembl_gene_id='ENSG00000120709', ncbi_gene_ids='51307|100128966', biotype='protein_coding', description='family with sequence similarity 53 member C [Source:HGNC Symbol;Acc:HGNC:1336]', synonyms='C5ORF6', species_id='uHJU', bionty_source_id='AM1N', created_by_id='DzTjkKse'),
Gene(id='GW6BxckXIrBB', symbol='INSYN1', ensembl_gene_id='ENSG00000205363', ncbi_gene_ids='388135', biotype='protein_coding', description='inhibitory synaptic factor 1 [Source:HGNC Symbol;Acc:HGNC:33753]', synonyms='MGC131524|LOC388135|C15ORF59', species_id='uHJU', bionty_source_id='AM1N', created_by_id='DzTjkKse')]
ln.save(gene_records);
Link pathway to genes#
Now that we are tracking all pathways and genes records, we can link both of them to make the pathways even more queryable.
gene_records_ids = {record.symbol: record for record in gene_records}
for pathway_record in pathway_records:
pathway_genes = go_bp_parsed.get(pathway_record.ontology_id)[1]
pathway_genes_records = [gene_records_ids.get(gene) for gene in pathway_genes]
pathway_record.genes.set(pathway_genes_records)
Now genes are linked to pathways:
pathway_record.genes.list("symbol")
['XIAP', 'CAST', 'CARD8', 'CARD18', 'CST7']
A interferon-beta treated dataset#
We will now conduct a pathway enrichment analysis on a small peripheral blood mononuclear cell dataset that is split into control and stimulated groups. The stimulated group was treated with interferon beta.
Letβs load the dataset and look at the cell type annotations.
adata = ds.anndata_seurat_ifnb()
Show code cell output
adata
AnnData object with n_obs Γ n_vars = 13999 Γ 14053
obs: 'orig.ident', 'nCount_RNA', 'nFeature_RNA', 'stim', 'seurat_annotations'
var: 'features'
uns: 'log1p'
adata.obs["seurat_annotations"].value_counts()
seurat_annotations
CD14 Mono 4362
CD4 Naive T 2504
CD4 Memory T 1762
CD16 Mono 1044
B 978
CD8 T 814
NK 633
T activated 619
DC 472
B Activated 388
Mk 236
pDC 132
Eryth 55
Name: count, dtype: int64
For simplicity, we subset to βB Activatedβ cells:
adata_ba = adata[adata.obs.seurat_annotations == "B Activated"].copy()
adata_ba
AnnData object with n_obs Γ n_vars = 388 Γ 14053
obs: 'orig.ident', 'nCount_RNA', 'nFeature_RNA', 'stim', 'seurat_annotations'
var: 'features'
uns: 'log1p'
Pathway enrichment analysis using Enrichr#
This analysis is based on the GSEApy scRNA-seq Example.
First, we compute differentially expressed genes using a Wilcoxon test between stimulated and control cells.
# compute differentially expressed genes
sc.tl.rank_genes_groups(
adata_ba,
groupby="stim",
use_raw=False,
method="wilcoxon",
groups=["STIM"],
reference="CTRL",
)
rank_genes_groups_df = sc.get.rank_genes_groups_df(adata_ba, "STIM")
rank_genes_groups_df.head()
names | scores | logfoldchanges | pvals | pvals_adj | |
---|---|---|---|---|---|
0 | ISG15 | 16.881584 | 5.923428 | 6.147295e-64 | 6.536230e-60 |
1 | ISG20 | 16.857113 | 4.167713 | 9.302256e-64 | 6.536230e-60 |
2 | IFIT3 | 14.587233 | 31.232290 | 3.386569e-48 | 1.586382e-44 |
3 | IFI6 | 14.128634 | 6.471180 | 2.530019e-45 | 8.888589e-42 |
4 | MX1 | 13.442097 | 6.241539 | 3.425901e-41 | 9.628837e-38 |
Next, we filter out up/down-regulated differentially expressed gene sets:
degs_up = rank_genes_groups_df[
(rank_genes_groups_df["logfoldchanges"] > 0)
& (rank_genes_groups_df["pvals_adj"] < 0.05)
]
degs_dw = rank_genes_groups_df[
(rank_genes_groups_df["logfoldchanges"] < 0)
& (rank_genes_groups_df["pvals_adj"] < 0.05)
]
degs_up.shape, degs_dw.shape
((89, 5), (47, 5))
Run pathway enrichment analysis on DEGs and plot top 10 pathways:
enr_up = gp.enrichr(degs_up.names, gene_sets="GO_Biological_Process_2023").res2d
gp.dotplot(enr_up, figsize=(2, 3), title="Up", cmap=plt.cm.autumn_r);
enr_dw = gp.enrichr(degs_dw.names, gene_sets="GO_Biological_Process_2023").res2d
gp.dotplot(enr_dw, figsize=(2, 3), title="Down", cmap=plt.cm.winter_r, size=10);
Track datasets containing annotated pathways in LaminDB#
Letβs enable tracking of the current notebook as the transform of this file:
ln.track()
π‘ notebook imports: gseapy==1.0.6 lamin_usecases==0.0.1 lamindb==0.52.2 lnschema_bionty==0.30.4 matplotlib==3.7.2 scanpy==1.9.4
β
saved: Transform(id='6oxEIEduvo6wz8', name='Manage a pathway registry', short_name='enrichr', version='0', type=notebook, updated_at=2023-09-06 17:23:18, created_by_id='DzTjkKse')
β
saved: Run(id='YMrU2wFqwdmvUDvIS6cy', run_at=2023-09-06 17:23:18, transform_id='6oxEIEduvo6wz8', created_by_id='DzTjkKse')
We further create a File object to track the dataset.
file = ln.File.from_anndata(
adata_ba, description="seurat_ifnb_activated_Bcells", field=lb.Gene.symbol
)
π‘ file will be copied to default storage upon `save()` with key `None` ('.lamindb/K4FoVqAHbgzY8tX3vDke.h5ad')
π‘ parsing feature names of X stored in slot 'var'
β
9324 terms (66.30%) are validated for symbol
β 4729 terms (33.70%) are not validated for symbol: AL627309.1, RP11-206L10.2, LINC00115, KLHL17, C1orf159, ACAP3, CPSF3L, GLTPD1, RP4-758J18.2, AL645728.1, RP11-345P4.9, SLC35E2B, SLC35E2, RP5-892K4.1, C1orf86, AL590822.2, MORN1, RP3-395M20.12, RP3-395M20.9, FAM213B, ...
β
linked: FeatureSet(id='3rnQOuc4y3gLBQP5CpyH', n=10599, type='number', registry='bionty.Gene', hash='ehMHlXCXiKumXLoHU96e', created_by_id='DzTjkKse')
π‘ parsing feature names of slot 'obs'
β 5 terms (100.00%) are not validated for name: orig.ident, nCount_RNA, nFeature_RNA, stim, seurat_annotations
β no validated features, skip creating feature set
ln.save(file)
β
saved 1 feature set for slot: 'var'
β
storing file 'K4FoVqAHbgzY8tX3vDke' at '.lamindb/K4FoVqAHbgzY8tX3vDke.h5ad'
We further create two feature sets for degs_up
and degs_dw
which we can later associate with the associated pathways:
degs_up_featureset = ln.FeatureSet.from_values(degs_up.names, lb.Gene.symbol)
Show code cell output
β
76 terms (85.40%) are validated for symbol
β 13 terms (14.60%) are not validated for symbol: EPSTI1, WARS, LAP3, SAMD9L, NMI, TMEM123, CMPK2, H3F3B, PSMA2.1, PHF11, CLEC2D, DDX58, CD48
degs_dw_featureset = ln.FeatureSet.from_values(degs_dw.names, lb.Gene.symbol)
Show code cell output
β
44 terms (93.60%) are validated for symbol
β 3 terms (6.40%) are not validated for symbol: GNB2L1, TMEM66, HLA-DQB1
Link the top 10 pathways to the corresponding differentially expressed genes:
# get ontology ids for the top 10 pathways
enr_up_top10 = [
pw_id[0] for pw_id in enr_up.head(10).Term.apply(parse_ontology_id_from_keys)
]
enr_dw_top10 = [
pw_id[0] for pw_id in enr_dw.head(10).Term.apply(parse_ontology_id_from_keys)
]
# get pathway records
enr_up_top10_pathways = lb.Pathway.from_values(enr_up_top10, lb.Pathway.ontology_id)
enr_dw_top10_pathways = lb.Pathway.from_values(enr_dw_top10, lb.Pathway.ontology_id)
Link feature sets to file:
file.features.add_feature_set(degs_up_featureset, slot="up-DEGs")
file.features.add_feature_set(degs_dw_featureset, slot="down-DEGs")
Associate the pathways to the differentially expressed genes:
degs_up_featureset.pathways.set(enr_up_top10_pathways)
degs_dw_featureset.pathways.set(enr_dw_top10_pathways)
degs_up_featureset.pathways.list("name")
['negative regulation of viral process',
'positive regulation of interferon-beta production',
'interleukin-27-mediated signaling pathway',
'response to type II interferon',
'defense response to virus',
'antiviral innate immune response',
'response to interferon-beta',
'regulation of viral genome replication',
'defense response to symbiont',
'negative regulation of viral genome replication']
Querying for pathways#
Querying for pathways is now simple with .filter
:
lb.Pathway.filter(name__contains="interferon-beta").df()
name | ontology_id | abbr | synonyms | description | bionty_source_id | updated_at | created_by_id | |
---|---|---|---|---|---|---|---|---|
id | ||||||||
uu9GYFx2 | negative regulation of interferon-beta production | GO:0032688 | None | down regulation of interferon-beta production|... | Any Process That Stops, Prevents, Or Reduces T... | DKg7 | 2023-09-06 17:22:36 | DzTjkKse |
GD9xCHBK | regulation of interferon-beta production | GO:0032648 | None | regulation of IFN-beta production | Any Process That Modulates The Frequency, Rate... | DKg7 | 2023-09-06 17:22:36 | DzTjkKse |
SGYMKD7O | positive regulation of interferon-beta production | GO:0032728 | None | positive regulation of IFN-beta production|up-... | Any Process That Activates Or Increases The Fr... | DKg7 | 2023-09-06 17:22:36 | DzTjkKse |
l06ZujxW | cellular response to interferon-beta | GO:0035458 | None | cellular response to fibroblast interferon|cel... | Any Process That Results In A Change In State ... | DKg7 | 2023-09-06 17:22:36 | DzTjkKse |
mCgM7JYR | response to interferon-beta | GO:0035456 | None | response to fiblaferon|response to fibroblast ... | Any Process That Results In A Change In State ... | DKg7 | 2023-09-06 17:22:36 | DzTjkKse |
Query pathways from a gene:
lb.Pathway.filter(genes__symbol="KIR2DL1").df()
name | ontology_id | abbr | synonyms | description | bionty_source_id | updated_at | created_by_id | |
---|---|---|---|---|---|---|---|---|
id | ||||||||
TSXmNUbN | immune response-inhibiting cell surface recept... | GO:0002767 | None | immune response-inhibiting cell surface recept... | The Series Of Molecular Signals Initiated By A... | DKg7 | 2023-09-06 17:22:36 | DzTjkKse |
Query files from a pathway:
ln.File.filter(feature_sets__pathways__name__icontains="interferon-beta").first()
File(id='K4FoVqAHbgzY8tX3vDke', suffix='.h5ad', accessor='AnnData', description='seurat_ifnb_activated_Bcells', size=5896640, hash='vAWd5emmLj0nv0E0x5LOSA', hash_type='md5', updated_at=2023-09-06 17:23:20, storage_id='u0MPvpBF', transform_id='6oxEIEduvo6wz8', run_id='YMrU2wFqwdmvUDvIS6cy', created_by_id='DzTjkKse')
Query featuresets from a pathway to learn from which geneset this pathway was computed:
pathway = lb.Pathway.filter(ontology_id="GO:0035456").one()
pathway
Pathway(id='mCgM7JYR', name='response to interferon-beta', ontology_id='GO:0035456', synonyms='response to fiblaferon|response to fibroblast interferon|response to interferon beta', description='Any Process That Results In A Change In State Or Activity Of A Cell Or An Organism (In Terms Of Movement, Secretion, Enzyme Production, Gene Expression, Etc.) As A Result Of An Interferon-Beta Stimulus. Interferon-Beta Is A Type I Interferon.', updated_at=2023-09-06 17:22:36, bionty_source_id='DKg7', created_by_id='DzTjkKse')
degs = ln.FeatureSet.filter(pathways__ontology_id=pathway.ontology_id).one()
Now we can get the list of genes that are differentially expressed and belong to this pathway:
pathway_genes = set(pathway.genes.list("symbol"))
degs_genes = set(degs.genes.list("symbol"))
pathway_genes.intersection(degs_genes)
{'BST2',
'IFI16',
'IFITM2',
'IFITM3',
'IRF1',
'OAS1',
'PLSCR1',
'STAT1',
'XAF1'}
# clean up test instance
!lamin delete --force enrichr
!rm -r ./enrichr
Show code cell output
π‘ deleting instance testuser1/enrichr
β
deleted instance settings file: /home/runner/.lamin/instance--testuser1--enrichr.env
β
instance cache deleted
β
deleted '.lndb' sqlite file
β consider manually deleting your stored data: /home/runner/work/lamin-usecases/lamin-usecases/docs/enrichr