-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refine DataTree.equals and DataTree.identical #9473
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1238,65 +1238,67 @@ def isomorphic( | |
except (TypeError, TreeIsomorphismError): | ||
return False | ||
|
||
def equals(self, other: DataTree, from_root: bool = True) -> bool: | ||
def _matching( | ||
self, | ||
other: DataTree, | ||
nodes_match: Callable[[DataTree, DataTree], bool], | ||
) -> bool: | ||
if not self.isomorphic(other, from_root=True, strict_names=True): | ||
return False | ||
|
||
return all( | ||
nodes_match(node, other_node) | ||
for node, other_node in zip(self.descendants, other.descendants) | ||
) | ||
|
||
def equals(self, other: DataTree) -> bool: | ||
""" | ||
Two DataTrees are equal if they have isomorphic node structures, with matching node names, | ||
and if they have matching variables and coordinates, all of which are equal. | ||
|
||
By default this method will check the whole tree above the given node. | ||
|
||
Parameters | ||
---------- | ||
other : DataTree | ||
The other tree object to compare to. | ||
from_root : bool, optional, default is True | ||
Whether or not to first traverse to the root of the two trees before checking for isomorphism. | ||
If neither tree has a parent then this has no effect. | ||
|
||
See Also | ||
-------- | ||
Dataset.equals | ||
DataTree.isomorphic | ||
DataTree.identical | ||
""" | ||
if not self.isomorphic(other, from_root=from_root, strict_names=True): | ||
to_ds = lambda x: x._to_dataset_view(inherited=True, rebuild_dims=False) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wouldn't it be simpler (and clearer) to use |
||
matcher = lambda x, y: to_ds(x).equals(to_ds(y)) | ||
if not matcher(self, other): | ||
return False | ||
return self._matching(other, nodes_match=matcher) | ||
|
||
return all( | ||
[ | ||
node.ds.equals(other_node.ds) | ||
for node, other_node in zip(self.subtree, other.subtree) | ||
] | ||
) | ||
|
||
def identical(self, other: DataTree, from_root=True) -> bool: | ||
def identical(self, other: DataTree) -> bool: | ||
""" | ||
Like equals, but will also check all dataset attributes and the attributes on | ||
all variables and coordinates. | ||
|
||
By default this method will check the whole tree above the given node. | ||
all variables and coordinates, and requires the coordinates are defined at the | ||
exact same levels of the DataTree hierarchy. | ||
|
||
Parameters | ||
---------- | ||
other : DataTree | ||
The other tree object to compare to. | ||
from_root : bool, optional, default is True | ||
Whether or not to first traverse to the root of the two trees before checking for isomorphism. | ||
If neither tree has a parent then this has no effect. | ||
|
||
See Also | ||
-------- | ||
Dataset.identical | ||
DataTree.isomorphic | ||
DataTree.equals | ||
""" | ||
if not self.isomorphic(other, from_root=from_root, strict_names=True): | ||
# include inherited coordinates only at the root; otherwise require that | ||
# everything is also defined at the same level | ||
Comment on lines
+1293
to
+1294
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do this for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh you did talk about this in your comment above |
||
self_view = self._to_dataset_view(inherited=True, rebuild_dims=False) | ||
other_view = other._to_dataset_view(inherited=True, rebuild_dims=False) | ||
if not self_view.identical(other_view): | ||
return False | ||
|
||
return all( | ||
node.ds.identical(other_node.ds) | ||
for node, other_node in zip(self.subtree, other.subtree) | ||
) | ||
to_ds = lambda x: x._to_dataset_view(inherited=False, rebuild_dims=False) | ||
matcher = lambda x, y: to_ds(x).identical(to_ds(y)) | ||
return self._matching(other, nodes_match=matcher) | ||
|
||
def filter(self: DataTree, filterfunc: Callable[[DataTree], bool]) -> DataTree: | ||
""" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm wondering if there is a problem with removing the
from_root
argument but keeping it fixed toTrue
insidecheck_isomorphic
...Let's imagines I
assert_equal
two subtrees that have different parents. All the nodes in the subtrees could match, but if the structures above the node where the subtree starts are not isomorphic, it will returnFalse
. That doesn't seem like desired behaviour to me - it leaves no way to check that the two subtrees are equal.