[Git][debian-gis-team/xarray-datatree][upstream] New upstream version 0.0.14
Antonio Valentino (@antonio.valentino)
gitlab at salsa.debian.org
Sat Jan 27 11:46:34 GMT 2024
Antonio Valentino pushed to branch upstream at Debian GIS Project / xarray-datatree
Commits:
497d39b2 by Antonio Valentino at 2024-01-27T11:40:36+00:00
New upstream version 0.0.14
- - - - -
20 changed files:
- .github/workflows/pypipublish.yaml
- .gitignore
- .pre-commit-config.yaml
- ci/doc.yml
- datatree/datatree.py
- datatree/io.py
- datatree/mapping.py
- datatree/testing.py
- datatree/tests/test_io.py
- datatree/tests/test_mapping.py
- datatree/tests/test_treenode.py
- datatree/treenode.py
- + docs/README.md
- docs/source/api.rst
- docs/source/conf.py
- docs/source/data-structures.rst
- docs/source/hierarchical-data.rst
- docs/source/index.rst
- docs/source/whats-new.rst
- pyproject.toml
Changes:
=====================================
.github/workflows/pypipublish.yaml
=====================================
@@ -22,7 +22,7 @@ jobs:
- uses: actions/checkout at v4
with:
fetch-depth: 0
- - uses: actions/setup-python at v4
+ - uses: actions/setup-python at v5
name: Install Python
with:
python-version: 3.9
@@ -39,7 +39,7 @@ jobs:
python -m build --sdist --wheel .
- - uses: actions/upload-artifact at v3
+ - uses: actions/upload-artifact at v4
with:
name: releases
path: dist
@@ -48,11 +48,11 @@ jobs:
needs: build-artifacts
runs-on: ubuntu-latest
steps:
- - uses: actions/setup-python at v4
+ - uses: actions/setup-python at v5
name: Install Python
with:
python-version: '3.10'
- - uses: actions/download-artifact at v3
+ - uses: actions/download-artifact at v4
with:
name: releases
path: dist
@@ -72,12 +72,12 @@ jobs:
if: github.event_name == 'release'
runs-on: ubuntu-latest
steps:
- - uses: actions/download-artifact at v3
+ - uses: actions/download-artifact at v4
with:
name: releases
path: dist
- name: Publish package to PyPI
- uses: pypa/gh-action-pypi-publish at v1.8.10
+ uses: pypa/gh-action-pypi-publish at v1.8.11
with:
user: ${{ secrets.PYPI_USERNAME }}
password: ${{ secrets.PYPI_PASSWORD }}
=====================================
.gitignore
=====================================
@@ -131,3 +131,6 @@ dmypy.json
# version
_version.py
+
+# Ignore vscode specific settings
+.vscode/
=====================================
.pre-commit-config.yaml
=====================================
@@ -3,23 +3,23 @@ ci:
autoupdate_schedule: monthly
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v4.4.0
+ rev: v4.5.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
# isort should run before black as black sometimes tweaks the isort output
- repo: https://github.com/PyCQA/isort
- rev: 5.12.0
+ rev: 5.13.2
hooks:
- id: isort
# https://github.com/python/black#version-control-integration
- repo: https://github.com/psf/black
- rev: 23.9.1
+ rev: 23.12.1
hooks:
- id: black
- repo: https://github.com/keewis/blackdoc
- rev: v0.3.8
+ rev: v0.3.9
hooks:
- id: blackdoc
- repo: https://github.com/PyCQA/flake8
@@ -32,7 +32,7 @@ repos:
# - id: velin
# args: ["--write", "--compact"]
- repo: https://github.com/pre-commit/mirrors-mypy
- rev: v1.5.1
+ rev: v1.8.0
hooks:
- id: mypy
# Copied from setup.cfg
=====================================
ci/doc.yml
=====================================
@@ -13,8 +13,8 @@ dependencies:
- sphinx-book-theme >= 0.0.38
- nbsphinx
- sphinxcontrib-srclinks
+ - pickleshare
- pydata-sphinx-theme>=0.4.3
- - numpydoc
- ipython
- h5netcdf
- zarr
=====================================
datatree/datatree.py
=====================================
@@ -1496,7 +1496,12 @@ class DataTree(
)
def to_zarr(
- self, store, mode: str = "w", encoding=None, consolidated: bool = True, **kwargs
+ self,
+ store,
+ mode: str = "w-",
+ encoding=None,
+ consolidated: bool = True,
+ **kwargs,
):
"""
Write datatree contents to a Zarr store.
@@ -1505,7 +1510,7 @@ class DataTree(
----------
store : MutableMapping, str or Path, optional
Store or path to directory in file system
- mode : {{"w", "w-", "a", "r+", None}, default: "w"
+ mode : {{"w", "w-", "a", "r+", None}, default: "w-"
Persistence mode: “w” means create (overwrite if exists); “w-” means create (fail if exists);
“a” means override existing variables (create if does not exist); “r+” means modify existing
array values only (raise an error if any metadata or shapes would change). The default mode
=====================================
datatree/io.py
=====================================
@@ -176,7 +176,7 @@ def _create_empty_zarr_group(store, group, mode):
def _datatree_to_zarr(
dt: DataTree,
store,
- mode: str = "w",
+ mode: str = "w-",
encoding=None,
consolidated: bool = True,
**kwargs,
=====================================
datatree/mapping.py
=====================================
@@ -206,14 +206,17 @@ def map_over_subtree(func: Callable) -> Callable:
node_of_first_tree.path
)(func)
- # Now we can call func on the data in this particular set of corresponding nodes
- results = (
- func_with_error_context(
+ if node_of_first_tree.has_data:
+ # call func on the data in this particular set of corresponding nodes
+ results = func_with_error_context(
*node_args_as_datasetviews, **node_kwargs_as_datasetviews
)
- if node_of_first_tree.has_data
- else None
- )
+ elif node_of_first_tree.has_attrs:
+ # propagate attrs
+ results = node_of_first_tree.ds
+ else:
+ # nothing to propagate so use fastpath to create empty node in new tree
+ results = None
# TODO implement mapping over multiple trees in-place using if conditions from here on?
out_data_objects[node_of_first_tree.path] = results
@@ -279,7 +282,7 @@ def _handle_errors_with_path_context(path):
def add_note(err: BaseException, msg: str) -> None:
# TODO: remove once python 3.10 can be dropped
if sys.version_info < (3, 11):
- err.__notes__ = getattr(err, "__notes__", []) + [msg]
+ err.__notes__ = getattr(err, "__notes__", []) + [msg] # type: ignore[attr-defined]
else:
err.add_note(msg)
=====================================
datatree/testing.py
=====================================
@@ -1,4 +1,4 @@
-from xarray.testing import ensure_warnings
+from xarray.testing.assertions import ensure_warnings
from .datatree import DataTree
from .formatting import diff_tree_repr
=====================================
datatree/tests/test_io.py
=====================================
@@ -1,4 +1,5 @@
import pytest
+import zarr.errors
from datatree.io import open_datatree
from datatree.testing import assert_equal
@@ -109,3 +110,11 @@ class TestIO:
with pytest.warns(RuntimeWarning, match="consolidated"):
roundtrip_dt = open_datatree(filepath, engine="zarr")
assert_equal(original_dt, roundtrip_dt)
+
+ @requires_zarr
+ def test_to_zarr_default_write_mode(self, tmpdir, simple_datatree):
+ simple_datatree.to_zarr(tmpdir)
+
+ # with default settings, to_zarr should not overwrite an existing dir
+ with pytest.raises(zarr.errors.ContainsGroupError):
+ simple_datatree.to_zarr(tmpdir)
=====================================
datatree/tests/test_mapping.py
=====================================
@@ -264,6 +264,17 @@ class TestMapOverSubTree:
dt.map_over_subtree(check_for_data)
+ def test_keep_attrs_on_empty_nodes(self, create_test_datatree):
+ # GH278
+ dt = create_test_datatree()
+ dt["set1/set2"].attrs["foo"] = "bar"
+
+ def empty_func(ds):
+ return ds
+
+ result = dt.map_over_subtree(empty_func)
+ assert result["set1/set2"].attrs == dt["set1/set2"].attrs
+
@pytest.mark.xfail(
reason="probably some bug in pytests handling of exception notes"
)
=====================================
datatree/tests/test_treenode.py
=====================================
@@ -95,7 +95,7 @@ class TestFamilyTree:
michael = TreeNode(children={"Tony": tony})
vito = TreeNode(children={"Michael": michael})
assert tony.root is vito
- assert tony.lineage == (tony, michael, vito)
+ assert tony.parents == (michael, vito)
assert tony.ancestors == (vito, michael, tony)
@@ -279,12 +279,15 @@ class TestIterators:
class TestAncestry:
+ def test_parents(self):
+ _, leaf = create_test_tree()
+ expected = ["e", "b", "a"]
+ assert [node.name for node in leaf.parents] == expected
+
def test_lineage(self):
_, leaf = create_test_tree()
- lineage = leaf.lineage
expected = ["f", "e", "b", "a"]
- for node, expected_name in zip(lineage, expected):
- assert node.name == expected_name
+ assert [node.name for node in leaf.lineage] == expected
def test_ancestors(self):
_, leaf = create_test_tree()
=====================================
datatree/treenode.py
=====================================
@@ -121,8 +121,7 @@ class TreeNode(Generic[Tree]):
)
def _is_descendant_of(self, node: Tree) -> bool:
- _self, *lineage = list(node.lineage)
- return any(n is self for n in lineage)
+ return any(n is self for n in node.parents)
def _detach(self, parent: Tree | None) -> None:
if parent is not None:
@@ -236,26 +235,53 @@ class TreeNode(Generic[Tree]):
"""Method call after attaching `children`."""
pass
- def iter_lineage(self: Tree) -> Iterator[Tree]:
+ def _iter_parents(self: Tree) -> Iterator[Tree]:
"""Iterate up the tree, starting from the current node."""
- node: Tree | None = self
+ node: Tree | None = self.parent
while node is not None:
yield node
node = node.parent
+ def iter_lineage(self: Tree) -> Tuple[Tree, ...]:
+ """Iterate up the tree, starting from the current node."""
+ from warnings import warn
+
+ warn(
+ "`iter_lineage` has been deprecated, and in the future will raise an error."
+ "Please use `parents` from now on.",
+ DeprecationWarning,
+ )
+ return tuple((self, *self.parents))
+
@property
def lineage(self: Tree) -> Tuple[Tree, ...]:
"""All parent nodes and their parent nodes, starting with the closest."""
- return tuple(self.iter_lineage())
+ from warnings import warn
+
+ warn(
+ "`lineage` has been deprecated, and in the future will raise an error."
+ "Please use `parents` from now on.",
+ DeprecationWarning,
+ )
+ return self.iter_lineage()
+
+ @property
+ def parents(self: Tree) -> Tuple[Tree, ...]:
+ """All parent nodes and their parent nodes, starting with the closest."""
+ return tuple(self._iter_parents())
@property
def ancestors(self: Tree) -> Tuple[Tree, ...]:
"""All parent nodes and their parent nodes, starting with the most distant."""
- if self.parent is None:
- return (self,)
- else:
- ancestors = tuple(reversed(list(self.lineage)))
- return ancestors
+
+ from warnings import warn
+
+ warn(
+ "`ancestors` has been deprecated, and in the future will raise an error."
+ "Please use `parents`. Example: `tuple(reversed(node.parents))`",
+ DeprecationWarning,
+ )
+ return tuple((*reversed(self.parents), self))
@property
def root(self: Tree) -> Tree:
@@ -351,7 +377,7 @@ class TreeNode(Generic[Tree]):
depth
width
"""
- return len(self.ancestors) - 1
+ return len(self.parents)
@property
def depth(self: Tree) -> int:
@@ -591,9 +617,9 @@ class NamedNode(TreeNode, Generic[Tree]):
if self.is_root:
return "/"
else:
- root, *ancestors = self.ancestors
+ root, *ancestors = tuple(reversed(self.parents))
# don't include name of root because (a) root might not have a name & (b) we want path relative to root.
- names = [node.name for node in ancestors]
+ names = [*(node.name for node in ancestors), self.name]
return "/" + "/".join(names)
def relative_to(self: NamedNode, other: NamedNode) -> str:
@@ -608,7 +634,7 @@ class NamedNode(TreeNode, Generic[Tree]):
)
this_path = NodePath(self.path)
- if other.path in list(ancestor.path for ancestor in self.lineage):
+ if other.path in list(parent.path for parent in (self, *self.parents)):
return str(this_path.relative_to(other.path))
else:
common_ancestor = self.find_common_ancestor(other)
@@ -623,18 +649,17 @@ class NamedNode(TreeNode, Generic[Tree]):
Raise ValueError if they are not in the same tree.
"""
- common_ancestor = None
- for node in other.iter_lineage():
- if node.path in [ancestor.path for ancestor in self.ancestors]:
- common_ancestor = node
- break
+ if self is other:
+ return self
- if not common_ancestor:
- raise NotFoundInTreeError(
- "Cannot find common ancestor because nodes do not lie within the same tree"
- )
+ other_paths = [op.path for op in other.parents]
+ for parent in (self, *self.parents):
+ if parent.path in other_paths:
+ return parent
- return common_ancestor
+ raise NotFoundInTreeError(
+ "Cannot find common ancestor because nodes do not lie within the same tree"
+ )
def _path_to_ancestor(self, ancestor: NamedNode) -> NodePath:
"""Return the relative path from this node to the given ancestor node"""
@@ -643,12 +668,12 @@ class NamedNode(TreeNode, Generic[Tree]):
raise NotFoundInTreeError(
"Cannot find relative path to ancestor because nodes do not lie within the same tree"
)
- if ancestor.path not in list(a.path for a in self.ancestors):
+ if ancestor.path not in list(a.path for a in (self, *self.parents)):
raise NotFoundInTreeError(
"Cannot find relative path to ancestor because given node is not an ancestor of this node"
)
- lineage_paths = list(ancestor.path for ancestor in self.lineage)
- generation_gap = list(lineage_paths).index(ancestor.path)
- path_upwards = "../" * generation_gap if generation_gap > 0 else "/"
+ parents_paths = list(parent.path for parent in (self, *self.parents))
+ generation_gap = list(parents_paths).index(ancestor.path)
+ path_upwards = "../" * generation_gap if generation_gap > 0 else "."
return NodePath(path_upwards)
=====================================
docs/README.md
=====================================
@@ -0,0 +1,14 @@
+# README - docs
+
+## Build the documentation locally
+
+```bash
+cd docs # From project's root
+make clean
+rm -rf source/generated # remove autodoc artefacts, that are not removed by `make clean`
+make html
+```
+
+## Access the documentation locally
+
+Open `docs/_build/html/index.html` in a web browser
=====================================
docs/source/api.rst
=====================================
@@ -10,10 +10,13 @@ DataTree
Creating a DataTree
-------------------
+Methods of creating a datatree.
+
.. autosummary::
:toctree: generated/
DataTree
+ DataTree.from_dict
Tree Attributes
---------------
@@ -38,6 +41,7 @@ Attributes relating to the recursive tree-like structure of a ``DataTree``.
DataTree.descendants
DataTree.siblings
DataTree.lineage
+ DataTree.parents
DataTree.ancestors
DataTree.groups
@@ -57,7 +61,6 @@ This interface echoes that of ``xarray.Dataset``.
DataTree.attrs
DataTree.encoding
DataTree.indexes
- DataTree.chunks
DataTree.nbytes
DataTree.ds
DataTree.to_dataset
@@ -66,12 +69,7 @@ This interface echoes that of ``xarray.Dataset``.
DataTree.is_empty
DataTree.is_hollow
-..
-
- Missing:
- ``DataTree.chunksizes``
-
-Dictionary interface
+Dictionary Interface
--------------------
``DataTree`` objects also have a dict-like interface mapping keys to either ``xarray.DataArray``s or to child ``DataTree`` nodes.
@@ -101,11 +99,36 @@ For manipulating, traversing, navigating, or mapping over the tree structure.
DataTree.relative_to
DataTree.iter_lineage
DataTree.find_common_ancestor
+ DataTree.map_over_subtree
map_over_subtree
DataTree.pipe
DataTree.match
DataTree.filter
+Pathlib-like Interface
+----------------------
+
+``DataTree`` objects deliberately echo some of the API of `pathlib.PurePath`.
+
+.. autosummary::
+ :toctree: generated/
+
+ DataTree.name
+ DataTree.parent
+ DataTree.parents
+ DataTree.relative_to
+
+Missing:
+
+..
+
+ ``DataTree.glob``
+ ``DataTree.joinpath``
+ ``DataTree.with_name``
+ ``DataTree.walk``
+ ``DataTree.rename``
+ ``DataTree.replace``
+
DataTree Contents
-----------------
@@ -254,9 +277,7 @@ Methods copied from :py:class:`numpy.ndarray` objects, here applying to the data
DataTree.clip
DataTree.conj
DataTree.conjugate
- DataTree.imag
DataTree.round
- DataTree.real
DataTree.rank
Reshaping and reorganising
@@ -282,13 +303,12 @@ Plotting
I/O
===
-Create or
+Open a datatree from an on-disk store or serialize the tree.
.. autosummary::
:toctree: generated/
open_datatree
- DataTree.from_dict
DataTree.to_dict
DataTree.to_netcdf
DataTree.to_zarr
=====================================
docs/source/conf.py
=====================================
@@ -39,7 +39,6 @@ sys.path.insert(0, parent)
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = [
- "numpydoc",
"sphinx.ext.autodoc",
"sphinx.ext.viewcode",
"sphinx.ext.linkcode",
@@ -57,8 +56,8 @@ extensions = [
]
extlinks = {
- "issue": ("https://github.com/TomNicholas/datatree/issues/%s", "GH#"),
- "pull": ("https://github.com/TomNicholas/datatree/pull/%s", "GH#"),
+ "issue": ("https://github.com/xarray-contrib/datatree/issues/%s", "GH#%s"),
+ "pull": ("https://github.com/xarray-contrib/datatree/pull/%s", "GH#%s"),
}
# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates", sphinx_autosummary_accessors.templates_path]
@@ -66,6 +65,69 @@ templates_path = ["_templates", sphinx_autosummary_accessors.templates_path]
# Generate the API documentation when building
autosummary_generate = True
+
+# Napoleon configurations
+
+napoleon_google_docstring = False
+napoleon_numpy_docstring = True
+napoleon_use_param = False
+napoleon_use_rtype = False
+napoleon_preprocess_types = True
+napoleon_type_aliases = {
+ # general terms
+ "sequence": ":term:`sequence`",
+ "iterable": ":term:`iterable`",
+ "callable": ":py:func:`callable`",
+ "dict_like": ":term:`dict-like <mapping>`",
+ "dict-like": ":term:`dict-like <mapping>`",
+ "path-like": ":term:`path-like <path-like object>`",
+ "mapping": ":term:`mapping`",
+ "file-like": ":term:`file-like <file-like object>`",
+ # special terms
+ # "same type as caller": "*same type as caller*", # does not work, yet
+ # "same type as values": "*same type as values*", # does not work, yet
+ # stdlib type aliases
+ "MutableMapping": "~collections.abc.MutableMapping",
+ "sys.stdout": ":obj:`sys.stdout`",
+ "timedelta": "~datetime.timedelta",
+ "string": ":class:`string <str>`",
+ # numpy terms
+ "array_like": ":term:`array_like`",
+ "array-like": ":term:`array-like <array_like>`",
+ "scalar": ":term:`scalar`",
+ "array": ":term:`array`",
+ "hashable": ":term:`hashable <name>`",
+ # matplotlib terms
+ "color-like": ":py:func:`color-like <matplotlib.colors.is_color_like>`",
+ "matplotlib colormap name": ":doc:`matplotlib colormap name <matplotlib:gallery/color/colormap_reference>`",
+ "matplotlib axes object": ":py:class:`matplotlib axes object <matplotlib.axes.Axes>`",
+ "colormap": ":py:class:`colormap <matplotlib.colors.Colormap>`",
+ # objects without namespace: xarray
+ "DataArray": "~xarray.DataArray",
+ "Dataset": "~xarray.Dataset",
+ "Variable": "~xarray.Variable",
+ "DatasetGroupBy": "~xarray.core.groupby.DatasetGroupBy",
+ "DataArrayGroupBy": "~xarray.core.groupby.DataArrayGroupBy",
+ # objects without namespace: numpy
+ "ndarray": "~numpy.ndarray",
+ "MaskedArray": "~numpy.ma.MaskedArray",
+ "dtype": "~numpy.dtype",
+ "ComplexWarning": "~numpy.ComplexWarning",
+ # objects without namespace: pandas
+ "Index": "~pandas.Index",
+ "MultiIndex": "~pandas.MultiIndex",
+ "CategoricalIndex": "~pandas.CategoricalIndex",
+ "TimedeltaIndex": "~pandas.TimedeltaIndex",
+ "DatetimeIndex": "~pandas.DatetimeIndex",
+ "Series": "~pandas.Series",
+ "DataFrame": "~pandas.DataFrame",
+ "Categorical": "~pandas.Categorical",
+ "Path": "~~pathlib.Path",
+ # objects with abbreviated namespace (from pandas)
+ "pd.Index": "~pandas.Index",
+ "pd.NaT": "~pandas.NaT",
+}
+
# The suffix of source filenames.
source_suffix = ".rst"
@@ -177,11 +239,6 @@ html_theme_options = {
# pixels large.
# html_favicon = None
-# Add any paths that contain custom static files (such as style sheets) here,
-# relative to this directory. They are copied after the builtin static files,
-# so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ["_static"]
-
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
# html_last_updated_fmt = '%b %d, %Y'
=====================================
docs/source/data-structures.rst
=====================================
@@ -75,7 +75,7 @@ Again these are not normally used unless explicitly accessed by the user.
Creating a DataTree
~~~~~~~~~~~~~~~~~~~
-One way to create a create a ``DataTree`` from scratch is to create each node individually,
+One way to create a ``DataTree`` from scratch is to create each node individually,
specifying the nodes' relationship to one another as you create each one.
The ``DataTree`` constructor takes:
=====================================
docs/source/hierarchical-data.rst
=====================================
@@ -369,25 +369,6 @@ You can see this tree is similar to the ``dt`` object above, except that it is m
(If you want to keep the name of the root node, you will need to add the ``name`` kwarg to :py:class:`from_dict`, i.e. ``DataTree.from_dict(non_empty_nodes, name=dt.root.name)``.)
-.. _Tree Contents:
-
-Tree Contents
--------------
-
-Hollow Trees
-~~~~~~~~~~~~
-
-A concept that can sometimes be useful is that of a "Hollow Tree", which means a tree with data stored only at the leaf nodes.
-This is useful because certain useful tree manipulation operations only make sense for hollow trees.
-
-You can check if a tree is a hollow tree by using the :py:meth:`~DataTree.is_hollow` property.
-We can see that the Simpson's family is not hollow because the data variable ``"age"`` is present at some nodes which
-have children (i.e. Abe and Homer).
-
-.. ipython:: python
-
- simpsons.is_hollow
-
.. _manipulating trees:
Manipulating Trees
@@ -412,6 +393,7 @@ We can use :py:meth:`DataTree.match` for this:
}
)
result = dt.match("*/B")
+ result
We can also subset trees by the contents of the nodes.
:py:meth:`DataTree.filter` retains only the nodes of a tree that meet a certain condition.
@@ -443,6 +425,25 @@ The result is a new tree, containing only the nodes matching the condition.
(Yes, under the hood :py:meth:`~DataTree.filter` is just syntactic sugar for the pattern we showed you in :ref:`iterating over trees` !)
+.. _Tree Contents:
+
+Tree Contents
+-------------
+
+Hollow Trees
+~~~~~~~~~~~~
+
+A concept that can sometimes be useful is that of a "Hollow Tree", which means a tree with data stored only at the leaf nodes.
+This is useful because certain useful tree manipulation operations only make sense for hollow trees.
+
+You can check if a tree is a hollow tree by using the :py:class:`~DataTree.is_hollow` property.
+We can see that the Simpson's family is not hollow because the data variable ``"age"`` is present at some nodes which
+have children (i.e. Abe and Homer).
+
+.. ipython:: python
+
+ simpsons.is_hollow
+
.. _tree computation:
Computation
@@ -539,7 +540,7 @@ See that the same change (fast-forwarding by adding 10 years to the age of each
Mapping Custom Functions Over Trees
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-You can map custom computation over each node in a tree using :py:func:`map_over_subtree`.
+You can map custom computation over each node in a tree using :py:meth:`DataTree.map_over_subtree`.
You can map any function, so long as it takes `xarray.Dataset` objects as one (or more) of the input arguments,
and returns one (or more) xarray datasets.
@@ -559,10 +560,13 @@ Then calculate the RMS value of these signals:
.. ipython:: python
- rms(readings)
+ voltages.map_over_subtree(rms)
.. _multiple trees:
+We can also use the :py:func:`map_over_subtree` decorator to promote a function which accepts datasets into one which
+accepts datatrees.
+
Operating on Multiple Trees
---------------------------
@@ -596,7 +600,7 @@ Notice that corresponding tree nodes do not need to have the same name or contai
Arithmetic Between Multiple Trees
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Arithmetic operations like multiplication are binary operations, so as long as we have wo isomorphic trees,
+Arithmetic operations like multiplication are binary operations, so as long as we have two isomorphic trees,
we can do arithmetic between them.
.. ipython:: python
=====================================
docs/source/index.rst
=====================================
@@ -50,7 +50,6 @@ Please raise any thoughts, issues, suggestions or bugs, no matter how small or l
Reading and Writing Files <io>
API Reference <api>
Terminology <terminology>
- How do I ... <howdoi>
Contributing Guide <contributing>
What's New <whats-new>
GitHub repository <https://github.com/xarray-contrib/datatree>
=====================================
docs/source/whats-new.rst
=====================================
@@ -15,9 +15,51 @@ What's New
np.random.seed(123456)
+.. _whats-new.v0.0.14:
+
+v0.0.14 (unreleased)
+--------------------
+
+New Features
+~~~~~~~~~~~~
+
+Breaking changes
+~~~~~~~~~~~~~~~~
+
+- Renamed `DataTree.lineage` to `DataTree.parents` to match `pathlib` vocabulary
+ (:issue:`283`, :pull:`286`)
+- Minimum required version of xarray is now 2023.12.0, i.e. the latest version.
+ This is required to prevent recent changes to xarray's internals from breaking datatree.
+ (:issue:`293`, :pull:`294`)
+ By `Tom Nicholas <https://github.com/TomNicholas>`_.
+- Change default write mode of :py:meth:`DataTree.to_zarr` to ``'w-'`` to match ``xarray``
+ default and prevent accidental directory overwrites. (:issue:`274`, :pull:`275`)
+ By `Sam Levang <https://github.com/slevang>`_.
+
+Deprecations
+~~~~~~~~~~~~
+
+- Renamed `DataTree.lineage` to `DataTree.parents` to match `pathlib` vocabulary
+ (:issue:`283`, :pull:`286`). `lineage` is now deprecated and use of `parents` is encouraged.
+ By `Etienne Schalk <https://github.com/etienneschalk>`_.
+
+Bug fixes
+~~~~~~~~~
+- Keep attributes on nodes containing no data in :py:func:`map_over_subtree`. (:issue:`278`, :pull:`279`)
+ By `Sam Levang <https://github.com/slevang>`_.
+
+Documentation
+~~~~~~~~~~~~~
+- Use ``napoleon`` instead of ``numpydoc`` to align with xarray documentation
+ (:issue:`284`, :pull:`298`).
+ By `Etienne Schalk <https://github.com/etienneschalk>`_.
+
+Internal Changes
+~~~~~~~~~~~~~~~~
+
.. _whats-new.v0.0.13:
-v0.0.13 (unreleased)
+v0.0.13 (27/10/2023)
--------------------
New Features
@@ -39,9 +81,6 @@ Breaking changes
- Disallow altering of given dataset inside function called by :py:func:`map_over_subtree` (:pull:`269`, reverts part of :pull:`194`).
By `Tom Nicholas <https://github.com/TomNicholas>`_.
-Deprecations
-~~~~~~~~~~~~
-
Bug fixes
~~~~~~~~~
@@ -335,7 +374,7 @@ Breaking changes
- Removes the option to delete all data in a node by assigning None to the node (in favour of deleting data by replacing
the node's ``.ds`` attribute with an empty Dataset), or to create a new empty node in the same way (in favour of
assigning an empty DataTree object instead).
-- Removes the ability to create a new node by assigning a ``Dataset`` object to ``DataTree.__setitem__`.
+- Removes the ability to create a new node by assigning a ``Dataset`` object to ``DataTree.__setitem__``.
- Several other minor API changes such as ``.pathstr`` -> ``.path``, and ``from_dict``'s dictionary argument now being
required. (:pull:`76`)
By `Tom Nicholas <https://github.com/TomNicholas>`_.
=====================================
pyproject.toml
=====================================
@@ -19,7 +19,7 @@ classifiers = [
]
requires-python = ">=3.9"
dependencies = [
- "xarray >=2022.6.0",
+ "xarray >=2023.12.0",
"packaging",
]
dynamic = ["version"]
View it on GitLab: https://salsa.debian.org/debian-gis-team/xarray-datatree/-/commit/497d39b228013ab8a35c58aa5cc1fd029cb886d5
--
View it on GitLab: https://salsa.debian.org/debian-gis-team/xarray-datatree/-/commit/497d39b228013ab8a35c58aa5cc1fd029cb886d5
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20240127/499ea7f4/attachment-0001.htm>
More information about the Pkg-grass-devel
mailing list