Sensitivity Analysis across Hyperparameters
In [1]:
Copied!
import pandas as pd
import numpy as np
np.random.seed(20251023)
data = pd.read_json("https://raw.githubusercontent.com/krisrs1128/distortions-data/main/data/mammoth_3d.json")
data.columns = ["x", "y", "z"]
import pandas as pd
import numpy as np
np.random.seed(20251023)
data = pd.read_json("https://raw.githubusercontent.com/krisrs1128/distortions-data/main/data/mammoth_3d.json")
data.columns = ["x", "y", "z"]
In [2]:
Copied!
data
data
Out[2]:
| x | y | z | |
|---|---|---|---|
| 0 | 430.826 | 106.864 | 24.492 |
| 1 | 397.734 | 99.293 | 14.370 |
| 2 | 480.505 | 80.876 | 131.561 |
| 3 | 422.736 | 87.715 | 13.504 |
| 4 | 414.840 | 113.363 | 78.061 |
| ... | ... | ... | ... |
| 9995 | 492.742 | -114.477 | 107.954 |
| 9996 | 502.376 | -94.579 | 117.065 |
| 9997 | 467.228 | -168.295 | 97.806 |
| 9998 | 494.780 | -111.272 | 115.444 |
| 9999 | 486.007 | -150.081 | 114.390 |
10000 rows × 3 columns
In [3]:
Copied!
from anndata import AnnData
import scanpy as sc
n_neighbors = 50
adata = AnnData(X=data, obs=pd.DataFrame(index=data.index))
sc.pp.neighbors(adata, n_neighbors=n_neighbors)
sc.tl.umap(adata)
embedding = adata.obsm["X_umap"].copy()
from anndata import AnnData
import scanpy as sc
n_neighbors = 50
adata = AnnData(X=data, obs=pd.DataFrame(index=data.index))
sc.pp.neighbors(adata, n_neighbors=n_neighbors)
sc.tl.umap(adata)
embedding = adata.obsm["X_umap"].copy()
In [4]:
Copied!
import numpy as np
radius = 3 * np.mean(adata.obsp["distances"].data)
parameters = {
"radius": 2 ** (np.linspace(-1, 1, 10)) * radius,
"scaling_epps": np.linspace(3, 7, 10)
}
import numpy as np
radius = 3 * np.mean(adata.obsp["distances"].data)
parameters = {
"radius": 2 ** (np.linspace(-1, 1, 10)) * radius,
"scaling_epps": np.linspace(3, 7, 10)
}
In [5]:
Copied!
radius
radius
Out[5]:
32.194828033447266
In [ ]:
Copied!
from distortions.geometry import Geometry
from distortions.geometry import expand_geoms
from distortions.geometry import metric_sensitivity
n_neighbors = 50
geom = Geometry("brute", laplacian_method="geometric", affinity_kwds={"radius": radius}, adjacency_kwds={"n_neighbors": n_neighbors}, laplacian_kwds={"scaling_epps": 5})
geoms, params_df = expand_geoms(geom, parameters)
Hvv, Hs = metric_sensitivity(geoms, embedding, data)
from distortions.geometry import Geometry
from distortions.geometry import expand_geoms
from distortions.geometry import metric_sensitivity
n_neighbors = 50
geom = Geometry("brute", laplacian_method="geometric", affinity_kwds={"radius": radius}, adjacency_kwds={"n_neighbors": n_neighbors}, laplacian_kwds={"scaling_epps": 5})
geoms, params_df = expand_geoms(geom, parameters)
Hvv, Hs = metric_sensitivity(geoms, embedding, data)
In [ ]:
Copied!
plot_data = []
for i in range(Hs.shape[1]):
for j in range(Hs.shape[0]):
plot_data.append({
"sample": i,
"combination": j,
"hv00": float(Hvv[j, i, 0, 0]),
"hv11": float(Hvv[j, i, 1, 1]),
"hv01": float(Hvv[j, i, 0, 1]),
"hv10": float(Hvv[j, i, 1, 0]),
"hs0": float(Hs[j, i, 0]),
"hs1": float(Hs[j, i, 1])
})
params_df["combination"] = params_df.index
plot_df = pd.DataFrame(plot_data)
plot_df = plot_df.merge(params_df)
plot_data = []
for i in range(Hs.shape[1]):
for j in range(Hs.shape[0]):
plot_data.append({
"sample": i,
"combination": j,
"hv00": float(Hvv[j, i, 0, 0]),
"hv11": float(Hvv[j, i, 1, 1]),
"hv01": float(Hvv[j, i, 0, 1]),
"hv10": float(Hvv[j, i, 1, 0]),
"hs0": float(Hs[j, i, 0]),
"hs1": float(Hs[j, i, 1])
})
params_df["combination"] = params_df.index
plot_df = pd.DataFrame(plot_data)
plot_df = plot_df.merge(params_df)
This is variation in the coordinate $H_{00}$ across choices of radius and scaling epsilon.
In [ ]:
Copied!
import altair as alt
alt.data_transformers.enable("vegafusion")
random_samples = np.random.choice(plot_df["sample"].unique(), size=20, replace=False)
charts = []
for i in random_samples:
random_subset = plot_df[plot_df["sample"] == i]
chart_ = alt.Chart(random_subset).mark_rect().encode(
x=alt.X('radius:N', title='Radius', axis=alt.Axis(format='.2f')),
y=alt.Y('scaling_epps:N', title='Epsilon', axis=alt.Axis(format='.2f')),
fill=alt.Fill('hv00:Q', title='h')
)
charts.append(chart_)
#[display(p) for p in charts]
import altair as alt
alt.data_transformers.enable("vegafusion")
random_samples = np.random.choice(plot_df["sample"].unique(), size=20, replace=False)
charts = []
for i in random_samples:
random_subset = plot_df[plot_df["sample"] == i]
chart_ = alt.Chart(random_subset).mark_rect().encode(
x=alt.X('radius:N', title='Radius', axis=alt.Axis(format='.2f')),
y=alt.Y('scaling_epps:N', title='Epsilon', axis=alt.Axis(format='.2f')),
fill=alt.Fill('hv00:Q', title='h')
)
charts.append(chart_)
#[display(p) for p in charts]
Here is change in the singular values across the same parameters.
In [ ]:
Copied!
charts = []
for i in random_samples:
random_subset = plot_df[plot_df["sample"] == i]
chart_ = alt.Chart(random_subset).mark_rect().encode(
x=alt.X('radius:N', title='Radius', axis=alt.Axis(format='.2f')),
y=alt.Y('scaling_epps:N', title='Epsilon', axis=alt.Axis(format='.2f')),
fill=alt.Fill('hs0:Q', title='h')
)
charts.append(chart_)
#[display(p) for p in charts]
charts = []
for i in random_samples:
random_subset = plot_df[plot_df["sample"] == i]
chart_ = alt.Chart(random_subset).mark_rect().encode(
x=alt.X('radius:N', title='Radius', axis=alt.Axis(format='.2f')),
y=alt.Y('scaling_epps:N', title='Epsilon', axis=alt.Axis(format='.2f')),
fill=alt.Fill('hs0:Q', title='h')
)
charts.append(chart_)
#[display(p) for p in charts]
Here are some line plots to visualize sensitivity across all samples.
In [ ]:
Copied!
import altair as alt
alt.data_transformers.enable("vegafusion")
ixl = np.random.choice(plot_df["sample"].unique(), size=500, replace=False)
sub_df = plot_df.loc[plot_df["sample"].isin(ixl)]
line_plots = []
p = alt.Chart(sub_df).mark_line(size=0.2).encode(
x=alt.X('scaling_epps:Q', title='Rescaling Epsilon'),
y=alt.Y('hv00:Q', title='Hvv[0,0]'),
detail=['sample:N', 'radius:Q']
)
line_plots.append(p)
p = alt.Chart(sub_df).mark_line(size=0.2).encode(
x=alt.X('radius:Q', title='Radius'),
y=alt.Y('hv00:Q', title='Hvv[0,0]'),
detail=['sample:N', 'scaling_epps:Q']
)
line_plots.append(p)
p = alt.Chart(sub_df).mark_line(size=0.2).encode(
x=alt.X('scaling_epps:Q', title='Rescaling Epsilon'),
y=alt.Y('hs0:Q', title='first singular value'),
color=alt.Color('radius:Q'),
detail=['sample:N', "radius:Q"]
)
line_plots.append(p)
p = alt.Chart(sub_df).mark_line(size=0.2).encode(
x=alt.X('radius:Q', title='Laplacian Radius'),
y=alt.Y('hs0:Q', title='first singular value'),
color=alt.Color('scaling_epps:Q'),
detail=['sample:N', "scaling_epps:Q"]
)
line_plots.append(p)
#[display(p) for p in line_plots]
import altair as alt
alt.data_transformers.enable("vegafusion")
ixl = np.random.choice(plot_df["sample"].unique(), size=500, replace=False)
sub_df = plot_df.loc[plot_df["sample"].isin(ixl)]
line_plots = []
p = alt.Chart(sub_df).mark_line(size=0.2).encode(
x=alt.X('scaling_epps:Q', title='Rescaling Epsilon'),
y=alt.Y('hv00:Q', title='Hvv[0,0]'),
detail=['sample:N', 'radius:Q']
)
line_plots.append(p)
p = alt.Chart(sub_df).mark_line(size=0.2).encode(
x=alt.X('radius:Q', title='Radius'),
y=alt.Y('hv00:Q', title='Hvv[0,0]'),
detail=['sample:N', 'scaling_epps:Q']
)
line_plots.append(p)
p = alt.Chart(sub_df).mark_line(size=0.2).encode(
x=alt.X('scaling_epps:Q', title='Rescaling Epsilon'),
y=alt.Y('hs0:Q', title='first singular value'),
color=alt.Color('radius:Q'),
detail=['sample:N', "radius:Q"]
)
line_plots.append(p)
p = alt.Chart(sub_df).mark_line(size=0.2).encode(
x=alt.X('radius:Q', title='Laplacian Radius'),
y=alt.Y('hs0:Q', title='first singular value'),
color=alt.Color('scaling_epps:Q'),
detail=['sample:N', "scaling_epps:Q"]
)
line_plots.append(p)
#[display(p) for p in line_plots]
In [ ]:
Copied!
p = alt.Chart(sub_df).mark_line(size=0.2, clip=True).encode(
x=alt.X('scaling_epps:Q', title='Rescaling Epsilon'),
y=alt.Y('hs0:Q', title='first singular value', scale=alt.Scale(domain=[0, 0.5])),
color=alt.Color('radius:Q'),
detail=['sample:N', 'radius:Q']
)
line_plots.append(p)
p = alt.Chart(sub_df).mark_line(size=0.2, clip=True).encode(
x=alt.X('radius:Q', title='Laplacian Radius'),
y=alt.Y('hs0:Q', title='first singular value', scale=alt.Scale(domain=[0, 0.5])),
color=alt.Color('scaling_epps:Q'),
detail=['sample:N', 'scaling_epps:Q']
)
line_plots.append(p)
#[display(p) for p in line_plots[-2:]]
p = alt.Chart(sub_df).mark_line(size=0.2, clip=True).encode(
x=alt.X('scaling_epps:Q', title='Rescaling Epsilon'),
y=alt.Y('hs0:Q', title='first singular value', scale=alt.Scale(domain=[0, 0.5])),
color=alt.Color('radius:Q'),
detail=['sample:N', 'radius:Q']
)
line_plots.append(p)
p = alt.Chart(sub_df).mark_line(size=0.2, clip=True).encode(
x=alt.X('radius:Q', title='Laplacian Radius'),
y=alt.Y('hs0:Q', title='first singular value', scale=alt.Scale(domain=[0, 0.5])),
color=alt.Color('scaling_epps:Q'),
detail=['sample:N', 'scaling_epps:Q']
)
line_plots.append(p)
#[display(p) for p in line_plots[-2:]]
In [ ]:
Copied!
p = alt.Chart(sub_df).mark_line(size=0.2).encode(
x=alt.X('scaling_epps:Q', title='Rescaling Epsilon'),
y=alt.Y('hs0:Q', title='first singular value', scale=alt.Scale(type='log')),
color=alt.Color('radius:Q'),
detail=['sample:N', 'radius:Q']
)
line_plots.append(p)
p = alt.Chart(sub_df).mark_line(size=0.2).encode(
x=alt.X('radius:Q', title='Laplacian Radius'),
y=alt.Y('hs0:Q', title='first singular value', scale=alt.Scale(type='log')),
color=alt.Color('scaling_epps:Q'),
detail=['sample:N', 'scaling_epps:Q']
)
line_plots.append(p)
#[display(p) for p in line_plots[-2:]]
p = alt.Chart(sub_df).mark_line(size=0.2).encode(
x=alt.X('scaling_epps:Q', title='Rescaling Epsilon'),
y=alt.Y('hs0:Q', title='first singular value', scale=alt.Scale(type='log')),
color=alt.Color('radius:Q'),
detail=['sample:N', 'radius:Q']
)
line_plots.append(p)
p = alt.Chart(sub_df).mark_line(size=0.2).encode(
x=alt.X('radius:Q', title='Laplacian Radius'),
y=alt.Y('hs0:Q', title='first singular value', scale=alt.Scale(type='log')),
color=alt.Color('scaling_epps:Q'),
detail=['sample:N', 'scaling_epps:Q']
)
line_plots.append(p)
#[display(p) for p in line_plots[-2:]]
In [ ]:
Copied!
sub_df.to_csv("data/sensitivity_sub_df.csv")
sub_df.to_csv("data/sensitivity_sub_df.csv")
In [ ]:
Copied!
import matplotlib.pyplot as plt
import matplotlib as mpl
def ellipse_sequence(single_sample):
Hvv_i, Hs_i = [], []
for i in range(len(single_sample)):
h = np.array([
[single_sample["hv00"].values[i],
single_sample["hv01"].values[i]],
[single_sample["hv10"].values[i],
single_sample["hv11"].values[i]]
])
s = np.array([
single_sample["hs0"].values[i],
single_sample["hs1"].values[i]
])
Hvv_i.append(h)
Hs_i.append(s)
return Hvv_i, Hs_i
def plot_ellipses(Hvv_i, Hs_i, sample_df, max_cols=5, cmap_name='cividis'):
"""
Facet by unique radius values and color ellipses by scaling_epps.
Hvv_i and Hs_i must align with rows of sample_df.
"""
radii = np.unique(sample_df["radius"].values)
n_r = len(radii)
ncols = min(max_cols, n_r)
nrows = int(np.ceil(n_r / ncols))
fig, axes = plt.subplots(nrows=nrows, ncols=ncols,
figsize=(4 * ncols, 4 * nrows),
squeeze=False, constrained_layout=True)
axes_flat = axes.ravel()
# Colormap normalized over scaling_epps in this sample_df
vmin = float(sample_df["scaling_epps"].min())
vmax = float(sample_df["scaling_epps"].max())
# Custom colormap: low="#1f77b4", high="#ff7f0e"
from matplotlib.colors import LinearSegmentedColormap
custom_cmap = LinearSegmentedColormap.from_list(
"custom_gradient", ["#1f77b4", "#ff7f0e"]
)
cmap = custom_cmap
norm = mpl.colors.Normalize(vmin=vmin, vmax=vmax)
sm = mpl.cm.ScalarMappable(cmap=cmap, norm=norm)
sm.set_array([])
# Map each radius value to an axis (use enumeration order)
theta = np.linspace(0, 2 * np.pi, 200)
radius_to_ax = {r: axes_flat[i] for i, r in enumerate(radii)}
for idx, (vals, vecs) in enumerate(zip(Hs_i, Hvv_i)):
circle = np.array([np.cos(theta), np.sin(theta)])
ellipse = vecs @ np.diag(np.sqrt(np.abs(vals))) @ circle
radius_val = sample_df["radius"].values[idx]
scaling = float(sample_df["scaling_epps"].values[idx])
color = cmap(norm(scaling))
ax = radius_to_ax[radius_val]
ax.plot(ellipse[0, :], ellipse[1, :], lw=1, color=color, alpha=0.9)
ax.set_title(f"radius={radius_val:.3g}")
ax.set_aspect('equal')
# Hide any unused axes
for j in range(len(radii), len(axes_flat)):
axes_flat[j].axis('off')
# Add a colorbar for scaling_epps
cbar = fig.colorbar(sm, ax=axes.ravel().tolist(),
orientation='horizontal', fraction=0.02, pad=0.04)
cbar.set_label('scaling_epps')
plt.show()
import matplotlib.pyplot as plt
import matplotlib as mpl
def ellipse_sequence(single_sample):
Hvv_i, Hs_i = [], []
for i in range(len(single_sample)):
h = np.array([
[single_sample["hv00"].values[i],
single_sample["hv01"].values[i]],
[single_sample["hv10"].values[i],
single_sample["hv11"].values[i]]
])
s = np.array([
single_sample["hs0"].values[i],
single_sample["hs1"].values[i]
])
Hvv_i.append(h)
Hs_i.append(s)
return Hvv_i, Hs_i
def plot_ellipses(Hvv_i, Hs_i, sample_df, max_cols=5, cmap_name='cividis'):
"""
Facet by unique radius values and color ellipses by scaling_epps.
Hvv_i and Hs_i must align with rows of sample_df.
"""
radii = np.unique(sample_df["radius"].values)
n_r = len(radii)
ncols = min(max_cols, n_r)
nrows = int(np.ceil(n_r / ncols))
fig, axes = plt.subplots(nrows=nrows, ncols=ncols,
figsize=(4 * ncols, 4 * nrows),
squeeze=False, constrained_layout=True)
axes_flat = axes.ravel()
# Colormap normalized over scaling_epps in this sample_df
vmin = float(sample_df["scaling_epps"].min())
vmax = float(sample_df["scaling_epps"].max())
# Custom colormap: low="#1f77b4", high="#ff7f0e"
from matplotlib.colors import LinearSegmentedColormap
custom_cmap = LinearSegmentedColormap.from_list(
"custom_gradient", ["#1f77b4", "#ff7f0e"]
)
cmap = custom_cmap
norm = mpl.colors.Normalize(vmin=vmin, vmax=vmax)
sm = mpl.cm.ScalarMappable(cmap=cmap, norm=norm)
sm.set_array([])
# Map each radius value to an axis (use enumeration order)
theta = np.linspace(0, 2 * np.pi, 200)
radius_to_ax = {r: axes_flat[i] for i, r in enumerate(radii)}
for idx, (vals, vecs) in enumerate(zip(Hs_i, Hvv_i)):
circle = np.array([np.cos(theta), np.sin(theta)])
ellipse = vecs @ np.diag(np.sqrt(np.abs(vals))) @ circle
radius_val = sample_df["radius"].values[idx]
scaling = float(sample_df["scaling_epps"].values[idx])
color = cmap(norm(scaling))
ax = radius_to_ax[radius_val]
ax.plot(ellipse[0, :], ellipse[1, :], lw=1, color=color, alpha=0.9)
ax.set_title(f"radius={radius_val:.3g}")
ax.set_aspect('equal')
# Hide any unused axes
for j in range(len(radii), len(axes_flat)):
axes_flat[j].axis('off')
# Add a colorbar for scaling_epps
cbar = fig.colorbar(sm, ax=axes.ravel().tolist(),
orientation='horizontal', fraction=0.02, pad=0.04)
cbar.set_label('scaling_epps')
plt.show()
In [ ]:
Copied!
for i in random_samples:
sample_df = plot_df[(plot_df["sample"] == i)]
Hvv_i, Hs_i = ellipse_sequence(sample_df)
#plot_ellipses(Hvv_i, Hs_i, sample_df)
for i in random_samples:
sample_df = plot_df[(plot_df["sample"] == i)]
Hvv_i, Hs_i = ellipse_sequence(sample_df)
#plot_ellipses(Hvv_i, Hs_i, sample_df)
Almost all the singular vectors were stable -- but a few were not. What might be responsible for this? Let's pick a few examples and analyze in more depth.
In [ ]:
Copied!
sub_df
sub_df
In [ ]:
Copied!
def sample_adj_diffs(df, col='hv00', sort_by='radius'):
# returns DataFrame of adjacent absolute diffs per row (aligned with the
# later row in the pair)
out = []
for s, g in df.groupby('sample'):
g_sorted = g.sort_values(sort_by).reset_index(drop=True)
diffs = g_sorted[col].diff().abs()
for i in range(1, len(g_sorted)):
out.append({
'sample': s,
'abs_diff': float(diffs.iloc[i]),
'radius_prev': float(g_sorted.loc[i-1, sort_by]),
'radius_curr': float(g_sorted.loc[i, sort_by]),
'hv00_prev': float(g_sorted.loc[i-1, col]),
'hv00_curr': float(g_sorted.loc[i, col])
})
return pd.DataFrame(out)
# compute all adjacent diffs
thresh = 0.1
adj = sample_adj_diffs(plot_df, col='hv00', sort_by='radius')
adj = adj[adj['abs_diff'] >= thresh].sort_values('abs_diff', ascending=False)
adj
def sample_adj_diffs(df, col='hv00', sort_by='radius'):
# returns DataFrame of adjacent absolute diffs per row (aligned with the
# later row in the pair)
out = []
for s, g in df.groupby('sample'):
g_sorted = g.sort_values(sort_by).reset_index(drop=True)
diffs = g_sorted[col].diff().abs()
for i in range(1, len(g_sorted)):
out.append({
'sample': s,
'abs_diff': float(diffs.iloc[i]),
'radius_prev': float(g_sorted.loc[i-1, sort_by]),
'radius_curr': float(g_sorted.loc[i, sort_by]),
'hv00_prev': float(g_sorted.loc[i-1, col]),
'hv00_curr': float(g_sorted.loc[i, col])
})
return pd.DataFrame(out)
# compute all adjacent diffs
thresh = 0.1
adj = sample_adj_diffs(plot_df, col='hv00', sort_by='radius')
adj = adj[adj['abs_diff'] >= thresh].sort_values('abs_diff', ascending=False)
adj
In [ ]:
Copied!
for i in adj["sample"].values[:5]:
sample_df = plot_df[(plot_df["sample"] == i)]
Hvv_i, Hs_i = ellipse_sequence(sample_df)
plot_ellipses(Hvv_i, Hs_i, sample_df)
for i in adj["sample"].values[:5]:
sample_df = plot_df[(plot_df["sample"] == i)]
Hvv_i, Hs_i = ellipse_sequence(sample_df)
plot_ellipses(Hvv_i, Hs_i, sample_df)
Overlaying Ellipses¶
In [ ]:
Copied!
from distortions.geometry import bind_metric
embedding = adata.obsm["X_umap"].copy()
embedding_list = []
for i in range(len(Hvv)):
embedding_i = bind_metric(embedding, Hvv[i], Hs[i])
embedding_i["parameter_group"] = i
embedding_i["sample"] = range(len(embedding_i))
embedding_list.append(embedding_i)
from distortions.geometry import bind_metric
embedding = adata.obsm["X_umap"].copy()
embedding_list = []
for i in range(len(Hvv)):
embedding_i = bind_metric(embedding, Hvv[i], Hs[i])
embedding_i["parameter_group"] = i
embedding_i["sample"] = range(len(embedding_i))
embedding_list.append(embedding_i)
In [ ]:
Copied!
combined_embedding = pd.concat(embedding_list)
ix = np.random.choice(embedding_list[0]["sample"], size=500, replace=False)
combined_embedding = combined_embedding[combined_embedding["sample"].isin(ix)]
combined_embedding = combined_embedding.merge(params_df, left_on="parameter_group", right_on="combination")
combined_embedding
combined_embedding = pd.concat(embedding_list)
ix = np.random.choice(embedding_list[0]["sample"], size=500, replace=False)
combined_embedding = combined_embedding[combined_embedding["sample"].isin(ix)]
combined_embedding = combined_embedding.merge(params_df, left_on="parameter_group", right_on="combination")
combined_embedding
In [ ]:
Copied!
from distortions.visualization import dplot
pal = ["#3C64D4", "#CC533F"]
p = {}
p["subset_rcol"] = dplot(combined_embedding, height = 350, width=450)\
.mapping(x="embedding_0", y="embedding_1", color="radius")\
.geom_ellipse(opacity=0.1, radiusMin=1, radiusMax=20, stroke=True)\
.scale_color(stroke=True, scheme=pal)\
.labs(x="UMAP1", y="UMAP2")
from distortions.visualization import dplot
pal = ["#3C64D4", "#CC533F"]
p = {}
p["subset_rcol"] = dplot(combined_embedding, height = 350, width=450)\
.mapping(x="embedding_0", y="embedding_1", color="radius")\
.geom_ellipse(opacity=0.1, radiusMin=1, radiusMax=20, stroke=True)\
.scale_color(stroke=True, scheme=pal)\
.labs(x="UMAP1", y="UMAP2")
In [ ]:
Copied!
p["subset_ecol"] = dplot(combined_embedding, height = 350, width=450)\
.mapping(x="embedding_0", y="embedding_1", color="scaling_epps")\
.geom_ellipse(opacity=0.1, radiusMin=1, radiusMax=20, stroke=True)\
.scale_color(stroke=True)\
.labs(x="UMAP1", y="UMAP2")
p["subset_ecol"] = dplot(combined_embedding, height = 350, width=450)\
.mapping(x="embedding_0", y="embedding_1", color="scaling_epps")\
.geom_ellipse(opacity=0.1, radiusMin=1, radiusMax=20, stroke=True)\
.scale_color(stroke=True)\
.labs(x="UMAP1", y="UMAP2")
In [ ]:
Copied!
#[v.save(f"{k}.svg") for (k, v) in p.items()]
#[v.save(f"{k}.svg") for (k, v) in p.items()]
In [ ]:
Copied!