Visualizing easydecon results
These recipes use Matplotlib only. Every example aligns values to
table.obs.index before plotting.
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import easydecon as ed
table = ed.get_table(sdata)
coords = np.asarray(table.obsm["spatial"])
If your platform’s y-axis convention is image-like, call ax.invert_yaxis()
after plotting.
Hard assignment map
assignment_column = result.diagnostics.get(
"results_column",
result.assigned_labels.columns[0],
)
labels = result.assigned_labels[assignment_column].reindex(table.obs.index)
fig, ax = plt.subplots(figsize=(6, 6))
for label in labels.dropna().astype(str).unique():
mask = labels.astype(str).eq(label).to_numpy()
ax.scatter(coords[mask, 0], coords[mask, 1], s=8, label=label)
ax.set_title("easydecon assignments")
ax.set_aspect("equal")
ax.set_xlabel("Spatial x")
ax.set_ylabel("Spatial y")
ax.legend(bbox_to_anchor=(1.02, 1), loc="upper left", frameon=False)
fig.tight_layout()
Unassigned locations are omitted here. Assignment counts are counts of spatial units, not cell counts.
Single-cell-type prior
cell_type = "Myeloid"
values = result.priors_df[cell_type].reindex(table.obs.index).fillna(0.0)
fig, ax = plt.subplots(figsize=(6, 6))
points = ax.scatter(coords[:, 0], coords[:, 1], c=values.to_numpy(), s=8)
fig.colorbar(points, ax=ax, label="Phase 1 prior")
ax.set_title(f"{cell_type} Phase 1 prior")
ax.set_aspect("equal")
fig.tight_layout()
Single-cell-type posterior
cell_type = "Myeloid"
if result.posterior_df is None:
raise ValueError("This workflow has no posterior_df; use assignment_df instead.")
values = result.posterior_df[cell_type].reindex(table.obs.index).fillna(0.0)
fig, ax = plt.subplots(figsize=(6, 6))
points = ax.scatter(coords[:, 0], coords[:, 1], c=values.to_numpy(), s=8)
fig.colorbar(points, ax=ax, label="Posterior support")
ax.set_title(f"{cell_type} posterior support")
ax.set_aspect("equal")
fig.tight_layout()
Phase 1 versus posterior comparison
def plot_spatial_values(table, values, title, colorbar_label):
coords = np.asarray(table.obsm["spatial"])
values = values.reindex(table.obs.index).fillna(0.0)
fig, ax = plt.subplots(figsize=(6, 6))
points = ax.scatter(coords[:, 0], coords[:, 1], c=values.to_numpy(), s=8)
fig.colorbar(points, ax=ax, label=colorbar_label)
ax.set_title(title)
ax.set_aspect("equal")
ax.set_xlabel("Spatial x")
ax.set_ylabel("Spatial y")
fig.tight_layout()
return fig, ax
plot_spatial_values(table, result.priors_df["Myeloid"], "Myeloid prior", "Prior")
plot_spatial_values(
table,
result.posterior_df["Myeloid"],
"Myeloid posterior",
"Posterior support",
)
Assignment counts
assignment_column = result.diagnostics.get(
"results_column",
result.assigned_labels.columns[0],
)
counts = (
result.assigned_labels[assignment_column]
.dropna()
.value_counts()
.sort_values(ascending=False)
)
fig, ax = plt.subplots(figsize=(7, 4))
counts.plot.bar(ax=ax)
ax.set_title("Assigned spatial locations per group")
ax.set_xlabel("Marker group")
ax.set_ylabel("Number of spatial locations")
fig.tight_layout()
Posterior heatmap
if result.posterior_df is None:
raise ValueError("This workflow has no posterior_df; use assignment_df instead.")
matrix = result.posterior_df.copy()
matrix = matrix.loc[matrix.max(axis=1).sort_values(ascending=False).index]
matrix = matrix.iloc[:100]
fig, ax = plt.subplots(figsize=(8, 5))
image = ax.imshow(matrix.to_numpy(), aspect="auto", interpolation="nearest")
ax.set_xticks(range(matrix.shape[1]))
ax.set_xticklabels(matrix.columns, rotation=90)
ax.set_ylabel("Spatial locations")
ax.set_title("Posterior support")
fig.colorbar(image, ax=ax, label="Posterior support")
fig.tight_layout()
Maximum-posterior confidence map
if result.posterior_df is None:
raise ValueError("This workflow has no posterior_df; use assignment_df instead.")
confidence = result.posterior_df.max(axis=1).reindex(table.obs.index).fillna(0.0)
fig, ax = plt.subplots(figsize=(6, 6))
points = ax.scatter(coords[:, 0], coords[:, 1], c=confidence.to_numpy(), s=8)
fig.colorbar(points, ax=ax, label="Maximum posterior support")
ax.set_title("Assignment confidence")
ax.set_aspect("equal")
fig.tight_layout()
Unassigned-location map
assignment_column = result.diagnostics.get(
"results_column",
result.assigned_labels.columns[0],
)
labels = result.assigned_labels[assignment_column].reindex(table.obs.index)
unassigned = labels.isna().to_numpy()
fig, ax = plt.subplots(figsize=(6, 6))
ax.scatter(coords[~unassigned, 0], coords[~unassigned, 1], s=6, alpha=0.3)
ax.scatter(coords[unassigned, 0], coords[unassigned, 1], s=10)
ax.set_title("Unassigned spatial locations")
ax.set_aspect("equal")
fig.tight_layout()
Niche composition plot
niches, smoothed = ed.detect_niches_from_easydecon_result(
sdata,
result,
n_neighbors=6,
n_niches=5,
)
fig, ax = ed.plot_niche_compositions(smoothed, niches)
Niche IDs are categorical labels and do not imply an ordering.