-
Notifications
You must be signed in to change notification settings - Fork 68
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
ImageStack select on Physical Coordinates #1147
Changes from 3 commits
b1cd92f
b643423
68aed0a
864ce5d
7c54c17
30edb89
1d22d23
52cb9c4
b845fdc
96026c3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -383,14 +383,30 @@ def sel(self, indexers: Mapping[Axes, Union[int, tuple]]): | |||||
ImageStack : | ||||||
a new image stack indexed by given value or range. | ||||||
""" | ||||||
|
||||||
# convert indexers to Dict[str, (int/slice)] format | ||||||
# TODO shanaxel42 check if this can be changed to xarray.copy(deep=false) | ||||||
stack = deepcopy(self) | ||||||
selector = indexing_utils.convert_to_selector(indexers) | ||||||
stack._data._data = indexing_utils.index_keep_dimensions(self.xarray, selector) | ||||||
return stack | ||||||
|
||||||
def sel_by_physical_coords(self, indexers: Mapping[Coordinates, Union[float, tuple]]): | ||||||
shanaxel42 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
""" | ||||||
Given a dictionary mapping the coordinate name to either a value or a range represented as a | ||||||
tuple, return an Imagestack with each the Coordinate dimension indexed accordingly. | ||||||
|
||||||
Parameters | ||||||
---------- | ||||||
indexers : Dict[Axes, (float/tuple)]: | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
A dictionary of coord:index where index is the value or range to index the coordinate | ||||||
dimension. | ||||||
|
||||||
Returns | ||||||
------- | ||||||
ImageStack : | ||||||
shanaxel42 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
a new image stack indexed by given value or range. | ||||||
""" | ||||||
new_indexers = indexing_utils.convert_coords_to_indices(self.xarray, indexers) | ||||||
return self.sel(new_indexers) | ||||||
|
||||||
def get_slice( | ||||||
self, | ||||||
selector: Mapping[Axes, Union[int, slice]] | ||||||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,8 +1,9 @@ | ||||||
from typing import Mapping, MutableMapping, Union | ||||||
from typing import Dict, Mapping, MutableMapping, Union | ||||||
|
||||||
import numpy as np | ||||||
import xarray as xr | ||||||
|
||||||
from starfish.types import Axes | ||||||
from starfish.types import Axes, Coordinates | ||||||
|
||||||
|
||||||
def convert_to_selector( | ||||||
|
@@ -26,8 +27,42 @@ def convert_to_selector( | |||||
return return_dict | ||||||
|
||||||
|
||||||
def index_keep_dimensions( | ||||||
data: xr.DataArray, indexers: Mapping[str, Union[int, slice]]) -> xr.DataArray: | ||||||
def convert_coords_to_indices(array: xr.DataArray, | ||||||
indexers: Mapping[Coordinates, Union[float, tuple]] | ||||||
) -> Dict[Axes, Union[int, tuple]]: | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this method can't return a float since we're converting to positional indexers. |
||||||
""" | ||||||
Convert mapping of physical coordinates to value or range to mapping of corresponding Axes and | ||||||
positional coordinates. | ||||||
|
||||||
Parameters | ||||||
---------- | ||||||
array: xr.DataArray | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: @ambrosejcarr always wrote the parameters docs with a space before and after the colon, so I just copied him. |
||||||
The xarray with both physical and positional coordinates. | ||||||
indexers: Mapping[Coordinates, Union[int, tuple]] | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
Mapping of physical coordinates to value or range | ||||||
|
||||||
Returns | ||||||
------- | ||||||
Mapping[Axes, Union[int, tuple]]: | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
Mapping of Axes and positional indices that correspond to the given physical indices. | ||||||
|
||||||
""" | ||||||
axes_indexers: Dict[Axes, Union[int, tuple]] = dict() | ||||||
if Coordinates.X in indexers: | ||||||
idx_x = find_nearest(array[Coordinates.X.value], indexers[Coordinates.X]) | ||||||
axes_indexers[Axes.X] = idx_x | ||||||
if Coordinates.Y in indexers: | ||||||
idx_y = find_nearest(array[Coordinates.Y.value], indexers[Coordinates.Y]) | ||||||
axes_indexers[Axes.Y] = idx_y | ||||||
if Coordinates.Z in indexers: | ||||||
ttung marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
idx_z = find_nearest(array[Coordinates.Z.value], indexers[Coordinates.Z]) | ||||||
axes_indexers[Axes.ZPLANE] = idx_z | ||||||
return axes_indexers | ||||||
|
||||||
|
||||||
def index_keep_dimensions(data: xr.DataArray, | ||||||
indexers: Mapping[str, Union[int, slice]], | ||||||
) -> xr.DataArray: | ||||||
"""Takes an xarray and key to index it. Indexes then adds back in lost dimensions""" | ||||||
# store original dims | ||||||
original_dims = data.dims | ||||||
|
@@ -39,3 +74,31 @@ def index_keep_dimensions( | |||||
data = data.expand_dims(tuple(missing_dims)) | ||||||
# Reorder to correct format | ||||||
return data.transpose(*original_dims) | ||||||
|
||||||
|
||||||
def find_nearest(array: xr.DataArray, value: Union[float, tuple] | ||||||
shanaxel42 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
) -> Union[int, tuple]: | ||||||
""" | ||||||
Given an xarray and value or tuple range return the indices of the closest corresponding | ||||||
value/values in the array. | ||||||
|
||||||
Parameters | ||||||
---------- | ||||||
array: xr.DataArray | ||||||
he array to do lookups in. | ||||||
shanaxel42 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
value: Union[float, tuple] | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
The value or values to lookup. | ||||||
|
||||||
Returns | ||||||
------- | ||||||
Union[int, tuple]: | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
The index or indicies of the entries closest to the given values in the array. | ||||||
""" | ||||||
array = np.asarray(array) | ||||||
if isinstance(value, tuple): | ||||||
idx1 = (np.abs(array - value[0])).argmin() | ||||||
idx2 = (np.abs(array - value[1])).argmin() | ||||||
return idx1, idx2 | ||||||
idx = (np.abs(array - value)).argmin() | ||||||
return idx |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,9 @@ | ||
from collections import OrderedDict | ||
|
||
from starfish.imagestack import indexing_utils as iu | ||
from starfish.imagestack.imagestack import ImageStack | ||
from starfish.types import Axes | ||
from starfish.test import test_utils | ||
from starfish.types import Axes, Coordinates, PhysicalCoordinateTypes | ||
|
||
|
||
def test_imagestack_indexing(): | ||
|
@@ -66,3 +68,107 @@ def test_imagestack_indexing(): | |
expected_shape = OrderedDict([(Axes.ROUND, 1), (Axes.CH, 1), | ||
(Axes.ZPLANE, 1), (Axes.Y, 190), (Axes.X, 190)]) | ||
assert indexed_stack.shape == expected_shape | ||
|
||
|
||
X_COORDS = 1, 2 | ||
Y_COORDS = 4, 6 | ||
Z_COORDS = 1, 3 | ||
|
||
|
||
def test_find_nearest(): | ||
""" | ||
Set up ImageStack with physical coordinates: | ||
x_coords = [1. 1.11111111 1.22222222 1.33333333 1.44444444 1.55555556 | ||
1.66666667 1.77777778 1.88888889 2.] | ||
|
||
y_coords = [4. 4.22222222 4.44444444 4.66666667 4.88888889 5.11111111 | ||
5.33333333 5.55555556 5.77777778 6. ] | ||
|
||
Test that find_nearest() finds the correct corresponding positional index values | ||
""" | ||
stack_shape = OrderedDict([(Axes.ROUND, 3), (Axes.CH, 2), | ||
(Axes.ZPLANE, 1), (Axes.Y, 10), (Axes.X, 10)]) | ||
|
||
physical_coords = OrderedDict([(PhysicalCoordinateTypes.X_MIN, X_COORDS[0]), | ||
(PhysicalCoordinateTypes.X_MAX, X_COORDS[1]), | ||
(PhysicalCoordinateTypes.Y_MIN, Y_COORDS[0]), | ||
(PhysicalCoordinateTypes.Y_MAX, Y_COORDS[1]), | ||
(PhysicalCoordinateTypes.Z_MIN, Z_COORDS[0]), | ||
(PhysicalCoordinateTypes.Z_MAX, Z_COORDS[1])]) | ||
|
||
stack = test_utils.imagestack_with_coords_factory(stack_shape, physical_coords) | ||
assert iu.find_nearest(stack.xarray[Coordinates.X.value], 1.2) == 2 | ||
assert iu.find_nearest(stack.xarray[Coordinates.X.value], 1.5) == 4 | ||
assert iu.find_nearest(stack.xarray[Coordinates.X.value], (1.2, 1.5)) == (2, 4) | ||
|
||
assert iu.find_nearest(stack.xarray[Coordinates.Y.value], 4) == 0 | ||
assert iu.find_nearest(stack.xarray[Coordinates.Y.value], 5.1) == 5 | ||
assert iu.find_nearest(stack.xarray[Coordinates.Y.value], (4, 5.1)) == (0, 5) | ||
|
||
# assert values outside the range are given min/max of array | ||
assert iu.find_nearest(stack.xarray[Coordinates.X.value], 5) == 9 | ||
assert iu.find_nearest(stack.xarray[Coordinates.X.value], -5) == 0 | ||
|
||
|
||
def test_convert_coords_to_indices(): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we add a test that does both coordinate and index selection? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. added! |
||
""" | ||
Set up ImageStack with physical coordinates: | ||
x_coords = [1. 1.11111111 1.22222222 1.33333333 1.44444444 1.55555556 | ||
1.66666667 1.77777778 1.88888889 2.] | ||
|
||
y_coords = [4. 4.22222222 4.44444444 4.66666667 4.88888889 5.11111111 | ||
5.33333333 5.55555556 5.77777778 6. ] | ||
|
||
Test that convert_coords_to_indices() correctly converts Coordinate indices | ||
to their corresponding positional indices | ||
""" | ||
stack_shape = OrderedDict([(Axes.ROUND, 3), (Axes.CH, 2), | ||
(Axes.ZPLANE, 1), (Axes.Y, 10), (Axes.X, 10)]) | ||
|
||
physical_coords = OrderedDict([(PhysicalCoordinateTypes.X_MIN, X_COORDS[0]), | ||
(PhysicalCoordinateTypes.X_MAX, X_COORDS[1]), | ||
(PhysicalCoordinateTypes.Y_MIN, Y_COORDS[0]), | ||
(PhysicalCoordinateTypes.Y_MAX, Y_COORDS[1]), | ||
(PhysicalCoordinateTypes.Z_MIN, Z_COORDS[0]), | ||
(PhysicalCoordinateTypes.Z_MAX, Z_COORDS[1])]) | ||
|
||
stack = test_utils.imagestack_with_coords_factory(stack_shape, physical_coords) | ||
coordinate_indices = {Coordinates.X: (1.2, 1.5), Coordinates.Y: (4, 5.1)} | ||
positional_indices = iu.convert_coords_to_indices(stack.xarray, coordinate_indices) | ||
|
||
assert positional_indices[Axes.X] == iu.find_nearest( | ||
stack.xarray[Coordinates.X.value], (1.2, 1.5)) | ||
assert positional_indices[Axes.Y] == iu.find_nearest( | ||
stack.xarray[Coordinates.Y.value], (4, 5.1)) | ||
|
||
|
||
def test_sel_by_physical_coords(): | ||
""" | ||
Set up ImageStack with physical coordinates: | ||
x_coords = [1. 1.11111111 1.22222222 1.33333333 1.44444444 1.55555556 | ||
1.66666667 1.77777778 1.88888889 2.] | ||
|
||
y_coords = [4. 4.22222222 4.44444444 4.66666667 4.88888889 5.11111111 | ||
5.33333333 5.55555556 5.77777778 6. ] | ||
|
||
Test that sel_by_physical_coords() correctly indexes the imagestack by the | ||
corresponding positional indexers | ||
""" | ||
stack_shape = OrderedDict([(Axes.ROUND, 3), (Axes.CH, 2), | ||
(Axes.ZPLANE, 1), (Axes.Y, 10), (Axes.X, 10)]) | ||
|
||
physical_coords = OrderedDict([(PhysicalCoordinateTypes.X_MIN, X_COORDS[0]), | ||
(PhysicalCoordinateTypes.X_MAX, X_COORDS[1]), | ||
(PhysicalCoordinateTypes.Y_MIN, Y_COORDS[0]), | ||
(PhysicalCoordinateTypes.Y_MAX, Y_COORDS[1]), | ||
(PhysicalCoordinateTypes.Z_MIN, Z_COORDS[0]), | ||
(PhysicalCoordinateTypes.Z_MAX, Z_COORDS[1])]) | ||
|
||
stack = test_utils.imagestack_with_coords_factory(stack_shape, physical_coords) | ||
|
||
indexed_stack_by_coords = stack.sel_by_physical_coords({Coordinates.X: (1.2, 1.5), | ||
Coordinates.Y: (4, 5.1)}) | ||
indexed_stack_by_pos = stack.sel({Axes.X: (2, 4), Axes.Y: (0, 5)}) | ||
|
||
# assert that the resulting xarrays are the same | ||
assert indexed_stack_by_coords.xarray.equals(indexed_stack_by_pos.xarray) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Checked, cannot do regular copy get an xarray error.