{ "cells": [ { "cell_type": "markdown", "id": "0", "metadata": {}, "source": [ "## Tutorial" ] }, { "cell_type": "markdown", "id": "1", "metadata": {}, "source": [ "Welcome to `ktplotspy`! This is a python library to help visualise `CellPhoneDB` results, ported from the original [ktplots R package](https://www.github.com/zktuong/ktplots) (which still has several other visualisation options). Here, we will go through a quick tutorial on how to use the functions in this package." ] }, { "cell_type": "markdown", "id": "2", "metadata": {}, "source": [ "**Import libraries**" ] }, { "cell_type": "code", "execution_count": null, "id": "3", "metadata": {}, "outputs": [], "source": [ "import anndata as ad\n", "import pandas as pd\n", "import ktplotspy as kpy\n", "import matplotlib.pyplot as plt\n", "\n", "from pathlib import Path" ] }, { "cell_type": "markdown", "id": "4", "metadata": {}, "source": [ "**Prepare input**" ] }, { "cell_type": "markdown", "id": "5", "metadata": {}, "source": [ "We will need 3 files to use this package, the h5ad file used for `CellPhoneDB`,the `means.txt`, `pvalues.txt`. `deconvoluted.txt` is only used for `plot_cpdb_chord`." ] }, { "cell_type": "code", "execution_count": null, "id": "6", "metadata": {}, "outputs": [], "source": [ "# read in the files\n", "# 1) .h5ad file used for performing CellPhoneDB\n", "DATADIR = Path(\"../../data/\")\n", "\n", "adata = ad.read_h5ad(DATADIR / \"kidneyimmune.h5ad\")\n", "\n", "# 2) output from CellPhoneDB\n", "means = pd.read_csv(DATADIR / \"out\" / \"means.txt\", sep=\"\\t\")\n", "pvals = pd.read_csv(DATADIR / \"out\" / \"pvalues.txt\", sep=\"\\t\")\n", "decon = pd.read_csv(DATADIR / \"out\" / \"deconvoluted.txt\", sep=\"\\t\")" ] }, { "cell_type": "markdown", "id": "7", "metadata": {}, "source": [ "### Heatmap" ] }, { "cell_type": "markdown", "id": "8", "metadata": {}, "source": [ "The original heatmap plot from `CellPhoneDB` can be achieved with this reimplemented function." ] }, { "cell_type": "code", "execution_count": null, "id": "9", "metadata": {}, "outputs": [], "source": [ "kpy.plot_cpdb_heatmap(pvals=pvals, figsize=(5, 5), title=\"Sum of significant interactions\")" ] }, { "cell_type": "markdown", "id": "10", "metadata": {}, "source": [ "You can also specify specific celltypes to plot." ] }, { "cell_type": "code", "execution_count": null, "id": "11", "metadata": {}, "outputs": [], "source": [ "kpy.plot_cpdb_heatmap(\n", " pvals=pvals, cell_types=[\"NK cell\", \"pDC\", \"B cell\", \"CD8T cell\"], figsize=(4, 4), title=\"Sum of significant interactions\"\n", ")" ] }, { "cell_type": "markdown", "id": "12", "metadata": {}, "source": [ "The current heatmap is directional (check `count_network` and `interaction_edges` for more details in `return_tables = True`).\n", "\n", "To obtain the heatmap where the interaction counts are not symmetrical, do:" ] }, { "cell_type": "code", "execution_count": null, "id": "13", "metadata": {}, "outputs": [], "source": [ "kpy.plot_cpdb_heatmap(\n", " pvals=pvals,\n", " figsize=(5, 5),\n", " title=\"Sum of significant interactions\",\n", " symmetrical=False,\n", ")" ] }, { "cell_type": "markdown", "id": "14", "metadata": {}, "source": [ "The values for the `symmetrical=False` mode follow the direction of the L-R direction where it's always moleculeA:celltypeA -> moleculeB:celltypeB.\n", "\n", "Therefore, if you trace on the `x-axis` for `celltype A` [MNPa(mono)] to `celltype B` [CD8T cell] on the `y-axis`:\n", "\n", "A -> B is 18 interactions\n", "\n", "Whereas if you trace on the `y-axis` for `celltype A` [MNPa(mono)] to `celltype B` [CD8T cell] on the `x-axis`:\n", "\n", "A -> B is 9 interactions\n", "\n", "`symmetrical=True` mode will return 18+9 = 27" ] }, { "cell_type": "markdown", "id": "15", "metadata": {}, "source": [ "### Dot plot" ] }, { "cell_type": "markdown", "id": "16", "metadata": {}, "source": [ "A simple usage of `plot_cpdb` is like as follows:" ] }, { "cell_type": "code", "execution_count": null, "id": "17", "metadata": {}, "outputs": [], "source": [ "# TODO: How to specify the default plot resolution??\n", "kpy.plot_cpdb(\n", " adata=adata,\n", " cell_type1=\"B cell\",\n", " cell_type2=\".\", # this means all cell-types\n", " means=means,\n", " pvals=pvals,\n", " celltype_key=\"celltype\",\n", " genes=[\"PTPRC\", \"TNFSF13B\"],\n", " figsize=(13, 4),\n", " title=\"interacting interactions!\",\n", ")" ] }, { "cell_type": "markdown", "id": "18", "metadata": {}, "source": [ "You can toggle `keep_id_cp_interaction` to keep the original interaction id. This is useful when there are duplicate interaction names (from cellphonedb V5 onwards)." ] }, { "cell_type": "code", "execution_count": null, "id": "19", "metadata": {}, "outputs": [], "source": [ "kpy.plot_cpdb(\n", " adata=adata,\n", " cell_type1=\"B cell\",\n", " cell_type2=\".\", # this means all cell-types\n", " means=means,\n", " pvals=pvals,\n", " celltype_key=\"celltype\",\n", " genes=[\"PTPRC\", \"TNFSF13B\"],\n", " figsize=(13, 4),\n", " title=\"interacting interactions!\",\n", " keep_id_cp_interaction=True,\n", ")" ] }, { "cell_type": "markdown", "id": "20", "metadata": {}, "source": [ "You can also specify a `gene_family`." ] }, { "cell_type": "code", "execution_count": null, "id": "21", "metadata": {}, "outputs": [], "source": [ "kpy.plot_cpdb(\n", " adata=adata,\n", " cell_type1=\".\",\n", " cell_type2=\".\",\n", " means=means,\n", " pvals=pvals,\n", " celltype_key=\"celltype\",\n", " gene_family=\"chemokines\",\n", " highlight_size=1,\n", " figsize=(20, 8),\n", ")" ] }, { "cell_type": "markdown", "id": "22", "metadata": {}, "source": [ "Or don't specify either and it will try to plot all significant interactions." ] }, { "cell_type": "code", "execution_count": null, "id": "23", "metadata": {}, "outputs": [], "source": [ "kpy.plot_cpdb(\n", " adata=adata,\n", " cell_type1=\"B cell\",\n", " cell_type2=\"pDC|T\",\n", " means=means,\n", " pvals=pvals,\n", " celltype_key=\"celltype\",\n", " highlight_size=1,\n", " figsize=(6.5, 5.5),\n", ")" ] }, { "cell_type": "markdown", "id": "24", "metadata": {}, "source": [ "If you prefer, you can also use the `squidpy` inspired plotting style:" ] }, { "cell_type": "code", "execution_count": null, "id": "25", "metadata": {}, "outputs": [], "source": [ "kpy.plot_cpdb(\n", " adata=adata,\n", " cell_type1=\"B cell\",\n", " cell_type2=\".\",\n", " means=means,\n", " pvals=pvals,\n", " celltype_key=\"celltype\",\n", " genes=[\"PTPRC\", \"CD40\", \"CLEC2D\"],\n", " default_style=False,\n", " figsize=(13, 4),\n", ")" ] }, { "cell_type": "markdown", "id": "26", "metadata": {}, "source": [ "### Chord diagram\n", "\n", "There is a re-implementation of a circos/chord diagram by leveraging on the `pyCirclize` package." ] }, { "cell_type": "code", "execution_count": null, "id": "27", "metadata": {}, "outputs": [], "source": [ "kpy.plot_cpdb_chord(\n", " adata=adata,\n", " cell_type1=\"B cell\",\n", " cell_type2=\".\",\n", " means=means,\n", " pvals=pvals,\n", " deconvoluted=decon,\n", " celltype_key=\"celltype\",\n", " interaction=[\"PTPRC\", \"CD40\", \"CLEC2D\"],\n", " link_kwargs={\"direction\": 1, \"allow_twist\": True, \"r1\": 95, \"r2\": 90},\n", " sector_text_kwargs={\"color\": \"black\", \"size\": 12, \"r\": 105, \"adjust_rotation\": True},\n", " legend_kwargs={\"loc\": \"center\", \"bbox_to_anchor\": (1, 1), \"fontsize\": 8},\n", " link_offset=1,\n", ")" ] }, { "cell_type": "markdown", "id": "28", "metadata": {}, "source": [ "Colour of links can be made to be the same as the celltype." ] }, { "cell_type": "code", "execution_count": null, "id": "29", "metadata": {}, "outputs": [], "source": [ "kpy.plot_cpdb_chord(\n", " adata=adata,\n", " cell_type1=\"B cell\",\n", " cell_type2=\".\",\n", " means=means,\n", " pvals=pvals,\n", " deconvoluted=decon,\n", " celltype_key=\"celltype\",\n", " interaction=[\"PTPRC\", \"CD40\", \"CLEC2D\"],\n", " link_kwargs={\"direction\": 1, \"allow_twist\": True, \"r1\": 90, \"r2\": 90},\n", " sector_text_kwargs={\"color\": \"black\", \"size\": 12, \"r\": 105, \"adjust_rotation\": True},\n", " legend_kwargs={\"loc\": \"center\", \"bbox_to_anchor\": (1, 1), \"fontsize\": 8},\n", " link_offset=1,\n", " same_producer_colors=True,\n", ")" ] }, { "cell_type": "markdown", "id": "30", "metadata": {}, "source": [ "If your `adata` already has e.g. `adata.uns['celltype_colors']`, it will retrieve the `sector_colours` correctly:" ] }, { "cell_type": "code", "execution_count": null, "id": "31", "metadata": {}, "outputs": [], "source": [ "import scanpy as sc\n", "\n", "sc.pl.violin(adata, [\"n_genes\"], groupby=\"celltype\", rotation=90)\n", "kpy.plot_cpdb_chord(\n", " adata=adata,\n", " cell_type1=\"B cell\",\n", " cell_type2=\".\",\n", " means=means,\n", " pvals=pvals,\n", " deconvoluted=decon,\n", " celltype_key=\"celltype\",\n", " interaction=[\"PTPRC\", \"CD40\", \"CLEC2D\"],\n", " same_producer_colors=True,\n", " link_kwargs={\"direction\": 1, \"allow_twist\": True},\n", " sector_text_kwargs={\"color\": \"black\", \"size\": 12, \"r\": 105, \"adjust_rotation\": True},\n", " legend_kwargs={\"loc\": \"center\", \"bbox_to_anchor\": (1, 1), \"fontsize\": 8},\n", ")" ] }, { "cell_type": "markdown", "id": "32", "metadata": {}, "source": [ "You can also provide dictionaries to change the colours for both `sectors` and `links`." ] }, { "cell_type": "code", "execution_count": null, "id": "33", "metadata": {}, "outputs": [], "source": [ "kpy.plot_cpdb_chord(\n", " adata=adata,\n", " cell_type1=\"B cell\",\n", " cell_type2=\".\",\n", " means=means,\n", " pvals=pvals,\n", " deconvoluted=decon,\n", " celltype_key=\"celltype\",\n", " interaction=[\"PTPRC\", \"TNFSF13B\", \"BMPR2\"],\n", " sector_colors={\n", " \"B cell\": \"red\",\n", " \"NK cell\": \"blue\",\n", " \"CD4T cell\": \"black\",\n", " \"pDC\": \"brown\",\n", " \"Neutrophil\": \"grey\",\n", " \"Mast cell\": \"orange\",\n", " \"NKT cell\": \"pink\",\n", " \"CD8T cell\": \"cyan\",\n", " },\n", " link_colors={\"CD22-PTPRC\": \"red\", \"TNFSF13B-TNFRSF13B\": \"blue\"},\n", ")" ] }, { "cell_type": "markdown", "id": "34", "metadata": {}, "source": [ "You can also just plot a specific interaction:" ] }, { "cell_type": "code", "execution_count": null, "id": "35", "metadata": {}, "outputs": [], "source": [ "kpy.plot_cpdb_chord(\n", " adata=adata,\n", " interaction=\"CLEC2D-KLRB1\",\n", " keep_celltypes=[\"NKT cell\", \"Mast cell\", \"NK cell\"],\n", " celltype_key=\"celltype\",\n", " means=means,\n", " pvals=pvals,\n", " deconvoluted=decon,\n", " link_kwargs={\"direction\": 1, \"allow_twist\": False, \"r1\": 95, \"r2\": 90},\n", " sector_text_kwargs={\"color\": \"black\", \"size\": 12, \"r\": 105, \"adjust_rotation\": True},\n", " legend_kwargs={\"loc\": \"center\", \"bbox_to_anchor\": (1, 1), \"fontsize\": 8},\n", ")" ] }, { "cell_type": "markdown", "id": "36", "metadata": {}, "source": [ "You can also fix the sector size to be equal, although this will cause the links to be squished." ] }, { "cell_type": "code", "execution_count": null, "id": "37", "metadata": {}, "outputs": [], "source": [ "kpy.plot_cpdb_chord(\n", " adata=adata,\n", " interaction=\"CLEC2D-KLRB1\",\n", " keep_celltypes=[\"NKT cell\", \"Mast cell\", \"NK cell\"],\n", " celltype_key=\"celltype\",\n", " means=means,\n", " pvals=pvals,\n", " deconvoluted=decon,\n", " link_kwargs={\"direction\": 1, \"allow_twist\": False, \"r1\": 95, \"r2\": 90},\n", " sector_text_kwargs={\"color\": \"black\", \"size\": 12, \"r\": 105, \"adjust_rotation\": True},\n", " legend_kwargs={\"loc\": \"center\", \"bbox_to_anchor\": (1, 1), \"fontsize\": 8},\n", " equal_sector_size=True,\n", ")" ] }, { "cell_type": "markdown", "id": "38", "metadata": {}, "source": [ "## Saving the plots\n", "\n", "For `plot_cpdb`, because it's written with `plotnine`, you need to save it as follows:\n", "\n", "```python\n", "p = plot_cpdb(...)\n", "p.save(...)\n", "```\n", "see also:\n", "https://plotnine.org/reference/ggplot.html#plotnine.ggplot.save\n", "\n", "For `plot_cpdb_chord`, because it's written with `pyCirclize`, you can save it as follows:\n", "\n", "```python\n", "p = plot_cpdb_chord(...)\n", "p.savefig(...)\n", "```\n", "see also:\n", "https://moshi4.github.io/pyCirclize/api-docs/circos/#pycirclize.circos.Circos.savefig\n", "\n", "For other functions, you can use seaborn/matplotlib saving conventions e.g. `plt.savefig`" ] }, { "cell_type": "markdown", "id": "39", "metadata": {}, "source": [ "That's it for now! Please check out the original [ktplots R package](https://www.github.com/zktuong/ktplots) if you are after other kinds of visualisations." ] }, { "cell_type": "code", "execution_count": null, "id": "40", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "ktplotspy", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.8" } }, "nbformat": 4, "nbformat_minor": 5 }