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

Unify FunctionSource in Map and Reduce #1540

Merged
merged 1 commit into from
Oct 9, 2019
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
4 changes: 2 additions & 2 deletions docs/source/_static/data_processing_examples/iss_pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import starfish
from starfish.image import ApplyTransform, Filter, LearnTransform, Segment
from starfish.spots import FindSpots, DecodeSpots, AssignTargets
from starfish.types import Axes
from starfish.types import Axes, FunctionSource

test = os.getenv("TESTING") is not None

Expand All @@ -20,7 +20,7 @@ def iss_pipeline(fov, codebook):
learn_translation = LearnTransform.Translation(reference_stack=fov.get_image('dots'),
axes=Axes.ROUND, upsampling=100)
max_projector = Filter.Reduce(
(Axes.CH, Axes.ZPLANE), func="max", module=Filter.Reduce.FunctionSource.np)
(Axes.CH, Axes.ZPLANE), func="max", module=FunctionSource.np)
transforms_list = learn_translation.run(max_projector.run(primary_image))
warp = ApplyTransform.Warp()
registered = warp.run(primary_image, transforms_list=transforms_list, in_place=False, verbose=True)
Expand Down
4 changes: 2 additions & 2 deletions docs/source/_static/tutorials/exec_image_manipulations.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
# Here, we demonstrate selecting the last 50 pixels of (x, y) for a rounds 2 and 3 using the
# :py:meth:`ImageStack.sel` method.

from starfish.types import Axes
from starfish.types import Axes, FunctionSource

cropped_image: starfish.ImageStack = image.sel(
{Axes.ROUND: (2, 3), Axes.X: (30, 80), Axes.Y: (50, 100)}
Expand All @@ -73,7 +73,7 @@
#
from starfish.image import Filter

max_projector = Filter.Reduce((Axes.CH,), func="max", module=Filter.Reduce.FunctionSource.np)
max_projector = Filter.Reduce((Axes.CH,), func="max", module=FunctionSource.np)
projected_image: starfish.ImageStack = max_projector.run(image)

###################################################################################################
Expand Down
15 changes: 8 additions & 7 deletions docs/source/getting_started/loading_data/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,10 @@ we'll collapse all the spots across channels in each round, mimicing a "dots" im
.. code-block:: python

In[11]: from starfish.image import Filter
In[12]: max_projector = Filter.Reduce((Axes.CH,), func="max", module=Filter.Reduce.FunctionSource.np)
In[13]: max_projector.run(image)
Out[13]: <starfish.ImageStack (r: 4, c: 1, z: 1, y: 1000, x: 1000)>
In[12]: from starfish.types import FunctionSource
In[13]: max_projector = Filter.Reduce((Axes.CH,), func="max", module=FunctionSource.np)
In[14]: max_projector.run(image)
Out[14]: <starfish.ImageStack (r: 4, c: 1, z: 1, y: 1000, x: 1000)>

Visualizing Data
----------------
Expand All @@ -124,10 +125,10 @@ to enable the :code:`qt` environment in IPython:

.. code-block:: python

In[14]: ipython = get_ipython()
In[15]: ipython.magic("gui qt5")
In[16]: starfish.display(image)
Out[16]: <napari.components._viewer.model.Viewer at 0x15f7b44e0>
In[15]: ipython = get_ipython()
In[16]: ipython.magic("gui qt5")
In[17]: starfish.display(image)
Out[17]: <napari.components._viewer.model.Viewer at 0x15f7b44e0>

Typing the above code should display an image viewer that looks something like this:,

Expand Down
3 changes: 2 additions & 1 deletion notebooks/BaristaSeq.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@
"outputs": [],
"source": [
"from starfish.image import Filter\n",
"max_projector = Filter.Reduce((Axes.ZPLANE,), func=\"max\", module=Filter.Reduce.FunctionSource.np)\n",
"from starfish.types import FunctionSource\n",
"max_projector = Filter.Reduce((Axes.ZPLANE,), func=\"max\", module=FunctionSource.np)\n",
"z_projected_image = max_projector.run(img)\n",
"z_projected_nissl = max_projector.run(nissl)\n",
"\n",
Expand Down
6 changes: 3 additions & 3 deletions notebooks/ISS.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"import pprint\n",
"\n",
"from starfish import data, FieldOfView\n",
"from starfish.types import Features, Axes\n",
"from starfish.types import Axes, Features, FunctionSource\n",
"from starfish.util.plot import imshow_plane"
]
},
Expand Down Expand Up @@ -138,9 +138,9 @@
"from starfish.image import Filter\n",
"\n",
"rcz_max_projector = Filter.Reduce(\n",
" (Axes.ROUND, Axes.CH, Axes.ZPLANE,), func=\"max\", module=Filter.Reduce.FunctionSource.np)\n",
" (Axes.ROUND, Axes.CH, Axes.ZPLANE,), func=\"max\", module=FunctionSource.np)\n",
"per_round_max_projector = Filter.Reduce(\n",
" (Axes.CH, Axes.ZPLANE,), func=\"max\", module=Filter.Reduce.FunctionSource.np)\n",
" (Axes.CH, Axes.ZPLANE,), func=\"max\", module=FunctionSource.np)\n",
"\n",
"dots = fov.get_image(\"dots\")\n",
"dots_single_plane = rcz_max_projector.run(dots)\n",
Expand Down
4 changes: 2 additions & 2 deletions notebooks/MERFISH.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"\n",
"from starfish import display\n",
"from starfish import data, FieldOfView\n",
"from starfish.types import Features, Axes\n",
"from starfish.types import Axes, Features, FunctionSource\n",
"\n",
"from starfish.util.plot import (\n",
" imshow_plane, intensity_histogram, overlay_spot_calls\n",
Expand Down Expand Up @@ -325,7 +325,7 @@
"import warnings\n",
"\n",
"all_max_projector = Filter.Reduce(\n",
" (Axes.ROUND, Axes.CH, Axes.ZPLANE,), func=\"max\", module=Filter.Reduce.FunctionSource.np)\n",
" (Axes.ROUND, Axes.CH, Axes.ZPLANE,), func=\"max\", module=FunctionSource.np)\n",
"\n",
"f, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 5))\n",
"\n",
Expand Down
3 changes: 2 additions & 1 deletion notebooks/py/BaristaSeq.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@

# EPY: START code
from starfish.image import Filter
max_projector = Filter.Reduce((Axes.ZPLANE,), func="max", module=Filter.Reduce.FunctionSource.np)
from starfish.types import FunctionSource
max_projector = Filter.Reduce((Axes.ZPLANE,), func="max", module=FunctionSource.np)
z_projected_image = max_projector.run(img)
z_projected_nissl = max_projector.run(nissl)

Expand Down
6 changes: 3 additions & 3 deletions notebooks/py/ISS.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import pprint

from starfish import data, FieldOfView
from starfish.types import Features, Axes
from starfish.types import Axes, Features, FunctionSource
from starfish.util.plot import imshow_plane
# EPY: END code

Expand Down Expand Up @@ -91,9 +91,9 @@
from starfish.image import Filter

rcz_max_projector = Filter.Reduce(
(Axes.ROUND, Axes.CH, Axes.ZPLANE,), func="max", module=Filter.Reduce.FunctionSource.np)
(Axes.ROUND, Axes.CH, Axes.ZPLANE,), func="max", module=FunctionSource.np)
per_round_max_projector = Filter.Reduce(
(Axes.CH, Axes.ZPLANE,), func="max", module=Filter.Reduce.FunctionSource.np)
(Axes.CH, Axes.ZPLANE,), func="max", module=FunctionSource.np)

dots = fov.get_image("dots")
dots_single_plane = rcz_max_projector.run(dots)
Expand Down
4 changes: 2 additions & 2 deletions notebooks/py/MERFISH.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@

from starfish import display
from starfish import data, FieldOfView
from starfish.types import Features, Axes
from starfish.types import Axes, Features, FunctionSource

from starfish.util.plot import (
imshow_plane, intensity_histogram, overlay_spot_calls
Expand Down Expand Up @@ -224,7 +224,7 @@
import warnings

all_max_projector = Filter.Reduce(
(Axes.ROUND, Axes.CH, Axes.ZPLANE,), func="max", module=Filter.Reduce.FunctionSource.np)
(Axes.ROUND, Axes.CH, Axes.ZPLANE,), func="max", module=FunctionSource.np)

f, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 5))

Expand Down
69 changes: 1 addition & 68 deletions starfish/core/image/Filter/map.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
import importlib
from enum import Enum
from typing import (
Callable,
cast,
Mapping,
Optional,
Set,
Union
)

from starfish.core.imagestack.imagestack import ImageStack
from starfish.core.types import Axes, Clip
from starfish.core.types import Axes, Clip, FunctionSource
from ._base import FilterAlgorithm


Expand Down Expand Up @@ -65,68 +60,6 @@ class Map(FilterAlgorithm):

"""

class FunctionSource(Enum):
"""Each FunctionSource declares a package from which reduction methods can be obtained.
Generally, the packages should be those that are included as starfish's dependencies for
reproducibility.

Many packages are broken into subpackages which are not necessarily implicitly imported when
importing the top-level package. For example, ``scipy.linalg`` is not implicitly imported
when one imports ``scipy``. To avoid the complexity of enumerating each scipy subpackage in
FunctionSource, we assemble the fully-qualified method name, and then try all the
permutations of how one could import that method.

In the example of ``scipy.linalg.norm``, we try the following:

1. import ``scipy``, attempt to resolve ``linalg.norm``.
2. import ``scipy.linalg``, attempt to resolve ``norm``.
"""

def __init__(self, top_level_package: str, aliases: Optional[Mapping[str, str]] = None):
self.top_level_package = top_level_package
self.aliases = aliases or {}

def _resolve_method(self, method: str) -> Callable:
"""Resolve a method. The method itself might be enclosed in a package, such as
subpackage.actual_method. In that case, we will need to attempt to resolve it in the
following sequence:

1. import top_level_package, then try to resolve subpackage.actual_method recursively
through ``getattr`` calls.
2. import top_level_package.subpackage, then try to resolve actual_method through
``gettatr`` calls.

This is done instead of just creating a bunch of FunctionSource for libraries that have
a lot of packages that are not implicitly imported by importing the top-level package.
"""
# first resolve the aliases.
actual_method = self.aliases.get(method, method)

method_splitted = actual_method.split(".")
splitted = [self.top_level_package]
splitted.extend(method_splitted)

for divider in range(1, len(splitted)):
import_section = splitted[:divider]
getattr_section = splitted[divider:]

imported = importlib.import_module(".".join(import_section))

try:
for getattr_name in getattr_section:
imported = getattr(imported, getattr_name)
return cast(Callable, imported)
except AttributeError:
pass

raise AttributeError(
f"Unable to resolve the method {actual_method} from package "
f"{self.top_level_package}")

np = ("numpy",)
"""Function source for the numpy libraries"""
scipy = ("scipy",)

def __init__(
self,
func: str,
Expand Down
72 changes: 3 additions & 69 deletions starfish/core/image/Filter/reduce.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import importlib
from enum import Enum
from typing import (
Callable,
cast,
Iterable,
Mapping,
MutableMapping,
Optional,
Sequence,
Expand All @@ -14,7 +10,7 @@
import numpy as np

from starfish.core.imagestack.imagestack import ImageStack
from starfish.core.types import Axes, Clip, Coordinates, Number
from starfish.core.types import Axes, Clip, Coordinates, FunctionSource, Number
from starfish.core.util.dtype import preserve_float_range
from ._base import FilterAlgorithm

Expand Down Expand Up @@ -66,12 +62,12 @@ class Reduce(FilterAlgorithm):
Reducing via linalg.norm
>>> from starfish.core.imagestack.test.factories.synthetic_stack import synthetic_stack
>>> from starfish.image import Filter
>>> from starfish.types import Axes
>>> from starfish.types import Axes, FunctionSource
>>> stack = synthetic_stack()
>>> reducer = Filter.Reduce(
{Axes.ROUND},
func="linalg.norm",
module=Filter.Reduce.FunctionSource.scipy,
module=FunctionSource.scipy,
ord=2,
)
>>> norm = reducer.run(stack)
Expand All @@ -82,68 +78,6 @@ class Reduce(FilterAlgorithm):

"""

class FunctionSource(Enum):
"""Each FunctionSource declares a package from which reduction methods can be obtained.
Generally, the packages should be those that are included as starfish's dependencies for
reproducibility.

Many packages are broken into subpackages which are not necessarily implicitly imported when
importing the top-level package. For example, ``scipy.linalg`` is not implicitly imported
when one imports ``scipy``. To avoid the complexity of enumerating each scipy subpackage in
FunctionSource, we assemble the fully-qualified method name, and then try all the
permutations of how one could import that method.

In the example of ``scipy.linalg.norm``, we try the following:

1. import ``scipy``, attempt to resolve ``linalg.norm``.
2. import ``scipy.linalg``, attempt to resolve ``norm``.
"""

def __init__(self, top_level_package: str, aliases: Optional[Mapping[str, str]] = None):
self.top_level_package = top_level_package
self.aliases = aliases or {}

def _resolve_method(self, method: str) -> Callable:
"""Resolve a method. The method itself might be enclosed in a package, such as
subpackage.actual_method. In that case, we will need to attempt to resolve it in the
following sequence:

1. import top_level_package, then try to resolve subpackage.actual_method recursively
through ``getattr`` calls.
2. import top_level_package.subpackage, then try to resolve actual_method through
``gettatr`` calls.

This is done instead of just creating a bunch of FunctionSource for libraries that have
a lot of packages that are not implicitly imported by importing the top-level package.
"""
# first resolve the aliases.
actual_method = self.aliases.get(method, method)

method_splitted = actual_method.split(".")
splitted = [self.top_level_package]
splitted.extend(method_splitted)

for divider in range(1, len(splitted)):
import_section = splitted[:divider]
getattr_section = splitted[divider:]

imported = importlib.import_module(".".join(import_section))

try:
for getattr_name in getattr_section:
imported = getattr(imported, getattr_name)
return cast(Callable, imported)
except AttributeError:
pass

raise AttributeError(
f"Unable to resolve the method {actual_method} from package "
f"{self.top_level_package}")

np = ("numpy", {'max': 'amax'})
"""Function source for the numpy libraries"""
scipy = ("scipy",)

def __init__(
self,
dims: Iterable[Union[Axes, str]],
Expand Down
4 changes: 2 additions & 2 deletions starfish/core/image/Filter/test/test_reduce.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from starfish.core.image.Filter.reduce import Reduce
from starfish.core.imagestack.test.factories import imagestack_with_coords_factory
from starfish.core.imagestack.test.imagestack_test_utils import verify_physical_coordinates
from starfish.types import Axes, PhysicalCoordinateTypes
from starfish.types import Axes, FunctionSource, PhysicalCoordinateTypes


X_COORDS = 1, 2
Expand Down Expand Up @@ -100,7 +100,7 @@ def make_expected_image_stack(func):
(
make_expected_image_stack('norm'),
'linalg.norm',
Reduce.FunctionSource.scipy,
FunctionSource.scipy,
{'ord': 2},
),
]
Expand Down
1 change: 1 addition & 0 deletions starfish/core/types/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
TransformType
)
from ._decoded_spots import DecodedSpots
from ._functionsource import FunctionSource
from ._spot_attributes import SpotAttributes
from ._spot_finding_results import SpotFindingResults

Expand Down
Loading