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

upgrade major dependencies #141

Merged
merged 2 commits into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
6 changes: 3 additions & 3 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ name: Python Package Tests

on:
push:
branches: [ master, v2.0 ]
branches: [ master ]
pull_request:
branches: [ master, v2.0 ]
branches: [ master ]

jobs:
build:
Expand All @@ -16,7 +16,7 @@ jobs:
strategy:
matrix:
os : [ubuntu-latest, windows-latest, macos-latest]
python-version: ['3.9', '3.10', '3.11']
python-version: ['3.10', '3.11']
steps:
- uses: actions/[email protected]
- name: Set up Python ${{ matrix.python-version }}
Expand Down
5 changes: 1 addition & 4 deletions bento/tools/_flux_enrichment.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,10 @@ def fe(
cell_raster = get_points(
sdata, points_key=f"{instance_key}_raster", astype="pandas", sync=False
)[features]
cell_raster_matrix = np.mat(cell_raster)
mat = sparse.csr_matrix(cell_raster_matrix) # sparse matrix in csr format

samples = cell_raster.index.astype(str)

enrichment = dc.run_wsum(
mat=[mat, samples, features],
mat=cell_raster,
net=net,
source=source,
target=target,
Expand Down
98 changes: 60 additions & 38 deletions bento/tools/_shape_features.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from spatialdata._core.spatialdata import SpatialData
from spatialdata.models import PointsModel, ShapesModel
from tqdm.auto import tqdm
import copy

from .._utils import get_points, get_shape, set_shape_metadata

Expand All @@ -31,7 +32,7 @@ def area(sdata: SpatialData, shape_key: str, recompute: bool = False):
shape_key : str
Key in `sdata.shapes[shape_key]` that contains the shape information.
recompute : bool, optional
If True, forces the computation of the area even if it already exists in the shape metadata.
If True, forces the computation of the area even if it already exists in the shape metadata.
If False (default), the computation is skipped if the area already exists.

Returns
Expand All @@ -46,7 +47,9 @@ def area(sdata: SpatialData, shape_key: str, recompute: bool = False):

# Calculate pixel-wise area
area = get_shape(sdata=sdata, shape_key=shape_key, sync=False).area
set_shape_metadata(sdata=sdata, shape_key=shape_key, metadata=area, column_names=feature_key)
set_shape_metadata(
sdata=sdata, shape_key=shape_key, metadata=area, column_names=feature_key
)


def _poly_aspect_ratio(poly):
Expand Down Expand Up @@ -92,7 +95,9 @@ def aspect_ratio(sdata: SpatialData, shape_key: str, recompute: bool = False):
return

ar = get_shape(sdata, shape_key, sync=False).apply(_poly_aspect_ratio)
set_shape_metadata(sdata=sdata, shape_key=shape_key, metadata=ar, column_names=feature_key)
set_shape_metadata(
sdata=sdata, shape_key=shape_key, metadata=ar, column_names=feature_key
)


def bounds(sdata: SpatialData, shape_key: str, recompute: bool = False):
Expand All @@ -118,9 +123,7 @@ def bounds(sdata: SpatialData, shape_key: str, recompute: bool = False):
"""

feat_names = ["minx", "miny", "maxx", "maxy"]
feature_keys = [
f"{shape_key}_{k}" for k in feat_names
]
feature_keys = [f"{shape_key}_{k}" for k in feat_names]
if (
all([k in sdata.shapes[shape_key].keys() for k in feature_keys])
and not recompute
Expand All @@ -129,7 +132,12 @@ def bounds(sdata: SpatialData, shape_key: str, recompute: bool = False):

bounds = get_shape(sdata, shape_key, sync=False).bounds

set_shape_metadata(sdata=sdata, shape_key=shape_key, metadata=bounds[feat_names], column_names=feature_keys)
set_shape_metadata(
sdata=sdata,
shape_key=shape_key,
metadata=bounds[feat_names],
column_names=feature_keys,
)


def density(sdata: SpatialData, shape_key: str, recompute: bool = False):
Expand All @@ -148,7 +156,6 @@ def density(sdata: SpatialData, shape_key: str, recompute: bool = False):
Density (molecules / shape area) of each polygon
"""


feature_key = f"{shape_key}_density"
if feature_key in sdata.shapes[shape_key].keys() and not recompute:
return
Expand All @@ -162,14 +169,16 @@ def density(sdata: SpatialData, shape_key: str, recompute: bool = False):
area(sdata, shape_key)

set_shape_metadata(
sdata=sdata,
shape_key=shape_key,
metadata=count / sdata.shapes[shape_key][f"{shape_key}_area"],
column_names=feature_key
sdata=sdata,
shape_key=shape_key,
metadata=count / sdata.shapes[shape_key][f"{shape_key}_area"],
column_names=feature_key,
)


def opening(sdata: SpatialData, shape_key: str, proportion: float, recompute: bool = False):
def opening(
sdata: SpatialData, shape_key: str, proportion: float, recompute: bool = False
):
"""Compute the opening (morphological) of distance d for each cell.

Parameters
Expand All @@ -192,7 +201,12 @@ def opening(sdata: SpatialData, shape_key: str, proportion: float, recompute: bo

shapes = get_shape(sdata, shape_key, sync=False)
d = proportion * sdata.shapes[shape_key][f"{shape_key}_radius"]
set_shape_metadata(sdata=sdata, shape_key=shape_key, metadata=shapes.buffer(-d).buffer(d), column_names=feature_key)
set_shape_metadata(
sdata=sdata,
shape_key=shape_key,
metadata=shapes.buffer(-d).buffer(d),
column_names=feature_key,
)


def _second_moment_polygon(centroid, pts):
Expand Down Expand Up @@ -227,7 +241,6 @@ def second_moment(sdata: SpatialData, shape_key: str, recompute: bool = False):
The second moment for each polygon
"""


feature_key = f"{shape_key}_moment"
if feature_key in sdata.shapes[shape_key].keys() and not recompute:
return
Expand All @@ -242,7 +255,10 @@ def second_moment(sdata: SpatialData, shape_key: str, recompute: bool = False):
for centroid, r in zip(shape_centroids, rasters)
]

set_shape_metadata(sdata=sdata, shape_key=shape_key, metadata=moments, column_names=feature_key)
set_shape_metadata(
sdata=sdata, shape_key=shape_key, metadata=moments, column_names=feature_key
)


def _raster_polygon(poly, step=1):
"""
Expand Down Expand Up @@ -299,10 +315,9 @@ def raster(
Long DataFrame of points annotated by shape from `.shapes[shape_key]['{shape_key}']`
"""


feature_key = f"{shape_key}_raster"
shape_feature_key = f"{shape_key}_raster"

if feature_key in sdata.shapes[shape_key].keys() and not recompute:
if shape_feature_key in sdata.shapes[shape_key].keys() and not recompute:
return

shapes = get_shape(sdata, shape_key, sync=False)
Expand All @@ -316,19 +331,23 @@ def raster(

# Add raster to sdata.shapes as 2d array per cell (for point_features compatibility)
set_shape_metadata(
sdata=sdata,
shape_key=shape_key,
metadata=[df[["x", "y"]].values for df in raster_all],
column_names=feature_key
sdata=sdata,
shape_key=shape_key,
metadata=[df[["x", "y"]].values for df in raster_all],
column_names=shape_feature_key,
)

# Add raster to sdata.points as long dataframe (for flux compatibility)
raster_all = pd.concat(raster_all).reset_index(drop=True)
transform = sdata.points[points_key].attrs
sdata.points[feature_key] = PointsModel.parse(

sdata.points[shape_feature_key] = PointsModel.parse(
raster_all, coordinates={"x": "x", "y": "y"}
)
sdata.points[feature_key].attrs = transform

transform = copy.deepcopy(sdata.points[points_key].attrs)
if "feature_key" in transform["spatialdata_attrs"]:
del transform["spatialdata_attrs"]["feature_key"]
sdata.points[shape_feature_key].attrs = transform


def perimeter(sdata: SpatialData, shape_key: str, recompute: bool = False):
Expand All @@ -345,16 +364,15 @@ def perimeter(sdata: SpatialData, shape_key: str, recompute: bool = False):
Perimeter of each polygon
"""


feature_key = f"{shape_key}_perimeter"
if feature_key in sdata.shapes[shape_key].keys() and not recompute:
return

set_shape_metadata(
sdata=sdata,
shape_key=shape_key,
metadata=get_shape(sdata, shape_key, sync=False).length,
column_names=feature_key
sdata=sdata,
shape_key=shape_key,
metadata=get_shape(sdata, shape_key, sync=False).length,
column_names=feature_key,
)


Expand All @@ -372,7 +390,6 @@ def radius(sdata: SpatialData, shape_key: str, recompute: bool = False):
Radius of each polygon in `obs['cell_shape']`
"""


feature_key = f"{shape_key}_radius"
if feature_key in sdata.shapes[shape_key].keys() and not recompute:
return
Expand All @@ -381,7 +398,12 @@ def radius(sdata: SpatialData, shape_key: str, recompute: bool = False):

# Get average distance from boundary to centroid
shape_radius = shapes.apply(_shape_radius)
set_shape_metadata(sdata=sdata, shape_key=shape_key, metadata=shape_radius, column_names=feature_key)
set_shape_metadata(
sdata=sdata,
shape_key=shape_key,
metadata=shape_radius,
column_names=feature_key,
)


def _shape_radius(poly):
Expand All @@ -407,7 +429,6 @@ def span(sdata: SpatialData, shape_key: str, recompute: bool = False):
Length of longest diagonal for each polygon
"""


feature_key = f"{shape_key}_span"

if feature_key in sdata.shapes[shape_key].keys() and not recompute:
Expand All @@ -421,8 +442,10 @@ def get_span(poly):
return int(distance_matrix(shape_coo, shape_coo).max())

span = get_shape(sdata, shape_key, sync=False).exterior.apply(get_span)
set_shape_metadata(sdata=sdata, shape_key=shape_key, metadata=span, column_names=feature_key)

set_shape_metadata(
sdata=sdata, shape_key=shape_key, metadata=span, column_names=feature_key
)


def list_shape_features():
"""Return a dictionary of available shape features and their descriptions.
Expand Down Expand Up @@ -534,10 +557,9 @@ def analyze_shapes(
shape_features[feature](sdata, shape, **kws)



def register_shape_feature(name: str, func: Callable):
"""Register a shape feature function. The function should take an SpatialData object and a shape name as input.
The function should add the feature to the SpatialData object as a column in SpatialData.table.obs.
The function should add the feature to the SpatialData object as a column in SpatialData.table.obs.
This should be done in place and not return anything.

Parameters
Expand Down
7 changes: 4 additions & 3 deletions docs/source/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,13 @@ We currently use [Rye](https://rye-up.com/) for package management.
3. Install the package in editable mode:

```console
pip install -e .
rye sync
```

Run tests:
4. Run tests with rye:
```console
python -m unittest tests
rye test
```

### Documentation

Expand Down
8 changes: 4 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,19 @@ authors = [
dependencies = [
"adjusttext>=1.1.1",
"astropy>=6.0.1",
"decoupler~=1.4.0",
"decoupler>=1.7.0",
"emoji>=2.11.0",
"kneed>=0.8.5",
"matplotlib-scalebar>=0.8.1",
"minisom>=2.3.2",
"pandas<2.0.0",
"pandas>=2.0.0",
"rasterio>=1.3.9",
"rtree>=1.2.0",
"scipy~=1.10.0",
"seaborn>=0.13.2",
"shapely~=2.0.1",
"sparse>=0.15.1",
"spatialdata==0.0.16",
"spatialdata>=0.1.0",
"tensorly>=0.8.1",
"tqdm>=4.66.2",
"upsetplot>=0.9.0",
Expand All @@ -30,7 +30,7 @@ dependencies = [
]
license = "BSD-2-Clause"
readme = "README.md"
requires-python = ">= 3.9"
requires-python = ">= 3.10"

[project.optional-dependencies]
docs = [
Expand Down
Loading