Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix clims when plotting shapes element annotations with matplotlib rendering #368

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 2 additions & 9 deletions src/spatialdata_plot/pl/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
LinearSegmentedColormap,
ListedColormap,
Normalize,
TwoSlopeNorm,
to_rgba,
)
from matplotlib.figure import Figure
Expand Down Expand Up @@ -339,7 +338,7 @@ def _get_collection_shape(
c = cmap(c)
else:
try:
norm = colors.Normalize(vmin=min(c), vmax=max(c))
norm = colors.Normalize(vmin=min(c), vmax=max(c)) if norm is None else norm
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this function Normalize() is initialized without clip, while in _prepare_cmap_norm() the default is to set clip=True. I would choose one of the two as our default choice. The user will be able to specify clip, vcenter, etc by passing a norm object directly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm let me double check that if we don't pass norm as user, whether ultimately the norm is always created anyway, then we can get rid of normalize instance initiated here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok there is code left over of when vmin and vmax were removed. Not certain whether to address this in a different PR.

except ValueError as e:
raise ValueError(
"Could not convert values in the `color` column to float, if `color` column represents"
Expand All @@ -353,7 +352,7 @@ def _get_collection_shape(
c = cmap(c)
else:
try:
norm = colors.Normalize(vmin=min(c), vmax=max(c))
norm = colors.Normalize(vmin=min(c), vmax=max(c)) if norm is None else norm
except ValueError as e:
raise ValueError(
"Could not convert values in the `color` column to float, if `color` column represents"
Expand Down Expand Up @@ -506,12 +505,6 @@ def _prepare_cmap_norm(

if norm is None:
norm = Normalize(vmin=vmin, vmax=vmax, clip=True)
elif isinstance(norm, Normalize) or not norm:
pass # TODO
elif vcenter is None:
norm = Normalize(vmin=vmin, vmax=vmax, clip=True)
else:
norm = TwoSlopeNorm(vmin=vmin, vmax=vmax, vcenter=vcenter)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that vcenter is removed, it should be removed also from the function signature. Also, kwargs is in the signature but not used, so I would remove it.


na_color, na_color_modified_by_user = _sanitise_na_color(na_color)
cmap.set_bad(na_color)
Expand Down
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are the two files different?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just different color perhaps due to made changes and then generating the labels with a different color. Other than that there are no changes.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/_images/Shapes_can_set_clims_clip.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/_images/Shapes_colorbar_can_be_normalised.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 17 additions & 4 deletions tests/pl/test_render_shapes.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ def test_plot_colorbar_respects_input_limits(self, sdata_blobs: SpatialData):
sdata_blobs.pl.render_shapes("blobs_polygons", color="cluster", groups=["c1"]).pl.show()

def test_plot_colorbar_can_be_normalised(self, sdata_blobs: SpatialData):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does groups=["c1"] do here? My understanding is that it doesn't have any effect because:

  • cluster is numerical and not categorical
  • the values of clusters do not include c1

But I may be missing something and be wrong.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it does nothing. @timtreis is this purely to test that the colorbar stays limited from 1-50?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the argument now as typically it is used to just color certain categorical values within a column, which from the title of the test function definition is not the intention.

sdata_blobs["table"].obs["region"] = ["blobs_polygons"] * sdata_blobs["table"].n_obs
sdata_blobs["table"].obs["region"] = "blobs_polygons"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

region is the instance_key right? Usually we have the region_key called region and the instance_key called instance_id. I'd consider making a separate PR to fix this repo-wide, unless this is only here, then I'd fix this now. CC @timtreis

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no region is the region_key_key

sdata_blobs["table"].uns["spatialdata_attrs"]["region"] = "blobs_polygons"
sdata_blobs.shapes["blobs_polygons"]["cluster"] = [1, 2, 3, 5, 20]
norm = Normalize(vmin=0, vmax=5, clip=True)
Expand Down Expand Up @@ -186,7 +186,7 @@ def test_plot_can_plot_with_annotation_despite_random_shuffling(self, sdata_blob

def test_plot_can_plot_queried_with_annotation_despite_random_shuffling(self, sdata_blobs: SpatialData):
sdata_blobs["table"].obs["region"] = "blobs_circles"
new_table = sdata_blobs["table"][:5]
new_table = sdata_blobs["table"][:5].copy()
new_table.uns["spatialdata_attrs"]["region"] = "blobs_circles"
new_table.obs["instance_id"] = np.array(range(5))

Expand Down Expand Up @@ -214,7 +214,7 @@ def test_plot_can_plot_queried_with_annotation_despite_random_shuffling(self, sd

def test_plot_can_color_two_shapes_elements_by_annotation(self, sdata_blobs: SpatialData):
sdata_blobs["table"].obs["region"] = "blobs_circles"
new_table = sdata_blobs["table"][:10]
new_table = sdata_blobs["table"][:10].copy()
new_table.uns["spatialdata_attrs"]["region"] = ["blobs_circles", "blobs_polygons"]
new_table.obs["instance_id"] = np.concatenate((np.array(range(5)), np.array(range(5))))

Expand All @@ -230,7 +230,7 @@ def test_plot_can_color_two_shapes_elements_by_annotation(self, sdata_blobs: Spa

def test_plot_can_color_two_queried_shapes_elements_by_annotation(self, sdata_blobs: SpatialData):
sdata_blobs["table"].obs["region"] = "blobs_circles"
new_table = sdata_blobs["table"][:10]
new_table = sdata_blobs["table"][:10].copy()
new_table.uns["spatialdata_attrs"]["region"] = ["blobs_circles", "blobs_polygons"]
new_table.obs["instance_id"] = np.concatenate((np.array(range(5)), np.array(range(5))))

Expand Down Expand Up @@ -316,3 +316,16 @@ def test_plot_datashader_can_color_by_value(self, sdata_blobs: SpatialData):
sdata_blobs["table"].uns["spatialdata_attrs"]["region"] = "blobs_polygons"
sdata_blobs.shapes["blobs_polygons"]["value"] = [1, 10, 1, 20, 1]
sdata_blobs.pl.render_shapes(element="blobs_polygons", color="value", method="datashader").pl.show()

def test_plot_can_set_clims_clip(self, sdata_blobs: SpatialData):
table_shapes = sdata_blobs["table"][:5].copy()
table_shapes.obs.instance_id = list(range(5))
table_shapes.obs["region"] = "blobs_circles"
table_shapes.obs["dummy_gene_expression"] = [i * 10 for i in range(5)]
table_shapes.uns["spatialdata_attrs"]["region"] = "blobs_circles"
sdata_blobs["new_table"] = table_shapes

norm = Normalize(vmin=20, vmax=40, clip=True)
sdata_blobs.pl.render_shapes(
"blobs_circles", color="dummy_gene_expression", norm=norm, table_name="new_table"
).pl.show()
Loading