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

feat: update validation for uns['spatial'] #1129

Merged
merged 15 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
14 changes: 7 additions & 7 deletions cellxgene_schema_cli/cellxgene_schema/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@

VISIUM_AND_IS_SINGLE_TRUE_MATRIX_SIZE = 4992
SPATIAL_HIRES_IMAGE_MAX_DIMENSION_SIZE = 2000
SPATIAL_HIRES_IMAGE_MAX_DIEMSNION_SIZE_VISIUM_11MM = 4000
SPATIAL_HIRES_IMAGE_MAX_DIMENSION_SIZE_VISIUM_11MM = 4000

CONDITION_IS_VISIUM = "a descendant of 'EFO:0010961' (Visium Spatial Gene Expression)"
CONDITION_IS_SEQV2 = f"'{ASSAY_SLIDE_SEQV2}' (Slide-seqV2)"


ERROR_SUFFIX_VISIUM = f"obs['assay_ontology_term_id'] is either {CONDITION_IS_VISIUM} or {CONDITION_IS_SEQV2}"
ERROR_SUFFIX_VISIUM_AND_IS_SINGLE_TRUE = f"{ERROR_SUFFIX_VISIUM} and uns['spatial']['is_single'] is True"
ERROR_SUFFIX_SPATIAL = f"obs['assay_ontology_term_id'] is either {CONDITION_IS_VISIUM} or {CONDITION_IS_SEQV2}"
ERROR_SUFFIX_VISIUM_AND_IS_SINGLE_TRUE = f"{ERROR_SUFFIX_SPATIAL} and uns['spatial']['is_single'] is True"
ejmolinelli marked this conversation as resolved.
Show resolved Hide resolved
ERROR_SUFFIX_VISIUM_AND_IS_SINGLE_TRUE_FORBIDDEN = f"is only allowed for {ERROR_SUFFIX_VISIUM_AND_IS_SINGLE_TRUE}"
ERROR_SUFFIX_VISIUM_AND_IS_SINGLE_TRUE_REQUIRED = f"is required for {ERROR_SUFFIX_VISIUM_AND_IS_SINGLE_TRUE}"
ERROR_SUFFIX_VISIUM_AND_IS_SINGLE_TRUE_IN_TISSUE_0 = f"{ERROR_SUFFIX_VISIUM_AND_IS_SINGLE_TRUE} and in_tissue is 0"
Expand Down Expand Up @@ -1474,7 +1474,7 @@ def _validate_spatial_assay_ontology_term_id(self):
# Validate assay ontology term ids are identical.
term_count = obs["assay_ontology_term_id"].nunique()
if term_count > 1:
self.errors.append(f"When {ERROR_SUFFIX_VISIUM}" ", all observations must contain the same value.")
self.errors.append(f"When {ERROR_SUFFIX_SPATIAL}" ", all observations must contain the same value.")

def _validate_spatial_cell_type_ontology_term_id(self):
"""
Expand Down Expand Up @@ -1604,7 +1604,7 @@ def _check_spatial_uns(self):
uns_spatial = self.adata.uns.get("spatial")
is_supported_spatial_assay = self._is_supported_spatial_assay()
if uns_spatial is not None and not is_supported_spatial_assay:
self.errors.append(f"uns['spatial'] is only allowed when {ERROR_SUFFIX_VISIUM}")
self.errors.append(f"uns['spatial'] is only allowed when {ERROR_SUFFIX_SPATIAL}")
return

# Exit if we aren't dealing with a supported spatial assay as no further checks are necessary.
Expand All @@ -1613,7 +1613,7 @@ def _check_spatial_uns(self):

# spatial is required for supported spatial assays.
if not isinstance(uns_spatial, dict):
self.errors.append("A dict in uns['spatial'] is required when " f"{ERROR_SUFFIX_VISIUM}.")
self.errors.append("A dict in uns['spatial'] is required when " f"{ERROR_SUFFIX_SPATIAL}.")
return

# is_single is required.
Expand Down Expand Up @@ -1695,7 +1695,7 @@ def _check_spatial_uns(self):
_assay_term = self.adata.obs["assay_ontology_term_id"].values[0]
_max_size = SPATIAL_HIRES_IMAGE_MAX_DIMENSION_SIZE
if is_ontological_descendant_of(ONTOLOGY_PARSER, _assay_term, "EFO:0022860", True):
_max_size = SPATIAL_HIRES_IMAGE_MAX_DIEMSNION_SIZE_VISIUM_11MM
_max_size = SPATIAL_HIRES_IMAGE_MAX_DIMENSION_SIZE_VISIUM_11MM
self._validate_spatial_image_shape("hires", uns_images["hires"], _max_size)

# fullres is optional.
Expand Down
38 changes: 35 additions & 3 deletions cellxgene_schema_cli/tests/test_validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
ERROR_SUFFIX_VISIUM_AND_IS_SINGLE_TRUE_FORBIDDEN,
ERROR_SUFFIX_VISIUM_AND_IS_SINGLE_TRUE_IN_TISSUE_0,
ERROR_SUFFIX_VISIUM_AND_IS_SINGLE_TRUE_REQUIRED,
SPATIAL_HIRES_IMAGE_MAX_DIEMSNION_SIZE_VISIUM_11MM,
SPATIAL_HIRES_IMAGE_MAX_DIMENSION_SIZE,
SPATIAL_HIRES_IMAGE_MAX_DIMENSION_SIZE_VISIUM_11MM,
Validator,
validate,
)
Expand Down Expand Up @@ -781,7 +781,7 @@ def test__validate_images_image_is_shape_error(self, image_name):
"assay_ontology_term_id, hi_res_size, image_max",
[
("EFO:0022858", 2001, SPATIAL_HIRES_IMAGE_MAX_DIMENSION_SIZE),
("EFO:0022860", 4001, SPATIAL_HIRES_IMAGE_MAX_DIEMSNION_SIZE_VISIUM_11MM),
("EFO:0022860", 4001, SPATIAL_HIRES_IMAGE_MAX_DIMENSION_SIZE_VISIUM_11MM),
],
)
def test__validate_images_hires_max_dimension_greater_than_error(
Expand All @@ -801,11 +801,43 @@ def test__validate_images_hires_max_dimension_greater_than_error(
f"The largest dimension of uns['spatial'][library_id]['images']['hires'] must be {image_max} pixels, it has a largest dimension of {hi_res_size} pixels."
]

@pytest.mark.parametrize(
"assay_ontology_term_id, hi_res_size, size_requirement",
[
("EFO:0022858", SPATIAL_HIRES_IMAGE_MAX_DIMENSION_SIZE, SPATIAL_HIRES_IMAGE_MAX_DIMENSION_SIZE),
("EFO:0022858", SPATIAL_HIRES_IMAGE_MAX_DIMENSION_SIZE_VISIUM_11MM, SPATIAL_HIRES_IMAGE_MAX_DIMENSION_SIZE),
("EFO:0022860", SPATIAL_HIRES_IMAGE_MAX_DIMENSION_SIZE, SPATIAL_HIRES_IMAGE_MAX_DIMENSION_SIZE_VISIUM_11MM),
(
"EFO:0022860",
SPATIAL_HIRES_IMAGE_MAX_DIMENSION_SIZE_VISIUM_11MM,
SPATIAL_HIRES_IMAGE_MAX_DIMENSION_SIZE_VISIUM_11MM,
),
],
)
def test__validate_images_hires_max_dimension(self, assay_ontology_term_id, hi_res_size, size_requirement):
validator: Validator = Validator()
validator._set_schema_def()
validator.adata = adata_visium.copy()
validator.adata.obs["assay_ontology_term_id"] = assay_ontology_term_id
validator.adata.uns["spatial"][visium_library_id]["images"]["hires"] = np.zeros(
(1, hi_res_size, 3), dtype=np.uint8
)

# Confirm hires is identified as invalid.
validator.reset()
validator._check_spatial_uns()
if hi_res_size == size_requirement:
assert validator.errors == []
else:
assert validator.errors == [
f"The largest dimension of uns['spatial'][library_id]['images']['hires'] must be {size_requirement} pixels, it has a largest dimension of {hi_res_size} pixels."
]

@pytest.mark.parametrize(
"assay_ontology_term_id, hi_res_size, image_max",
[
("EFO:0022858", 1999, SPATIAL_HIRES_IMAGE_MAX_DIMENSION_SIZE),
("EFO:0022860", 3999, SPATIAL_HIRES_IMAGE_MAX_DIEMSNION_SIZE_VISIUM_11MM),
("EFO:0022860", 3999, SPATIAL_HIRES_IMAGE_MAX_DIMENSION_SIZE_VISIUM_11MM),
],
)
def test__validate_images_hires_max_dimension_less_than_error(self, assay_ontology_term_id, hi_res_size, image_max):
Expand Down
Loading