spatialproteomics 0.7.9__tar.gz → 0.8.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {spatialproteomics-0.7.9/spatialproteomics.egg-info → spatialproteomics-0.8.0}/PKG-INFO +1 -1
- {spatialproteomics-0.7.9 → spatialproteomics-0.8.0}/pyproject.toml +1 -1
- {spatialproteomics-0.7.9 → spatialproteomics-0.8.0}/spatialproteomics/pl/plot.py +10 -6
- {spatialproteomics-0.7.9 → spatialproteomics-0.8.0}/spatialproteomics/pl/utils.py +7 -4
- {spatialproteomics-0.7.9 → spatialproteomics-0.8.0}/spatialproteomics/pp/preprocessing.py +145 -23
- {spatialproteomics-0.7.9 → spatialproteomics-0.8.0}/spatialproteomics/pp/utils.py +4 -4
- {spatialproteomics-0.7.9 → spatialproteomics-0.8.0/spatialproteomics.egg-info}/PKG-INFO +1 -1
- {spatialproteomics-0.7.9 → spatialproteomics-0.8.0}/LICENSE +0 -0
- {spatialproteomics-0.7.9 → spatialproteomics-0.8.0}/README.md +0 -0
- {spatialproteomics-0.7.9 → spatialproteomics-0.8.0}/setup.cfg +0 -0
- {spatialproteomics-0.7.9 → spatialproteomics-0.8.0}/spatialproteomics/__init__.py +0 -0
- {spatialproteomics-0.7.9 → spatialproteomics-0.8.0}/spatialproteomics/base_logger.py +0 -0
- {spatialproteomics-0.7.9 → spatialproteomics-0.8.0}/spatialproteomics/constants.py +0 -0
- {spatialproteomics-0.7.9 → spatialproteomics-0.8.0}/spatialproteomics/container.py +0 -0
- {spatialproteomics-0.7.9 → spatialproteomics-0.8.0}/spatialproteomics/image_container/ImageContainer.py +0 -0
- {spatialproteomics-0.7.9 → spatialproteomics-0.8.0}/spatialproteomics/image_container/__init__.py +0 -0
- {spatialproteomics-0.7.9 → spatialproteomics-0.8.0}/spatialproteomics/la/__init__.py +0 -0
- {spatialproteomics-0.7.9 → spatialproteomics-0.8.0}/spatialproteomics/la/label.py +0 -0
- {spatialproteomics-0.7.9 → spatialproteomics-0.8.0}/spatialproteomics/la/utils.py +0 -0
- {spatialproteomics-0.7.9 → spatialproteomics-0.8.0}/spatialproteomics/nh/__init__.py +0 -0
- {spatialproteomics-0.7.9 → spatialproteomics-0.8.0}/spatialproteomics/nh/neighborhood.py +0 -0
- {spatialproteomics-0.7.9 → spatialproteomics-0.8.0}/spatialproteomics/nh/utils.py +0 -0
- {spatialproteomics-0.7.9 → spatialproteomics-0.8.0}/spatialproteomics/pl/__init__.py +0 -0
- {spatialproteomics-0.7.9 → spatialproteomics-0.8.0}/spatialproteomics/pp/__init__.py +0 -0
- {spatialproteomics-0.7.9 → spatialproteomics-0.8.0}/spatialproteomics/pp/intensity.py +0 -0
- {spatialproteomics-0.7.9 → spatialproteomics-0.8.0}/spatialproteomics/sd/__init__.py +0 -0
- {spatialproteomics-0.7.9 → spatialproteomics-0.8.0}/spatialproteomics/sd/utils.py +0 -0
- {spatialproteomics-0.7.9 → spatialproteomics-0.8.0}/spatialproteomics/tl/__init__.py +0 -0
- {spatialproteomics-0.7.9 → spatialproteomics-0.8.0}/spatialproteomics/tl/tool.py +0 -0
- {spatialproteomics-0.7.9 → spatialproteomics-0.8.0}/spatialproteomics/tl/utils.py +0 -0
- {spatialproteomics-0.7.9 → spatialproteomics-0.8.0}/spatialproteomics.egg-info/SOURCES.txt +0 -0
- {spatialproteomics-0.7.9 → spatialproteomics-0.8.0}/spatialproteomics.egg-info/dependency_links.txt +0 -0
- {spatialproteomics-0.7.9 → spatialproteomics-0.8.0}/spatialproteomics.egg-info/requires.txt +0 -0
- {spatialproteomics-0.7.9 → spatialproteomics-0.8.0}/spatialproteomics.egg-info/top_level.txt +0 -0
- {spatialproteomics-0.7.9 → spatialproteomics-0.8.0}/tests/test_container.py +0 -0
- {spatialproteomics-0.7.9 → spatialproteomics-0.8.0}/tests/test_integration.py +0 -0
- {spatialproteomics-0.7.9 → spatialproteomics-0.8.0}/tests/test_to_zarr.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: spatialproteomics
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Summary: Spatialproteomics is an interoperable toolbox for analyzing highly multiplexed fluorescence image data
|
|
5
5
|
Author: Matthias Meyer-Bender, Harald Vohringer
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -245,6 +245,10 @@ class PlotAccessor:
|
|
|
245
245
|
min_val = obs_colors["min"]
|
|
246
246
|
max_val = obs_colors["max"]
|
|
247
247
|
|
|
248
|
+
# handling the special case where all values in the segmentation are the same,
|
|
249
|
+
# which would lead to a division by zero in the normalization step
|
|
250
|
+
max_val = max_val if max_val > min_val else min_val + 1
|
|
251
|
+
|
|
248
252
|
# Create the colorbar with dynamic positioning and size
|
|
249
253
|
sm = plt.cm.ScalarMappable(cmap=cmap, norm=plt.Normalize(vmin=min_val, vmax=max_val))
|
|
250
254
|
sm.set_array([])
|
|
@@ -294,8 +298,8 @@ class PlotAccessor:
|
|
|
294
298
|
normalize: bool = True,
|
|
295
299
|
merge: bool = True,
|
|
296
300
|
layer_key: str = Layers.IMAGE,
|
|
297
|
-
amin: Optional[float] = None,
|
|
298
|
-
amax: Optional[float] = None,
|
|
301
|
+
amin: Optional[float | List] = None,
|
|
302
|
+
amax: Optional[float | List] = None,
|
|
299
303
|
) -> xr.Dataset:
|
|
300
304
|
"""
|
|
301
305
|
Colorizes a stack of images.
|
|
@@ -312,10 +316,10 @@ class PlotAccessor:
|
|
|
312
316
|
Merge the channel dimension. Default is True.
|
|
313
317
|
layer_key : str, optional
|
|
314
318
|
The key of the layer containing the image. Default is '_image'.
|
|
315
|
-
amin : float, optional
|
|
316
|
-
The minimum absolute value for normalization. Default is None.
|
|
317
|
-
amax : float, optional
|
|
318
|
-
The maximum absolute value for normalization. Default is None.
|
|
319
|
+
amin : float | List, optional
|
|
320
|
+
The minimum absolute value for normalization (either global or per channel). Default is None.
|
|
321
|
+
amax : float | List, optional
|
|
322
|
+
The maximum absolute value for normalization (either global or per channel). Default is None.
|
|
319
323
|
|
|
320
324
|
Returns
|
|
321
325
|
-------
|
|
@@ -77,8 +77,8 @@ def _colorize(
|
|
|
77
77
|
colors: List[str] = ["C1", "C2", "C3", "C4", "C5"],
|
|
78
78
|
background: str = "black",
|
|
79
79
|
normalize: bool = True,
|
|
80
|
-
amin: Optional[float] = None,
|
|
81
|
-
amax: Optional[float] = None,
|
|
80
|
+
amin: Optional[float | List] = None,
|
|
81
|
+
amax: Optional[float | List] = None,
|
|
82
82
|
) -> np.ndarray:
|
|
83
83
|
"""
|
|
84
84
|
Apply colorization to an image based on a given colors.
|
|
@@ -88,8 +88,8 @@ def _colorize(
|
|
|
88
88
|
colors (List[str], optional): The list of colors to be used for colorization. Defaults to ["C1", "C2", "C3", "C4", "C5"].
|
|
89
89
|
background (str, optional): The background color. Defaults to "black".
|
|
90
90
|
normalize (bool, optional): Whether to normalize the image before colorization. Defaults to True.
|
|
91
|
-
amin (Optional[float], optional): The minimum value for normalization. Defaults to None.
|
|
92
|
-
amax (Optional[float], optional): The maximum value for normalization. Defaults to None.
|
|
91
|
+
amin (Optional[float | List], optional): The minimum value for normalization. Defaults to None.
|
|
92
|
+
amax (Optional[float | List], optional): The maximum value for normalization. Defaults to None.
|
|
93
93
|
|
|
94
94
|
Returns:
|
|
95
95
|
np.ndarray: The colorized image.
|
|
@@ -219,6 +219,9 @@ def _render_obs(
|
|
|
219
219
|
vmin: float = None,
|
|
220
220
|
vmax: float = None,
|
|
221
221
|
) -> np.ndarray:
|
|
222
|
+
# handling the special case where all values in the segmentation are the same,
|
|
223
|
+
# which would lead to a division by zero in the normalization step
|
|
224
|
+
vmax = vmax if vmax > vmin else vmin + 1
|
|
222
225
|
# normalize the segmentation to be in the range [0, 1]
|
|
223
226
|
segmentation = (segmentation - vmin) / (vmax - vmin)
|
|
224
227
|
|
|
@@ -313,6 +313,7 @@ def filter_by_obs(
|
|
|
313
313
|
func: Callable,
|
|
314
314
|
segmentation_key: str = SDLayers.SEGMENTATION,
|
|
315
315
|
table_key: str = SDLayers.TABLE,
|
|
316
|
+
reindex: bool = False,
|
|
316
317
|
copy: bool = False,
|
|
317
318
|
):
|
|
318
319
|
"""
|
|
@@ -324,6 +325,7 @@ def filter_by_obs(
|
|
|
324
325
|
func (Callable): A filtering function that takes in the values of the feature and returns a boolean array.
|
|
325
326
|
segmentation_key (str): The key of the segmentation mask in the object. Default is SDLayers.SEGMENTATION.
|
|
326
327
|
table_key (str): The key of the table in the object. Default is SDLayers.TABLE.
|
|
328
|
+
reindex (bool): Whether to reindex the obs after filtering. Default is False.
|
|
327
329
|
copy (bool): If True, a copy of the object is returned. Default is False.
|
|
328
330
|
"""
|
|
329
331
|
import spatialdata
|
|
@@ -347,13 +349,15 @@ def filter_by_obs(
|
|
|
347
349
|
# setting all cells that are not in cells to 0
|
|
348
350
|
segmentation = _remove_unlabeled_cells(segmentation, cells_sel)
|
|
349
351
|
# relabeling cells in the segmentation mask so the IDs go from 1 to n again
|
|
350
|
-
|
|
352
|
+
if reindex:
|
|
353
|
+
segmentation, relabel_dict = _relabel_cells(segmentation)
|
|
351
354
|
|
|
352
355
|
# removing the cells which are not in cells_sel
|
|
353
356
|
adata = adata[adata.obs[SDFeatures.ID].isin(cells_sel), :].copy()
|
|
354
357
|
# updating the cell coords of the object
|
|
355
|
-
|
|
356
|
-
|
|
358
|
+
if reindex:
|
|
359
|
+
adata.obs[SDFeatures.ID] = [relabel_dict[cell] for cell in adata.obs[SDFeatures.ID].values]
|
|
360
|
+
adata.obs.index = [f"Cell_{x}" for x in adata.obs[SDFeatures.ID].values]
|
|
357
361
|
|
|
358
362
|
# overwriting the segmentation mask in the object
|
|
359
363
|
# get transformations
|
|
@@ -743,7 +747,7 @@ class PreprocessingAccessor:
|
|
|
743
747
|
|
|
744
748
|
def add_channel(self, channels: Union[str, list], array: np.ndarray, layer_key: str = Layers.IMAGE) -> xr.Dataset:
|
|
745
749
|
"""
|
|
746
|
-
Adds channel(s) to an existing
|
|
750
|
+
Adds channel(s) to an existing spatialproteomics object.
|
|
747
751
|
|
|
748
752
|
Parameters
|
|
749
753
|
----------
|
|
@@ -752,12 +756,12 @@ class PreprocessingAccessor:
|
|
|
752
756
|
array : np.ndarray
|
|
753
757
|
The numpy array representing the channel(s) to be added.
|
|
754
758
|
layer_key : str
|
|
755
|
-
The layer key where the channel(s) should be added. Default is '
|
|
759
|
+
The layer key where the channel(s) should be added. Default is '_image'.
|
|
756
760
|
|
|
757
761
|
Returns
|
|
758
762
|
-------
|
|
759
763
|
xr.Dataset
|
|
760
|
-
The updated
|
|
764
|
+
The updated spatialproteomics object with added channel(s).
|
|
761
765
|
"""
|
|
762
766
|
assert type(array) is np.ndarray, "Added channels must be numpy arrays."
|
|
763
767
|
assert array.ndim in [2, 3], "Added channels must be 2D or 3D arrays."
|
|
@@ -795,10 +799,34 @@ class PreprocessingAccessor:
|
|
|
795
799
|
|
|
796
800
|
return xr.merge([self._obj, da], join="outer", compat="no_conflicts")
|
|
797
801
|
|
|
802
|
+
def drop_channel(self, channels: Union[str, list]) -> xr.Dataset:
|
|
803
|
+
"""
|
|
804
|
+
Drops channel(s) from an existing spatialproteomics object.
|
|
805
|
+
|
|
806
|
+
Parameters
|
|
807
|
+
----------
|
|
808
|
+
channels : Union[str, list]
|
|
809
|
+
The name of the channel or a list of channel names to be dropped.
|
|
810
|
+
|
|
811
|
+
Returns
|
|
812
|
+
-------
|
|
813
|
+
xr.Dataset
|
|
814
|
+
The updated spatialproteomics object with dropped channel(s).
|
|
815
|
+
"""
|
|
816
|
+
if type(channels) is str:
|
|
817
|
+
channels = [channels]
|
|
818
|
+
|
|
819
|
+
nonexistent_channels = set(channels) - set(self._obj.coords[Dims.CHANNELS].values)
|
|
820
|
+
assert (
|
|
821
|
+
len(nonexistent_channels) == 0
|
|
822
|
+
), f"Some of the channels to be dropped do not exist in the object: {nonexistent_channels}. Please set the channel_names argument to a list of existing channels."
|
|
823
|
+
|
|
824
|
+
return self._obj.drop_sel({Dims.CHANNELS: channels})
|
|
825
|
+
|
|
798
826
|
def add_segmentation(
|
|
799
827
|
self,
|
|
800
828
|
segmentation: Union[str, np.ndarray] = None,
|
|
801
|
-
reindex: bool =
|
|
829
|
+
reindex: bool = False,
|
|
802
830
|
keep_labels: bool = True,
|
|
803
831
|
add_obs: bool = True,
|
|
804
832
|
) -> xr.Dataset:
|
|
@@ -1569,7 +1597,9 @@ class PreprocessingAccessor:
|
|
|
1569
1597
|
|
|
1570
1598
|
return xr.merge([obj, new_img, new_seg], join="outer", compat="no_conflicts")
|
|
1571
1599
|
|
|
1572
|
-
def filter_by_obs(
|
|
1600
|
+
def filter_by_obs(
|
|
1601
|
+
self, col: str, func: Callable, segmentation_key: str = Layers.SEGMENTATION, reindex: bool = False
|
|
1602
|
+
):
|
|
1573
1603
|
"""
|
|
1574
1604
|
Filter the object by observations based on a given feature and filtering function.
|
|
1575
1605
|
|
|
@@ -1577,6 +1607,7 @@ class PreprocessingAccessor:
|
|
|
1577
1607
|
col (str): The name of the feature to filter by.
|
|
1578
1608
|
func (Callable): A filtering function that takes in the values of the feature and returns a boolean array.
|
|
1579
1609
|
segmentation_key (str): The key of the segmentation mask in the object. Default is Layers.SEGMENTATION.
|
|
1610
|
+
reindex (bool): Whether to reindex the cell IDs after filtering. Default is False.
|
|
1580
1611
|
|
|
1581
1612
|
Returns:
|
|
1582
1613
|
xr.Dataset: The filtered object with the selected cells and updated segmentation mask.
|
|
@@ -1586,7 +1617,6 @@ class PreprocessingAccessor:
|
|
|
1586
1617
|
|
|
1587
1618
|
Notes:
|
|
1588
1619
|
- This method filters the object by selecting only the cells that satisfy the filtering condition.
|
|
1589
|
-
- It also updates the segmentation mask to remove cells that are not selected and relabels the remaining cells.
|
|
1590
1620
|
|
|
1591
1621
|
Example:
|
|
1592
1622
|
To filter the object by the feature "area" and keep only the cells with an area greater than 70px:
|
|
@@ -1615,10 +1645,11 @@ class PreprocessingAccessor:
|
|
|
1615
1645
|
segmentation = obj[segmentation_key].values
|
|
1616
1646
|
# setting all cells that are not in cells to 0
|
|
1617
1647
|
segmentation = _remove_unlabeled_cells(segmentation, cells_sel)
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1648
|
+
if reindex:
|
|
1649
|
+
# relabeling cells in the segmentation mask so the IDs go from 1 to n again
|
|
1650
|
+
segmentation, relabel_dict = _relabel_cells(segmentation)
|
|
1651
|
+
# updating the cell coords of the object
|
|
1652
|
+
obj.coords[Dims.CELLS] = [relabel_dict[cell] for cell in obj.coords[Dims.CELLS].values]
|
|
1622
1653
|
|
|
1623
1654
|
# creating a data array with the segmentation mask, so that we can merge it to the original
|
|
1624
1655
|
da = xr.DataArray(
|
|
@@ -1635,7 +1666,11 @@ class PreprocessingAccessor:
|
|
|
1635
1666
|
return xr.merge([obj, da], join="outer", compat="no_conflicts")
|
|
1636
1667
|
|
|
1637
1668
|
def remove_outlying_cells(
|
|
1638
|
-
self,
|
|
1669
|
+
self,
|
|
1670
|
+
dilation_size: int = 25,
|
|
1671
|
+
threshold: int = 5,
|
|
1672
|
+
segmentation_key: str = Layers.SEGMENTATION,
|
|
1673
|
+
reindex: bool = False,
|
|
1639
1674
|
):
|
|
1640
1675
|
"""
|
|
1641
1676
|
Removes outlying cells from the image container. It does so by dilating the segmentation mask and removing cells that belong to a connected component with less than 'threshold' cells.
|
|
@@ -1648,6 +1683,8 @@ class PreprocessingAccessor:
|
|
|
1648
1683
|
The minimum number of cells in a connected component required for the cells to be kept. Default is 5.
|
|
1649
1684
|
segmentation_key : str
|
|
1650
1685
|
The key of the segmentation mask in the object. Default is '_segmentation'.
|
|
1686
|
+
reindex : bool
|
|
1687
|
+
Whether to reindex the cell IDs after filtering. Default is False.
|
|
1651
1688
|
|
|
1652
1689
|
Returns
|
|
1653
1690
|
-------
|
|
@@ -1679,10 +1716,11 @@ class PreprocessingAccessor:
|
|
|
1679
1716
|
obj = self._obj.sel(query)
|
|
1680
1717
|
# setting all cells that are not in cells to 0
|
|
1681
1718
|
segmentation = _remove_unlabeled_cells(segmentation, cells_sel)
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1719
|
+
if reindex:
|
|
1720
|
+
# relabeling cells in the segmentation mask so the IDs go from 1 to n again
|
|
1721
|
+
segmentation, relabel_dict = _relabel_cells(segmentation)
|
|
1722
|
+
# updating the cell coords of the object
|
|
1723
|
+
obj.coords[Dims.CELLS] = [relabel_dict[cell] for cell in obj.coords[Dims.CELLS].values]
|
|
1686
1724
|
|
|
1687
1725
|
# creating a data array with the segmentation mask, so that we can merge it to the original
|
|
1688
1726
|
da = xr.DataArray(
|
|
@@ -2058,13 +2096,16 @@ class PreprocessingAccessor:
|
|
|
2058
2096
|
|
|
2059
2097
|
return xr.merge([obj, da], join="outer", compat="no_conflicts")
|
|
2060
2098
|
|
|
2061
|
-
def mask_cells(
|
|
2099
|
+
def mask_cells(
|
|
2100
|
+
self, mask_key: str = Layers.MASK, segmentation_key=Layers.SEGMENTATION, reindex: bool = False
|
|
2101
|
+
) -> xr.Dataset:
|
|
2062
2102
|
"""
|
|
2063
2103
|
Mask cells in the segmentation mask.
|
|
2064
2104
|
|
|
2065
2105
|
Parameters:
|
|
2066
2106
|
mask_key (str): The key of the mask to use for masking.
|
|
2067
2107
|
segmentation_key (str): The key of the segmentation mask in the object. Default is Layers.SEGMENTATION.
|
|
2108
|
+
reindex (bool): Whether to reindex the cell IDs after filtering. Default is False.
|
|
2068
2109
|
|
|
2069
2110
|
Returns:
|
|
2070
2111
|
xr.Dataset: The object with the masked cells in the segmentation mask.
|
|
@@ -2093,10 +2134,11 @@ class PreprocessingAccessor:
|
|
|
2093
2134
|
segmentation = obj[segmentation_key].values
|
|
2094
2135
|
# setting all cells that are not in cells to 0
|
|
2095
2136
|
segmentation = _remove_unlabeled_cells(segmentation, cells_sel)
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2137
|
+
if reindex:
|
|
2138
|
+
# relabeling cells in the segmentation mask so the IDs go from 1 to n again
|
|
2139
|
+
segmentation, relabel_dict = _relabel_cells(segmentation)
|
|
2140
|
+
# updating the cell coords of the object
|
|
2141
|
+
obj.coords[Dims.CELLS] = [relabel_dict[cell] for cell in obj.coords["cells"].values]
|
|
2100
2142
|
|
|
2101
2143
|
# creating a data array with the segmentation mask, so that we can merge it to the original
|
|
2102
2144
|
da = xr.DataArray(
|
|
@@ -2199,3 +2241,83 @@ class PreprocessingAccessor:
|
|
|
2199
2241
|
# if there are multiple layers with channels as coordinates, this will turn it into float32, since it will introduce NaNs for the new channel in other layers
|
|
2200
2242
|
obj = self._obj.pp.add_channel(channels=key_added, array=merged)
|
|
2201
2243
|
return obj
|
|
2244
|
+
|
|
2245
|
+
def find_bright_cells(
|
|
2246
|
+
self,
|
|
2247
|
+
channels: Optional[Union[str, list]] = None,
|
|
2248
|
+
frac_channels: float = 0.5,
|
|
2249
|
+
quantile: float = 0.95,
|
|
2250
|
+
key: str = Layers.INTENSITY,
|
|
2251
|
+
key_added: str = "is_bright",
|
|
2252
|
+
) -> xr.Dataset:
|
|
2253
|
+
"""
|
|
2254
|
+
Flags cells that are bright across multiple channels (e.g., due to a lot of antibodies binding to that cell).
|
|
2255
|
+
A cell is considered bright in a channel if its intensity exceeds the given quantile threshold
|
|
2256
|
+
for that channel. Cells that are bright in at least frac_channels of the
|
|
2257
|
+
considered channels are flagged.
|
|
2258
|
+
|
|
2259
|
+
Parameters
|
|
2260
|
+
----------
|
|
2261
|
+
channels : Optional[Union[str, list]]
|
|
2262
|
+
The channels to consider. If None, all channels are used.
|
|
2263
|
+
frac_channels : float
|
|
2264
|
+
The fraction of channels in which a cell must be bright to be flagged.
|
|
2265
|
+
Default is 0.5.
|
|
2266
|
+
quantile : float
|
|
2267
|
+
The quantile threshold above which a cell is considered bright in a
|
|
2268
|
+
given channel. Computed per channel. Default is 0.95.
|
|
2269
|
+
key : str
|
|
2270
|
+
The key of the intensity layer to use. Default is Layers.INTENSITY.
|
|
2271
|
+
key_added : str
|
|
2272
|
+
The name of the feature added to obs. Default is 'is_bright'.
|
|
2273
|
+
|
|
2274
|
+
Returns
|
|
2275
|
+
-------
|
|
2276
|
+
xr.Dataset
|
|
2277
|
+
The updated image container with the 'is_bright' flag added to obs.
|
|
2278
|
+
|
|
2279
|
+
Raises
|
|
2280
|
+
------
|
|
2281
|
+
ValueError
|
|
2282
|
+
If no intensity matrix is found at the specified layer.
|
|
2283
|
+
|
|
2284
|
+
Example
|
|
2285
|
+
-------
|
|
2286
|
+
Flag cells that are bright in at least 50% of all channels at the 95th percentile:
|
|
2287
|
+
>>> ds = ds.pp.find_bright_cells()
|
|
2288
|
+
|
|
2289
|
+
Flag cells that are bright in at least 2 out of 3 specific channels:
|
|
2290
|
+
>>> ds = ds.pp.find_bright_cells(channels=['CD3', 'CD8', 'CD20'], frac_channels=0.66)
|
|
2291
|
+
"""
|
|
2292
|
+
if key not in self._obj:
|
|
2293
|
+
raise ValueError(f"No intensity matrix found at layer {key}. Please run pp.add_quantification() first.")
|
|
2294
|
+
|
|
2295
|
+
assert 0 <= frac_channels <= 1, "frac_channels must be between 0 and 1."
|
|
2296
|
+
assert 0 <= quantile <= 1, "quantile must be between 0 and 1."
|
|
2297
|
+
|
|
2298
|
+
# resolve channels
|
|
2299
|
+
all_channels = self._obj.coords[Dims.CHANNELS].values
|
|
2300
|
+
if channels is None:
|
|
2301
|
+
channels = all_channels
|
|
2302
|
+
elif isinstance(channels, str):
|
|
2303
|
+
channels = [channels]
|
|
2304
|
+
|
|
2305
|
+
nonexistent = set(channels) - set(all_channels)
|
|
2306
|
+
assert (
|
|
2307
|
+
len(nonexistent) == 0
|
|
2308
|
+
), f"The following channels were not found: {nonexistent}. Please adjust the 'channels' argument to only contain existing channels."
|
|
2309
|
+
|
|
2310
|
+
# get the intensity matrix for the selected channels, shape (n_cells, n_channels)
|
|
2311
|
+
intensity = self._obj[key].sel({Dims.CHANNELS: channels}).values
|
|
2312
|
+
|
|
2313
|
+
# compute per-channel quantile thresholds
|
|
2314
|
+
thresholds = np.quantile(intensity, quantile, axis=0) # shape (n_channels,)
|
|
2315
|
+
|
|
2316
|
+
# a cell is bright in a channel if it exceeds that channel's threshold
|
|
2317
|
+
is_above = intensity > thresholds # shape (n_cells, n_channels)
|
|
2318
|
+
|
|
2319
|
+
# flag cells that are bright in at least frac_channels of the considered channels
|
|
2320
|
+
min_channels = int(np.ceil(frac_channels * len(channels)))
|
|
2321
|
+
is_bright = is_above.sum(axis=1) >= min_channels # shape (n_cells,)
|
|
2322
|
+
|
|
2323
|
+
return self._obj.pp.add_feature(key_added, is_bright.astype(float))
|
|
@@ -161,8 +161,8 @@ def _normalize(
|
|
|
161
161
|
pmax: float = 99.8,
|
|
162
162
|
eps: float = 1e-20,
|
|
163
163
|
clip: bool = False,
|
|
164
|
-
amin: Optional[float] = None,
|
|
165
|
-
amax: Optional[float] = None,
|
|
164
|
+
amin: Optional[float | List] = None,
|
|
165
|
+
amax: Optional[float | List] = None,
|
|
166
166
|
) -> np.ndarray:
|
|
167
167
|
"""
|
|
168
168
|
Performs a min max normalisation.
|
|
@@ -181,9 +181,9 @@ def _normalize(
|
|
|
181
181
|
clip: bool
|
|
182
182
|
Ensures that normed image array contains no values greater than 1.
|
|
183
183
|
amin: float
|
|
184
|
-
Absolute value to perform normalization. If set, this overrides pmin.
|
|
184
|
+
Absolute value to perform normalization. If set, this overrides pmin. Can be a list of values, in which case each channel is normalized independently.
|
|
185
185
|
amax: float
|
|
186
|
-
Absolute value to perform normalization. If set, this overrides pmax.
|
|
186
|
+
Absolute value to perform normalization. If set, this overrides pmax. Can be a list of values, in which case each channel is normalized independently.
|
|
187
187
|
|
|
188
188
|
Returns
|
|
189
189
|
-------
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: spatialproteomics
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Summary: Spatialproteomics is an interoperable toolbox for analyzing highly multiplexed fluorescence image data
|
|
5
5
|
Author: Matthias Meyer-Bender, Harald Vohringer
|
|
6
6
|
Requires-Python: >=3.10
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{spatialproteomics-0.7.9 → spatialproteomics-0.8.0}/spatialproteomics/image_container/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{spatialproteomics-0.7.9 → spatialproteomics-0.8.0}/spatialproteomics.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
{spatialproteomics-0.7.9 → spatialproteomics-0.8.0}/spatialproteomics.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|