From 4b9e69d0f2574a0dcdbd14cd18f30fb22c3d95a1 Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Thu, 20 Oct 2022 14:08:41 +0200 Subject: [PATCH 1/3] Validate that axes are meaningful before plotting Catch obviously wrong axis where there is no variation of the setpoints along the axis and try falling back to guessing the shape. --- plottr/data/datadict.py | 15 +++++++++++++-- plottr/node/grid.py | 17 +++++++++++++---- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/plottr/data/datadict.py b/plottr/data/datadict.py index 01e6491b..a1fb63c0 100644 --- a/plottr/data/datadict.py +++ b/plottr/data/datadict.py @@ -1010,7 +1010,10 @@ def validate(self) -> bool: shp = None shpsrc = '' - for n, v in self.data_items(): + + data_items = dict(self.data_items()) + + for n, v in data_items.items(): if type(v['values']) not in [np.ndarray, np.ma.core.MaskedArray]: self[n]['values'] = np.array(v['values']) @@ -1023,6 +1026,14 @@ def validate(self) -> bool: msg += f" {v['values'].shape}, " msg += f"and '{shpsrc}' has {shp}.\n" + if 'axes' in v: + for axis_num, na in enumerate(v['axes']): + # check that the data of the axes matches its use + max_step_along_axes = np.max(np.abs(np.diff(data_items[na]['values'],axis=axis_num))) + if max_step_along_axes == 0: + msg += (f"Malformed data: {na} is expected to be {axis_num}th " + "axis but has no variation along that axis.\n") + if msg != '\n': raise ValueError(msg) @@ -1087,7 +1098,7 @@ def guess_shape_from_datadict(data: DataDict) -> \ def datadict_to_meshgrid(data: DataDict, target_shape: Union[Tuple[int, ...], None] = None, - inner_axis_order: Union[None, List[str]] = None, + inner_axis_order: Union[None, Sequence[str]] = None, use_existing_shape: bool = False) \ -> MeshgridDataDict: """ diff --git a/plottr/node/grid.py b/plottr/node/grid.py index 261cf9d8..88213aca 100644 --- a/plottr/node/grid.py +++ b/plottr/node/grid.py @@ -3,7 +3,6 @@ A node and widget for placing data onto a grid (or not). """ - from enum import Enum, unique from typing import Tuple, Dict, Any, List, Optional, Sequence, cast @@ -490,9 +489,19 @@ def process( inner_axis_order=order, ) elif method is GridOption.metadataShape: - dout = dd.datadict_to_meshgrid( - data, use_existing_shape=True - ) + try: + dout = dd.datadict_to_meshgrid( + data, use_existing_shape=True + ) + except ValueError as err: + if "Malformed data" in str(err): + self.node_logger.warning( + "Shape/Setpoint order does" + " not match data. Falling back to guessing shape" + ) + dout = dd.datadict_to_meshgrid(data) + else: + raise err except GriddingError: dout = data.expand() self.node_logger.info("data could not be gridded. Falling back " From c7e56f60a83db3f3f27a1743c5b8e044903902fb Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Mon, 24 Oct 2022 13:17:45 +0200 Subject: [PATCH 2/3] Adapt test now that error is caught in validation --- test/pytest/test_gridder.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/test/pytest/test_gridder.py b/test/pytest/test_gridder.py index 026c271f..e2a5dbae 100644 --- a/test/pytest/test_gridder.py +++ b/test/pytest/test_gridder.py @@ -93,12 +93,9 @@ def test_set_grid_with_order(qtbot): assert fc.outputValues()['dataOut']['vals']['axes'] == ['y', 'z', 'x'] # finally, specify manually. omitting inner shape doesn't work + # but will be caught by validation (and no data returned) node.grid = GridOption.specifyShape, dict(shape=(5, 2, 5)) - assert fc.outputValues()['dataOut'].data_vals('vals').shape == (5,2,5) - assert not num.arrays_equal( - fc.outputValues()['dataOut'].data_vals('vals'), - vv.transpose((1,2,0)), - ) + assert fc.outputValues()['dataOut'] is None # but using the right inner axis order should do it node.grid = GridOption.specifyShape, dict(order=['x', 'y', 'z'], From d23fd4b9937271270c6214136c3af31b4c9218cb Mon Sep 17 00:00:00 2001 From: "Jens H. Nielsen" Date: Mon, 24 Oct 2022 14:11:14 +0200 Subject: [PATCH 3/3] Don't attemt to validate empty axis --- plottr/data/datadict.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/plottr/data/datadict.py b/plottr/data/datadict.py index a1fb63c0..845e3e5a 100644 --- a/plottr/data/datadict.py +++ b/plottr/data/datadict.py @@ -1029,10 +1029,13 @@ def validate(self) -> bool: if 'axes' in v: for axis_num, na in enumerate(v['axes']): # check that the data of the axes matches its use - max_step_along_axes = np.max(np.abs(np.diff(data_items[na]['values'],axis=axis_num))) - if max_step_along_axes == 0: - msg += (f"Malformed data: {na} is expected to be {axis_num}th " - "axis but has no variation along that axis.\n") + # if data present + axis_data = data_items[na]['values'] + if axis_data.size > 0: + max_step_along_axes = np.max(np.abs(np.diff(data_items[na]['values'],axis=axis_num))) + if max_step_along_axes == 0: + msg += (f"Malformed data: {na} is expected to be {axis_num}th " + "axis but has no variation along that axis.\n") if msg != '\n': raise ValueError(msg)