Decomposition Analysis
by Ziqian Zheng zzheng92@wisc.edu and Jiyuan Yang jiyuan.yang@stjude.org.
Last update: Mar 7th 2025
Environment Setup: Package Loading
[ ]:
import numpy as np
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
import scanpy as sc
import cv2
from tqdm import tqdm
results_folder = 'Spotiphy_Result/'
if not os.path.exists(results_folder):
# Create result folder if it does not exist
os.makedirs(results_folder)
Data Loading
[ ]:
# Load the Visium data
adata_st = sc.read_h5ad("data/ST_mouse_brain.h5ad")
adata_st.var_names_make_unique()
# Load the cluster information
meta = pd.read_excel("ST_decomposition.seurat_meta.xlsx", header=0)
meta['seurat_clusters'].value_counts()
Map Cells from iscRNA Data to Corresponding Spots. Spots with no cells will be labels as -1
.
[ ]:
cell_location = meta[['location_x', 'location_y']].values
spot_location = adata_st.obsm['spatial']
d = np.sum(np.abs(spot_location[:, :, np.newaxis] - cell_location.T), axis=1)
adata_st.obs['cluster'] = '-1'
adata_st.obs[['x', 'y']] = spot_location
cluster_column = 'seurat_clusters'
for i in range(len(adata_st)):
if min(d[i]) < 1:
idx = np.where(d[i]==0)[0][0]
adata_st.obs.iloc[i, -3] = str(meta.iloc[idx][cluster_column])
[ ]:
img = adata_st.uns['spatial']['sampleID']['images']['hires'].copy() #sampleID varies depends on Input.
if img.shape[2] == 3:
# Add an alpha channel if it doesn't exist
alpha_channel = np.ones((*img.shape[:2], 1))
img = np.concatenate([img, alpha_channel], axis=2)
img[np.max(img[:, :, :3], axis=2)-np.min(img[:, :, :3], axis=2)<30/255] = [1., 1., 1. ,1.]
adata_st.uns['spatial']['sampleID']['images']['hires'] = img.copy()
[ ]:
adata_st.obs['cluster'].value_counts()
[ ]:
# Remove spots with no cells for clear visualization
adata_st_temp = adata_st[adata_st.obs['cluster']!= '-1' ]
with mpl.rc_context({'figure.figsize': [4, 4.5], 'figure.dpi': 400}):
ax = sc.pl.spatial(adata_st_temp, color=['cluster'], img_key='hires', size=1.3, show=False)
ax[0].get_figure().savefig(results_folder+'Tumor_cluster_RL_cycling.jpg', bbox_inches='tight')
mpl.pyplot.show()
[ ]:
with open(results_folder+"type_list.txt", "r") as file:
type_list = [line.strip() for line in file.readlines()]
print(type_list)
[ ]:
cell_proportion = np.load(results_folder+'proportion.npy')
adata_st.obs['proportion'] = cell_proportion
adata_st.obs.groupby('cluster')['proportion'].quantile(0.9)
[ ]:
img = cv2.imread("data/img_mouse_brain.png")[:, :, [2, 1, 0]]
img[np.max(img, axis=2)-np.min(img, axis=2)<30] = [255, 255, 255]
alpha = 0.4
img = img * alpha + np.ones(img.shape) * 255 * (1 - alpha)
img = img.astype(np.int32)
colors = {'1': [180, 119, 31], '2': [14, 127, 255], '3': [44, 160, 44], '4': [40, 39, 214]}
adata_st_temp = adata_st[adata_st.obs['cluster']!='-1']
img1 = img.copy()
img2 = np.ones(img.shape)
quantile = adata_st.obs.groupby('cluster')['proportion'].quantile(0.9)
for i in tqdm(range(len(adata_st_temp))):
cluster = adata_st_temp.obs.iloc[i, -4]
location = adata_st_temp.obs.iloc[i, -3:-1]
alpha = adata_st_temp.obs.iloc[i, -1] * (1/quantile.loc[cluster])
if alpha > 1:
alpha = 1.
cv2.circle(img1, location, int(73*1.3/2), colors[cluster], -1)
cv2.circle(img2, location, int(73*1.3/2), (alpha, alpha, alpha), -1)
[ ]:
img = img1 * img2 + img * (1-img2)
img = img.astype(np.int32)
cv2.imwrite(f'{results_folder}Seurat_cluster.jpg', img[:, :, [2, 1, 0]])
[ ]:
mpl.pyplot.imshow(img)