From 6b926c459f24c3ff1983d5083a889c96a6bd19d9 Mon Sep 17 00:00:00 2001 From: Davis Vann Bennett Date: Wed, 16 Jun 2021 17:47:43 -0400 Subject: [PATCH 1/6] refactor ahead of enabling recursive mode --- poetry.lock | 200 +++++++++++++++------------- src/xarray_multiscale/multiscale.py | 55 ++++---- tests/test_multiscale.py | 7 + 3 files changed, 141 insertions(+), 121 deletions(-) diff --git a/poetry.lock b/poetry.lock index 10cadfb..5ecb3f1 100644 --- a/poetry.lock +++ b/poetry.lock @@ -24,17 +24,17 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "attrs" -version = "20.3.0" +version = "21.2.0" description = "Classes Without Boilerplate" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.extras] -dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "furo", "sphinx", "pre-commit"] -docs = ["furo", "sphinx", "zope.interface"] -tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] -tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"] +docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"] [[package]] name = "black" @@ -60,11 +60,15 @@ d = ["aiohttp (>=3.3.2)", "aiohttp-cors"] [[package]] name = "click" -version = "7.1.2" +version = "8.0.1" description = "Composable command line interface toolkit" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.6" + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} [[package]] name = "cloudpickle" @@ -109,7 +113,7 @@ dev = ["pytest (>=5)", "pytest-cov", "coveralls", "black", "mypy", "pylint"] [[package]] name = "dask" -version = "2021.4.1" +version = "2021.6.0" description = "Parallel PyData with Task Scheduling" category = "main" optional = false @@ -124,15 +128,15 @@ toolz = ">=0.8.2" [package.extras] array = ["numpy (>=1.16)"] -complete = ["bokeh (>=1.0.0,!=2.0.0)", "distributed (>=2021.04.1)", "numpy (>=1.16)", "pandas (>=0.25.0)"] +complete = ["bokeh (>=1.0.0,!=2.0.0)", "distributed (==2021.06.0)", "numpy (>=1.16)", "pandas (>=0.25.0)"] dataframe = ["numpy (>=1.16)", "pandas (>=0.25.0)"] diagnostics = ["bokeh (>=1.0.0,!=2.0.0)"] -distributed = ["distributed (>=2021.04.1)"] +distributed = ["distributed (==2021.06.0)"] test = ["pytest", "pytest-rerunfailures", "pytest-xdist"] [[package]] name = "fasteners" -version = "0.16" +version = "0.16.3" description = "A python package that provides useful locks." category = "dev" optional = false @@ -143,7 +147,7 @@ six = "*" [[package]] name = "fsspec" -version = "2021.4.0" +version = "2021.6.0" description = "File-system specification" category = "main" optional = false @@ -168,7 +172,7 @@ ssh = ["paramiko"] [[package]] name = "importlib-metadata" -version = "4.0.1" +version = "4.5.0" description = "Read metadata from Python packages" category = "dev" optional = false @@ -192,7 +196,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "more-itertools" -version = "8.7.0" +version = "8.8.0" description = "More routines for operating on iterables, beyond itertools" category = "dev" optional = false @@ -224,7 +228,7 @@ python-versions = "*" [[package]] name = "numcodecs" -version = "0.7.3" +version = "0.8.0" description = "A Python package providing buffer compression and transformation codecs for use in data storage and communication applications." category = "dev" optional = false @@ -238,7 +242,7 @@ msgpack = ["msgpack"] [[package]] name = "numpy" -version = "1.20.2" +version = "1.20.3" description = "NumPy is the fundamental package for array computing with Python." category = "main" optional = false @@ -395,7 +399,7 @@ numpy = ">=1.16.5" [[package]] name = "six" -version = "1.15.0" +version = "1.16.0" description = "Python 2 and 3 compatibility utilities" category = "main" optional = false @@ -427,7 +431,7 @@ python-versions = "*" [[package]] name = "typing-extensions" -version = "3.7.4.3" +version = "3.10.0.0" description = "Backported and Experimental Type Hints for Python 3.5+" category = "main" optional = false @@ -463,7 +467,7 @@ viz = ["matplotlib", "seaborn", "nc-time-axis"] [[package]] name = "zarr" -version = "2.8.1" +version = "2.8.3" description = "An implementation of chunked, compressed, N-dimensional arrays for Python." category = "dev" optional = false @@ -508,15 +512,15 @@ atomicwrites = [ {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, ] attrs = [ - {file = "attrs-20.3.0-py2.py3-none-any.whl", hash = "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6"}, - {file = "attrs-20.3.0.tar.gz", hash = "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"}, + {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"}, + {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"}, ] black = [ {file = "black-20.8b1.tar.gz", hash = "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"}, ] click = [ - {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, - {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, + {file = "click-8.0.1-py3-none-any.whl", hash = "sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6"}, + {file = "click-8.0.1.tar.gz", hash = "sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a"}, ] cloudpickle = [ {file = "cloudpickle-1.6.0-py3-none-any.whl", hash = "sha256:3a32d0eb0bc6f4d0c57fbc4f3e3780f7a81e6fee0fa935072884d58ae8e1cc7c"}, @@ -534,28 +538,28 @@ dacite = [ {file = "dacite-1.6.0.tar.gz", hash = "sha256:d48125ed0a0352d3de9f493bf980038088f45f3f9d7498f090b50a847daaa6df"}, ] dask = [ - {file = "dask-2021.4.1-py3-none-any.whl", hash = "sha256:344c342d699466c3f742019b7a33caf2472b751f38370b200ede7d2f354aa1e4"}, - {file = "dask-2021.4.1.tar.gz", hash = "sha256:195e4eeb154222ea7a1c368119b5f321ee4ec9d78531471fe0145a527f744aa8"}, + {file = "dask-2021.6.0-py3-none-any.whl", hash = "sha256:ac4ec1e622bc220a057ad424ec5303f8ad96a70d672ccdb4ad60bf56d00fa1b7"}, + {file = "dask-2021.6.0.tar.gz", hash = "sha256:234f62f8e9e5fd60cd669de16ad29b2c7bdad76c3a2ff0ac1eb854e80106f5af"}, ] fasteners = [ - {file = "fasteners-0.16-py2.py3-none-any.whl", hash = "sha256:74b6847e0a6bb3b56c8511af8e24c40e4cf7a774dfff5b251c260ed338096a4b"}, - {file = "fasteners-0.16.tar.gz", hash = "sha256:c995d8c26b017c5d6a6de9ad29a0f9cdd57de61ae1113d28fac26622b06a0933"}, + {file = "fasteners-0.16.3-py2.py3-none-any.whl", hash = "sha256:8408e52656455977053871990bd25824d85803b9417aa348f10ba29ef0c751f7"}, + {file = "fasteners-0.16.3.tar.gz", hash = "sha256:b1ab4e5adfbc28681ce44b3024421c4f567e705cc3963c732bf1cba3348307de"}, ] fsspec = [ - {file = "fsspec-2021.4.0-py3-none-any.whl", hash = "sha256:70dae1d8d51072c4a1196acb9ba1bf8f5b9cdd83c4bb67e8a31dac604a49594b"}, - {file = "fsspec-2021.4.0.tar.gz", hash = "sha256:8b1a69884855d1a8c038574292e8b861894c3373282d9a469697a2b41d5289a6"}, + {file = "fsspec-2021.6.0-py3-none-any.whl", hash = "sha256:e926b66c074ff3fc732d7678187a294e4a0bf112ccb51594fa9dff5a9fbdc4a1"}, + {file = "fsspec-2021.6.0.tar.gz", hash = "sha256:931961514c67acab86519ca16985491e639ebf992dbc3a1aae24d44202b52426"}, ] importlib-metadata = [ - {file = "importlib_metadata-4.0.1-py3-none-any.whl", hash = "sha256:d7eb1dea6d6a6086f8be21784cc9e3bcfa55872b52309bc5fad53a8ea444465d"}, - {file = "importlib_metadata-4.0.1.tar.gz", hash = "sha256:8c501196e49fb9df5df43833bdb1e4328f64847763ec8a50703148b73784d581"}, + {file = "importlib_metadata-4.5.0-py3-none-any.whl", hash = "sha256:833b26fb89d5de469b24a390e9df088d4e52e4ba33b01dc5e0e4f41b81a16c00"}, + {file = "importlib_metadata-4.5.0.tar.gz", hash = "sha256:b142cc1dd1342f31ff04bb7d022492b09920cb64fed867cd3ea6f80fe3ebd139"}, ] locket = [ {file = "locket-0.2.1-py2.py3-none-any.whl", hash = "sha256:12b6ada59d1f50710bca9704dbadd3f447dbf8dac6664575c1281cadab8e6449"}, {file = "locket-0.2.1.tar.gz", hash = "sha256:3e1faba403619fe201552f083f1ecbf23f550941bc51985ac6ed4d02d25056dd"}, ] more-itertools = [ - {file = "more-itertools-8.7.0.tar.gz", hash = "sha256:c5d6da9ca3ff65220c3bfd2a8db06d698f05d4d2b9be57e1deb2be5a45019713"}, - {file = "more_itertools-8.7.0-py3-none-any.whl", hash = "sha256:5652a9ac72209ed7df8d9c15daf4e1aa0e3d2ccd3c87f8265a0673cd9cbc9ced"}, + {file = "more-itertools-8.8.0.tar.gz", hash = "sha256:83f0308e05477c68f56ea3a888172c78ed5d5b3c282addb67508e7ba6c8f813a"}, + {file = "more_itertools-8.8.0-py3-none-any.whl", hash = "sha256:2cf89ec599962f2ddc4d568a05defc40e0a587fbc10d5989713638864c36be4d"}, ] mypy = [ {file = "mypy-0.790-cp35-cp35m-macosx_10_6_x86_64.whl", hash = "sha256:bd03b3cf666bff8d710d633d1c56ab7facbdc204d567715cb3b9f85c6e94f669"}, @@ -578,61 +582,61 @@ mypy-extensions = [ {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, ] numcodecs = [ - {file = "numcodecs-0.7.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:211dce8ac804280bf296b1eb70e991c43d6ab1ce4ce96d7f64844262032a3e62"}, - {file = "numcodecs-0.7.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:d4d6bd3aedaaf1b7d6b594977e07315d0dab4b4ffb9ac8ef58a370931833149e"}, - {file = "numcodecs-0.7.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:aefc8821a93fcc777c17643038793b240273a3afcf284691fbdc32640ad87b02"}, - {file = "numcodecs-0.7.3-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:b997e29ddd19c7b49e91a174105397985183c67afdaf05c82008f9ad0388ef2f"}, - {file = "numcodecs-0.7.3-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:7e9faba532f12ebdb1dc658e707deb4c66137c638b07d8161031373ec05ca398"}, - {file = "numcodecs-0.7.3-cp36-cp36m-win32.whl", hash = "sha256:1f9a22486eeaca99c711f810f08b6af7434845f0d823194d2c10beabe45178d6"}, - {file = "numcodecs-0.7.3-cp36-cp36m-win_amd64.whl", hash = "sha256:e1873ef81099970ad74d73820314f7bf5b3d217c84f3d4ae3147d50c258e3710"}, - {file = "numcodecs-0.7.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:97f879876249979ad2e501190f2c24088acfd21a0e2a2e4aa86cb0595ba98a81"}, - {file = "numcodecs-0.7.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ca37cfd2791b4ea5d975fac28967f4f3bf270afe3f8e3a0176673d69335a7fe9"}, - {file = "numcodecs-0.7.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5754e914d4455658600577658f0a27dba7f78ae338b19fd71d80bc5e42a738dc"}, - {file = "numcodecs-0.7.3-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:47c9e7c62c90f39d8a307acba39d85b92945fc0b9ec5f22a926d008793f575cd"}, - {file = "numcodecs-0.7.3-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6336fb0606f724cee96660048f98411ecda7844aac68c61c2869b7e9d9b3719e"}, - {file = "numcodecs-0.7.3-cp37-cp37m-win32.whl", hash = "sha256:1e1f4ba0cc4e4be41454d3574d85e732bca1f9dadf7bd3ced7a7051b9aaaa51a"}, - {file = "numcodecs-0.7.3-cp37-cp37m-win_amd64.whl", hash = "sha256:37d7a58555b941e4febdf722ba69c108fbf3795ff1a1c48dc3bfd40de47225de"}, - {file = "numcodecs-0.7.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b93b56a5913365ab6e149384bdb061e526bebe12147cbeb9497b21467f0967a6"}, - {file = "numcodecs-0.7.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:9e3ca62ad65e22eb4e1c83152e9a0eea48b41ac378591a38970963de0670dd78"}, - {file = "numcodecs-0.7.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6421c4369a58ac448a8077b8f9ba3b5cbec2e27fad3a330fa5718b472d79040b"}, - {file = "numcodecs-0.7.3-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:9062964ccdec50f950bd30e92d3e9a85b84748411d4c30fac701412d1eb8c2d3"}, - {file = "numcodecs-0.7.3-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:6c57795c96874bbb479834e87a62c10c0fcd64957acdbde72e8c0a8a39fb6d39"}, - {file = "numcodecs-0.7.3-cp38-cp38-win32.whl", hash = "sha256:f2d7c8395673f57ec8edc95eb9820604f34a90d6e5d29a1da7e86c8b00df5726"}, - {file = "numcodecs-0.7.3-cp38-cp38-win_amd64.whl", hash = "sha256:5eec04dd2d59725ef61c139fe3ec96085374b8f59a9c8de9dcf4e8cbde6bd179"}, - {file = "numcodecs-0.7.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e335a33f269e0817eacabd710a9ea2de2d7da3bb9d89f049b5d1d594b7a21216"}, - {file = "numcodecs-0.7.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:bd6152f95fedda73574045ce36d98002992b7df2bbc5b2775460d517c892b2f1"}, - {file = "numcodecs-0.7.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:6a54e3335ac08d06288506ca4f0ff56608b5df76288cb317a42efe1ac646e834"}, - {file = "numcodecs-0.7.3-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:48b33118374e6f749abbb940bdef711169f9e9055c5bb532cc322de68767e01d"}, - {file = "numcodecs-0.7.3-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ccb2d808f908db05f192287f8de84519e8640e47ad8cd16f74fb3768bb085994"}, - {file = "numcodecs-0.7.3-cp39-cp39-win32.whl", hash = "sha256:89c56add9b1c3dba3b5ce4dc1bfc02629916b720e68b0d6d43bedfef90326fa6"}, - {file = "numcodecs-0.7.3-cp39-cp39-win_amd64.whl", hash = "sha256:6d0f341a6810f9428221164a58b01b08bd5e847aba38b3a431d6b6f44696808c"}, - {file = "numcodecs-0.7.3.tar.gz", hash = "sha256:022b12ad83eb623ec53f154859d49f6ec43b15c36052fa864eaf2d9ee786dd85"}, + {file = "numcodecs-0.8.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:7a88075fa31b353dea5530bf7d0a358aca93f57aecb62edca13ea142532dfcd4"}, + {file = "numcodecs-0.8.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:d41312116974845e21c942241520d951b88f3b53882695dd16650dc3ed9bd82b"}, + {file = "numcodecs-0.8.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:e602cab14c9e4e0bf1563b0f44f115a64fddfb0b6b52fc83de1746d7cdfd69ff"}, + {file = "numcodecs-0.8.0-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:019861b72c5afab4732d96ba6b53d1b56ed14eb8d810bca4909e71b5c58ddece"}, + {file = "numcodecs-0.8.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:ac07c7c5dd7a4ed4fbee5cb79b0acbdc6474c3b3280c7cdb97dc1274ca438feb"}, + {file = "numcodecs-0.8.0-cp36-cp36m-win32.whl", hash = "sha256:972955f1d6d650e7e4efd29fbe7697050e56b3f04fb2f58de13faec5bc19365f"}, + {file = "numcodecs-0.8.0-cp36-cp36m-win_amd64.whl", hash = "sha256:313c07960eade9169454ba1dae55973c2131ada45c3eaf1c572d4677e3804f14"}, + {file = "numcodecs-0.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8ceb27f735cb16e2f0516bcb92c52aaa14ed2a54e11f994b0232596d19ad41b8"}, + {file = "numcodecs-0.8.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:b6c0132bcf5e232f9b70c2dc8c6b6e0248c380fda46d0979252b94168b31a3a6"}, + {file = "numcodecs-0.8.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:88b5faf32db97f7295e71a87d734cb0bf1cc441ab63b73cb739f0c096a765d5d"}, + {file = "numcodecs-0.8.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:a5d881290ec51da96e0a903e0669bad09b7d9ac8be05810e86770fb5b742a53b"}, + {file = "numcodecs-0.8.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:a55883a6349f827fc87fab87b7ad0b27752801a6fb5a6c0b518d1c5e59ba3c54"}, + {file = "numcodecs-0.8.0-cp37-cp37m-win32.whl", hash = "sha256:c16fc74473cfff5a3a838884b2318216afaeeb61360765c76082c421d0d4587f"}, + {file = "numcodecs-0.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3154e4b85ed20b4741fb75a5d58aba7b2c5f8a5b7096c74d106201bdb3545e7d"}, + {file = "numcodecs-0.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:877bc1b105022481fe660371d1fff22e78c4950d67551a610527f1b20011910c"}, + {file = "numcodecs-0.8.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:d3b9aa1a7ccc09a687b0bd4ff2dae55067c4787001db539987ba4052dc3cd1d8"}, + {file = "numcodecs-0.8.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:9549d59986df8f43c40b01a6c671f3fca4a3bf28c3fa7158ef9b6bf904a40f15"}, + {file = "numcodecs-0.8.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:676bfe0f5ff7f9bd66ea2188660584cee2d04e285033dce599cb9538a51e3b88"}, + {file = "numcodecs-0.8.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d6d171f5d924b27a783d66ddaeb5ab7b1d15365705820745d48df43fbe3b4c3d"}, + {file = "numcodecs-0.8.0-cp38-cp38-win32.whl", hash = "sha256:568589d985c2137a4825ddcd1b286d383b4bc6b6fac846e3f09569aaf61ba5ac"}, + {file = "numcodecs-0.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:ea760153a0304748394500b189e6c072cbfc54c809322c4ff9fa85b300790094"}, + {file = "numcodecs-0.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:71536798011177b9d8a00dec721cecb9110cd20ebe98162f14a395c2a2b45c89"}, + {file = "numcodecs-0.8.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:1f01309661b605a285b64d1cb98ff4acf4b45f76559bb34b5b3bbfe921eef71d"}, + {file = "numcodecs-0.8.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f3e177d9d35a4615fe09891548923fd22eabdd716ada28eb2e19075a1793d831"}, + {file = "numcodecs-0.8.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:e0fb5a4c1144840f86d2930baac4b7678c41d2d46f8dc71f99aff77772a445fa"}, + {file = "numcodecs-0.8.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:562755b5db20588049c7ff60eeb8fbfb1686a3639fe0a3cb55a6c49389ed5b94"}, + {file = "numcodecs-0.8.0-cp39-cp39-win32.whl", hash = "sha256:c9fce99dc72a1b081501f89a7a0c393cde362bd562f5204bac818800e337392b"}, + {file = "numcodecs-0.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:d44b2882c6861d1dbcf5dd4c7c943848929cf48dda8944fc1ef3ebebe993efa3"}, + {file = "numcodecs-0.8.0.tar.gz", hash = "sha256:7c7d0ea56b5e2a267ae785bdce47abed62829ef000f03be8e32e30df62d3749c"}, ] numpy = [ - {file = "numpy-1.20.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e9459f40244bb02b2f14f6af0cd0732791d72232bbb0dc4bab57ef88e75f6935"}, - {file = "numpy-1.20.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:a8e6859913ec8eeef3dbe9aed3bf475347642d1cdd6217c30f28dee8903528e6"}, - {file = "numpy-1.20.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:9cab23439eb1ebfed1aaec9cd42b7dc50fc96d5cd3147da348d9161f0501ada5"}, - {file = "numpy-1.20.2-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:9c0fab855ae790ca74b27e55240fe4f2a36a364a3f1ebcfd1fb5ac4088f1cec3"}, - {file = "numpy-1.20.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:61d5b4cf73622e4d0c6b83408a16631b670fc045afd6540679aa35591a17fe6d"}, - {file = "numpy-1.20.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:d15007f857d6995db15195217afdbddfcd203dfaa0ba6878a2f580eaf810ecd6"}, - {file = "numpy-1.20.2-cp37-cp37m-win32.whl", hash = "sha256:d76061ae5cab49b83a8cf3feacefc2053fac672728802ac137dd8c4123397677"}, - {file = "numpy-1.20.2-cp37-cp37m-win_amd64.whl", hash = "sha256:bad70051de2c50b1a6259a6df1daaafe8c480ca98132da98976d8591c412e737"}, - {file = "numpy-1.20.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:719656636c48be22c23641859ff2419b27b6bdf844b36a2447cb39caceb00935"}, - {file = "numpy-1.20.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:aa046527c04688af680217fffac61eec2350ef3f3d7320c07fd33f5c6e7b4d5f"}, - {file = "numpy-1.20.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:2428b109306075d89d21135bdd6b785f132a1f5a3260c371cee1fae427e12727"}, - {file = "numpy-1.20.2-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:e8e4fbbb7e7634f263c5b0150a629342cc19b47c5eba8d1cd4363ab3455ab576"}, - {file = "numpy-1.20.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:edb1f041a9146dcf02cd7df7187db46ab524b9af2515f392f337c7cbbf5b52cd"}, - {file = "numpy-1.20.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:c73a7975d77f15f7f68dacfb2bca3d3f479f158313642e8ea9058eea06637931"}, - {file = "numpy-1.20.2-cp38-cp38-win32.whl", hash = "sha256:6c915ee7dba1071554e70a3664a839fbc033e1d6528199d4621eeaaa5487ccd2"}, - {file = "numpy-1.20.2-cp38-cp38-win_amd64.whl", hash = "sha256:471c0571d0895c68da309dacee4e95a0811d0a9f9f532a48dc1bea5f3b7ad2b7"}, - {file = "numpy-1.20.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4703b9e937df83f5b6b7447ca5912b5f5f297aba45f91dbbbc63ff9278c7aa98"}, - {file = "numpy-1.20.2-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:abc81829c4039e7e4c30f7897938fa5d4916a09c2c7eb9b244b7a35ddc9656f4"}, - {file = "numpy-1.20.2-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:377751954da04d4a6950191b20539066b4e19e3b559d4695399c5e8e3e683bf6"}, - {file = "numpy-1.20.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:6e51e417d9ae2e7848314994e6fc3832c9d426abce9328cf7571eefceb43e6c9"}, - {file = "numpy-1.20.2-cp39-cp39-win32.whl", hash = "sha256:780ae5284cb770ade51d4b4a7dce4faa554eb1d88a56d0e8b9f35fca9b0270ff"}, - {file = "numpy-1.20.2-cp39-cp39-win_amd64.whl", hash = "sha256:924dc3f83de20437de95a73516f36e09918e9c9c18d5eac520062c49191025fb"}, - {file = "numpy-1.20.2-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:97ce8b8ace7d3b9288d88177e66ee75480fb79b9cf745e91ecfe65d91a856042"}, - {file = "numpy-1.20.2.zip", hash = "sha256:878922bf5ad7550aa044aa9301d417e2d3ae50f0f577de92051d739ac6096cee"}, + {file = "numpy-1.20.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:70eb5808127284c4e5c9e836208e09d685a7978b6a216db85960b1a112eeace8"}, + {file = "numpy-1.20.3-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6ca2b85a5997dabc38301a22ee43c82adcb53ff660b89ee88dded6b33687e1d8"}, + {file = "numpy-1.20.3-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c5bf0e132acf7557fc9bb8ded8b53bbbbea8892f3c9a1738205878ca9434206a"}, + {file = "numpy-1.20.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db250fd3e90117e0312b611574cd1b3f78bec046783195075cbd7ba9c3d73f16"}, + {file = "numpy-1.20.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:637d827248f447e63585ca3f4a7d2dfaa882e094df6cfa177cc9cf9cd6cdf6d2"}, + {file = "numpy-1.20.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:8b7bb4b9280da3b2856cb1fc425932f46fba609819ee1c62256f61799e6a51d2"}, + {file = "numpy-1.20.3-cp37-cp37m-win32.whl", hash = "sha256:67d44acb72c31a97a3d5d33d103ab06d8ac20770e1c5ad81bdb3f0c086a56cf6"}, + {file = "numpy-1.20.3-cp37-cp37m-win_amd64.whl", hash = "sha256:43909c8bb289c382170e0282158a38cf306a8ad2ff6dfadc447e90f9961bef43"}, + {file = "numpy-1.20.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f1452578d0516283c87608a5a5548b0cdde15b99650efdfd85182102ef7a7c17"}, + {file = "numpy-1.20.3-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6e51534e78d14b4a009a062641f465cfaba4fdcb046c3ac0b1f61dd97c861b1b"}, + {file = "numpy-1.20.3-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e515c9a93aebe27166ec9593411c58494fa98e5fcc219e47260d9ab8a1cc7f9f"}, + {file = "numpy-1.20.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1c09247ccea742525bdb5f4b5ceeacb34f95731647fe55774aa36557dbb5fa4"}, + {file = "numpy-1.20.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:66fbc6fed94a13b9801fb70b96ff30605ab0a123e775a5e7a26938b717c5d71a"}, + {file = "numpy-1.20.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:ea9cff01e75a956dbee133fa8e5b68f2f92175233de2f88de3a682dd94deda65"}, + {file = "numpy-1.20.3-cp38-cp38-win32.whl", hash = "sha256:f39a995e47cb8649673cfa0579fbdd1cdd33ea497d1728a6cb194d6252268e48"}, + {file = "numpy-1.20.3-cp38-cp38-win_amd64.whl", hash = "sha256:1676b0a292dd3c99e49305a16d7a9f42a4ab60ec522eac0d3dd20cdf362ac010"}, + {file = "numpy-1.20.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:830b044f4e64a76ba71448fce6e604c0fc47a0e54d8f6467be23749ac2cbd2fb"}, + {file = "numpy-1.20.3-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:55b745fca0a5ab738647d0e4db099bd0a23279c32b31a783ad2ccea729e632df"}, + {file = "numpy-1.20.3-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5d050e1e4bc9ddb8656d7b4f414557720ddcca23a5b88dd7cff65e847864c400"}, + {file = "numpy-1.20.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9c65473ebc342715cb2d7926ff1e202c26376c0dcaaee85a1fd4b8d8c1d3b2f"}, + {file = "numpy-1.20.3-cp39-cp39-win32.whl", hash = "sha256:16f221035e8bd19b9dc9a57159e38d2dd060b48e93e1d843c49cb370b0f415fd"}, + {file = "numpy-1.20.3-cp39-cp39-win_amd64.whl", hash = "sha256:6690080810f77485667bfbff4f69d717c3be25e5b11bb2073e76bb3f578d99b4"}, + {file = "numpy-1.20.3-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e465afc3b96dbc80cf4a5273e5e2b1e3451286361b4af70ce1adb2984d392f9"}, + {file = "numpy-1.20.3.zip", hash = "sha256:e55185e51b18d788e49fe8305fd73ef4470596b33fc2c1ceb304566b99c71a69"}, ] packaging = [ {file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"}, @@ -695,18 +699,26 @@ pyyaml = [ {file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"}, {file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"}, {file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347"}, + {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541"}, {file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"}, {file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"}, {file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"}, {file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa"}, + {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0"}, {file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"}, {file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"}, {file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"}, {file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247"}, + {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc"}, {file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"}, {file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"}, {file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"}, {file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122"}, + {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6"}, {file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"}, {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"}, {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"}, @@ -776,8 +788,8 @@ scipy = [ {file = "scipy-1.6.1.tar.gz", hash = "sha256:c4fceb864890b6168e79b0e714c585dbe2fd4222768ee90bc1aa0f8218691b11"}, ] six = [ - {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, - {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] toml = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, @@ -820,9 +832,9 @@ typed-ast = [ {file = "typed_ast-1.4.3.tar.gz", hash = "sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65"}, ] typing-extensions = [ - {file = "typing_extensions-3.7.4.3-py2-none-any.whl", hash = "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"}, - {file = "typing_extensions-3.7.4.3-py3-none-any.whl", hash = "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918"}, - {file = "typing_extensions-3.7.4.3.tar.gz", hash = "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c"}, + {file = "typing_extensions-3.10.0.0-py2-none-any.whl", hash = "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497"}, + {file = "typing_extensions-3.10.0.0-py3-none-any.whl", hash = "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"}, + {file = "typing_extensions-3.10.0.0.tar.gz", hash = "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342"}, ] wcwidth = [ {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, @@ -833,8 +845,8 @@ xarray = [ {file = "xarray-0.17.0.tar.gz", hash = "sha256:9c2edad2a4e588f9117c666a4249920b9717fb75703b96998cf65fcd4f60551f"}, ] zarr = [ - {file = "zarr-2.8.1-py3-none-any.whl", hash = "sha256:9e5e3ce14d6b10ddba8de2dad6f636210e719a6ca76569c86eef39702bdaabfb"}, - {file = "zarr-2.8.1.tar.gz", hash = "sha256:138e5f64bbaf7aece6da1f229b611a7a04742e15358dde49c6e9bae8404a3bf2"}, + {file = "zarr-2.8.3-py3-none-any.whl", hash = "sha256:9357fb40ce0bffdf81b9aac912c83829618ea367b7898f5a150eecde4cfa1338"}, + {file = "zarr-2.8.3.tar.gz", hash = "sha256:8aece33269ba3ee2af9320aa528d5fe93f76c30e4ad7fdbfb604b1db3f0d779f"}, ] zipp = [ {file = "zipp-3.4.1-py3-none-any.whl", hash = "sha256:51cb66cc54621609dd593d1787f286ee42a5c0adbb4b29abea5a63edc3e03098"}, diff --git a/src/xarray_multiscale/multiscale.py b/src/xarray_multiscale/multiscale.py index 2c3c05e..fa9560b 100644 --- a/src/xarray_multiscale/multiscale.py +++ b/src/xarray_multiscale/multiscale.py @@ -1,5 +1,6 @@ import numpy as np import dask.array as da +import xarray from xarray import DataArray from typing import Any, List, Optional, Tuple, Union, Sequence, Callable, Dict from scipy.interpolate import interp1d @@ -7,6 +8,7 @@ from dask.array import coarsen + def multiscale( array: Any, reduction: Callable[[Any], Any], @@ -14,6 +16,7 @@ def multiscale( pad_mode: Optional[str] = None, preserve_dtype: bool = True, chunks: Optional[Union[Sequence[int], Dict[str, int]]] = None, + recursive: bool = False, ) -> List[DataArray]: """ Lazily generate a multiscale representation of an array @@ -33,6 +36,8 @@ def multiscale( chunks: Sequence or Dict of ints, defaults to None. If `chunks` is supplied, all DataArrays are rechunked with these chunks before being returned. + recursive: boolean, defaults to False. ToDo + Returns a list of DataArrays, one per level of downscaling. These DataArrays have `coords` properties that track the changing offset (if any) induced by the downsampling operation. Additionally, the scale factors are stored each DataArray's attrs propery under the key `scale_factors` ------- @@ -55,40 +60,19 @@ def multiscale( levels = range( 0, 1 + get_downscale_depth(padded_shape, scale_factors, pad=needs_padding) ) - scales: Tuple[Tuple[int]] = tuple( + scales = tuple( tuple(s ** l for s in scale_factors) for l in levels ) result = [_ingest_array(array, scales[0])] - data = result[0].data base_attrs = result[0].attrs base_coords = result[0].coords for scale in scales[1:]: downscaled = downscale( - data, reduction, scale, pad_mode=pad_mode, preserve_dtype=preserve_dtype + result[0], reduction, scale, pad_mode=pad_mode, preserve_dtype=preserve_dtype ) + result.append(downscaled) - # hideous - new_coords = tuple( - DataArray( - (offset * (base_coords[bc][1] - base_coords[bc][0])) - + (base_coords[bc][:s] * sc), - name=base_coords[bc].name, - attrs=base_coords[bc].attrs, - ) - for s, bc, offset, sc in zip( - downscaled.shape, base_coords, get_downsampled_offset(scale), scale - ) - ) - - result.append( - DataArray( - data=downscaled, - coords=new_coords, - attrs=base_attrs, - name=result[0].name, - ) - ) if chunks is not None: if isinstance(chunks, Sequence): _chunks = {k: v for k, v in zip(result[0].dims, chunks)} @@ -209,13 +193,12 @@ def prepad( extended_coords, dims=k, attrs=old_coord.attrs ) result = DataArray( - result, coords=new_coords, dims=array.dims, attrs=array.attrs - ) + result, coords=new_coords, dims=array.dims, attrs=array.attrs) return result def downscale( - array: Union[np.array, da.array], + array: Union[np.array, da.array, xarray.DataArray], reduction: Callable, scale_factors: Sequence[int], pad_mode: Optional[str] = None, @@ -241,6 +224,7 @@ def downscale( Returns the downscaled version of the input as a dask array. ------- """ + trim_excess = False if pad_mode == None: trim_excess = True @@ -258,6 +242,23 @@ def downscale( if preserve_dtype: coarsened = coarsened.astype(array.dtype) + if isinstance(array, xarray.DataArray): + base_coords = array.coords + new_coords = base_coords + if len(base_coords) > 0: + new_coords = tuple( + DataArray( + (offset * abs(base_coords[bc][1] - base_coords[bc][0])) + + (base_coords[bc][:s] * sc), + name=base_coords[bc].name, + attrs=base_coords[bc].attrs, + ) + for s, bc, offset, sc in zip( + coarsened.shape, base_coords, get_downsampled_offset(scale_factors), scale_factors + ) + ) + coarsened = DataArray(coarsened, dims=array.dims, coords=new_coords, attrs=array.attrs, name=array.name) + return coarsened diff --git a/tests/test_multiscale.py b/tests/test_multiscale.py index a24cc3e..ce47226 100644 --- a/tests/test_multiscale.py +++ b/tests/test_multiscale.py @@ -51,17 +51,24 @@ def test_downscale_2d(): [[1, 0, 1, 0], [0, 1, 0, 1], [1, 0, 1, 0], [0, 1, 0, 1]], dtype="uint8" ) arr_dask = da.from_array(arr_numpy, chunks=chunks) + arr_xarray = DataArray(arr_dask) downscaled_numpy_float = downscale( arr_numpy, np.mean, scale, preserve_dtype=False ).compute() + downscaled_dask_float = downscale( arr_dask, np.mean, scale, preserve_dtype=False ).compute() + downscaled_xarray_float = downscale( + arr_xarray, np.mean, scale, preserve_dtype=False + ).compute() + answer_float = np.array([[0.5, 0.5, 0.5, 0.5], [0.5, 0.5, 0.5, 0.5]]) assert np.array_equal(downscaled_numpy_float, answer_float) assert np.array_equal(downscaled_dask_float, answer_float) + assert np.array_equal(downscaled_xarray_float, answer_float) downscaled_numpy_int = downscale( arr_numpy, np.mean, scale, dtype=arr_numpy.dtype From 63ddaa727d702c41daa48c6cf3f51332b855a0dd Mon Sep 17 00:00:00 2001 From: Davis Vann Bennett Date: Wed, 16 Jun 2021 19:25:31 -0400 Subject: [PATCH 2/6] add recursive mode --- src/xarray_multiscale/multiscale.py | 20 +++++++++++--------- tests/test_multiscale.py | 26 ++++++++------------------ 2 files changed, 19 insertions(+), 27 deletions(-) diff --git a/src/xarray_multiscale/multiscale.py b/src/xarray_multiscale/multiscale.py index fa9560b..2ccddda 100644 --- a/src/xarray_multiscale/multiscale.py +++ b/src/xarray_multiscale/multiscale.py @@ -64,21 +64,26 @@ def multiscale( tuple(s ** l for s in scale_factors) for l in levels ) result = [_ingest_array(array, scales[0])] - base_attrs = result[0].attrs - base_coords = result[0].coords - for scale in scales[1:]: - downscaled = downscale( - result[0], reduction, scale, pad_mode=pad_mode, preserve_dtype=preserve_dtype - ) + for level in levels[1:]: + if recursive: + scale = scale_factors + downscaled = downscale(result[-1], reduction, scale, pad_mode=pad_mode) + else: + scale = scales[level] + downscaled = downscale(result[0], reduction, scale, pad_mode=pad_mode) result.append(downscaled) + if preserve_dtype: + result = [r.astype(array.dtype) for r in result] + if chunks is not None: if isinstance(chunks, Sequence): _chunks = {k: v for k, v in zip(result[0].dims, chunks)} else: _chunks = chunks result = [r.chunk(_chunks) for r in result] + return result @@ -202,7 +207,6 @@ def downscale( reduction: Callable, scale_factors: Sequence[int], pad_mode: Optional[str] = None, - preserve_dtype: bool = True, **kwargs, ) -> DataArray: """ @@ -239,8 +243,6 @@ def downscale( **kwargs, ) - if preserve_dtype: - coarsened = coarsened.astype(array.dtype) if isinstance(array, xarray.DataArray): base_coords = array.coords diff --git a/tests/test_multiscale.py b/tests/test_multiscale.py index ce47226..addc8fa 100644 --- a/tests/test_multiscale.py +++ b/tests/test_multiscale.py @@ -54,33 +54,20 @@ def test_downscale_2d(): arr_xarray = DataArray(arr_dask) downscaled_numpy_float = downscale( - arr_numpy, np.mean, scale, preserve_dtype=False - ).compute() + arr_numpy, np.mean, scale).compute() downscaled_dask_float = downscale( - arr_dask, np.mean, scale, preserve_dtype=False - ).compute() + arr_dask, np.mean, scale).compute() downscaled_xarray_float = downscale( - arr_xarray, np.mean, scale, preserve_dtype=False - ).compute() + arr_xarray, np.mean, scale).compute() answer_float = np.array([[0.5, 0.5, 0.5, 0.5], [0.5, 0.5, 0.5, 0.5]]) + assert np.array_equal(downscaled_numpy_float, answer_float) assert np.array_equal(downscaled_dask_float, answer_float) assert np.array_equal(downscaled_xarray_float, answer_float) - downscaled_numpy_int = downscale( - arr_numpy, np.mean, scale, dtype=arr_numpy.dtype - ).compute() - downscaled_dask_int = downscale( - arr_dask, np.mean, scale, dtype=arr_numpy.dtype - ).compute() - - answer_int = answer_float.astype("int") - assert np.array_equal(downscaled_numpy_int, answer_int) - assert np.array_equal(downscaled_dask_int, answer_int) - def test_multiscale(): ndim = 3 @@ -94,7 +81,7 @@ def test_multiscale(): pyr_trimmed = multiscale(array, np.mean, 2, pad_mode=None) pyr_padded = multiscale(array, np.mean, 2, pad_mode="reflect") - + pyr_trimmed_recursive = multiscale(array, np.mean, 2, pad_mode=None, recursive=True) assert [p.shape for p in pyr_padded] == [ shape, (5, 5, 5), @@ -111,4 +98,7 @@ def test_multiscale(): assert np.array_equal( pyr_trimmed[-2].data.mean().compute(), pyr_trimmed[-1].data.compute().mean() ) + assert np.array_equal( + pyr_trimmed_recursive[-2].data.mean().compute(), pyr_trimmed_recursive[-1].data.compute().mean() + ) assert np.allclose(pyr_padded[0].data.mean().compute(), 0.17146776406035666) From fb12bd1b042c4dc43ceae7d911092f6b914e4921 Mon Sep 17 00:00:00 2001 From: Davis Vann Bennett Date: Thu, 17 Jun 2021 14:06:29 -0400 Subject: [PATCH 3/6] update docstrings and tests; change 'recursive' to 'chained' in multiscale signature. --- src/xarray_multiscale/multiscale.py | 153 ++++++++++++++++------------ tests/test_multiscale.py | 4 +- 2 files changed, 92 insertions(+), 65 deletions(-) diff --git a/src/xarray_multiscale/multiscale.py b/src/xarray_multiscale/multiscale.py index 2ccddda..fdd9ef1 100644 --- a/src/xarray_multiscale/multiscale.py +++ b/src/xarray_multiscale/multiscale.py @@ -4,11 +4,9 @@ from xarray import DataArray from typing import Any, List, Optional, Tuple, Union, Sequence, Callable, Dict from scipy.interpolate import interp1d -from dask.array.core import slices_from_chunks, normalize_chunks from dask.array import coarsen - def multiscale( array: Any, reduction: Callable[[Any], Any], @@ -16,31 +14,50 @@ def multiscale( pad_mode: Optional[str] = None, preserve_dtype: bool = True, chunks: Optional[Union[Sequence[int], Dict[str, int]]] = None, - recursive: bool = False, + chained: bool = True, ) -> List[DataArray]: """ - Lazily generate a multiscale representation of an array + Generate a lazy, coordinate-aware multiscale representation of an array. Parameters ---------- - array: ndarray to be downscaled. + array : numpy array, dask array, or xarray DataArray + The array to be downscaled + + reduction : callable + A function that aggregates chunks of data over windows. See the documentation of `dask.array.coarsen` for the expected + signature of this callable. - reduction: a function that aggregates data over windows. + scale_factors : iterable of ints + The desired downscaling factors, one for each axis. - scale_factors: an iterable of integers that specifies how much to downscale each axis of the array. + pad_mode : string or None, default=None + How arrays should be padded prior to downscaling in order to ensure that each array dimension + is evenly divisible by the respective scale factor. When set to `None` (default), the input will be sliced before downscaling + if its dimensions are not divisible by `scale_factors`. - pad_mode: How (or if) the input should be padded. When set to `None` the input will be trimmed as needed. + preserve_dtype : bool, default=True + Determines whether the multiresolution arrays are all cast to the same dtype as the input. - preserve_dtype: boolean, defaults to True, determines whether lower levels of the pyramid are coerced to the same dtype as the input. This assumes that - the reduction function accepts a "dtype" kwarg, e.g. numpy.mean(x, dtype='int'). + chunks : sequence or dict of ints, or None, default=None. + If `chunks` is supplied, all output arrays are returned with this chunking. If not None, this + argument is passed directly to the `xarray.DataArray.chunk` method of each output array. - chunks: Sequence or Dict of ints, defaults to None. If `chunks` is supplied, all DataArrays are rechunked with these chunks before being returned. + chained : bool, default=True + If True (default), the nth downscaled array is generated by applying the reduction function on the n-1th + downscaled array with the user-supplied `scale_factors`. This means that the nth downscaled array directly depends on the n-1th + downscaled array. Note that nonlinear reductions like the windowed mode may give inaccurate results with `chained` set to True. - recursive: boolean, defaults to False. ToDo + If False, the nth downscaled array is generated by applying the reduction function on the 0th downscaled array + (i.e., the input array) with the `scale_factors` raised to the nth power. This means that the nth downscaled array directly + depends on the input array. - Returns a list of DataArrays, one per level of downscaling. These DataArrays have `coords` properties that track the changing offset (if any) - induced by the downsampling operation. Additionally, the scale factors are stored each DataArray's attrs propery under the key `scale_factors` + Returns ------- + result : list of DataArrays + The `coords` attribute of these DataArrays properties that track the changing offset (if any) + induced by the downsampling operation. Additionally, the scale factors are stored each DataArray's attrs propery under the key `scale_factors` + """ needs_padding = pad_mode != None @@ -56,19 +73,16 @@ def multiscale( else: padded_shape = prepad(array, scale_factors, pad_mode=pad_mode).shape - # figure out the maximum depth levels = range( 0, 1 + get_downscale_depth(padded_shape, scale_factors, pad=needs_padding) ) - scales = tuple( - tuple(s ** l for s in scale_factors) for l in levels - ) + scales = tuple(tuple(s ** l for s in scale_factors) for l in levels) result = [_ingest_array(array, scales[0])] for level in levels[1:]: - if recursive: + if chained: scale = scale_factors - downscaled = downscale(result[-1], reduction, scale, pad_mode=pad_mode) + downscaled = downscale(result[-1], reduction, scale, pad_mode=pad_mode) else: scale = scales[level] downscaled = downscale(result[0], reduction, scale, pad_mode=pad_mode) @@ -80,14 +94,21 @@ def multiscale( if chunks is not None: if isinstance(chunks, Sequence): _chunks = {k: v for k, v in zip(result[0].dims, chunks)} - else: + elif isinstance(chunks, dict): _chunks = chunks + else: + raise ValueError( + f"Chunks must be an instance or a dict, not {type(chunks)}" + ) result = [r.chunk(_chunks) for r in result] return result def _ingest_array(array: Any, scales: Sequence[int]): + """ + Ingest an array in preparation for downscaling + """ if hasattr(array, "coords"): # if the input is a xarray.DataArray, assign a new variable to the DataArray and use the variable # `array` to refer to the data property of that array @@ -101,7 +122,7 @@ def _ingest_array(array: Any, scales: Sequence[int]): data = da.asarray(array) dims = tuple(f"dim_{d}" for d in range(data.ndim)) coords = { - dim: DataArray(offset + np.arange(s, dtype="float32"), dims=dim) + dim: DataArray(offset + np.arange(s, dtype="float"), dims=dim) for dim, s, offset in zip(dims, array.shape, get_downsampled_offset(scales)) } name = None @@ -118,7 +139,13 @@ def even_padding(length: int, window: int) -> int: Parameters ---------- length : int - window: int + + window : int + + Returns + ------- + int + Value that, when added to `length`, results in a sum that is evenly divided by `window` """ return (window - (length % window)) % window @@ -132,8 +159,10 @@ def logn(x: float, n: float) -> float: x : float or int. n: float or int. - Returns np.log(x) / np.log(n) + Returns ------- + float + np.log(x) / np.log(n) """ result: float = np.log(x) / np.log(n) @@ -147,20 +176,25 @@ def prepad( rechunk: bool = True, ) -> da.array: """ - Pad an array such that its new dimensions are evenly divisible by some integer. + Lazily pad an array such that its new dimensions are evenly divisible by some integer. Parameters ---------- - array: An ndarray that will be padded. + array : ndarray + Array that will be padded. - scale_factors: An iterable of integers. The output array is guaranteed to have dimensions that are each evenly divisible - by the corresponding scale factor, and chunks that are smaller than or equal to the scale factor (if the array has chunks) + scale_factors : Sequence of ints + The output array is guaranteed to have dimensions that are each evenly divisible + by the corresponding scale factor, and chunks that are smaller than or equal + to the scale factor (if the array has chunks) - mode: String. The edge mode used by the padding routine. See `dask.array.pad` for more documentation. + pad_mode : str + The edge mode used by the padding routine. This parameter will be passed to + `dask.array.pad` as the `mode` keyword. - Returns a dask array with padded dimensions. + Returns ------- - + dask array """ if pad_mode == None: @@ -198,7 +232,8 @@ def prepad( extended_coords, dims=k, attrs=old_coord.attrs ) result = DataArray( - result, coords=new_coords, dims=array.dims, attrs=array.attrs) + result, coords=new_coords, dims=array.dims, attrs=array.attrs + ) return result @@ -214,16 +249,22 @@ def downscale( Parameters ---------- - array: The narray to be downscaled. + array : numpy array, dask array, xarray DataArray + The array to be downscaled. - reduction: The function to apply to each window of the array. + reduction : callable + A function that aggregates chunks of data over windows. See the documentation of `dask.array.coarsen` for the expected + signature of this callable. - scale_factors: A list if ints specifying how much to downscale the array per dimension. + scale_factors : iterable of ints + The desired downscaling factors, one for each axis. - trim_excess: A boolean that determines whether the size of the input array should be increased or decreased such that - each scale factor tiles its respective array axis. Defaults to False, which will result in the input being padded. + trim_excess : bool, default=False + Whether the size of the input array should be increased or decreased such that + each scale factor tiles its respective array axis. Defaults to False, which will result in the input being padded. - **kwargs: extra kwargs passed to dask.array.coarsen + **kwargs + extra kwargs passed to dask.array.coarsen Returns the downscaled version of the input as a dask array. ------- @@ -243,7 +284,6 @@ def downscale( **kwargs, ) - if isinstance(array, xarray.DataArray): base_coords = array.coords new_coords = base_coords @@ -256,10 +296,19 @@ def downscale( attrs=base_coords[bc].attrs, ) for s, bc, offset, sc in zip( - coarsened.shape, base_coords, get_downsampled_offset(scale_factors), scale_factors + coarsened.shape, + base_coords, + get_downsampled_offset(scale_factors), + scale_factors, ) ) - coarsened = DataArray(coarsened, dims=array.dims, coords=new_coords, attrs=array.attrs, name=array.name) + coarsened = DataArray( + coarsened, + dims=array.dims, + coords=new_coords, + attrs=array.attrs, + name=array.name, + ) return coarsened @@ -324,25 +373,3 @@ def slice_span(sl: slice) -> int: Measure the length of a slice """ return sl.stop - sl.start - - -def blocked_pyramid( - arr, block_size: Sequence, scale_factors: Sequence[int] = (2, 2, 2), **kwargs -): - full_pyr = multiscale(arr, scale_factors=scale_factors, **kwargs) - slices = slices_from_chunks(normalize_chunks(block_size, arr.shape)) - absolute_block_size = tuple(map(slice_span, slices[0])) - - results = [] - for idx, sl in enumerate(slices): - regions = [ - tuple(map(downscale_slice, sl, tuple(np.power(scale_factors, exp)))) - for exp in range(len(full_pyr)) - ] - if tuple(map(slice_span, sl)) == absolute_block_size: - pyr = multiscale(arr[sl], scale_factors=scale_factors, **kwargs) - else: - pyr = [full_pyr[l][r] for l, r in enumerate(regions)] - assert len(pyr) == len(regions) - results.append((regions, pyr)) - return results diff --git a/tests/test_multiscale.py b/tests/test_multiscale.py index addc8fa..9c1cd49 100644 --- a/tests/test_multiscale.py +++ b/tests/test_multiscale.py @@ -81,7 +81,7 @@ def test_multiscale(): pyr_trimmed = multiscale(array, np.mean, 2, pad_mode=None) pyr_padded = multiscale(array, np.mean, 2, pad_mode="reflect") - pyr_trimmed_recursive = multiscale(array, np.mean, 2, pad_mode=None, recursive=True) + pyr_trimmed_unchained = multiscale(array, np.mean, 2, pad_mode=None, chained=False) assert [p.shape for p in pyr_padded] == [ shape, (5, 5, 5), @@ -99,6 +99,6 @@ def test_multiscale(): pyr_trimmed[-2].data.mean().compute(), pyr_trimmed[-1].data.compute().mean() ) assert np.array_equal( - pyr_trimmed_recursive[-2].data.mean().compute(), pyr_trimmed_recursive[-1].data.compute().mean() + pyr_trimmed_unchained[-2].data.mean().compute(), pyr_trimmed_unchained[-1].data.compute().mean() ) assert np.allclose(pyr_padded[0].data.mean().compute(), 0.17146776406035666) From d83db778f3885b5ccc89313704aff0c995d62cce Mon Sep 17 00:00:00 2001 From: Davis Vann Bennett Date: Thu, 17 Jun 2021 14:10:57 -0400 Subject: [PATCH 4/6] bump version to reflect some breaking api changes --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 7b9fa7e..757f188 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "xarray-multiscale" -version = "0.1.6" +version = "0.2.0" description = "" authors = ["Davis Vann Bennett "] From a8c8a0bf027188bd6ef714440f0486a4f3afe901 Mon Sep 17 00:00:00 2001 From: Davis Vann Bennett Date: Thu, 17 Jun 2021 14:13:12 -0400 Subject: [PATCH 5/6] remove metadata, since this can be handled by other existing libraries --- tests/test_metadata.py | 78 ------------------------------------------ 1 file changed, 78 deletions(-) delete mode 100644 tests/test_metadata.py diff --git a/tests/test_metadata.py b/tests/test_metadata.py deleted file mode 100644 index b7078c0..0000000 --- a/tests/test_metadata.py +++ /dev/null @@ -1,78 +0,0 @@ -import dask.array as da -import numpy as np -from xarray import DataArray -from xarray_multiscale.metadata.neuroglancer import GroupMeta -from xarray_multiscale.metadata.util import SpatialTransform, infer_c_or_f_contiguous -from xarray_multiscale.metadata import neuroglancer, cosem_ome -import zarr -from xarray_multiscale.multiscale import multiscale - -def test_array_order_inferece(): - data= np.arange(10) - assert infer_c_or_f_contiguous(data) == 'C' - - data = np.arange(10).reshape((2,5)) - assert infer_c_or_f_contiguous(data) == 'C' - - data = np.arange(10).reshape((2,5), order='F') - assert infer_c_or_f_contiguous(data) == 'F' - - data = zarr.zeros((10,10), order='C') - assert infer_c_or_f_contiguous(data) == 'C' - - data = zarr.zeros((10,10), order='F') - assert infer_c_or_f_contiguous(data) == 'F' - - data = da.zeros((10,10)) - assert infer_c_or_f_contiguous(data) == 'C' - -def test_SpatialTransform(): - data = DataArray(np.zeros((10,10,10))) - transform = SpatialTransform.fromDataArray(data) - assert transform == SpatialTransform(axes=['dim_0','dim_1','dim_2'], - units=[None] * 3, - translate=[0.0] * 3, - scale=[1.0] * 3) - - coords = [DataArray(np.arange(10), dims=('z'), attrs={'units': 'nm'}), - DataArray(np.arange(10) + 5, dims=('y',), attrs={'units': 'm'}), - DataArray(10 + (np.arange(10) * 10), dims=('x',), attrs={'units': 'km'})] - - data = DataArray(np.zeros((10,10,10)), coords=coords) - transform = SpatialTransform.fromDataArray(data) - assert transform == SpatialTransform(axes=['z','y','x'], - units=['nm','m','km'], - translate=[0.0, 5.0, 10.0], - scale=[1.0, 1.0, 10.0 ] ) - - transform = SpatialTransform.fromDataArray(data, reverse_axes=True) - assert transform == SpatialTransform(axes=['x','y','z'], - units=['km','m','nm'], - translate=[10.0, 5.0, 0.0], - scale=[10.0, 1.0, 1.0 ] ) - -def test_neuroglancer_metadata(): - coords = [DataArray(np.arange(16) + .5, dims=('z'), attrs={'units': 'nm'}), - DataArray(np.arange(16) + 1/3, dims=('y',), attrs={'units': 'm'}), - DataArray(10 + (np.arange(16) * 100.1), dims=('x',), attrs={'units': 'km'})] - - data = DataArray(np.zeros((16, 16, 16)), coords=coords) - multi = multiscale(data, np.mean, (2,2,2))[:4] - neuroglancer_metadata = neuroglancer.GroupMeta.fromDataArraySequence(multi) - - assert neuroglancer_metadata == neuroglancer.GroupMeta(axes=['x','y','z'], - units=['km','m','nm'], - scales=[[1,1,1], [2,2,2], [4,4,4], [8,8,8]], - pixelResolution=neuroglancer.PixelResolution(dimensions=[100.1, 1.0, 1.0], unit='km')) - -def test_cosem_ome(): - coords = [DataArray(np.arange(16), dims=('z'), attrs={'units': 'nm'}), - DataArray(np.arange(16) + 5, dims=('y',), attrs={'units': 'm'}), - DataArray(10 + (np.arange(16) * 10), dims=('x',), attrs={'units': 'km'})] - - data = DataArray(np.zeros((16, 16, 16)), coords=coords, name='data') - multi = multiscale(data, np.mean, (2,2,2))[:2] - paths = ['s0', 's1'] - cosem_ome_group_metadata = cosem_ome.GroupMeta.fromDataArraySequence(multi, paths=paths) - scale_metas = [cosem_ome.ScaleMeta(path = p, transform=SpatialTransform.fromDataArray(m)) for p,m in zip(paths, multi)] - assert cosem_ome_group_metadata == cosem_ome.GroupMeta(name='data', multiscales=[cosem_ome.MultiscaleMeta(datasets=scale_metas)]) \ No newline at end of file From 4bb424a3e4fefbcc3d4abac63e8fb0d2d9f15604 Mon Sep 17 00:00:00 2001 From: Davis Vann Bennett Date: Thu, 17 Jun 2021 14:14:47 -0400 Subject: [PATCH 6/6] actually remove metadata, and remove unused deps --- pyproject.toml | 2 - src/xarray_multiscale/metadata/__init__.py | 0 src/xarray_multiscale/metadata/cosem_ome.py | 47 --------------- .../metadata/neuroglancer.py | 37 ------------ src/xarray_multiscale/metadata/util.py | 58 ------------------- 5 files changed, 144 deletions(-) delete mode 100644 src/xarray_multiscale/metadata/__init__.py delete mode 100644 src/xarray_multiscale/metadata/cosem_ome.py delete mode 100644 src/xarray_multiscale/metadata/neuroglancer.py delete mode 100644 src/xarray_multiscale/metadata/util.py diff --git a/pyproject.toml b/pyproject.toml index 757f188..6f604e7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,8 +11,6 @@ mypy = "^0.790" scipy = "^1.5.4" numpy = "^1.19.4" dask = "^^2020.12.0" -cytoolz = "^0.11.0" -dacite = "^1.6.0" [tool.poetry.dev-dependencies] pytest = "^5.2" diff --git a/src/xarray_multiscale/metadata/__init__.py b/src/xarray_multiscale/metadata/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/xarray_multiscale/metadata/cosem_ome.py b/src/xarray_multiscale/metadata/cosem_ome.py deleted file mode 100644 index 5354731..0000000 --- a/src/xarray_multiscale/metadata/cosem_ome.py +++ /dev/null @@ -1,47 +0,0 @@ -from dataclasses import dataclass, asdict -from typing import Sequence -from xarray import DataArray -from .util import BaseMeta, SpatialTransform -from typing import Optional - - -@dataclass -class ScaleMeta(BaseMeta): - path: str - transform: SpatialTransform - - -@dataclass -class MultiscaleMeta(BaseMeta): - datasets: Sequence[ScaleMeta] - - -@dataclass -class GroupMeta(BaseMeta): - name: str - multiscales: Sequence[MultiscaleMeta] - - @classmethod - def fromDataArraySequence( - cls, dataarrays: Sequence[DataArray], paths: Sequence[str] - ): - name: str = str(dataarrays[0].name) - multiscales = [ - MultiscaleMeta( - datasets=[ - ScaleMeta(path=path, transform=SpatialTransform.fromDataArray(arr)) - for path, arr in zip(paths, dataarrays) - ] - ) - ] - return cls(name=name, multiscales=multiscales) - - -@dataclass -class ArrayMeta(BaseMeta): - name: Optional[str] - transform: SpatialTransform - - @classmethod - def fromDataArray(cls, data: DataArray) -> "ArrayMeta": - return cls(name=str(data.name), transform=SpatialTransform.fromDataArray(data)) diff --git a/src/xarray_multiscale/metadata/neuroglancer.py b/src/xarray_multiscale/metadata/neuroglancer.py deleted file mode 100644 index 2329dbb..0000000 --- a/src/xarray_multiscale/metadata/neuroglancer.py +++ /dev/null @@ -1,37 +0,0 @@ -from typing import Sequence -from dataclasses import dataclass - -from xarray.core.dataarray import DataArray -from typing import Union, List -import numpy as np -from xarray_multiscale.metadata.util import BaseMeta, SpatialTransform - - -@dataclass -class PixelResolution(BaseMeta): - # fortran-ordered - dimensions: Sequence[float] - unit: Union[str, None] - - -@dataclass -class GroupMeta(BaseMeta): - # see https://github.com/google/neuroglancer/issues/176#issuecomment-553027775 - # these properties must be stored in the opposite order of C-contiguous axis indexing - axes: Sequence[Union[str, None]] - units: Sequence[Union[str, None]] - scales: Sequence[Sequence[int]] - pixelResolution: PixelResolution - - @classmethod - def fromDataArraySequence(cls, dataarrays: Sequence[DataArray]) -> "GroupMeta": - transforms = [ - SpatialTransform.fromDataArray(array, reverse_axes=True) - for array in dataarrays - ] - pixelresolution = PixelResolution(transforms[0].scale, transforms[0].units[0]) - scales: List[List[int]] = [ - np.round(np.divide(t.scale, transforms[0].scale)).astype("int").tolist() - for t in transforms - ] - return cls(transforms[0].axes, transforms[0].units, scales, pixelresolution) diff --git a/src/xarray_multiscale/metadata/util.py b/src/xarray_multiscale/metadata/util.py deleted file mode 100644 index eaa7ff7..0000000 --- a/src/xarray_multiscale/metadata/util.py +++ /dev/null @@ -1,58 +0,0 @@ -from dataclasses import dataclass, asdict -from typing import Sequence, Union, Dict, Any, Optional -from xarray import DataArray -from dacite import from_dict - - -def infer_c_or_f_contiguous(array: Any) -> str: - data_order = "C" - if hasattr(array, "order"): - data_order = array.order - elif hasattr(array, "flags"): - if array.flags["F_CONTIGUOUS"] and not array.flags["C_CONTIGUOUS"]: - data_order = "F" - return data_order - - -@dataclass -class BaseMeta: - def asdict(self): - return asdict(self) - - -@dataclass -class SpatialTransform(BaseMeta): - axes: Sequence[str] - units: Sequence[Union[str, None]] - translate: Sequence[float] - scale: Sequence[float] - - def __post_init__(self): - assert ( - len(self.axes) == len(self.units) == len(self.translate) == len(self.scale) - ) - - @classmethod - def fromDataArray( - cls, dataarray: DataArray, reverse_axes=False - ) -> "SpatialTransform": - """ - Generate a spatial transform from a DataArray. - """ - - orderer = slice(None) - if reverse_axes: - orderer = slice(-1, None, -1) - axes = [str(d) for d in dataarray.dims[orderer]] - units = [dataarray.coords[ax].attrs.get("units") for ax in axes] - translate = [float(dataarray.coords[ax][0]) for ax in axes] - scale = [ - abs(float(dataarray.coords[ax][1]) - float(dataarray.coords[ax][0])) - for ax in axes - ] - - return cls(axes=axes, units=units, translate=translate, scale=scale) - - @classmethod - def fromDict(cls, d: Dict[str, Any]): - return from_dict(cls, d)