{ "cells": [ { "cell_type": "markdown", "source": [ "# Decomposition Analysis\n", "- by Ziqian Zheng and Jiyuan Yang .\n", "- Last update: Mar 7th 2025" ], "metadata": { "collapsed": false }, "id": "a44e56f503687220" }, { "cell_type": "markdown", "source": [ "## Environment Setup: Package Loading " ], "metadata": { "collapsed": false }, "id": "e2af2630a16d7468" }, { "cell_type": "code", "execution_count": null, "outputs": [], "source": [ "import numpy as np\n", "import pandas as pd\n", "import matplotlib as mpl\n", "import matplotlib.pyplot as plt\n", "import scanpy as sc\n", "import cv2\n", "from tqdm import tqdm\n", "\n", "results_folder = 'Spotiphy_Result/'\n", "if not os.path.exists(results_folder):\n", " # Create result folder if it does not exist\n", " os.makedirs(results_folder)" ], "metadata": { "collapsed": false }, "id": "14e5ba95a813e739" }, { "cell_type": "markdown", "source": [ "## Data Loading " ], "metadata": { "collapsed": false }, "id": "10a2ab4c4ee03511" }, { "cell_type": "code", "execution_count": null, "outputs": [], "source": [ "# Load the Visium data\n", "adata_st = sc.read_h5ad(\"data/ST_mouse_brain.h5ad\")\n", "adata_st.var_names_make_unique()\n", "# Load the cluster information\n", "meta = pd.read_excel(\"ST_decomposition.seurat_meta.xlsx\", header=0)\n", "meta['seurat_clusters'].value_counts()" ], "metadata": { "collapsed": false }, "id": "b2c5fb813e9d1699" }, { "cell_type": "markdown", "source": [ "Map Cells from iscRNA Data to Corresponding Spots. Spots with no cells will be labels as `-1`." ], "metadata": { "collapsed": false }, "id": "99934d1a5322c876" }, { "cell_type": "code", "execution_count": null, "outputs": [], "source": [ "cell_location = meta[['location_x', 'location_y']].values\n", "spot_location = adata_st.obsm['spatial']\n", "d = np.sum(np.abs(spot_location[:, :, np.newaxis] - cell_location.T), axis=1)\n", "adata_st.obs['cluster'] = '-1'\n", "adata_st.obs[['x', 'y']] = spot_location\n", "cluster_column = 'seurat_clusters'\n", "for i in range(len(adata_st)):\n", " if min(d[i]) < 1:\n", " idx = np.where(d[i]==0)[0][0]\n", " adata_st.obs.iloc[i, -3] = str(meta.iloc[idx][cluster_column])" ], "metadata": { "collapsed": false }, "id": "4a70dbb6bb00269" }, { "cell_type": "code", "execution_count": null, "outputs": [], "source": [ "img = adata_st.uns['spatial']['sampleID']['images']['hires'].copy() #sampleID varies depends on Input.\n", "if img.shape[2] == 3:\n", " # Add an alpha channel if it doesn't exist\n", " alpha_channel = np.ones((*img.shape[:2], 1))\n", " img = np.concatenate([img, alpha_channel], axis=2)\n", "\n", "img[np.max(img[:, :, :3], axis=2)-np.min(img[:, :, :3], axis=2)<30/255] = [1., 1., 1. ,1.]\n", "adata_st.uns['spatial']['sampleID']['images']['hires'] = img.copy()" ], "metadata": { "collapsed": false }, "id": "a82517236ff0c417" }, { "cell_type": "code", "execution_count": null, "outputs": [], "source": [ "adata_st.obs['cluster'].value_counts()" ], "metadata": { "collapsed": false }, "id": "137d9f8a37d21d7c" }, { "cell_type": "code", "execution_count": null, "outputs": [], "source": [ "# Remove spots with no cells for clear visualization\n", "adata_st_temp = adata_st[adata_st.obs['cluster']!= '-1' ]\n", "\n", "with mpl.rc_context({'figure.figsize': [4, 4.5], 'figure.dpi': 400}):\n", " ax = sc.pl.spatial(adata_st_temp, color=['cluster'], img_key='hires', size=1.3, show=False)\n", " ax[0].get_figure().savefig(results_folder+'Tumor_cluster_RL_cycling.jpg', bbox_inches='tight')\n", " mpl.pyplot.show()" ], "metadata": { "collapsed": false }, "id": "93b1a534388e7bc1" }, { "cell_type": "code", "execution_count": null, "outputs": [], "source": [ "with open(results_folder+\"type_list.txt\", \"r\") as file:\n", " type_list = [line.strip() for line in file.readlines()]\n", "print(type_list)" ], "metadata": { "collapsed": false }, "id": "6143473a6050cf67" }, { "cell_type": "code", "execution_count": null, "outputs": [], "source": [ "cell_proportion = np.load(results_folder+'proportion.npy')\n", "adata_st.obs['proportion'] = cell_proportion\n", "adata_st.obs.groupby('cluster')['proportion'].quantile(0.9)" ], "metadata": { "collapsed": false }, "id": "be08d30c817c5387" }, { "cell_type": "code", "execution_count": null, "outputs": [], "source": [ "img = cv2.imread(\"data/img_mouse_brain.png\")[:, :, [2, 1, 0]]\n", "img[np.max(img, axis=2)-np.min(img, axis=2)<30] = [255, 255, 255]\n", "alpha = 0.4\n", "img = img * alpha + np.ones(img.shape) * 255 * (1 - alpha)\n", "img = img.astype(np.int32)\n", "\n", "colors = {'1': [180, 119, 31], '2': [14, 127, 255], '3': [44, 160, 44], '4': [40, 39, 214]}\n", "adata_st_temp = adata_st[adata_st.obs['cluster']!='-1']\n", "img1 = img.copy()\n", "img2 = np.ones(img.shape)\n", "\n", "quantile = adata_st.obs.groupby('cluster')['proportion'].quantile(0.9)\n", "for i in tqdm(range(len(adata_st_temp))):\n", " cluster = adata_st_temp.obs.iloc[i, -4]\n", " location = adata_st_temp.obs.iloc[i, -3:-1]\n", " alpha = adata_st_temp.obs.iloc[i, -1] * (1/quantile.loc[cluster])\n", " if alpha > 1:\n", " alpha = 1.\n", " cv2.circle(img1, location, int(73*1.3/2), colors[cluster], -1)\n", " cv2.circle(img2, location, int(73*1.3/2), (alpha, alpha, alpha), -1)" ], "metadata": { "collapsed": false }, "id": "3de5a7818e106211" }, { "cell_type": "code", "execution_count": null, "outputs": [], "source": [ "img = img1 * img2 + img * (1-img2)\n", "img = img.astype(np.int32)\n", "cv2.imwrite(f'{results_folder}Seurat_cluster.jpg', img[:, :, [2, 1, 0]])" ], "metadata": { "collapsed": false }, "id": "ebcf662d54c22847" }, { "cell_type": "code", "execution_count": null, "outputs": [], "source": [ "mpl.pyplot.imshow(img)" ], "metadata": { "collapsed": false }, "id": "702dc4fa6ab8526" } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 2 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", "version": "2.7.6" } }, "nbformat": 4, "nbformat_minor": 5 }