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

Plotting annotations fix #175

Merged
merged 8 commits into from
Nov 5, 2023
Merged

Conversation

melonora
Copy link
Collaborator

@melonora melonora commented Oct 26, 2023

closes #164

This PR fixes the issue reported in #164. The problem was that when filtering the anndata object it would result in an anndata object with potentially 0 rows. This PR checks whether the anndata object to be added to the metadata has 0 rows. If this is the case then the adata metadata is set to be None. Also if there is no table, adata will be none, but in addition region_key will also be None.

In addition to the implementation of @aeisenbarth this handles the case when a table is present, but there are no annotations for a given element.

@melonora melonora marked this pull request as ready for review October 26, 2023 17:43
@melonora
Copy link
Collaborator Author

I added a test that integrates the sdata_widget and adata view widget. Not certain whether we want to move these in a particular file or whether this is ok.

@codecov-commenter
Copy link

codecov-commenter commented Oct 31, 2023

Codecov Report

Attention: 1 lines in your changes are missing coverage. Please review.

Comparison is base (b906ee2) 75.79% compared to head (fa33e62) 76.84%.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #175      +/-   ##
==========================================
+ Coverage   75.79%   76.84%   +1.04%     
==========================================
  Files          16       16              
  Lines        1719     1723       +4     
==========================================
+ Hits         1303     1324      +21     
+ Misses        416      399      -17     
Files Coverage Δ
src/napari_spatialdata/_view.py 80.88% <ø> (+1.35%) ⬆️
src/napari_spatialdata/_viewer.py 72.41% <100.00%> (+8.32%) ⬆️
src/napari_spatialdata/utils/_utils.py 78.01% <85.71%> (+0.83%) ⬆️

... and 1 file with indirect coverage changes

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@@ -252,18 +252,10 @@ def _select_layer(self) -> None:
self.model.adata.obsm[Key.obsm.spatial][:, ::-1][:, :2], 0, values=0, axis=1
)

if "points" in layer.metadata:
Copy link
Member

Choose a reason for hiding this comment

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

nice!

"adata": sdata.table[sdata.table.obs[sdata.table.uns["spatialdata_attrs"]["region_key"]] == key]
if sdata.table
else None,
"adata": adata,
"region_key": sdata.table.uns["spatialdata_attrs"]["instance_key"] if sdata.table else None,
Copy link
Member

Choose a reason for hiding this comment

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

in lines 169 and 214 we have "region_key" on the right hand side, here we have "instance_key", I think this is a bug.

Copy link
Member

Choose a reason for hiding this comment

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

Could this instance_key be the cause of the bug with reversed indices? See my comment in the discussion page.

@@ -367,3 +367,18 @@ def get_elements_meta_mapping(
name_to_add = name
elements[name] = elements_metadata
return elements, name_to_add


def _get_metadata_adata(sdata: SpatialData, key: str) -> None | AnnData:
Copy link
Member

Choose a reason for hiding this comment

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

Nice to have this separation, it makes the code more readable. Since there is a bug with the order of the rows (see my comment in the discussion), we could fix it by replacing this function with match_table_to_element() https://github.com/scverse/spatialdata/blob/bf3377fd36f94ef51a233ae71312b1c28e978e6e/src/spatialdata/_core/query/relational_query.py#L134, which returns a table with the correct order.

assert viewer.layers[-1].metadata["adata"]
assert view_widget.obs_widget.item(0)

# Filtering adata leads to 0 rows so adata should be set to None
Copy link
Member

Choose a reason for hiding this comment

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

nice!

@LucaMarconato
Copy link
Member

Thanks @melonora for the fix! The bug that I have reported in #164 is gone.

During my review I have anyway spotted two parts of the code to double check.

One is the use of a instance_key when in other equivalent parts of the code region_key was used.

The other is that I noticed that the current implementation is not robust against reordering of the rows of the table (as I show in the code below). I think that this can be fixed by replacing the function _get_metadata_adata() with a call of match_table_to_element() (https://github.com/scverse/spatialdata/blob/bf3377fd36f94ef51a233ae71312b1c28e978e6e/src/spatialdata/_core/query/relational_query.py#L134), which returns a table where the rows are reordered to match the element. Anyway, I can't exclude that the bug is caused by the strange use of instance_key that I commented above.

Here is the code to reproduce the behavior. The table is essentially the same, but the two visualizations are different.

##
from napari_spatialdata import Interactive
from spatialdata.datasets import blobs

sdata0 = blobs()
table0 = sdata0.table

table1 = table0.copy()
table1.obs = table1.obs.iloc[::-1]
sdata1 = blobs()
del sdata1.table
sdata1.table = table1

print(table0.obs['instance_id'])
print(table1.obs['instance_id'])
##
Interactive(sdata0)
Interactive(sdata1)

sdata0:
sdata0

sdata1:
sdata1

On this topic, @kevinyamauchi @giovp I think this bug is a symptom that we should drop region, region_key, instance_key altogether and just use the obs indices, and by limiting tables to annotate 0 or 1 spatial element, but not more than 1.

@LucaMarconato
Copy link
Member

LucaMarconato commented Nov 5, 2023

@melonora I have checked if this happens for circles and it seems it doesn't: the two plots are identical when sorting the rows. So it seems that the bug is not do to not using match_table_to_element() but to the use of instance_key where there should be region_key. I haven't verified this further yet.

I have expanded the code above to check also circles, also with spatialdata-plot (doesn't work atm because of unrelated bugs).

##
from napari_spatialdata import Interactive
from spatialdata.datasets import blobs
import pandas as pd

# the table annotates labels
sdata0 = blobs()
table0 = sdata0.table

# the table annotates labels, but by reversing the table rows, the plot should be identical
table1 = table0.copy()
table1.obs = table1.obs.iloc[::-1]
sdata1 = blobs()
del sdata1.table
sdata1.table = table1

print(table0.obs['instance_id'])
print(table1.obs['instance_id'])
##

# the table annotates the circles
table2 = table0.copy()[:5]
table2.obs = pd.DataFrame(table2.obs)
table2.uns['spatialdata_attrs']['region'] = 'blobs_circles'
table2.obs['region'] = 'blobs_circles'
table2.obs['instance_id'] = list(range(5))
sdata2 = blobs()
del sdata2.table
sdata2.table = table2

# the table annotates the circles, but by reversing the table rows, the plot should be identical
table3 = table2.copy()
table3.obs = table3.obs.iloc[::-1]
sdata3 = blobs()
del sdata3.table
sdata3.table = table3

print(table2.obs['instance_id'])
print(table3.obs['instance_id'])

##
Interactive(sdata0)
Interactive(sdata1)
Interactive(sdata2)
Interactive(sdata3)

##
import spatialdata_plot

# the two plots should be identical; I bet it's true, but I can't check it because of a bug
sdata0.pl.render_labels(color='channel_0_sum').pl.show()  # doesn't work because
sdata1.pl.render_labels(color='channel_0_sum').pl.show()

# the two plots should be identical; I bet it's true, but I can't check it because there is another bug
sdata2.pl.render_shapes(color='channel_0_sum').pl.show()
sdata3.pl.render_shapes(color='channel_0_sum').pl.show()

import matplotlib.pyplot as plt
plt.show()

@melonora
Copy link
Collaborator Author

melonora commented Nov 5, 2023

I see, will look into it

@LucaMarconato
Copy link
Member

LucaMarconato commented Nov 5, 2023

Still trying to understand the reasons for the behavior observed above. Sharing some initial considerations.

@LucaMarconato
Copy link
Member

Let's merge this PR as it is, since it fixes the case empty tables, and break the problem in two smaller ones. Let's make a PR for cleaning up _models.py and after that let's fix the plotting when the rows of the table are shuffled.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Error when plotting annotations when the element "region" is not selected
3 participants