import scomv
import stlearn as st
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import cv2
import pandas as pd
import numpy as np
import anndata
import scanpy as sc
/Users/riuyamas/miniconda3/envs/scomv-pypi-install-2/lib/python3.11/site-packages/numba/core/decorators.py:282: RuntimeWarning: nopython is set for njit and is ignored
warnings.warn('nopython is set for njit and is ignored', RuntimeWarning)
/Users/riuyamas/miniconda3/envs/scomv-pypi-install-2/lib/python3.11/site-packages/stlearn/tl/cci/het.py:206: NumbaDeprecationWarning: The keyword argument 'nopython=False' was supplied. From Numba 0.59.0 the default is being changed to True and use of 'nopython=False' will raise a warning as the argument will have no effect. See https://numba.readthedocs.io/en/stable/reference/deprecation.html#deprecation-of-object-mode-fall-back-behaviour-when-using-jit for details.
@jit(parallel=True, nopython=False)
# Download tutorial dataset
#!mkdir tutorial_data
#!wget -P tutorial_data/ https://raw.githubusercontent.com/RyosukeNomural/SpatialCompassV/main/docs/tutorials/tutorial_data/xenium_data/cell_feature_matrix.h5
#!wget -P tutorial_data/ https://raw.githubusercontent.com/RyosukeNomural/SpatialCompassV/main/docs/tutorials/tutorial_data/xenium_data/cells.csv.gz
# Load Xenium data using stlearn
adata = st.ReadXenium(
feature_cell_matrix_file="tutorial_data/cell_feature_matrix.h5",
cell_summary_file="tutorial_data/cells.csv.gz",
library_id="example data",
image_path=None,
scale=1,
spot_diameter_fullres=10
)
Warning: Using default pixel size of 0.2125 microns. Consider providing experiment_xenium_file for accurate pixel size.
# Gridding at 10μm interval using stlearn
N_COL = int((adata.obs.imagecol.max() - adata.obs.imagecol.min()) / 10)
N_ROW = int((adata.obs.imagerow.max() - adata.obs.imagerow.min()) / 10)
grid = st.tl.cci.grid(adata, n_row=N_ROW, n_col=N_COL, n_cpus=10, verbose=False)
from scomv.preparation.skny_calc_distance import calculate_distance
## apply SKNY
grid = calculate_distance(
grid, pos_marker_ls=['CDH1',"EPCAM"],
)
# Figure 2B
fig, ax = plt.subplots(figsize=(8, 6), dpi=150)
ax.imshow(cv2.cvtColor(grid.uns["marker_median_delineation"], cv2.COLOR_BGR2RGB),
interpolation="nearest")
ax.axis("off")
plt.show()
import plotly.express as px
import plotly.io as pio
import cv2
import numpy as np
# Convert the OpenCV BGR image to RGB for Plotly visualization
img = cv2.cvtColor(grid.uns["marker_median_delineation"], cv2.COLOR_BGR2RGB)
# Minimum coordinates of the original spatial data
x_min = float(adata.obs.imagecol.min())
y_min = float(adata.obs.imagerow.min())
# Grid bin size used when generating the grid image
bin_size = 10
# Create Plotly image figure
fig = px.imshow(img)
h, w = img.shape[:2]
# Convert grid coordinates back to absolute spatial coordinates
xx_abs = (np.arange(w)[None, :] * bin_size + x_min).repeat(h, axis=0)
yy_abs = (np.arange(h)[:, None] * bin_size + y_min).repeat(w, axis=1)
# Round coordinates to the nearest multiple of 10 for display
xx_round10 = np.round(xx_abs / 10) * 10
yy_round10 = np.round(yy_abs / 10) * 10
# Stack the coordinates so they can be accessed in hover data
custom = np.dstack([xx_round10, yy_round10])
# Axis tick configuration
# step = 20 in grid space corresponds to ~200 units in absolute coordinates
step = 20
x_tickvals = list(range(0, w, step))
y_tickvals = list(range(0, h, step))
# Generate axis labels rounded to multiples of 10
x_ticktext = [str(int(round((v * bin_size + x_min) / 10) * 10)) for v in x_tickvals]
y_ticktext = [str(int(round((v * bin_size + y_min) / 10) * 10)) for v in y_tickvals]
fig.update_xaxes(
tickmode="array",
tickvals=x_tickvals,
ticktext=x_ticktext
)
fig.update_yaxes(
tickmode="array",
tickvals=y_tickvals,
ticktext=y_ticktext
)
# Customize hover information to display rounded absolute spatial coordinates
fig.update_traces(
customdata=custom,
hovertemplate=
"imagecol: %{customdata[0]:.0f}<br>"
"imagerow: %{customdata[1]:.0f}"
"<extra></extra>"
)
fig.update_layout(
xaxis_title="imagecol",
yaxis_title="imagerow",
height=700
)
# Use the interactive notebook renderer
pio.renderers.default = "notebook_connected"
fig.show()
# annotation each section to obs object
df_shotest = getattr(grid, "shortest")
df_grid = grid.to_df()
# extract grid info
df_grid = pd.merge(
pd.DataFrame(index=["grid_" + str(i+1) for i in range(N_ROW * N_COL)]),
df_grid, right_index=True, left_index=True, how="left"
).fillna(np.nan)
# extract section info
df_region = pd.DataFrame(
np.array(df_shotest["region"]).reshape(N_ROW, N_COL).T.reshape(N_ROW * N_COL),
index=["grid_" + str(i+1) for i in range(N_ROW * N_COL)], columns=["region"]
)
# marge
df_grid_region = pd.merge(
df_grid, df_region,
right_index=True, left_index=True, how="left"
)
df_grid_region = df_grid_region.dropna()
# add to obs
grid.obs = pd.merge(
grid.obs, df_grid_region[["region"]],
right_index=True, left_index=True, how="left"
)
# shaping
grid.obs["region_10"] = [str(i*10) for i in grid.obs["region"]]
grid.obs["region_10"] = ["("+str(int(float(i.split(", ")[0][1:])))+", "+str(int(float(i.split(", ")[-1][:-1])))+"]" if i != "nan" else np.nan for i in grid.obs["region_10"]]
# exclude because of small number
grid.obs["region_10"] = grid.obs["region_10"].replace(
{"(-150, -120]": np.nan}
)
from scomv.preparation.choose_roi import extract_roi, contour_regions
# select ROI
roi = (2400, 3400, 2400, 3800)
subset_grid, filtered_shortest, xy_list = extract_roi(
grid=grid,
roi=roi,
bin_size=10,
region_col="region_10",
)
g_x_cont, g_y_cont, g_x_inside, g_y_inside = contour_regions(
filtered_shortest, adata,
min_x=0, max_x=4000, min_y=0, max_y=4000,
show=True,
)
from scomv.preparation.scomv_calc_vector import compute_min_vectors_polar
outline_points = list(zip(g_x_cont, g_y_cont))
inside_points = list(zip(g_x_inside, g_y_inside))
min_vector_df = compute_min_vectors_polar(
xy_list=xy_list,
outline_points=outline_points,
inside_points=inside_points,
invert_y=True,
make_inside_negative=True,
)
# load cell_annotation file
!wget -P tutorial_data/ https://raw.githubusercontent.com/RyosukeNomural/SpatialCompassV/main/docs/tutorials/tutorial_data/Cell_Barcode_Type_Matrices.xlsx
--2026-03-14 12:51:14-- https://raw.githubusercontent.com/RyosukeNomural/SpatialCompassV/main/docs/tutorials/tutorial_data/Cell_Barcode_Type_Matrices.xlsx
gw.east.ncc.go.jp (gw.east.ncc.go.jp) をDNSに問いあわせています... 160.190.222.8
gw.east.ncc.go.jp (gw.east.ncc.go.jp)|160.190.222.8|:8080 に接続しています... 接続しました。
Proxy による接続要求を送信しました、応答を待っています... 200 OK
長さ: 9409803 (9.0M) [application/octet-stream]
`tutorial_data/Cell_Barcode_Type_Matrices.xlsx.1' に保存中
Cell_Barcode_Type_M 100%[===================>] 8.97M --.-KB/s 時間 0.1s
2026-03-14 12:51:14 (78.8 MB/s) - `tutorial_data/Cell_Barcode_Type_Matrices.xlsx.1' へ保存完了 [9409803/9409803]
!pip install openpyxl
Collecting openpyxl
Using cached openpyxl-3.1.5-py2.py3-none-any.whl.metadata (2.5 kB)
Collecting et-xmlfile (from openpyxl)
Using cached et_xmlfile-2.0.0-py3-none-any.whl.metadata (2.7 kB)
Using cached openpyxl-3.1.5-py2.py3-none-any.whl (250 kB)
Using cached et_xmlfile-2.0.0-py3-none-any.whl (18 kB)
Installing collected packages: et-xmlfile, openpyxl
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2/2 [openpyxl]1/2 [openpyxl]
Successfully installed et-xmlfile-2.0.0 openpyxl-3.1.5
# load cell_annotation file
file_path = "tutorial_data/Cell_Barcode_Type_Matrices.xlsx"
xls = pd.ExcelFile(file_path)
cell_ann_df = pd.read_excel(file_path, sheet_name=xls.sheet_names[3])
print(cell_ann_df.head())
adata_obs = adata.obs
cell_ann_df.index = cell_ann_df.index + 1
adata_obs = adata_obs[["imagecol", "imagerow"]]
adata_obs.index = adata_obs.index.astype(int)
cell_df = pd.concat([adata_obs, cell_ann_df], axis=1)
cell_df = cell_df[["imagecol", "imagerow", "Cluster"]]
cell_df["Cluster"].unique()
#cell_df.to_csv("cell_annotation_df.csv")
Barcode Cluster
0 1 DCIS_2
1 2 DCIS_2
2 3 Unlabeled
3 4 Invasive_Tumor
4 5 DCIS_2
array(['DCIS_2', 'Unlabeled', 'Invasive_Tumor', 'Macrophages_1',
'Stromal', 'DCIS_1', 'Myoepi_ACTA2+', 'CD8+_T_Cells',
'Endothelial', 'Prolif_Invasive_Tumor', 'T_Cell_&_Tumor_Hybrid',
'Mast_Cells', 'CD4+_T_Cells', 'B_Cells', 'Macrophages_2',
'Stromal_&_T_Cell_Hybrid', 'Perivascular-Like', 'LAMP3+_DCs',
'IRF7+_DCs', 'Myoepi_KRT15+'], dtype=object)
from scomv.cell_pipeline import CellPolarPipeline
# Initialize the pipeline with cell-level data and precomputed minimum-distance vectors
cell_pipe = CellPolarPipeline(
cell_df=cell_df,
min_vector_df=min_vector_df
)
# Run the pipeline for a specified ROI (xmin, xmax, ymin, ymax)
# Disable histogram plotting during the run
cell_out = cell_pipe.run(
roi=(2400, 3400, 2400, 3800),
plot_hist=False
)
# Distance matrix between cells based on polar-vector representations
dist = cell_pipe.dist_df
# Plot explained variance of PCoA components
cell_pipe.plot_explained_variance(n_components=8)
# Plot a heatmap of similarities (displayed as 1 - distance)
cell_pipe.heatmap(font_size=14, figsize=(8, 8))
<seaborn.matrix.ClusterGrid at 0x16961df10>
from scomv.cell import compute_cluster_polar_distributions
_ = compute_cluster_polar_distributions(cell_out["cell_df_filtered"], min_vector_df)
adata_2 = anndata.AnnData(X=np.zeros((len(cell_df), 0))) # Dummy X
adata_2.obs = cell_df.copy()
# Store the coordinates used as the basis in adata.obsm
adata_2.obsm["spatial"] = cell_df[["imagecol", "imagerow"]].to_numpy()
# draw
sc.pl.embedding(
adata_2,
basis="spatial",
color="Cluster",
size=80,
legend_loc=None,
frameon=True,
show=False,
)
ax = plt.gca()
ax.set_xlabel("X coordinate", fontsize=15)
ax.set_ylabel("Y coordinate", fontsize=15)
ax.tick_params(axis="both", labelsize=13)
# limit the range
ax.set_xlim(2400, 3400)
ax.set_ylim(2400, 3800)
# Align to the image coordinate system
ax.invert_yaxis()
ax.set_aspect("equal", adjustable="box")
legend = ax.legend(
*ax.get_legend_handles_labels(),
loc="center left",
bbox_to_anchor=(1.02, 0.5),
frameon=False,
fontsize=12
)
plt.tight_layout()
plt.show()
/Users/riuyamas/miniconda3/envs/scomv-pypi-install-2/lib/python3.11/site-packages/anndata/_core/anndata.py:859: UserWarning:
AnnData expects .obs.index to contain strings, but got values like:
[1, 2, 3, 4, 5]
Inferred to be: integer
sc.pl.embedding(
adata_2,
basis="spatial",
color="Cluster",
size=80,
legend_loc="none",
frameon=True,
show=False,
)
ax = plt.gca()
ax.set_xlabel("X coordinate", fontsize=15)
ax.set_ylabel("Y coordinate", fontsize=15)
ax.tick_params(axis="both", labelsize=13)
ax.set_xlim(2400, 3400)
ax.set_ylim(2400, 3800)
ax.invert_yaxis()
ax.set_aspect("equal", adjustable="box")
# Retrieve category names and colors stored in adata
cats = adata_2.obs["Cluster"].astype("category").cat.categories
colors = adata_2.uns.get("Cluster_colors")
handles = [mpatches.Patch(color=c, label=str(cat)) for c, cat in zip(colors, cats)]
ax.legend(handles=handles, loc="center left", bbox_to_anchor=(1.02, 0.5), frameon=False, fontsize=12)
plt.subplots_adjust(right=0.80)
plt.tight_layout()
plt.show()
# List of cluster categories
clusters = adata_2.obs["Cluster"].astype("category").cat.categories
# Create a copy of adata and rename DCIS_2 → DCIS
adata_2_renamed = adata_2.copy()
adata_2_renamed.obs["Cluster"] = (
adata_2_renamed.obs["Cluster"].replace({"DCIS_2": "DCIS"}).astype("category")
)
# Color settings: DCIS in yellow, others keep original colors
orig_cats = adata_2.obs["Cluster"].astype("category").cat.categories
orig_colors = adata_2.uns["Cluster_colors"]
color_dict = dict(zip(orig_cats, orig_colors))
cats = list(adata_2_renamed.obs["Cluster"].cat.categories)
colors = []
for c in cats:
if c == "DCIS":
colors.append("#FFD700")
else:
orig_name = "DCIS_2" if c == "DCIS" and "DCIS_2" in orig_cats else c
colors.append(color_dict.get(orig_name, "gray"))
adata_2_renamed.uns["Cluster_colors"] = colors
# Plot "DCIS + each cluster" separately
for cluster in clusters:
cluster_name = "DCIS" if cluster == "DCIS_2" else cluster
plt.figure(figsize=(10, 10))
sc.pl.spatial(
adata_2_renamed[adata_2_renamed.obs["Cluster"].isin([cluster_name, "DCIS"])],
img_key="hires",
color="Cluster",
size=1.5,
spot_size=20,
legend_loc="lower right",
frameon=True,
show=False,
)
ax = plt.gca()
# Remove axis titles and labels
ax.set_title("")
ax.set_xlabel("")
ax.set_ylabel("")
ax.tick_params(axis="both", labelsize=11)
# Manually construct legend for selected clusters
handle_map = {cat: col for cat, col in zip(cats, adata_2_renamed.uns["Cluster_colors"])}
show_labels = [cluster_name, "DCIS"]
show_labels = list(dict.fromkeys(show_labels)) # Remove duplicates for DCIS plots
handles = [
mpatches.Patch(color=handle_map[label], label=label)
for label in show_labels
]
ax.legend(handles=handles, loc="lower right", frameon=True, fontsize=12)
ax.set_axis_on()
# Set ROI limits
ax.set_xlim(2400, 3400)
# ax.set_xticks(range(2400, 3401, 200))
ax.set_ylim(2400, 3800)
# ax.set_yticks(range(2400, 3801, 200))
# Align to image coordinate system
ax.invert_yaxis()
ax.set_aspect("equal", adjustable="box")
# Save figure (optional)
# plt.savefig(f"{root}/cell_location_fig/{cluster_name}_no_tick.png", dpi=300)
plt.show()
print(f"Figure: {cluster_name} + DCIS")
/Users/riuyamas/miniconda3/envs/scomv-pypi-install-2/lib/python3.11/site-packages/anndata/_core/anndata.py:183: ImplicitModificationWarning:
Transforming to str index.
/var/folders/rp/843_v26x35314skd4rnkz6jm0000gp/T/ipykernel_32438/3277719802.py:7: FutureWarning:
The behavior of Series.replace (and DataFrame.replace) with CategoricalDtype is deprecated. In a future version, replace will only be used for cases that preserve the categories. To change the categories, use ser.cat.rename_categories instead.
/var/folders/rp/843_v26x35314skd4rnkz6jm0000gp/T/ipykernel_32438/3277719802.py:31: FutureWarning:
Use `squidpy.pl.spatial_scatter` instead.
<Figure size 1000x1000 with 0 Axes>
Figure: B_Cells + DCIS
/var/folders/rp/843_v26x35314skd4rnkz6jm0000gp/T/ipykernel_32438/3277719802.py:31: FutureWarning:
Use `squidpy.pl.spatial_scatter` instead.
<Figure size 1000x1000 with 0 Axes>
Figure: CD4+_T_Cells + DCIS
/var/folders/rp/843_v26x35314skd4rnkz6jm0000gp/T/ipykernel_32438/3277719802.py:31: FutureWarning:
Use `squidpy.pl.spatial_scatter` instead.
<Figure size 1000x1000 with 0 Axes>
Figure: CD8+_T_Cells + DCIS
/var/folders/rp/843_v26x35314skd4rnkz6jm0000gp/T/ipykernel_32438/3277719802.py:31: FutureWarning:
Use `squidpy.pl.spatial_scatter` instead.
<Figure size 1000x1000 with 0 Axes>
Figure: DCIS_1 + DCIS
/var/folders/rp/843_v26x35314skd4rnkz6jm0000gp/T/ipykernel_32438/3277719802.py:31: FutureWarning:
Use `squidpy.pl.spatial_scatter` instead.
<Figure size 1000x1000 with 0 Axes>
Figure: DCIS + DCIS
/var/folders/rp/843_v26x35314skd4rnkz6jm0000gp/T/ipykernel_32438/3277719802.py:31: FutureWarning:
Use `squidpy.pl.spatial_scatter` instead.
<Figure size 1000x1000 with 0 Axes>
Figure: Endothelial + DCIS
/var/folders/rp/843_v26x35314skd4rnkz6jm0000gp/T/ipykernel_32438/3277719802.py:31: FutureWarning:
Use `squidpy.pl.spatial_scatter` instead.
<Figure size 1000x1000 with 0 Axes>
Figure: IRF7+_DCs + DCIS
/var/folders/rp/843_v26x35314skd4rnkz6jm0000gp/T/ipykernel_32438/3277719802.py:31: FutureWarning:
Use `squidpy.pl.spatial_scatter` instead.
<Figure size 1000x1000 with 0 Axes>
Figure: Invasive_Tumor + DCIS
/var/folders/rp/843_v26x35314skd4rnkz6jm0000gp/T/ipykernel_32438/3277719802.py:31: FutureWarning:
Use `squidpy.pl.spatial_scatter` instead.
<Figure size 1000x1000 with 0 Axes>
Figure: LAMP3+_DCs + DCIS
/var/folders/rp/843_v26x35314skd4rnkz6jm0000gp/T/ipykernel_32438/3277719802.py:31: FutureWarning:
Use `squidpy.pl.spatial_scatter` instead.
<Figure size 1000x1000 with 0 Axes>
Figure: Macrophages_1 + DCIS
/var/folders/rp/843_v26x35314skd4rnkz6jm0000gp/T/ipykernel_32438/3277719802.py:31: FutureWarning:
Use `squidpy.pl.spatial_scatter` instead.
<Figure size 1000x1000 with 0 Axes>
Figure: Macrophages_2 + DCIS
/var/folders/rp/843_v26x35314skd4rnkz6jm0000gp/T/ipykernel_32438/3277719802.py:31: FutureWarning:
Use `squidpy.pl.spatial_scatter` instead.
<Figure size 1000x1000 with 0 Axes>
Figure: Mast_Cells + DCIS
/var/folders/rp/843_v26x35314skd4rnkz6jm0000gp/T/ipykernel_32438/3277719802.py:31: FutureWarning:
Use `squidpy.pl.spatial_scatter` instead.
<Figure size 1000x1000 with 0 Axes>
Figure: Myoepi_ACTA2+ + DCIS
/var/folders/rp/843_v26x35314skd4rnkz6jm0000gp/T/ipykernel_32438/3277719802.py:31: FutureWarning:
Use `squidpy.pl.spatial_scatter` instead.
<Figure size 1000x1000 with 0 Axes>
Figure: Myoepi_KRT15+ + DCIS
/var/folders/rp/843_v26x35314skd4rnkz6jm0000gp/T/ipykernel_32438/3277719802.py:31: FutureWarning:
Use `squidpy.pl.spatial_scatter` instead.
<Figure size 1000x1000 with 0 Axes>
Figure: Perivascular-Like + DCIS
/var/folders/rp/843_v26x35314skd4rnkz6jm0000gp/T/ipykernel_32438/3277719802.py:31: FutureWarning:
Use `squidpy.pl.spatial_scatter` instead.
<Figure size 1000x1000 with 0 Axes>
Figure: Prolif_Invasive_Tumor + DCIS
/var/folders/rp/843_v26x35314skd4rnkz6jm0000gp/T/ipykernel_32438/3277719802.py:31: FutureWarning:
Use `squidpy.pl.spatial_scatter` instead.
<Figure size 1000x1000 with 0 Axes>
Figure: Stromal + DCIS
/var/folders/rp/843_v26x35314skd4rnkz6jm0000gp/T/ipykernel_32438/3277719802.py:31: FutureWarning:
Use `squidpy.pl.spatial_scatter` instead.
<Figure size 1000x1000 with 0 Axes>
Figure: Stromal_&_T_Cell_Hybrid + DCIS
/var/folders/rp/843_v26x35314skd4rnkz6jm0000gp/T/ipykernel_32438/3277719802.py:31: FutureWarning:
Use `squidpy.pl.spatial_scatter` instead.
<Figure size 1000x1000 with 0 Axes>
Figure: T_Cell_&_Tumor_Hybrid + DCIS
/var/folders/rp/843_v26x35314skd4rnkz6jm0000gp/T/ipykernel_32438/3277719802.py:31: FutureWarning:
Use `squidpy.pl.spatial_scatter` instead.
<Figure size 1000x1000 with 0 Axes>
Figure: Unlabeled + DCIS
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import scanpy as sc
import anndata
# ----------------------------
# 0) 事前: Clusterの欠損を落とす(任意だが安全)
# ----------------------------
adata_2 = adata_2.copy()
adata_2.obs_names = adata_2.obs_names.astype(str)
adata_2_clean = adata_2[~adata_2.obs["Cluster"].isna()].copy()
# ----------------------------
# 1) DCIS_1, DCIS_2 → DCIS に統合
# ----------------------------
adata_2_renamed = adata_2_clean.copy()
# 末尾スペースなどの事故を避けたい場合(任意)
adata_2_renamed.obs["Cluster"] = adata_2_renamed.obs["Cluster"].astype(str).str.strip()
adata_2_renamed.obs["Cluster"] = (
adata_2_renamed.obs["Cluster"]
.replace({"DCIS_1": "DCIS", "DCIS_2": "DCIS"})
.astype("category")
)
# ----------------------------
# 2) 色設定: DCISを黄色、他は元の色を引き継ぐ
# ----------------------------
orig = adata_2_clean.copy()
orig.obs["Cluster"] = orig.obs["Cluster"].astype(str).str.strip().astype("category")
orig_cats = list(orig.obs["Cluster"].cat.categories)
# orig_colorsが無い/長さが合わない時の保険(scanpyに自動生成させる)
if "Cluster_colors" not in orig.uns or len(orig.uns["Cluster_colors"]) != len(orig_cats):
tmp = orig.copy()
tmp.uns.pop("Cluster_colors", None)
sc.pl.embedding(tmp, basis="spatial", color="Cluster", show=False)
plt.close()
orig_colors = tmp.uns["Cluster_colors"]
else:
orig_colors = orig.uns["Cluster_colors"]
color_dict = dict(zip(orig_cats, orig_colors))
# 統合後カテゴリ順に色リストを作る
cats = list(adata_2_renamed.obs["Cluster"].cat.categories)
colors = []
for c in cats:
if c == "DCIS":
colors.append("#FFD700") # DCISは黄色固定
else:
# 統合されていないクラスタはそのまま対応
colors.append(color_dict.get(c, "gray"))
adata_2_renamed.uns["Cluster_colors"] = colors
# ----------------------------
# 3) "DCIS + 各クラスタ" を統合後カテゴリで回す
# (DCIS自身は除外)
# ----------------------------
clusters = [c for c in cats if c != "DCIS"]
for cluster in clusters:
plt.figure(figsize=(10, 10))
sub = adata_2_renamed[adata_2_renamed.obs["Cluster"].isin([cluster, "DCIS"])].copy()
sc.pl.spatial(
sub,
img_key="hires",
color="Cluster",
size=1.5,
spot_size=20,
legend_loc=None, # いったん消して手動legend
frameon=True,
show=False,
)
ax = plt.gca()
# タイトル・軸ラベルを消す
ax.set_title("")
ax.set_xlabel("")
ax.set_ylabel("")
ax.tick_params(axis="both", labelsize=11)
# 手動legend(その図に出ている2つだけ)
handle_map = dict(zip(cats, adata_2_renamed.uns["Cluster_colors"]))
show_labels = [cluster, "DCIS"]
handles = [mpatches.Patch(color=handle_map[l], label=l) for l in show_labels]
ax.legend(handles=handles, loc="lower right", frameon=True, fontsize=12)
ax.set_axis_on()
# ROI
ax.set_xlim(2400, 3400)
ax.set_ylim(2400, 3800)
# ROI
xmin, xmax = 2400, 3400
ymin, ymax = 2400, 3800
ax.set_xlim(xmin, xmax)
ax.set_ylim(ymin, ymax)
# 200 px ごとに目盛りを出す例
ax.set_xticks(np.arange(xmin, xmax + 1, 200))
ax.set_yticks(np.arange(ymin, ymax + 1, 200))
ax.tick_params(axis="both", labelsize=11)
# 画像座標系
ax.invert_yaxis()
ax.set_aspect("equal", adjustable="box")
plt.tight_layout()
plt.show()
print(f"Figure: {cluster} + DCIS")
/Users/riuyamas/miniconda3/envs/scomv-pypi-install-2/lib/python3.11/site-packages/anndata/_core/anndata.py:183: ImplicitModificationWarning:
Transforming to str index.
/var/folders/rp/843_v26x35314skd4rnkz6jm0000gp/T/ipykernel_32438/1292577244.py:71: FutureWarning:
Use `squidpy.pl.spatial_scatter` instead.
<Figure size 1000x1000 with 0 Axes>
Figure: B_Cells + DCIS
/var/folders/rp/843_v26x35314skd4rnkz6jm0000gp/T/ipykernel_32438/1292577244.py:71: FutureWarning:
Use `squidpy.pl.spatial_scatter` instead.