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

general fixes to adapt table model #417

Closed
wants to merge 126 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
126 commits
Select commit Hold shift + click to select a range
5848756
[pre-commit.ci] pre-commit autoupdate (#394)
pre-commit-ci[bot] Nov 21, 2023
9d68766
initial tests multi_table design
melonora Nov 23, 2023
d5e3ee3
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 23, 2023
a47c0cf
add mock new table
melonora Nov 25, 2023
e1c71aa
Merge branch 'multi_table' of https://github.com/melonora/spatialdata…
melonora Nov 25, 2023
f857d4e
create test class and cleanup
melonora Nov 25, 2023
3c32b27
additional cleanup
melonora Nov 25, 2023
3f47c3b
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 25, 2023
bfb4c7c
additional cleanup
melonora Nov 25, 2023
8cc1acf
additional cleanup
melonora Nov 25, 2023
c4de59c
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 25, 2023
c6d0976
add pseudo methods
melonora Nov 26, 2023
e8eeda8
Merge branch 'multi_table' of https://github.com/melonora/spatialdata…
melonora Nov 26, 2023
cd3def6
Change table type in init
melonora Nov 27, 2023
b815980
make tables plural and add to validation in __init__
melonora Nov 27, 2023
3031950
revert to old public accessor
melonora Nov 27, 2023
c149c71
Validate each table in dictionary
melonora Nov 27, 2023
cb826e0
iterate dict values
melonora Nov 27, 2023
da4cc86
add comment
melonora Nov 27, 2023
d6589a0
adjust table getter
melonora Nov 27, 2023
1ad33b0
Add tables getter
melonora Nov 27, 2023
1d1907b
Fix missing parenthesis
melonora Nov 27, 2023
bec136f
change to warnings.warn DeprecationWarning
melonora Nov 27, 2023
22e2bef
allow for backward compatibility in init
melonora Nov 27, 2023
ef0a2dc
[pre-commit.ci] pre-commit autoupdate (#408)
pre-commit-ci[bot] Nov 28, 2023
c75c684
fix dict subscriptable
melonora Nov 28, 2023
9819a09
fix string representation of sdata
melonora Nov 28, 2023
59b5d21
add deprecation decorator for future
melonora Nov 28, 2023
3cdc492
Allow for tables not annotating elements
melonora Nov 28, 2023
2c51b69
switch to using tables with deprecation
melonora Nov 28, 2023
54631ee
fix string representation
melonora Nov 28, 2023
29a37a7
write tables element group
melonora Nov 28, 2023
0c258e4
adjust io to multi_table
melonora Nov 29, 2023
60de077
Alter io to give None as default value for spatialdata attrs keys
melonora Nov 29, 2023
53f9ae5
add tables setter
melonora Dec 2, 2023
b1d451c
raise keyerror table getter
melonora Dec 2, 2023
0f8df0e
remove commented tables setter
melonora Dec 2, 2023
f12d8de
raise keyerror in table deleter
melonora Dec 2, 2023
d7a3092
add deprecation warning
melonora Dec 2, 2023
e94d994
fix tests
melonora Dec 2, 2023
f3d41c7
add DeprecationWarning
melonora Dec 2, 2023
396a970
comment test
melonora Dec 2, 2023
d646ca3
change setter into method
melonora Dec 2, 2023
433a9f3
circumvent mappingproxy set issue
melonora Dec 2, 2023
d3d2083
adjust set get test
melonora Dec 2, 2023
304aa38
add get table keys
melonora Dec 2, 2023
02ae3e5
add column getters
melonora Dec 3, 2023
b550292
add change set target table
melonora Dec 4, 2023
535d917
Give default table name
melonora Dec 5, 2023
5e231ac
fix spatialdata without table
melonora Dec 5, 2023
6a18365
add int32 because of windows and add docstring
melonora Dec 6, 2023
e844067
fix filtering by coordinate system
melonora Dec 6, 2023
9826d35
Change to Path to not be linux / mac specific
melonora Dec 6, 2023
b3eea20
Change to Path to not be linux / mac specific
melonora Dec 6, 2023
f18251f
table should annotate existing element
melonora Dec 6, 2023
88c1579
return table with AnnData having 0 rows
melonora Dec 6, 2023
2a6d675
Adjust for windows
melonora Dec 6, 2023
cfcc8ac
adjust for accessing table elements
melonora Dec 6, 2023
957c4c9
fix change annotation target
melonora Dec 7, 2023
36b1e0c
fix set annotation target
melonora Dec 7, 2023
9f5b8c2
fix/add tests
melonora Dec 7, 2023
0c07957
fix init from elements
melonora Dec 7, 2023
a1c0df2
fix init from elements tests
melonora Dec 7, 2023
230e38e
add validation check
melonora Dec 7, 2023
219d272
add table validation SpatialData.__init
melonora Dec 7, 2023
3051cf0
fix ruff
melonora Dec 7, 2023
1bc1f11
only concatenate if annotating
melonora Dec 7, 2023
3a51b4a
change into warning because of filtering
melonora Dec 7, 2023
8381f29
fix last tests
melonora Dec 7, 2023
dc0d186
adjust to tables
melonora Dec 7, 2023
9c3c483
use tables parameter
melonora Dec 8, 2023
b1d62ef
fix some mypy
melonora Dec 8, 2023
3817b78
some mypy fixes
melonora Dec 9, 2023
76ce40d
some more mypy
melonora Dec 9, 2023
caecb83
fix another mypy
melonora Dec 9, 2023
2dc7a74
circumvent typing error on py3.9
melonora Dec 11, 2023
cde90e2
mypy yet again
melonora Dec 11, 2023
a20cb95
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 11, 2023
854a5ce
fix pre_commit
melonora Dec 11, 2023
81a18bb
down to 12 mypy errors
melonora Dec 12, 2023
f85a093
Merge branch 'multi_table' of https://github.com/melonora/spatialdata…
melonora Dec 12, 2023
2c3138a
down to 1mypy error
melonora Dec 13, 2023
dd1db66
fixed mypy errors
melonora Dec 13, 2023
9ac3a15
fix set_table_annotation
melonora Dec 13, 2023
444a335
added docstring
melonora Dec 14, 2023
c9e7d99
refactor data loader (#299)
giovp Dec 14, 2023
fd7a8a0
add documentation
melonora Dec 15, 2023
d5d7743
add documentation
melonora Dec 15, 2023
15cd907
Merge branch 'main' into multi_table
melonora Dec 15, 2023
e61e007
minor adjustment docstring
melonora Dec 15, 2023
af1b93f
Merge branch 'multi_table' of https://github.com/melonora/spatialdata…
melonora Dec 15, 2023
05964c5
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 15, 2023
5e16bbd
Added / adjusted docstrings
melonora Dec 15, 2023
432bb1d
Merge branch 'multi_table' of https://github.com/melonora/spatialdata…
melonora Dec 15, 2023
45b14a2
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 15, 2023
67fd7d1
fix mypy after merge
melonora Dec 15, 2023
899d8c3
Merge branch 'multi_table' of https://github.com/melonora/spatialdata…
melonora Dec 15, 2023
351b7c9
refactor function name
melonora Dec 15, 2023
c620c45
[pre-commit.ci] pre-commit autoupdate (#411)
pre-commit-ci[bot] Dec 17, 2023
898b5ab
small fixes
LucaMarconato Dec 17, 2023
d592606
added gen_elements docstrings
melonora Dec 18, 2023
c479976
tiny comments
LucaMarconato Dec 18, 2023
94ebd70
fix ruff pre-commit
melonora Dec 18, 2023
6beb53b
removed types from docstring
LucaMarconato Dec 18, 2023
a708594
refactor of set_table_annotation_target
melonora Dec 18, 2023
c1d83f3
Merge branch 'multi_table' of https://github.com/melonora/spatialdata…
melonora Dec 18, 2023
b957906
add quotes
melonora Dec 18, 2023
31fca71
fix (?)
LucaMarconato Dec 18, 2023
e780d6f
refactor error messages
melonora Dec 19, 2023
4eed839
fix incremental update (#329)
giovp Dec 19, 2023
f7cebb5
add concatenate argument
melonora Dec 19, 2023
aca8965
Merge branch 'multi_table' of https://github.com/melonora/spatialdata…
melonora Dec 19, 2023
aff28c2
Merge branch 'main' into multi_table
melonora Dec 19, 2023
c2c062f
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 19, 2023
c3690b7
add util functions to init
melonora Dec 19, 2023
415022c
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 19, 2023
207b411
add util functions to init
melonora Dec 19, 2023
29f8ecf
Merge branch 'multi_table' of https://github.com/melonora/spatialdata…
melonora Dec 19, 2023
a5d4d5d
add tables class
melonora Dec 19, 2023
de74482
add table class
melonora Dec 21, 2023
31dc81d
add deprecation back
melonora Dec 21, 2023
d3eef8e
rename function in tests
melonora Dec 21, 2023
f16b357
rename function in tests
melonora Dec 21, 2023
3c80380
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 21, 2023
3a713a8
fix precommit
giovp Dec 21, 2023
798a978
update precommit and remove add_table, store_table and general fixes
giovp Dec 21, 2023
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
2 changes: 1 addition & 1 deletion .github/workflows/test_and_deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ jobs:
PLATFORM: ${{ matrix.os }}
DISPLAY: :42
run: |
pytest -v --cov --color=yes --cov-report=xml
pytest --cov --color=yes --cov-report=xml
- name: Upload coverage to Codecov
uses: codecov/[email protected]
with:
Expand Down
8 changes: 4 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,25 @@ ci:
skip: []
repos:
- repo: https://github.com/psf/black
rev: 23.10.1
rev: 23.12.0
hooks:
- id: black
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v3.0.3
rev: v4.0.0-alpha.8
hooks:
- id: prettier
- repo: https://github.com/asottile/blacken-docs
rev: 1.16.0
hooks:
- id: blacken-docs
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.6.1
rev: v1.7.1
hooks:
- id: mypy
additional_dependencies: [numpy, types-requests]
exclude: tests/|docs/
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.1.3
rev: v0.1.8
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
2 changes: 1 addition & 1 deletion .readthedocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ build:
python: "3.10"
sphinx:
configuration: docs/conf.py
fail_on_warning: false
fail_on_warning: true
python:
install:
- method: pip
Expand Down
8 changes: 0 additions & 8 deletions docs/_templates/autosummary/class.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,8 @@ Attributes table
~~~~~~~~~~~~~~~~~~

.. autosummary::

{% for item in attributes %}

~{{ fullname }}.{{ item }}

{%- endfor %}
{% endif %}
{% endblock %}
Expand All @@ -27,13 +24,10 @@ Methods table
~~~~~~~~~~~~~

.. autosummary::

{% for item in methods %}

{%- if item != '__init__' %}
~{{ fullname }}.{{ item }}
{%- endif -%}

{%- endfor %}
{% endif %}
{% endblock %}
Expand All @@ -46,7 +40,6 @@ Attributes
{% for item in attributes %}

.. autoattribute:: {{ [objname, item] | join(".") }}

{%- endfor %}

{% endif %}
Expand All @@ -61,7 +54,6 @@ Methods
{%- if item != '__init__' %}

.. automethod:: {{ [objname, item] | join(".") }}

{%- endif -%}
{%- endfor %}

Expand Down
14 changes: 9 additions & 5 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,10 @@ Operations on `SpatialData` objects.
match_table_to_element
concatenate
rasterize
transform
aggregate
```

### Utilities
### Operations Utilities

```{eval-rst}
.. autosummary::
Expand All @@ -49,6 +48,7 @@ The elements (building-blocks) that consitute `SpatialData`.

```{eval-rst}
.. currentmodule:: spatialdata.models

.. autosummary::
:toctree: generated

Expand All @@ -61,9 +61,11 @@ The elements (building-blocks) that consitute `SpatialData`.
TableModel
```

### Utilities
### Models Utilities

```{eval-rst}
.. currentmodule:: spatialdata.models

.. autosummary::
:toctree: generated

Expand Down Expand Up @@ -94,9 +96,11 @@ The transformations that can be defined between elements and coordinate systems
Sequence
```

### Utilities
### Transformations Utilities

```{eval-rst}
.. currentmodule:: spatialdata.transformations

.. autosummary::
:toctree: generated

Expand All @@ -119,7 +123,7 @@ The transformations that can be defined between elements and coordinate systems
ImageTilesDataset
```

## Input/output
## Input/Output

```{eval-rst}
.. currentmodule:: spatialdata
Expand Down
1 change: 0 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ dev = [
docs = [
"sphinx>=4.5",
"sphinx-book-theme>=1.0.0",
"sphinx_rtd_theme",
"myst-nb",
"sphinxcontrib-bibtex>=1.0.0",
"sphinx-autodoc-typehints",
Expand Down
116 changes: 116 additions & 0 deletions src/spatialdata/_core/_elements.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
"""SpatialData elements."""
from __future__ import annotations

from collections import UserDict
from collections.abc import Iterable
from typing import Any
from warnings import warn

from anndata import AnnData
from dask.dataframe.core import DataFrame as DaskDataFrame
from datatree import DataTree
from geopandas import GeoDataFrame

from spatialdata._types import Raster_T
from spatialdata._utils import multiscale_spatial_image_from_data_tree
from spatialdata.models import (
Image2DModel,
Image3DModel,
Labels2DModel,
Labels3DModel,
PointsModel,
ShapesModel,
TableModel,
get_axes_names,
get_model,
)


class Elements(UserDict[str, Any]):
def __init__(self, shared_keys: set[str | None]) -> None:
self._shared_keys = shared_keys
super().__init__()

@staticmethod
def _check_key(key: str, element_keys: Iterable[str], shared_keys: set[str | None]) -> None:
if key in element_keys:
warn(f"Key `{key}` already exists. Overwriting it.", UserWarning, stacklevel=2)
else:
if key in shared_keys:
raise KeyError(f"Key `{key}` already exists.")

def __setitem__(self, key: str, value: Any) -> None:
self._shared_keys.add(key)
super().__setitem__(key, value)

def __delitem__(self, key: str) -> None:
self._shared_keys.remove(key)
super().__delitem__(key)


class Images(Elements):
def __setitem__(self, key: str, value: Raster_T) -> None:
self._check_key(key, self.keys(), self._shared_keys)
if isinstance(value, (DataTree)):
value = multiscale_spatial_image_from_data_tree(value)
schema = get_model(value)
if schema not in (Image2DModel, Image3DModel):
raise TypeError(f"Unknown element type with schema: {schema!r}.")
ndim = len(get_axes_names(value))
if ndim == 3:
Image2DModel().validate(value)
super().__setitem__(key, value)
elif ndim == 4:
Image3DModel().validate(value)
super().__setitem__(key, value)
else:
NotImplementedError("TODO: implement for ndim > 4.")


class Labels(Elements):
def __setitem__(self, key: str, value: Raster_T) -> None:
self._check_key(key, self.keys(), self._shared_keys)
if isinstance(value, (DataTree)):
value = multiscale_spatial_image_from_data_tree(value)
schema = get_model(value)
if schema not in (Labels2DModel, Labels3DModel):
raise TypeError(f"Unknown element type with schema: {schema!r}.")
ndim = len(get_axes_names(value))
if ndim == 2:
Labels2DModel().validate(value)
super().__setitem__(key, value)
elif ndim == 3:
Labels3DModel().validate(value)
super().__setitem__(key, value)
else:
NotImplementedError("TODO: implement for ndim > 3.")


class Shapes(Elements):
def __setitem__(self, key: str, value: GeoDataFrame) -> None:
self._check_key(key, self.keys(), self._shared_keys)
schema = get_model(value)
if schema != ShapesModel:
raise TypeError(f"Unknown element type with schema: {schema!r}.")
ShapesModel().validate(value)
super().__setitem__(key, value)


class Points(Elements):
def __setitem__(self, key: str, value: DaskDataFrame) -> None:
self._check_key(key, self.keys(), self._shared_keys)
schema = get_model(value)
if schema != PointsModel:
raise TypeError(f"Unknown element type with schema: {schema!r}.")
PointsModel().validate(value)
super().__setitem__(key, value)


class Tables(Elements):
def __setitem__(self, key: str, value: AnnData) -> None:
self._check_key(key, self.keys(), self._shared_keys)
schema = get_model(value)
if schema != TableModel:
raise TypeError(f"Unknown element type with schema: {schema!r}.")
TableModel().validate(value)
super().__setitem__(key, value)
30 changes: 20 additions & 10 deletions src/spatialdata/_core/concatenate.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@

from copy import copy # Should probably go up at the top
from itertools import chain
from typing import TYPE_CHECKING, Any
from typing import Any

import numpy as np
from anndata import AnnData

if TYPE_CHECKING:
from spatialdata._core.spatialdata import SpatialData

from spatialdata._core.spatialdata import SpatialData
from spatialdata.models import TableModel

__all__ = [
Expand All @@ -25,6 +23,8 @@ def _concatenate_tables(
) -> AnnData:
import anndata as ad

if not all(TableModel.ATTRS_KEY in table.uns for table in tables):
raise ValueError("Not all tables are annotating a spatial element")
region_keys = [table.uns[TableModel.ATTRS_KEY][TableModel.REGION_KEY_KEY] for table in tables]
instance_keys = [table.uns[TableModel.ATTRS_KEY][TableModel.INSTANCE_KEY] for table in tables]
regions = [table.uns[TableModel.ATTRS_KEY][TableModel.REGION_KEY] for table in tables]
Expand Down Expand Up @@ -73,6 +73,7 @@ def concatenate(
sdatas: list[SpatialData],
region_key: str | None = None,
instance_key: str | None = None,
concatenate_table: bool = True,
**kwargs: Any,
) -> SpatialData:
"""
Expand All @@ -87,15 +88,15 @@ def concatenate(
If all region_keys are the same, the `region_key` is used.
instance_key
The key to use for the instance column in the concatenated object.
concatenate_table
Whether to merge the tables in case of having the same element name.
kwargs
See :func:`anndata.concat` for more details.

Returns
-------
The concatenated :class:`spatialdata.SpatialData` object.
"""
from spatialdata import SpatialData

merged_images = {**{k: v for sdata in sdatas for k, v in sdata.images.items()}}
if len(merged_images) != np.sum([len(sdata.images) for sdata in sdatas]):
raise KeyError("Images must have unique names across the SpatialData objects to concatenate")
Expand All @@ -108,20 +109,29 @@ def concatenate(
merged_shapes = {**{k: v for sdata in sdatas for k, v in sdata.shapes.items()}}
if len(merged_shapes) != np.sum([len(sdata.shapes) for sdata in sdatas]):
raise KeyError("Shapes must have unique names across the SpatialData objects to concatenate")
# TODO: finish

assert isinstance(sdatas, list), "sdatas must be a list"
assert len(sdatas) > 0, "sdatas must be a non-empty list"

merged_table = _concatenate_tables(
[sdata.table for sdata in sdatas if sdata.table is not None], region_key, instance_key, **kwargs
)
if not concatenate_table:
merged_tables = {**{k: v for sdata in sdatas for k, v in sdata.tables.items()}}
if len(merged_shapes) != np.sum([len(sdata.tables) for sdata in sdatas]):
raise KeyError(
"Tables must have unique names across the SpatialData objects to concatenate unless"
"concatenate_table is set to True."
)
else:
merged_tables = _concatenate_tables(
[sdata.table for sdata in sdatas if sdata.table is not None], region_key, instance_key, **kwargs
)

return SpatialData(
images=merged_images,
labels=merged_labels,
points=merged_points,
shapes=merged_shapes,
table=merged_table,
tables=merged_tables,
)


Expand Down
Loading