From 9168b4c912952b34c76e0fcc72e8136be6dabc3b Mon Sep 17 00:00:00 2001 From: "Brett M. Morris" Date: Mon, 27 Feb 2023 14:01:47 -0500 Subject: [PATCH 1/6] adding support for uncertainty extraction to NDDataArray --- glue_astronomy/translators/nddata.py | 32 ++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/glue_astronomy/translators/nddata.py b/glue_astronomy/translators/nddata.py index 4b50536..8b79cd6 100644 --- a/glue_astronomy/translators/nddata.py +++ b/glue_astronomy/translators/nddata.py @@ -8,6 +8,8 @@ from glue.core import Data, Subset from glue.core.coordinates import Coordinates +from .spectrum1d import SpectralCoordinates + def _get_attribute(attribute, data): if isinstance(attribute, str): @@ -52,6 +54,7 @@ class NDDataArrayHandler: def to_data(self, obj): data = Data(coords=obj.wcs) data['data'] = obj.data + data['uncertainty'] = obj.uncertainty.array data.get_component('data').units = str(obj.unit) data.meta.update(obj.meta) return data @@ -70,20 +73,45 @@ def to_object(self, data_or_subset, attribute=None): data, subset_state = _get_data_and_subset_state(data_or_subset) - if isinstance(data.coords, WCS) or isinstance(data.coords, BaseHighLevelWCS): + if ( + isinstance(data.coords, WCS) or + isinstance(data.coords, BaseHighLevelWCS) or + isinstance(data.coords, SpectralCoordinates) + ): wcs = data.coords elif type(data.coords) is Coordinates or data.coords is None: wcs = None else: raise TypeError('data.coords should be an instance of Coordinates or WCS') + component_labels = [d.label for d in data.component_ids()] + if attribute is None: + if 'data' in component_labels: + # by default look for the data attribute + attribute = 'data' + elif 'flux' in component_labels: + # if there is no data attribute, try flux + attribute = 'flux' + attribute = _get_attribute(attribute, data) component = data.get_component(attribute) values = data.get_data(attribute) values, mask = _get_value_and_mask(subset_state, data, values) + if 'uncertainty' in component_labels: + uncertainty = StdDevUncertainty( + data.get_component('uncertainty').data + ) + else: + uncertainty = None + result = NDDataArray( - values, unit=component.units, mask=mask, wcs=wcs, meta=data.meta + values, + unit=component.units, + mask=mask, + wcs=wcs, + meta=data.meta, + uncertainty=uncertainty ) return result From b735190de09b977c4bcf16fa69ab476cad88a86c Mon Sep 17 00:00:00 2001 From: "Brett M. Morris" Date: Mon, 27 Feb 2023 16:00:56 -0500 Subject: [PATCH 2/6] uncertainty bugfix Update glue_astronomy/translators/nddata.py Co-authored-by: P. L. Lim <2090236+pllim@users.noreply.github.com> Update glue_astronomy/translators/nddata.py Co-authored-by: P. L. Lim <2090236+pllim@users.noreply.github.com> --- glue_astronomy/translators/nddata.py | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/glue_astronomy/translators/nddata.py b/glue_astronomy/translators/nddata.py index 8b79cd6..d41ca5d 100644 --- a/glue_astronomy/translators/nddata.py +++ b/glue_astronomy/translators/nddata.py @@ -54,8 +54,11 @@ class NDDataArrayHandler: def to_data(self, obj): data = Data(coords=obj.wcs) data['data'] = obj.data - data['uncertainty'] = obj.uncertainty.array data.get_component('data').units = str(obj.unit) + if obj.uncertainty is not None: + data['uncertainty'] = getattr( + obj.uncertainty, 'array', obj.uncertainty + ) data.meta.update(obj.meta) return data @@ -73,11 +76,7 @@ def to_object(self, data_or_subset, attribute=None): data, subset_state = _get_data_and_subset_state(data_or_subset) - if ( - isinstance(data.coords, WCS) or - isinstance(data.coords, BaseHighLevelWCS) or - isinstance(data.coords, SpectralCoordinates) - ): + if isinstance(data.coords, (WCS, BaseHighLevelWCS, SpectralCoordinates)): wcs = data.coords elif type(data.coords) is Coordinates or data.coords is None: wcs = None @@ -86,12 +85,10 @@ def to_object(self, data_or_subset, attribute=None): component_labels = [d.label for d in data.component_ids()] if attribute is None: - if 'data' in component_labels: - # by default look for the data attribute - attribute = 'data' - elif 'flux' in component_labels: - # if there is no data attribute, try flux - attribute = 'flux' + for desired_label in ('data', 'flux'): + if desired_label in component_labels: + attribute = desired_label + break attribute = _get_attribute(attribute, data) component = data.get_component(attribute) From 4c6c95521f2192a7a5c81806543a5e1fc96d6b15 Mon Sep 17 00:00:00 2001 From: "Brett M. Morris" Date: Tue, 28 Feb 2023 10:30:44 -0500 Subject: [PATCH 3/6] improving test coverage of nduncertainties --- glue_astronomy/translators/tests/test_nddata.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/glue_astronomy/translators/tests/test_nddata.py b/glue_astronomy/translators/tests/test_nddata.py index bbe9b56..e3330d8 100644 --- a/glue_astronomy/translators/tests/test_nddata.py +++ b/glue_astronomy/translators/tests/test_nddata.py @@ -136,6 +136,22 @@ def test_from_nddata(cls, kwargs, data_attr): assert image_new.unit is u.Jy +def test_nddata_uncertainty(): + data = [[2, 3], [4, 5]] * u.Jy + uncertainty = StdDevUncertainty(np.ones_like(data)) + spec = NDDataArray( + data=data, + uncertainty=uncertainty + ) + data_collection = DataCollection() + data_collection['data'] = spec + + data = data_collection['data'] + spec_new = data.get_object(NDDataArray) + assert isinstance(spec_new.uncertainty, StdDevUncertainty) + assert_equal(spec_new.uncertainty.array, uncertainty.array) + + @pytest.mark.parametrize('with_wcs', (False, True)) def test_from_ccddata(with_wcs): From 91d8dbc8989871182d358dbd5fd46ccc7e29cdc2 Mon Sep 17 00:00:00 2001 From: "Brett M. Morris" Date: Wed, 1 Mar 2023 09:21:31 -0500 Subject: [PATCH 4/6] enforce uncert representation is StdDevUncertainty --- glue_astronomy/translators/nddata.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/glue_astronomy/translators/nddata.py b/glue_astronomy/translators/nddata.py index d41ca5d..93554ed 100644 --- a/glue_astronomy/translators/nddata.py +++ b/glue_astronomy/translators/nddata.py @@ -8,7 +8,7 @@ from glue.core import Data, Subset from glue.core.coordinates import Coordinates -from .spectrum1d import SpectralCoordinates +from .spectrum1d import SpectralCoordinates, UNCERT_REF def _get_attribute(attribute, data): @@ -56,9 +56,8 @@ def to_data(self, obj): data['data'] = obj.data data.get_component('data').units = str(obj.unit) if obj.uncertainty is not None: - data['uncertainty'] = getattr( - obj.uncertainty, 'array', obj.uncertainty - ) + uncert = obj.uncertainty.represent_as(StdDevUncertainty) + data['uncertainty'] = uncert.array data.meta.update(obj.meta) return data @@ -96,9 +95,12 @@ def to_object(self, data_or_subset, attribute=None): values, mask = _get_value_and_mask(subset_state, data, values) if 'uncertainty' in component_labels: - uncertainty = StdDevUncertainty( + uncert_cls = UNCERT_REF[ + data.meta.get('uncertainty_type', 'std') + ] + uncertainty = uncert_cls( data.get_component('uncertainty').data - ) + ).represent_as(StdDevUncertainty) else: uncertainty = None From 5d00ab601665690be8634c315a06e4b55cee2828 Mon Sep 17 00:00:00 2001 From: "Brett M. Morris" Date: Wed, 1 Mar 2023 10:19:36 -0500 Subject: [PATCH 5/6] Update glue_astronomy/translators/tests/test_nddata.py Co-authored-by: Derek Homeier --- glue_astronomy/translators/tests/test_nddata.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/glue_astronomy/translators/tests/test_nddata.py b/glue_astronomy/translators/tests/test_nddata.py index e3330d8..febf24a 100644 --- a/glue_astronomy/translators/tests/test_nddata.py +++ b/glue_astronomy/translators/tests/test_nddata.py @@ -136,9 +136,10 @@ def test_from_nddata(cls, kwargs, data_attr): assert image_new.unit is u.Jy -def test_nddata_uncertainty(): +@pytest.mark.parametrize('uncertainty_type', (StdDevUncertainty, VarianceUncertainty, InverseVariance)) +def test_nddata_uncertainty(uncertainty_type): data = [[2, 3], [4, 5]] * u.Jy - uncertainty = StdDevUncertainty(np.ones_like(data)) + uncertainty = uncertainty_type(np.ones_like(data.value)) spec = NDDataArray( data=data, uncertainty=uncertainty @@ -149,7 +150,10 @@ def test_nddata_uncertainty(): data = data_collection['data'] spec_new = data.get_object(NDDataArray) assert isinstance(spec_new.uncertainty, StdDevUncertainty) - assert_equal(spec_new.uncertainty.array, uncertainty.array) + if isinstance(uncertainty, StdDevUncertainty): + assert_equal(spec_new.uncertainty.array, uncertainty.array) + else: + assert_equal(spec_new.uncertainty.array, uncertainty.represent_as(StdDevUncertainty).array) @pytest.mark.parametrize('with_wcs', (False, True)) From 5469ccbafaeb235085650b325c39b379249ab6d4 Mon Sep 17 00:00:00 2001 From: "Brett M. Morris" Date: Wed, 1 Mar 2023 10:23:23 -0500 Subject: [PATCH 6/6] Codestyle fixes, testing bugfix --- glue_astronomy/translators/tests/test_nddata.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/glue_astronomy/translators/tests/test_nddata.py b/glue_astronomy/translators/tests/test_nddata.py index febf24a..e55a557 100644 --- a/glue_astronomy/translators/tests/test_nddata.py +++ b/glue_astronomy/translators/tests/test_nddata.py @@ -4,7 +4,9 @@ from astropy import units as u from astropy.nddata import CCDData, NDDataArray -from astropy.nddata.nduncertainty import StdDevUncertainty +from astropy.nddata.nduncertainty import ( + StdDevUncertainty, VarianceUncertainty, InverseVariance +) from astropy.wcs import WCS from glue.core import Data, DataCollection @@ -136,7 +138,11 @@ def test_from_nddata(cls, kwargs, data_attr): assert image_new.unit is u.Jy -@pytest.mark.parametrize('uncertainty_type', (StdDevUncertainty, VarianceUncertainty, InverseVariance)) +@pytest.mark.parametrize( + 'uncertainty_type', ( + StdDevUncertainty, VarianceUncertainty, InverseVariance + ) +) def test_nddata_uncertainty(uncertainty_type): data = [[2, 3], [4, 5]] * u.Jy uncertainty = uncertainty_type(np.ones_like(data.value)) @@ -150,7 +156,7 @@ def test_nddata_uncertainty(uncertainty_type): data = data_collection['data'] spec_new = data.get_object(NDDataArray) assert isinstance(spec_new.uncertainty, StdDevUncertainty) - if isinstance(uncertainty, StdDevUncertainty): + if isinstance(uncertainty, StdDevUncertainty): assert_equal(spec_new.uncertainty.array, uncertainty.array) else: assert_equal(spec_new.uncertainty.array, uncertainty.represent_as(StdDevUncertainty).array)