From a07f4c7919aa88d257d95bacd58e6d7c7da4be76 Mon Sep 17 00:00:00 2001 From: Akio Taniguchi Date: Fri, 10 Apr 2020 15:58:16 +0900 Subject: [PATCH 1/3] #17 Update Poetry config --- poetry.lock | 15 ++++++++++++++- pyproject.toml | 1 + 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/poetry.lock b/poetry.lock index 55ab2da..5ddeb9d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -243,6 +243,15 @@ delayed = ["cloudpickle (>=0.2.2)", "toolz (>=0.8.2)"] diagnostics = ["bokeh (>=1.0.0)"] distributed = ["distributed (>=2.0)"] +[[package]] +category = "main" +description = "A backport of the dataclasses module for Python 3.6" +marker = "python_version >= \"3.6\" and python_version < \"3.7\"" +name = "dataclasses" +optional = false +python-versions = ">=3.6, <3.7" +version = "0.7" + [[package]] category = "dev" description = "Decorators for Humans" @@ -1445,7 +1454,7 @@ docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] testing = ["jaraco.itertools", "func-timeout"] [metadata] -content-hash = "6f0bf41c9c01d6b0502bd02c8331219aefc84d4b93601371178721e58089e045" +content-hash = "9abcd6e98074e02245ae6dd9f31b384a6eee1aafefec7425a06fadf2608fdbe2" python-versions = "^3.6" [metadata.files] @@ -1551,6 +1560,10 @@ dask = [ {file = "dask-2.14.0-py3-none-any.whl", hash = "sha256:ddcfc2afa13a359aa707e5b3369127286543967a268061b50ee0cc891793602f"}, {file = "dask-2.14.0.tar.gz", hash = "sha256:3885d0d071fb49707c7311f9762c1863800a6f13fa7a1dc12ec7006d8404320c"}, ] +dataclasses = [ + {file = "dataclasses-0.7-py3-none-any.whl", hash = "sha256:3459118f7ede7c8bea0fe795bff7c6c2ce287d01dd226202f7c9ebc0610a7836"}, + {file = "dataclasses-0.7.tar.gz", hash = "sha256:494a6dcae3b8bcf80848eea2ef64c0cc5cd307ffc263e17cdf42f3e5420808e6"}, +] decorator = [ {file = "decorator-4.4.2-py2.py3-none-any.whl", hash = "sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760"}, {file = "decorator-4.4.2.tar.gz", hash = "sha256:e3a62f0520172440ca0dcc823749319382e377f37f140a0b99ef45fecb84bfe7"}, diff --git a/pyproject.toml b/pyproject.toml index e666137..719a249 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,6 +13,7 @@ keywords = ["astronomy", "radio-astronomy", "single-dish"] python = "^3.6" astropy = "^4.0" dask = {version = "^2.12", extras = ["complete"]} +dataclasses = { version = "^0.7", python = "~3.6" } matplotlib = "^3.2" netcdf4 = "^1.5" numpy = "^1.18" From 3842a38052ac9b983a18c18868379266f72faddd Mon Sep 17 00:00:00 2001 From: Akio Taniguchi Date: Fri, 17 Apr 2020 15:50:50 +0900 Subject: [PATCH 2/3] #17 Add creator module --- sdarray/creator.py | 191 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 191 insertions(+) create mode 100644 sdarray/creator.py diff --git a/sdarray/creator.py b/sdarray/creator.py new file mode 100644 index 0000000..842595e --- /dev/null +++ b/sdarray/creator.py @@ -0,0 +1,191 @@ +"""Array creator module. + +""" +__all__ = ["ArrayDef", "CoordDef", "array_creator"] + + +# standard library +from dataclasses import dataclass +from datetime import datetime +from textwrap import dedent, indent, wrap +from typing import Callable, Optional, Sequence, Union + + +# dependent packages +import numpy as np +import xarray as xr + + +# constants +INDENT = " " * 4 +ELEMENT_TYPES = bool, str, int, float, datetime + + +# type aliases +Element = Union[ELEMENT_TYPES] +Array = Union[np.ndarray, Element] + + +# data classes +@dataclass(frozen=True) +class ArrayDef: + """Definition of xarray's array.""" + + name: str #: Name of array. + dims: Sequence[str] #: Dimensions of array. + short_desc: str #: Short description. + long_desc: str = "" #: Long description (if any). + + @property + def docstring(self) -> str: + """String for function's docstring.""" + return ( + f"{self.name}: " + f"{self.short_desc} " + f"{self.long_desc} " + f"Dims: ``{self.dims}``." + ) + + @property + def docstring_wrapped(self) -> str: + """String for function's docstring (wrapped).""" + return f"\n{INDENT}".join(wrap(self.docstring)) + + +@dataclass(frozen=True) +class CoordDef: + """Definition of xarray's coordinate.""" + + name: str #: Name of coordinate. + dims: Sequence[str] #: Dimension(s) of coordinate. + type: str #: Type of value(s). + default: Element #: Default value(s). + short_desc: str #: Short description. + long_desc: str = "" #: Long description (if any). + + @property + def docstring(self) -> str: + """String for function's docstring.""" + return ( + f"{self.name}: " + f"{self.short_desc} " + f"{self.long_desc} " + f"Dims: ``{self.dims}``. " + f"Type: ``{self.type}``. " + f"Default: ``{self.default!r}``." + ) + + @property + def docstring_wrapped(self) -> str: + """String for function's docstring (wrapped).""" + return f"\n{INDENT}".join(wrap(self.docstring)) + + +# main functions +def array_creator(cls: type) -> type: + """Decorator which adds array creator functions (static methods) to class. + + Args: + cls: Class which has class attributes (``array_def``, ``coord_defs``). + + Returns: + cls: Class with creator functions (``array``, ``ones``, ``zeros``). + + Raises: + AttributeError: Raised if class does not have + either ``array_def`` or ``coord_defs``. + + """ + if not hasattr(cls, "array_def"): + raise AttributeError("Class must have array_def attribute.") + + if not hasattr(cls, "coord_defs"): + raise AttributeError("Class must have coord_defs attribute.") + + creator = get_creator(cls.array_def, cls.coord_defs) + + def ones( + shape: Sequence[int], dtype: Optional[str] = None, **coords: Array, + ) -> xr.DataArray: + """Create xarray's DataArray filled with ones from given shape and coordinates. + + Args: + shape: Shape of array. + dtype: Data type of array. Default: ``float64``. + coords: Coordinates. See creator function for more details. + + Returns: + array: DataArray filled with ones with given coordinates. + + """ + return creator(np.ones(shape, dtype), **coords) + + def zeros( + shape: Sequence[int], dtype: Optional[str] = None, **coords: Array + ) -> xr.DataArray: + """Create xarray's DataArray filled with zeros from given shape and coordinates. + + Args: + shape: Shape of array. + dtype: Data type of array. Default: ``float64``. + coords: Coordinates. See creator function for more details. + + Returns: + array: DataArray filled with zeros with given coordinates. + + """ + return creator(np.zeros(shape, dtype), **coords) + + cls.__call__ = staticmethod(creator) + cls.ones = staticmethod(ones) + cls.zeros = staticmethod(zeros) + + return cls + + +# helper functions +def get_creator(array_def: ArrayDef, coord_defs: Sequence[CoordDef]) -> Callable: + """Return creator function with given definitions of array and coordinates. + + Args: + array_def: Definition of array. + coord_defs: Sequence of coordinate definitions. + + Returns: + creator: Creator function. + + """ + + def creator(array: Array, **coords: Array) -> xr.DataArray: + """\ + Create xarray's DataArray from given array and coordinates. + + Args: + {array} + {coords} + + Returns: + array: DataArray with given coordinates. + + """ + array = xr.DataArray(array, dims=array_def.dims) + + for cd in coord_defs: + coord = coords.get(cd.name, None) + shape = [array.sizes[dim] for dim in cd.dims] + + if coord is None: + coord = np.full(shape, cd.default) + elif isinstance(coord, ELEMENT_TYPES): + coord = np.full(shape, coord) + + array.coords[cd.name] = cd.dims, np.asarray(coord, cd.type) + + return array + + # update docstring + doc_array = indent(array_def.docstring_wrapped, INDENT) + doc_coords = indent("\n".join(cd.docstring_wrapped for cd in coord_defs), INDENT) + creator.__doc__ = dedent(creator.__doc__).format(array=doc_array, coords=doc_coords) + + return creator From ae1d090316d7fc1c55a25dd5d48478a598f03415 Mon Sep 17 00:00:00 2001 From: Akio Taniguchi Date: Fri, 29 May 2020 23:55:31 +0900 Subject: [PATCH 3/3] #17 Update creator module --- sdarray/creator.py | 139 +++++++++++++++++++++++++++++++-------------- 1 file changed, 95 insertions(+), 44 deletions(-) diff --git a/sdarray/creator.py b/sdarray/creator.py index 842595e..7b571e4 100644 --- a/sdarray/creator.py +++ b/sdarray/creator.py @@ -8,12 +8,12 @@ from dataclasses import dataclass from datetime import datetime from textwrap import dedent, indent, wrap -from typing import Callable, Optional, Sequence, Union +from typing import Callable, Hashable, Sequence, Union # dependent packages import numpy as np -import xarray as xr +from xarray import DataArray # constants @@ -22,17 +22,20 @@ # type aliases +Array = Union[np.ndarray, Union[ELEMENT_TYPES]] +Dtype = Union[np.dtype, type, str] +Dims = Union[Hashable, Sequence[Hashable]] Element = Union[ELEMENT_TYPES] -Array = Union[np.ndarray, Element] +Shape = Sequence[int] # data classes @dataclass(frozen=True) class ArrayDef: - """Definition of xarray's array.""" + """Definition of array.""" name: str #: Name of array. - dims: Sequence[str] #: Dimensions of array. + dims: Dims #: Dimensions of array. short_desc: str #: Short description. long_desc: str = "" #: Long description (if any). @@ -54,10 +57,10 @@ def docstring_wrapped(self) -> str: @dataclass(frozen=True) class CoordDef: - """Definition of xarray's coordinate.""" + """Definition of coordinate.""" name: str #: Name of coordinate. - dims: Sequence[str] #: Dimension(s) of coordinate. + dims: Dims #: Dimension(s) of coordinate. type: str #: Type of value(s). default: Element #: Default value(s). short_desc: str #: Short description. @@ -104,41 +107,9 @@ def array_creator(cls: type) -> type: creator = get_creator(cls.array_def, cls.coord_defs) - def ones( - shape: Sequence[int], dtype: Optional[str] = None, **coords: Array, - ) -> xr.DataArray: - """Create xarray's DataArray filled with ones from given shape and coordinates. - - Args: - shape: Shape of array. - dtype: Data type of array. Default: ``float64``. - coords: Coordinates. See creator function for more details. - - Returns: - array: DataArray filled with ones with given coordinates. - - """ - return creator(np.ones(shape, dtype), **coords) - - def zeros( - shape: Sequence[int], dtype: Optional[str] = None, **coords: Array - ) -> xr.DataArray: - """Create xarray's DataArray filled with zeros from given shape and coordinates. - - Args: - shape: Shape of array. - dtype: Data type of array. Default: ``float64``. - coords: Coordinates. See creator function for more details. - - Returns: - array: DataArray filled with zeros with given coordinates. - - """ - return creator(np.zeros(shape, dtype), **coords) - cls.__call__ = staticmethod(creator) - cls.ones = staticmethod(ones) - cls.zeros = staticmethod(zeros) + cls.ones = staticmethod(get_ones(creator)) + cls.zeros = staticmethod(get_zeros(creator)) return cls @@ -156,9 +127,9 @@ def get_creator(array_def: ArrayDef, coord_defs: Sequence[CoordDef]) -> Callable """ - def creator(array: Array, **coords: Array) -> xr.DataArray: + def creator(array: Array, **coords: Array) -> DataArray: """\ - Create xarray's DataArray from given array and coordinates. + Create DataArray from given array and coordinates. Args: {array} @@ -168,7 +139,7 @@ def creator(array: Array, **coords: Array) -> xr.DataArray: array: DataArray with given coordinates. """ - array = xr.DataArray(array, dims=array_def.dims) + array = DataArray(array, dims=array_def.dims) for cd in coord_defs: coord = coords.get(cd.name, None) @@ -189,3 +160,83 @@ def creator(array: Array, **coords: Array) -> xr.DataArray: creator.__doc__ = dedent(creator.__doc__).format(array=doc_array, coords=doc_coords) return creator + + +def get_ones(creator: Callable) -> Callable: + """Return ones function based on given creator function.""" + + def ones(shape: Shape, dtype: Dtype = float, **coords: Array) -> DataArray: + """Create DataArray filled with ones from given shape and coordinates. + + Args: + shape: Shape of array. + dtype: Data type of array. Default: ``float64``. + coords: Coordinates. See creator function for more details. + + Returns: + array: DataArray filled with ones with given coordinates. + + """ + return creator(np.ones(shape, dtype), **coords) + + return ones + + +def get_zeros(creator: Callable) -> Callable: + """Return zeros function based on given creator function.""" + + def zeros(shape: Shape, dtype: Dtype = float, **coords: Array) -> DataArray: + """Create DataArray filled with zeros from given shape and coordinates. + + Args: + shape: Shape of array. + dtype: Data type of array. Default: ``float64``. + coords: Coordinates. See creator function for more details. + + Returns: + array: DataArray filled with zeros with given coordinates. + + """ + return creator(np.zeros(shape, dtype), **coords) + + return zeros + + +def get_empty(creator: Callable) -> Callable: + """Return empty function based on given creator function.""" + + def empty(shape: Shape, dtype: Dtype = float, **coords: Array) -> DataArray: + """Create uninitialized DataArray from given shape and coordinates. + + Args: + shape: Shape of array. + dtype: Data type of array. Default: ``float64``. + coords: Coordinates. See creator function for more details. + + Returns: + array: Uninitialized DataArray with given coordinates. + + """ + return creator(np.empty(shape, dtype), **coords) + + return empty + + +def get_full(creator: Callable) -> Callable: + """Return full function based on given creator function.""" + + def full(shape: Shape, fill_value: Element, **coords: Array,) -> DataArray: + """Create uninitialized DataArray from given shape and coordinates. + + Args: + shape: Shape of array. + dtype: Data type of array. Default: ``float64``. + coords: Coordinates. See creator function for more details. + + Returns: + array: Uninitialized DataArray with given coordinates. + + """ + return creator(np.full(shape, fill_value), **coords) + + return full