From 24569cf7583eb5f496ce57a83322876a42368322 Mon Sep 17 00:00:00 2001 From: DanRyanIrish Date: Wed, 28 Mar 2018 19:30:11 -0400 Subject: [PATCH 01/13] Prep inputs of NDCube.plot and move creation of 2D image axes into 2D plot helper function. --- ndcube/mixins/plotting.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/ndcube/mixins/plotting.py b/ndcube/mixins/plotting.py index 2470eed1f..c2343f53c 100644 --- a/ndcube/mixins/plotting.py +++ b/ndcube/mixins/plotting.py @@ -2,11 +2,12 @@ import numpy as np import matplotlib.pyplot as plt - import astropy.units as u from sunpy.visualization.imageanimator import ImageAnimatorWCS import sunpy.visualization.wcsaxes_compat as wcsaxes_compat +from ndcube.mixins.sequence_plotting import _prep_axes_kwargs + __all__ = ['NDCubePlotMixin'] @@ -53,12 +54,14 @@ def plot(self, axes=None, plot_axis_indices=[-1, -2], axes_coordinates=None, # If old API is used, convert to new API. plot_axis_indices, axes_coordiantes, axes_units, data_unit, kwargs = _support_101_plot_API( plot_axis_indices, axes_coordinates, axes_units, data_unit, kwargs) - axis_data = ['x' for i in range(2)] - axis_data[plot_axis_indices[1]] = 'y' + # Check kwargs are in consistent formats and set default values if not done so by user. + naxis = len(self.dimensions) + plot_axis_indices, axes_coordinates, axes_units = _prep_axes_kwargs( + naxis, plot_axis_indices, axes_coordinates, axes_units) if self.data.ndim is 1: plot = self._plot_1D_cube(data_unit=data_unit, origin=origin) elif self.data.ndim is 2: - plot = self._plot_2D_cube(axes=axes, plot_axis_indices=axis_data[::-1], **kwargs) + plot = self._plot_2D_cube(axes=axes, plot_axis_indices=plot_axis_indices, **kwargs) else: plot = self._plot_3D_cube(plot_axis_indices=plot_axis_indices, axes_coordinates=axes_coordinates, axes_units=axes_units, @@ -101,8 +104,12 @@ def _plot_2D_cube(self, axes=None, plot_axis_indices=None, **kwargs): second axis in WCS object will become the second axis of plot_axis_indices. Default: ['x', 'y'] """ - if not plot_axis_indices: - plot_axis_indices = ['x', 'y'] + if plot_axis_indices is None: + axis_data = ['x', 'y'] + else: + axis_data = ['x' for i in range(2)] + axis_data[plot_axis_indices[1]] = 'y' + axis_data = axis_data[::-1] if axes is None: if self.wcs.naxis is not 2: missing_axis = self.missing_axis @@ -110,7 +117,7 @@ def _plot_2D_cube(self, axes=None, plot_axis_indices=None, **kwargs): index = 0 for i, bool_ in enumerate(missing_axis): if not bool_: - slice_list.append(plot_axis_indices[index]) + slice_list.append(axis_data[index]) index += 1 else: slice_list.append(1) From da43081615c4c1f619ee316cb21c3087de8a0e84 Mon Sep 17 00:00:00 2001 From: DanRyanIrish Date: Wed, 28 Mar 2018 20:39:10 -0400 Subject: [PATCH 02/13] Reimplement NDCube 1D plot method to be aware of uncertainties, units and masks and give default labels. --- ndcube/mixins/plotting.py | 69 +++++++++++++++++++++++++++++++-------- 1 file changed, 56 insertions(+), 13 deletions(-) diff --git a/ndcube/mixins/plotting.py b/ndcube/mixins/plotting.py index c2343f53c..423b77c70 100644 --- a/ndcube/mixins/plotting.py +++ b/ndcube/mixins/plotting.py @@ -6,7 +6,7 @@ from sunpy.visualization.imageanimator import ImageAnimatorWCS import sunpy.visualization.wcsaxes_compat as wcsaxes_compat -from ndcube.mixins.sequence_plotting import _prep_axes_kwargs +from ndcube.mixins.sequence_plotting import _prep_axes_kwargs, _derive_1D_coordinates_and_units, _determine_sequence_units, _make_1D_sequence_plot __all__ = ['NDCubePlotMixin'] @@ -17,7 +17,7 @@ class NDCubePlotMixin: """ def plot(self, axes=None, plot_axis_indices=[-1, -2], axes_coordinates=None, - axes_units=None, data_unit=None, origin=0, **kwargs): + axes_units=None, data_unit=None, **kwargs): """ Plots an interactive visualization of this cube with a slider controlling the wavelength axis for data having dimensions greater than 2. @@ -50,6 +50,7 @@ def plot(self, axes=None, plot_axis_indices=[-1, -2], axes_coordinates=None, same length as the axis which will provide all values for that slider. If None is specified for an axis then the array indices will be used for that axis. + """ # If old API is used, convert to new API. plot_axis_indices, axes_coordiantes, axes_units, data_unit, kwargs = _support_101_plot_API( @@ -68,7 +69,8 @@ def plot(self, axes=None, plot_axis_indices=[-1, -2], axes_coordinates=None, **kwargs) return plot - def _plot_1D_cube(self, data_unit=None, origin=0): + def _plot_1D_cube(self, axes=None, axes_coordinates=None, axes_units=None, data_unit=None, + **kwargs): """ Plots a graph. Keyword arguments are passed on to matplotlib. @@ -77,17 +79,58 @@ def _plot_1D_cube(self, data_unit=None, origin=0): ---------- data_unit: `astropy.unit.Unit` The data is changed to the unit given or the cube.unit if not given. + """ - index_not_one = [] - for i, _bool in enumerate(self.missing_axis): - if not _bool: - index_not_one.append(i) - if data_unit is None: - data_unit = self.wcs.wcs.cunit[index_not_one[0]] - plot = plt.plot(self.pixel_to_world(*[u.Quantity(np.arange(self.data.shape[0]), - unit=u.pix)])[0].to(data_unit), - self.data) - return plot + # Derive x-axis coordinates and unit from inputs. + x_axis_coordinates, unit_x_axis = _derive_1D_coordinates_and_units(axes_coordinates, + axes_units) + if x_axis_coordinates is None: + # Default is to derive x coords and defaul xlabel from WCS object. + default_xlabel = self.world_axis_physical_types[0] + x_axis_coordinates = "{0} [{1}]".format(self.axis_world_coords(), unit_x_axis) + elif isinstance(x_axis_coordinates, str): + # User has entered a str as x coords, get that extra coord. + default_xlabel = "{0} [{1}]".format(x_axis_coordinates, unit_x_axis) + x_axis_coordinates = self.extra_coords[x_axis_coordinates]["value"] + # Else user must have set the x-values manually. + # If a unit has been set for the x-axis, try to convert x coords to that unit. + if isinstance(unit_x_axis, (u.UnitBase, str)): + if isinstance(x_axis_coordinates, u.Quantity): + default_xlabel = " [{0}]".format(unit_x_axis) + x_axis_coordinates = x_axis_coordinates.to(unit_x_axis) + else: + raise TypeError("Can only set unit for x axis if x-axis coordinates is input as " + "None, an astropy Quantity or the name of an extra coord that " + "is an astropy Quantity.") + # Derive y-axis coordinates, uncertainty and unit from the NDCube's data. + if self.unit is None: + if data_unit is not None: + raise ValueError("Can only set y-axis unit if self.unit is set to a " + "compatible unit.") + else: + ydata = self.data + yerror = self.uncertainty.array + else: + if data_unit is None: + data_unit = self.unit + ydata = self.data + if self.uncertainty is None: + yerror = None + else: + yerror = self.uncertainty.array + else: + ydata = (self.data * self.unit).to(data_unit).value + if self.uncertainty is None: + yerror = None + else: + yerror = (self.uncertainty.array * self.unit).to(data_unit).value + # Combine data and uncertainty with mask. + ydata = np.ma.masked_array(ydata, self.mask) + if yerror is not None: + yerror = np.ma.masked_array(yerror, self.mask) + # Create plot + fig, ax = _make_1D_sequence_plot(xdata, ydata, yerror, unit_y_axis, default_xlabel, kwargs) + return ax def _plot_2D_cube(self, axes=None, plot_axis_indices=None, **kwargs): """ From 78ed488b072ba7e15c429ebb6364e7adcb78ca4d Mon Sep 17 00:00:00 2001 From: DanRyanIrish Date: Thu, 29 Mar 2018 07:45:16 -0400 Subject: [PATCH 03/13] Reimplement NDCube 2D plot method to be aware of uncertainties, units and masks and give default labels. --- ndcube/mixins/plotting.py | 143 ++++++++++++++++++++++++++++++++------ 1 file changed, 123 insertions(+), 20 deletions(-) diff --git a/ndcube/mixins/plotting.py b/ndcube/mixins/plotting.py index 423b77c70..dbdee2a92 100644 --- a/ndcube/mixins/plotting.py +++ b/ndcube/mixins/plotting.py @@ -10,6 +10,10 @@ __all__ = ['NDCubePlotMixin'] +INVALID_UNIT_SET_MESSAGE = "Can only set unit for axis if corresponding coordinates in " + \ + "axes_coordinates are set to None, an astropy Quantity or the name of an extra coord that " + \ + "is an astropy Quantity." + class NDCubePlotMixin: """ @@ -99,14 +103,12 @@ def _plot_1D_cube(self, axes=None, axes_coordinates=None, axes_units=None, data_ default_xlabel = " [{0}]".format(unit_x_axis) x_axis_coordinates = x_axis_coordinates.to(unit_x_axis) else: - raise TypeError("Can only set unit for x axis if x-axis coordinates is input as " - "None, an astropy Quantity or the name of an extra coord that " - "is an astropy Quantity.") + raise TypeError(INVALID_UNIT_SET_MESSAGE) # Derive y-axis coordinates, uncertainty and unit from the NDCube's data. if self.unit is None: if data_unit is not None: - raise ValueError("Can only set y-axis unit if self.unit is set to a " - "compatible unit.") + raise TypeError("Can only set y-axis unit if self.unit is set to a " + "compatible unit.") else: ydata = self.data yerror = self.uncertainty.array @@ -146,29 +148,130 @@ def _plot_2D_cube(self, axes=None, plot_axis_indices=None, **kwargs): The first axis in WCS object will become the first axis of plot_axis_indices and second axis in WCS object will become the second axis of plot_axis_indices. Default: ['x', 'y'] + """ + # Set default values of kwargs if not set. + if axes_coordinates is None: + axes_coordinates = [None, None] + if axes_units is None: + axes_units = [None, None] + # Set which cube dimensions are on the x an y axes. if plot_axis_indices is None: axis_data = ['x', 'y'] else: - axis_data = ['x' for i in range(2)] + axis_data = ['x', 'x'] axis_data[plot_axis_indices[1]] = 'y' axis_data = axis_data[::-1] + # Determine data to be plotted + if data_unit is None: + data = self.data + else: + # If user set data_unit, convert dat to desired unit if self.unit set. + if self.unit is None: + raise TypeError("Can only set data_unit if NDCube.unit is set.") + else: + data = (self.data * self.unit).to(data_unit).value + # Combine data with mask + data = np.ma.masked_array(data, self.mask) if axes is None: - if self.wcs.naxis is not 2: - missing_axis = self.missing_axis - slice_list = [] - index = 0 - for i, bool_ in enumerate(missing_axis): - if not bool_: - slice_list.append(axis_data[index]) - index += 1 + if axes_coordinates == [None, None]: + # Build slice list for WCS for initializing WCSAxes object. + if self.wcs.naxis is not 2: + slice_list = [] + index = 0 + for i, bool_ in enumerate(self.missing_axis): + if not bool_: + slice_list.append(axis_data[index]) + index += 1 + else: + slice_list.append(1) + if index is not 2: + raise ValueError("Dimensions of WCS and data don't match") + ax = wcsaxes_compat.gca_wcs(self.wcs, slices=slice_list) + # Set axis labels + x_wcs_axis = utils.wcs.data_axis_to_wcs_axis(plot_axis_indices[0], + self.missing_axis) + ax.set_xlabel("{0} [{1}]".format( + self.world_axis_physical_types[plot_axis_indices[0]], + self.wcs.wcs.cunit[x_wcs_axis]) + y_wcs_axis = utils.wcs.data_axis_to_wcs_axis(plot_axis_indices[1], + self.missing_axis) + ax.set_ylabel("{0} [{1}]".format( + self.world_axis_physical_types[plot_axis_indices[1]], + self.wcs.wcs.cunit[y_wcs_axis]) + # Plot data + ax.imshow(data, **kwargs) + else: + # Else manually set axes x and y values based on user's input for axes_coordinates. + axes_values = [] + default_labels = [] + for i, plot_axis_index in enumerate(plot_axis_indices): + # If axis coordinate is None, derive axis values from WCS. + if axes_coordinates[plot_axis_index] is None: + if axes_units[plot_axis_index] is None: + # N.B. This assumed axes are independent. Fix this before merging!!! + axis_value = self.axis_world_coords(plot_axis_index) + axes_units[plot_axis_index] = axis_value.unit + axis_value = self.axis_world_coords(plot_axis_index).value + else: + axis_value = self.axis_world_coords(plot_axis_index).to( + axes_units[plot_axis_index]).value + # Derive default axis label. + default_label = "{0} [{1}]".format( + self.world_axis_physical_types[plot_axis_index], + axes_units[plot_axis_index]) + elif isinstance(axes_coordinates[plot_axis_index], str): + # If axis coordinate is a string, derive axis values from + # corresponding extra coord. + axis_label_text = copy.deepcopy(axes_coordinates[plot_axis_index]) + axis_value = self.extra_coord[axes_coordinates[plot_axis_index]] + if isinstance(axis_value, u.Quantity): + if axes_units[plot_axis_index] is None: + axes_units[plot_axis_index] = axis_value.unit + axis_value = axis_value.value + else: + if axes_units[plot_axis_index] is not None: + raise TypeError(INVALID_UNIT_SET_MESSAGE) + default_label = "{0} [{1}]".format(axis_label_text, + axes_units[plot_axis_index]) else: - slice_list.append(1) - if index is not 2: - raise ValueError("Dimensions of WCS and data don't match") - axes = wcsaxes_compat.gca_wcs(self.wcs, slices=slice_list) - plot = axes.imshow(self.data, **kwargs) - return plot + # Else user must have manually set the axis coordinates. + if isinstance(axes_coordinates[plot_axis_index], u.Quantity): + if axes_units[plot_axis_index] is None: + axis_value = axes_coordinates[plot_axis_index].value + else: + axis_value = axes_coordinates[plot_axis_index].to( + axes_units[plot_axis_index]) + axes_units[plot_axis_index] = axis_value.unit + axis_value = axis_value.value + else: + if axes_units[plot_axis_index] is None: + axis_value = axes_coordinates[plot_axis_index] + else: + raise TypeError(INVALID_UNIT_SET_MESSAGE) + default_label = " [{0}]".format(axes_units[plot_axis_index]) + axes_values.append(axis_value) + default_labels.append(default_label) + # Initialize axes object and set values along axis. + fig, ax = plt.subplots(1, 1) + # Since we can't assume the x-axis will be uniform, create NonUniformImage + # axes and add it to the axes object. + im_ax = mpl.image.NonUniformImage( + ax, extent=(axes_values[0][0], axes_values[0][-1], + axes_values[1][0], axes_values[1][-1]), **kwargs) + im_ax.set_data(axes_coordinates[plot_axis_indices[0]], + axes_coordinates[plot_axis_indices[1]], data) + ax.add_image(im_ax) + # Set the limits, labels, etc. of the axes. + ax.set_xlim((axes_coordinates[plot_axis_indices[0]][0], + axes_coordinates[plot_axis_indices[0]][-1])) + ax.set_ylim((axes_coordinates[plot_axis_indices[1]][0], + axes_coordinates[plot_axis_indices[1]][-1])) + xlabel = kwargs.pop("xlabel", default_labels[0]) + ylabel = kwargs.pop("ylabel", default_labels[1]) + ax.set_xlabel(xlabel) + ax.set_ylabel(ylabel) + return ax def _plot_3D_cube(self, plot_axis_indices=None, axes_units=None, axes_coordinates=None, **kwargs): From 33f388fef9e6e1ce8b96be8a6aef9dd0662136f7 Mon Sep 17 00:00:00 2001 From: DanRyanIrish Date: Thu, 29 Mar 2018 09:37:09 -0400 Subject: [PATCH 04/13] Fix bugs and tests for NDCubePlotMixin 1D and 2D plot/image cases. --- ndcube/mixins/plotting.py | 37 +++++++++++++++++++++-------------- ndcube/tests/test_plotting.py | 26 ++++++++++++------------ 2 files changed, 34 insertions(+), 29 deletions(-) diff --git a/ndcube/mixins/plotting.py b/ndcube/mixins/plotting.py index dbdee2a92..043b8252f 100644 --- a/ndcube/mixins/plotting.py +++ b/ndcube/mixins/plotting.py @@ -6,6 +6,7 @@ from sunpy.visualization.imageanimator import ImageAnimatorWCS import sunpy.visualization.wcsaxes_compat as wcsaxes_compat +from ndcube import utils from ndcube.mixins.sequence_plotting import _prep_axes_kwargs, _derive_1D_coordinates_and_units, _determine_sequence_units, _make_1D_sequence_plot __all__ = ['NDCubePlotMixin'] @@ -64,7 +65,7 @@ def plot(self, axes=None, plot_axis_indices=[-1, -2], axes_coordinates=None, plot_axis_indices, axes_coordinates, axes_units = _prep_axes_kwargs( naxis, plot_axis_indices, axes_coordinates, axes_units) if self.data.ndim is 1: - plot = self._plot_1D_cube(data_unit=data_unit, origin=origin) + plot = self._plot_1D_cube(data_unit=data_unit) elif self.data.ndim is 2: plot = self._plot_2D_cube(axes=axes, plot_axis_indices=plot_axis_indices, **kwargs) else: @@ -90,19 +91,24 @@ def _plot_1D_cube(self, axes=None, axes_coordinates=None, axes_units=None, data_ axes_units) if x_axis_coordinates is None: # Default is to derive x coords and defaul xlabel from WCS object. - default_xlabel = self.world_axis_physical_types[0] - x_axis_coordinates = "{0} [{1}]".format(self.axis_world_coords(), unit_x_axis) + default_xlabel = "{0} [{1}]".format(self.world_axis_physical_types[0], unit_x_axis) + xdata = self.axis_world_coords() elif isinstance(x_axis_coordinates, str): # User has entered a str as x coords, get that extra coord. default_xlabel = "{0} [{1}]".format(x_axis_coordinates, unit_x_axis) - x_axis_coordinates = self.extra_coords[x_axis_coordinates]["value"] - # Else user must have set the x-values manually. + xdata = self.extra_coords[x_axis_coordinates]["value"] + else: + # Else user must have set the x-values manually. + default_xlabel = " [{0}]".format(unit_x_axis) + xdata = x_axis_coordinates # If a unit has been set for the x-axis, try to convert x coords to that unit. - if isinstance(unit_x_axis, (u.UnitBase, str)): - if isinstance(x_axis_coordinates, u.Quantity): - default_xlabel = " [{0}]".format(unit_x_axis) - x_axis_coordinates = x_axis_coordinates.to(unit_x_axis) + if isinstance(xdata, u.Quantity): + if unit_x_axis is None: + xdata = xdata.value else: + xdata = xdata.to(unit_x_axis).value + else: + if unit_x_axis is not None: raise TypeError(INVALID_UNIT_SET_MESSAGE) # Derive y-axis coordinates, uncertainty and unit from the NDCube's data. if self.unit is None: @@ -131,10 +137,11 @@ def _plot_1D_cube(self, axes=None, axes_coordinates=None, axes_units=None, data_ if yerror is not None: yerror = np.ma.masked_array(yerror, self.mask) # Create plot - fig, ax = _make_1D_sequence_plot(xdata, ydata, yerror, unit_y_axis, default_xlabel, kwargs) + fig, ax = _make_1D_sequence_plot(xdata, ydata, yerror, data_unit, default_xlabel, kwargs) return ax - def _plot_2D_cube(self, axes=None, plot_axis_indices=None, **kwargs): + def _plot_2D_cube(self, axes=None, plot_axis_indices=None, axes_coordinates=None, + axes_units=None, data_unit=None, **kwargs): """ Plots a 2D image onto the current axes. Keyword arguments are passed on to matplotlib. @@ -189,16 +196,16 @@ def _plot_2D_cube(self, axes=None, plot_axis_indices=None, **kwargs): raise ValueError("Dimensions of WCS and data don't match") ax = wcsaxes_compat.gca_wcs(self.wcs, slices=slice_list) # Set axis labels - x_wcs_axis = utils.wcs.data_axis_to_wcs_axis(plot_axis_indices[0], + x_wcs_axis = utils.cube.data_axis_to_wcs_axis(plot_axis_indices[0], self.missing_axis) ax.set_xlabel("{0} [{1}]".format( self.world_axis_physical_types[plot_axis_indices[0]], - self.wcs.wcs.cunit[x_wcs_axis]) - y_wcs_axis = utils.wcs.data_axis_to_wcs_axis(plot_axis_indices[1], + self.wcs.wcs.cunit[x_wcs_axis])) + y_wcs_axis = utils.cube.data_axis_to_wcs_axis(plot_axis_indices[1], self.missing_axis) ax.set_ylabel("{0} [{1}]".format( self.world_axis_physical_types[plot_axis_indices[1]], - self.wcs.wcs.cunit[y_wcs_axis]) + self.wcs.wcs.cunit[y_wcs_axis])) # Plot data ax.imshow(data, **kwargs) else: diff --git a/ndcube/tests/test_plotting.py b/ndcube/tests/test_plotting.py index 1d44888d4..4f58685f5 100644 --- a/ndcube/tests/test_plotting.py +++ b/ndcube/tests/test_plotting.py @@ -54,8 +54,8 @@ @pytest.mark.parametrize("test_input, test_kwargs, expected_values", [ (cube[0, 0], {}, - (u.Quantity([0.4, 0.8, 1.2, 1.6], unit="min"), np.array([1, 2, 3, 4]), - "", "", (0.4, 1.6), (1, 4))) + (np.array([0.4, 0.8, 1.2, 1.6]), np.array([1, 2, 3, 4]), + "time [None]", "Data [None]", (0.4, 1.6), (1, 4))) ]) def test_cube_plot_1D(test_input, test_kwargs, expected_values): # Unpack expected properties. @@ -64,21 +64,19 @@ def test_cube_plot_1D(test_input, test_kwargs, expected_values): # Run plot method. output = test_input.plot(**test_kwargs) # Check plot properties are correct. - assert type(output) is list - assert len(output) == 1 - output = output[0] - assert type(output) is matplotlib.lines.Line2D + assert isinstance(output, matplotlib.axes.Axes) output_xdata = (output.axes.lines[0].get_xdata()) - if type(expected_xdata) == u.Quantity: + if type(output_xdata) == u.Quantity: assert output_xdata.unit == expected_xdata.unit assert np.allclose(output_xdata.value, expected_xdata.value) else: - np.testing.assert_array_equal(output.axes.lines[0].get_xdata(), expected_xdata) - if type(expected_ydata) == u.Quantity: + assert np.allclose(output_xdata, expected_xdata) + output_ydata = (output.axes.lines[0].get_ydata()) + if type(output_ydata) == u.Quantity: assert output_ydata.unit == expected_ydata.unit assert np.allclose(output_ydata.value, expected_ydata.value) else: - np.testing.assert_array_equal(output.axes.lines[0].get_ydata(), expected_ydata) + assert np.allclose(output.axes.lines[0].get_ydata(), expected_ydata) assert output.axes.get_xlabel() == expected_xlabel assert output.axes.get_ylabel() == expected_ylabel output_xlim = output.axes.get_xlim() @@ -91,7 +89,7 @@ def test_cube_plot_1D(test_input, test_kwargs, expected_values): @pytest.mark.parametrize("test_input, test_kwargs, expected_values", [ (cube[0], {}, - (cube[0].data, "", "", + (cube[0].data, "time [min]", "em.wl [m]", (-0.5, 3.5, 2.5, -0.5))) ]) def test_cube_plot_2D(test_input, test_kwargs, expected_values): @@ -101,11 +99,11 @@ def test_cube_plot_2D(test_input, test_kwargs, expected_values): # Run plot method. output = test_input.plot(**test_kwargs) # Check plot properties are correct. - assert type(output) is matplotlib.image.AxesImage - np.testing.assert_array_equal(output.get_array(), expected_data) + assert isinstance(output, matplotlib.axes.Axes) + np.testing.assert_array_equal(output.images[0].get_array(), expected_data) assert output.axes.xaxis.get_label_text() == expected_xlabel assert output.axes.yaxis.get_label_text() == expected_ylabel - assert np.allclose(output.get_extent(), expected_extent) + assert np.allclose(output.images[0].get_extent(), expected_extent) @pytest.mark.parametrize("test_input, test_kwargs, expected_values", [ From 581bf5fc059f83990ac201a333773fa8e41fb919 Mon Sep 17 00:00:00 2001 From: DanRyanIrish Date: Thu, 29 Mar 2018 10:48:58 -0400 Subject: [PATCH 05/13] Improvements to and test coverage increase for NDCube 1D plot case. --- ndcube/mixins/plotting.py | 23 ++++-- ndcube/tests/test_plotting.py | 134 ++++++++++++++++++++++++++++------ 2 files changed, 129 insertions(+), 28 deletions(-) diff --git a/ndcube/mixins/plotting.py b/ndcube/mixins/plotting.py index 043b8252f..4adfa602b 100644 --- a/ndcube/mixins/plotting.py +++ b/ndcube/mixins/plotting.py @@ -21,7 +21,7 @@ class NDCubePlotMixin: Add plotting functionality to a NDCube class. """ - def plot(self, axes=None, plot_axis_indices=[-1, -2], axes_coordinates=None, + def plot(self, axes=None, plot_axis_indices=None, axes_coordinates=None, axes_units=None, data_unit=None, **kwargs): """ Plots an interactive visualization of this cube with a slider @@ -35,7 +35,7 @@ def plot(self, axes=None, plot_axis_indices=[-1, -2], axes_coordinates=None, ---------- plot_axis_indices: `list` The two axes that make the image. - Like [-1,-2] this implies cube instance -1 dimension + Default=[-1,-2]. This implies cube instance -1 dimension will be x-axis and -2 dimension will be y-axis. axes: `astropy.visualization.wcsaxes.core.WCSAxes` or None: @@ -65,7 +65,8 @@ def plot(self, axes=None, plot_axis_indices=[-1, -2], axes_coordinates=None, plot_axis_indices, axes_coordinates, axes_units = _prep_axes_kwargs( naxis, plot_axis_indices, axes_coordinates, axes_units) if self.data.ndim is 1: - plot = self._plot_1D_cube(data_unit=data_unit) + plot = self._plot_1D_cube(axes, axes_coordinates, + axes_units, data_unit, **kwargs) elif self.data.ndim is 2: plot = self._plot_2D_cube(axes=axes, plot_axis_indices=plot_axis_indices, **kwargs) else: @@ -91,25 +92,30 @@ def _plot_1D_cube(self, axes=None, axes_coordinates=None, axes_units=None, data_ axes_units) if x_axis_coordinates is None: # Default is to derive x coords and defaul xlabel from WCS object. - default_xlabel = "{0} [{1}]".format(self.world_axis_physical_types[0], unit_x_axis) + xname = self.world_axis_physical_types[0] xdata = self.axis_world_coords() elif isinstance(x_axis_coordinates, str): # User has entered a str as x coords, get that extra coord. - default_xlabel = "{0} [{1}]".format(x_axis_coordinates, unit_x_axis) + xname = x_axis_coordinates xdata = self.extra_coords[x_axis_coordinates]["value"] else: # Else user must have set the x-values manually. - default_xlabel = " [{0}]".format(unit_x_axis) + xname = "" xdata = x_axis_coordinates # If a unit has been set for the x-axis, try to convert x coords to that unit. if isinstance(xdata, u.Quantity): if unit_x_axis is None: + unit_x_axis = xdata.unit xdata = xdata.value else: xdata = xdata.to(unit_x_axis).value else: if unit_x_axis is not None: raise TypeError(INVALID_UNIT_SET_MESSAGE) + # Define default x axis label. + default_xlabel = "{0} [{1}]".format(xname, unit_x_axis) + # Combine data and uncertainty with mask. + xdata = np.ma.masked_array(xdata, self.mask) # Derive y-axis coordinates, uncertainty and unit from the NDCube's data. if self.unit is None: if data_unit is not None: @@ -117,7 +123,10 @@ def _plot_1D_cube(self, axes=None, axes_coordinates=None, axes_units=None, data_ "compatible unit.") else: ydata = self.data - yerror = self.uncertainty.array + if self.uncertainty is None: + yerror = None + else: + yerror = self.uncertainty.array else: if data_unit is None: data_unit = self.unit diff --git a/ndcube/tests/test_plotting.py b/ndcube/tests/test_plotting.py index 4f58685f5..921737c9e 100644 --- a/ndcube/tests/test_plotting.py +++ b/ndcube/tests/test_plotting.py @@ -38,24 +38,105 @@ mask=mask_cube, uncertainty=uncertainty, missing_axis=[False, False, False, True], - extra_coords=[('time', 0, u.Quantity(range(data.shape[0]), unit=u.pix)), - ('hello', 1, u.Quantity(range(data.shape[1]), unit=u.pix)), - ('bye', 2, u.Quantity(range(data.shape[2]), unit=u.pix))]) + extra_coords=[('time', 0, u.Quantity(range(data.shape[0]), unit=u.s)), + ('hello', 1, u.Quantity(range(data.shape[1]), unit=u.W)), + ('bye', 2, u.Quantity(range(data.shape[2]), unit=u.m)), + ('another time', 2, np.array( + [datetime.datetime(2000, 1, 1)+datetime.timedelta(minutes=i) + for i in range(data.shape[2])])) + ]) + +cube_unit = NDCube( + data, + wt, + mask=mask_cube, + unit=u.J, + uncertainty=uncertainty, + missing_axis=[False, False, False, True], + extra_coords=[('time', 0, u.Quantity(range(data.shape[0]), unit=u.s)), + ('hello', 1, u.Quantity(range(data.shape[1]), unit=u.W)), + ('bye', 2, u.Quantity(range(data.shape[2]), unit=u.m)), + ('another time', 2, np.array( + [datetime.datetime(2000, 1, 1)+datetime.timedelta(minutes=i) + for i in range(data.shape[2])])) + ]) + +cube_no_uncertainty = NDCube( + data, + wt, + mask=mask_cube, + missing_axis=[False, False, False, True], + extra_coords=[('time', 0, u.Quantity(range(data.shape[0]), unit=u.s)), + ('hello', 1, u.Quantity(range(data.shape[1]), unit=u.W)), + ('bye', 2, u.Quantity(range(data.shape[2]), unit=u.m)), + ('another time', 2, np.array( + [datetime.datetime(2000, 1, 1)+datetime.timedelta(minutes=i) + for i in range(data.shape[2])])) + ]) + +cube_unit_no_uncertainty = NDCube( + data, + wt, + mask=mask_cube, + unit=u.J, + missing_axis=[False, False, False, True], + extra_coords=[('time', 0, u.Quantity(range(data.shape[0]), unit=u.s)), + ('hello', 1, u.Quantity(range(data.shape[1]), unit=u.W)), + ('bye', 2, u.Quantity(range(data.shape[2]), unit=u.m)), + ('another time', 2, np.array( + [datetime.datetime(2000, 1, 1)+datetime.timedelta(minutes=i) + for i in range(data.shape[2])])) + ]) cubem = NDCube( data, wm, mask=mask_cube, uncertainty=uncertainty, - extra_coords=[('time', 0, u.Quantity(range(data.shape[0]), unit=u.pix)), - ('hello', 1, u.Quantity(range(data.shape[1]), unit=u.pix)), - ('bye', 2, u.Quantity(range(data.shape[2]), unit=u.pix))]) + extra_coords=[('time', 0, u.Quantity(range(data.shape[0]), unit=u.s)), + ('hello', 1, u.Quantity(range(data.shape[1]), unit=u.W)), + ('bye', 2, u.Quantity(range(data.shape[2]), unit=u.m)), + ('another time', 2, np.array( + [datetime.datetime(2000, 1, 1)+datetime.timedelta(minutes=i) + for i in range(data.shape[2])])) + ]) @pytest.mark.parametrize("test_input, test_kwargs, expected_values", [ (cube[0, 0], {}, - (np.array([0.4, 0.8, 1.2, 1.6]), np.array([1, 2, 3, 4]), - "time [None]", "Data [None]", (0.4, 1.6), (1, 4))) + (np.ma.masked_array([0.4, 0.8, 1.2, 1.6], cube[0, 0].mask), + np.ma.masked_array(cube[0, 0].data, cube[0, 0].mask), + "time [min]", "Data [None]", (0.4, 1.6), (1, 4))), + + (cube_unit[0, 0], {"axes_coordinates": "bye", "axes_units": "km", "data_unit": u.erg}, + (np.ma.masked_array(cube_unit[0, 0].extra_coords["bye"]["value"].to(u.km).value, + cube_unit[0, 0].mask), + np.ma.masked_array(u.Quantity(cube_unit[0, 0].data, + unit=cube_unit[0, 0].unit).to(u.erg).value, + cube_unit[0, 0].mask), + "bye [km]", "Data [erg]", (0, 0.003), (10000000, 40000000))), + + (cube_unit[0, 0], {"axes_coordinates": np.arange(10, 10+cube_unit[0, 0].data.shape[0])}, + (np.ma.masked_array(np.arange(10, 10+cube_unit[0, 0].data.shape[0]), cube_unit[0, 0].mask), + np.ma.masked_array(cube_unit[0, 0].data, cube_unit[0, 0].mask), + " [None]", "Data [J]", (10, 10+cube_unit[0, 0].data.shape[0]-1), (1, 4))), + + (cube_no_uncertainty[0, 0], {}, + (np.ma.masked_array([0.4, 0.8, 1.2, 1.6], cube_no_uncertainty[0, 0].mask), + np.ma.masked_array(cube_no_uncertainty[0, 0].data, cube_no_uncertainty[0, 0].mask), + "time [min]", "Data [None]", (0.4, 1.6), (1, 4))), + + (cube_unit_no_uncertainty[0, 0], {}, + (np.ma.masked_array([0.4, 0.8, 1.2, 1.6], cube_unit_no_uncertainty[0, 0].mask), + np.ma.masked_array(cube_no_uncertainty[0, 0].data, cube_unit_no_uncertainty[0, 0].mask), + "time [min]", "Data [J]", (0.4, 1.6), (1, 4))), + + (cube_unit_no_uncertainty[0, 0], {"data_unit": u.erg}, + (np.ma.masked_array([0.4, 0.8, 1.2, 1.6], cube_unit_no_uncertainty[0, 0].mask), + np.ma.masked_array(u.Quantity(cube_unit[0, 0].data, + unit=cube_unit[0, 0].unit).to(u.erg).value, + cube_unit[0, 0].mask), + "time [min]", "Data [erg]", (0.4, 1.6), (10000000, 40000000))) ]) def test_cube_plot_1D(test_input, test_kwargs, expected_values): # Unpack expected properties. @@ -64,21 +145,26 @@ def test_cube_plot_1D(test_input, test_kwargs, expected_values): # Run plot method. output = test_input.plot(**test_kwargs) # Check plot properties are correct. + # Type assert isinstance(output, matplotlib.axes.Axes) + # Check x axis data output_xdata = (output.axes.lines[0].get_xdata()) - if type(output_xdata) == u.Quantity: - assert output_xdata.unit == expected_xdata.unit - assert np.allclose(output_xdata.value, expected_xdata.value) + assert np.allclose(output_xdata.data, expected_xdata.data) + if isinstance(output_xdata.mask, np.ndarray): + np.testing.assert_array_equal(output_xdata.mask, expected_xdata.mask) else: - assert np.allclose(output_xdata, expected_xdata) + assert output_xdata.mask == expected_xdata.mask + # Check y axis data output_ydata = (output.axes.lines[0].get_ydata()) - if type(output_ydata) == u.Quantity: - assert output_ydata.unit == expected_ydata.unit - assert np.allclose(output_ydata.value, expected_ydata.value) + assert np.allclose(output_ydata.data, expected_ydata.data) + if isinstance(output_ydata.mask, np.ndarray): + np.testing.assert_array_equal(output_ydata.mask, expected_ydata.mask) else: - assert np.allclose(output.axes.lines[0].get_ydata(), expected_ydata) + assert output_ydata.mask == expected_ydata.mask + # Check axis labels assert output.axes.get_xlabel() == expected_xlabel assert output.axes.get_ylabel() == expected_ylabel + # Check axis limits output_xlim = output.axes.get_xlim() assert output_xlim[0] <= expected_xlim[0] assert output_xlim[1] >= expected_xlim[1] @@ -87,6 +173,16 @@ def test_cube_plot_1D(test_input, test_kwargs, expected_values): assert output_ylim[1] >= expected_ylim[1] +@pytest.mark.parametrize("test_input, test_kwargs, expected_error", [ + (cube[0, 0], {"axes_coordinates": np.arange(10, 10+cube_unit[0, 0].data.shape[0]), + "axes_units": u.C}, TypeError), + (cube[0, 0], {"data_unit": u.C}, TypeError) + ]) +def test_cube_plot_1D_errors(test_input, test_kwargs, expected_error): + with pytest.raises(expected_error): + output = test_input.plot(**test_kwargs) + + @pytest.mark.parametrize("test_input, test_kwargs, expected_values", [ (cube[0], {}, (cube[0].data, "time [min]", "em.wl [m]", @@ -110,7 +206,7 @@ def test_cube_plot_2D(test_input, test_kwargs, expected_values): (cubem, {}, (cubem.data, [np.array([0., 2.]), [0, 3], [0, 4]], "", "")) ]) -def test_cube_animate_ND(test_input, test_kwargs, expected_values): +def test_cube_plot_ND_as_2DAnimation(test_input, test_kwargs, expected_values): # Unpack expected properties. expected_data, expected_axis_ranges, expected_xlabel, expected_ylabel = expected_values # Run plot method. @@ -122,10 +218,6 @@ def test_cube_animate_ND(test_input, test_kwargs, expected_values): assert output.axes.yaxis.get_label_text() == expected_ylabel -def test_cube_plot_ND_as_2DAnimation(): - pass - - @pytest.mark.parametrize("input_values, expected_values", [ ((None, None, None, None, {"image_axes": [-1, -2], "axis_ranges": [np.arange(3), np.arange(3)], From 7ac0976d7ad0d6f3601e8753b88386ae9b9a2ab1 Mon Sep 17 00:00:00 2001 From: DanRyanIrish Date: Thu, 29 Mar 2018 11:17:55 -0400 Subject: [PATCH 06/13] Allow NDCube.axis_world_coords to handle negative axis numbers. --- CHANGELOG.rst | 2 ++ ndcube/ndcube.py | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index de000b8be..25a3ec4e4 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -21,6 +21,8 @@ API Changes Bug Fixes --------- +- Allowed `~ndcube.NDCubeBase.axis_world_coords` to accept negative + axis indices as arguments. [#106] 1.0.1 diff --git a/ndcube/ndcube.py b/ndcube/ndcube.py index 486c91463..df5e3ca4f 100644 --- a/ndcube/ndcube.py +++ b/ndcube/ndcube.py @@ -320,7 +320,10 @@ def axis_world_coords(self, *axes): int_axes = np.empty(len(axes), dtype=int) for i, axis in enumerate(axes): if isinstance(axis, int): - int_axes[i] = axis + if axis < 0: + int_axes[i] = n_dimensions + axis + else: + int_axes[i] = axis elif isinstance(axis, str): int_axes[i] = utils.cube.get_axis_number_from_axis_name( axis, world_axis_types) From 8a2ebfd583bbea669cdb62ccc9f366ae3fc983b5 Mon Sep 17 00:00:00 2001 From: DanRyanIrish Date: Thu, 29 Mar 2018 12:11:15 -0400 Subject: [PATCH 07/13] Fix bugs and increase code coverage of NDCube 2D plot case as part of writing tests. --- ndcube/mixins/plotting.py | 52 +++++++++++++++++++---------------- ndcube/tests/test_plotting.py | 33 ++++++++++++++++++++-- 2 files changed, 59 insertions(+), 26 deletions(-) diff --git a/ndcube/mixins/plotting.py b/ndcube/mixins/plotting.py index 4adfa602b..dd4adb57c 100644 --- a/ndcube/mixins/plotting.py +++ b/ndcube/mixins/plotting.py @@ -1,6 +1,8 @@ from warnings import warn +import copy import numpy as np +import matplotlib as mpl import matplotlib.pyplot as plt import astropy.units as u from sunpy.visualization.imageanimator import ImageAnimatorWCS @@ -68,7 +70,8 @@ def plot(self, axes=None, plot_axis_indices=None, axes_coordinates=None, plot = self._plot_1D_cube(axes, axes_coordinates, axes_units, data_unit, **kwargs) elif self.data.ndim is 2: - plot = self._plot_2D_cube(axes=axes, plot_axis_indices=plot_axis_indices, **kwargs) + plot = self._plot_2D_cube(axes, plot_axis_indices, axes_coordinates, + axes_units, data_unit, **kwargs) else: plot = self._plot_3D_cube(plot_axis_indices=plot_axis_indices, axes_coordinates=axes_coordinates, axes_units=axes_units, @@ -172,12 +175,9 @@ def _plot_2D_cube(self, axes=None, plot_axis_indices=None, axes_coordinates=None if axes_units is None: axes_units = [None, None] # Set which cube dimensions are on the x an y axes. - if plot_axis_indices is None: - axis_data = ['x', 'y'] - else: - axis_data = ['x', 'x'] - axis_data[plot_axis_indices[1]] = 'y' - axis_data = axis_data[::-1] + axis_data = ['x', 'x'] + axis_data[plot_axis_indices[1]] = 'y' + axis_data = axis_data[::-1] # Determine data to be plotted if data_unit is None: data = self.data @@ -190,7 +190,11 @@ def _plot_2D_cube(self, axes=None, plot_axis_indices=None, axes_coordinates=None # Combine data with mask data = np.ma.masked_array(data, self.mask) if axes is None: - if axes_coordinates == [None, None]: + try: + axes_coord_check = axes_coordinates == [None, None] + except: + axes_coord_check = False + if axes_coord_check: # Build slice list for WCS for initializing WCSAxes object. if self.wcs.naxis is not 2: slice_list = [] @@ -225,10 +229,10 @@ def _plot_2D_cube(self, axes=None, plot_axis_indices=None, axes_coordinates=None # If axis coordinate is None, derive axis values from WCS. if axes_coordinates[plot_axis_index] is None: if axes_units[plot_axis_index] is None: - # N.B. This assumed axes are independent. Fix this before merging!!! + # N.B. This assumes axes are independent. Fix this before merging!!! axis_value = self.axis_world_coords(plot_axis_index) axes_units[plot_axis_index] = axis_value.unit - axis_value = self.axis_world_coords(plot_axis_index).value + axis_value = self.axis_world_coords()[plot_axis_index].value else: axis_value = self.axis_world_coords(plot_axis_index).to( axes_units[plot_axis_index]).value @@ -240,26 +244,25 @@ def _plot_2D_cube(self, axes=None, plot_axis_indices=None, axes_coordinates=None # If axis coordinate is a string, derive axis values from # corresponding extra coord. axis_label_text = copy.deepcopy(axes_coordinates[plot_axis_index]) - axis_value = self.extra_coord[axes_coordinates[plot_axis_index]] + axis_value = self.extra_coords[axes_coordinates[plot_axis_index]]["value"] if isinstance(axis_value, u.Quantity): if axes_units[plot_axis_index] is None: axes_units[plot_axis_index] = axis_value.unit - axis_value = axis_value.value - else: - if axes_units[plot_axis_index] is not None: - raise TypeError(INVALID_UNIT_SET_MESSAGE) + axis_value = axis_value.value + else: + if axes_units[plot_axis_index] is not None: + raise TypeError(INVALID_UNIT_SET_MESSAGE) default_label = "{0} [{1}]".format(axis_label_text, axes_units[plot_axis_index]) else: # Else user must have manually set the axis coordinates. if isinstance(axes_coordinates[plot_axis_index], u.Quantity): if axes_units[plot_axis_index] is None: + axes_units[plot_axis_index] = axes_coordinates[plot_axis_index].unit axis_value = axes_coordinates[plot_axis_index].value else: axis_value = axes_coordinates[plot_axis_index].to( - axes_units[plot_axis_index]) - axes_units[plot_axis_index] = axis_value.unit - axis_value = axis_value.value + axes_units[plot_axis_index]).value else: if axes_units[plot_axis_index] is None: axis_value = axes_coordinates[plot_axis_index] @@ -272,17 +275,18 @@ def _plot_2D_cube(self, axes=None, plot_axis_indices=None, axes_coordinates=None fig, ax = plt.subplots(1, 1) # Since we can't assume the x-axis will be uniform, create NonUniformImage # axes and add it to the axes object. + if plot_axis_indices[0] < plot_axis_indices[1]: + data = data.transpose() im_ax = mpl.image.NonUniformImage( ax, extent=(axes_values[0][0], axes_values[0][-1], axes_values[1][0], axes_values[1][-1]), **kwargs) - im_ax.set_data(axes_coordinates[plot_axis_indices[0]], - axes_coordinates[plot_axis_indices[1]], data) + im_ax.set_data(axes_values[0], axes_values[1], data) ax.add_image(im_ax) # Set the limits, labels, etc. of the axes. - ax.set_xlim((axes_coordinates[plot_axis_indices[0]][0], - axes_coordinates[plot_axis_indices[0]][-1])) - ax.set_ylim((axes_coordinates[plot_axis_indices[1]][0], - axes_coordinates[plot_axis_indices[1]][-1])) + xlim = kwargs.pop("xlim", (axes_values[0][0], axes_values[0][-1])) + ax.set_xlim(xlim) + ylim = kwargs.pop("xlim", (axes_values[1][0], axes_values[1][-1])) + ax.set_ylim(ylim) xlabel = kwargs.pop("xlabel", default_labels[0]) ylabel = kwargs.pop("ylabel", default_labels[1]) ax.set_xlabel(xlabel) diff --git a/ndcube/tests/test_plotting.py b/ndcube/tests/test_plotting.py index 921737c9e..7673ef2f3 100644 --- a/ndcube/tests/test_plotting.py +++ b/ndcube/tests/test_plotting.py @@ -185,8 +185,27 @@ def test_cube_plot_1D_errors(test_input, test_kwargs, expected_error): @pytest.mark.parametrize("test_input, test_kwargs, expected_values", [ (cube[0], {}, - (cube[0].data, "time [min]", "em.wl [m]", - (-0.5, 3.5, 2.5, -0.5))) + (np.ma.masked_array(cube[0].data, cube[0].mask), "time [min]", "em.wl [m]", + (-0.5, 3.5, 2.5, -0.5))), + + (cube[0], {"axes_coordinates": ["bye", None], "axes_units": [None, u.cm]}, + (np.ma.masked_array(cube[0].data, cube[0].mask), "bye [m]", "em.wl [cm]", + (0.0, 3.0, 2e-9, 6e-9))), + + (cube[0], {"axes_coordinates": [np.arange(10, 10+cube[0].data.shape[1]), + u.Quantity(np.arange(10, 10+cube[0].data.shape[0]), unit=u.m)], + "axes_units": [None, u.cm]}, + (np.ma.masked_array(cube[0].data, cube[0].mask), " [None]", " [cm]", (10, 13, 1000, 1200))), + + (cube[0], {"axes_coordinates": [np.arange(10, 10+cube[0].data.shape[1]), + u.Quantity(np.arange(10, 10+cube[0].data.shape[0]), unit=u.m)]}, + (np.ma.masked_array(cube[0].data, cube[0].mask), " [None]", " [m]", (10, 13, 10, 12))), + + (cube_unit[0], {"plot_axis_indices": [0, 1], "axes_coordinates": [None, "bye"], + "data_unit": u.erg}, + (np.ma.masked_array((cube_unit[0].data * cube_unit[0].unit).to(u.erg).value, + cube_unit[0].mask).transpose(), + "em.wl [m]", "bye [m]", (2e-11, 6e-11, 0.0, 3.0))) ]) def test_cube_plot_2D(test_input, test_kwargs, expected_values): # Unpack expected properties. @@ -202,6 +221,16 @@ def test_cube_plot_2D(test_input, test_kwargs, expected_values): assert np.allclose(output.images[0].get_extent(), expected_extent) +@pytest.mark.parametrize("test_input, test_kwargs, expected_error", [ + (cube[0], {"axes_coordinates": ["another time", None], "axes_units": [u.cm, None]}, TypeError), + (cube[0], {"axes_coordinates": [np.arange(10, 10+cube[0].data.shape[1]), None], + "axes_units": [u.cm, None]}, TypeError), + (cube[0], {"data_unit": u.cm}, TypeError) + ]) +def test_cube_plot_2D_errors(test_input, test_kwargs, expected_error): + with pytest.raises(expected_error): + output = test_input.plot(**test_kwargs) + @pytest.mark.parametrize("test_input, test_kwargs, expected_values", [ (cubem, {}, (cubem.data, [np.array([0., 2.]), [0, 3], [0, 4]], "", "")) From b93c5b3cf0bf57687cc44aa199d206cda2c1ccd7 Mon Sep 17 00:00:00 2001 From: DanRyanIrish Date: Thu, 29 Mar 2018 14:54:54 -0400 Subject: [PATCH 08/13] Make NDCube 2D animations aware of masks and data units. Currently not working apparently because of a glitch in ImageAnimatorWCS. --- ndcube/mixins/plotting.py | 46 +++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/ndcube/mixins/plotting.py b/ndcube/mixins/plotting.py index dd4adb57c..a2b190962 100644 --- a/ndcube/mixins/plotting.py +++ b/ndcube/mixins/plotting.py @@ -293,8 +293,8 @@ def _plot_2D_cube(self, axes=None, plot_axis_indices=None, axes_coordinates=None ax.set_ylabel(ylabel) return ax - def _plot_3D_cube(self, plot_axis_indices=None, axes_units=None, - axes_coordinates=None, **kwargs): + def _plot_3D_cube(self, plot_axis_indices=None, axes_coordinates=None, + axes_units=None, data_unit=None, **kwargs): """ Plots an interactive visualization of this cube using sliders to move through axes plot using in the image. @@ -320,14 +320,42 @@ def _plot_3D_cube(self, plot_axis_indices=None, axes_units=None, If None is specified for an axis then the array indices will be used for that axis. """ - if plot_axis_indices is None: - plot_axis_indices = [-1, -2] + # For convenience in inserting dummy variables later, ensure + # plot_axis_indices are all positive. + plot_axis_indices = [i if i >= 0 else self.data.ndim + i for i in plot_axis_indices] + # If axes kwargs not set by user, set them as list of Nones for + # each axis for consistent behaviour. + if axes_coordinates is None: + axes_coordinates = [None] * self.data.ndim if axes_units is None: - axes_units = [None, None] - i = ImageAnimatorWCS(self.data, wcs=self.wcs, image_axes=plot_axis_indices, - unit_x_axis=axes_units[0], unit_y_axis=axes_units[1], - axis_ranges=axes_coordinates, **kwargs) - return i + axes_units = [None] * self.data.ndim + # If data_unit set, convert data to that unit + if data_unit is None: + data = self.data + else: + data = (self.data * self.unit).to(data_unit).value + # Combine data values with mask. + data = np.ma.masked_array(data, self.mask) + # If there are missing axes in WCS object, add corresponding dummy axes to data. + if data.ndim < self.wcs.naxis: + new_shape = list(data.shape) + for i in np.arange(self.wcs.naxis)[self.missing_axis[::-1]]: + new_shape.insert(i, 1) + # Also insert dummy coordinates and units. + axes_coordinates.insert(i, None) + axes_units.insert(i, None) + # Iterate plot_axis_indices if neccessary + for j, pai in enumerate(plot_axis_indices): + if pai >= i: + plot_axis_indices[j] = plot_axis_indices[j] + 1 + # Reshape data + data = data.reshape(new_shape) + + ax = ImageAnimatorWCS(data, wcs=self.wcs, image_axes=plot_axis_indices, + unit_x_axis=axes_units[plot_axis_indices[0]], + unit_y_axis=axes_units[plot_axis_indices[1]], + axis_ranges=axes_coordinates[1], **kwargs) + return ax def _support_101_plot_API(plot_axis_indices, axes_coordinates, axes_units, data_unit, kwargs): From f81e1c8c2a0692d2d76d35dfd28b438ecd2a1905 Mon Sep 17 00:00:00 2001 From: DanRyanIrish Date: Thu, 29 Mar 2018 22:22:13 -0400 Subject: [PATCH 09/13] Rearranged NDCubePlotMixin.plot without changing functionality to make plath for future 1D animation functionality. --- ndcube/mixins/plotting.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/ndcube/mixins/plotting.py b/ndcube/mixins/plotting.py index a2b190962..775289202 100644 --- a/ndcube/mixins/plotting.py +++ b/ndcube/mixins/plotting.py @@ -66,17 +66,21 @@ def plot(self, axes=None, plot_axis_indices=None, axes_coordinates=None, naxis = len(self.dimensions) plot_axis_indices, axes_coordinates, axes_units = _prep_axes_kwargs( naxis, plot_axis_indices, axes_coordinates, axes_units) - if self.data.ndim is 1: - plot = self._plot_1D_cube(axes, axes_coordinates, - axes_units, data_unit, **kwargs) - elif self.data.ndim is 2: - plot = self._plot_2D_cube(axes, plot_axis_indices, axes_coordinates, + if naxis is 1: + ax = self._plot_1D_cube(axes, axes_coordinates, axes_units, data_unit, **kwargs) else: - plot = self._plot_3D_cube(plot_axis_indices=plot_axis_indices, - axes_coordinates=axes_coordinates, axes_units=axes_units, - **kwargs) - return plot + if len(plot_axis_indices) == 1: + raise NotImplementedError() + else: + if naxis == 2: + ax = self._plot_2D_cube(axes, plot_axis_indices, axes_coordinates, + axes_units, data_unit, **kwargs) + else: + ax = self._plot_3D_cube( + plot_axis_indices=plot_axis_indices, axes_coordinates=axes_coordinates, + axes_units=axes_units, **kwargs) + return ax def _plot_1D_cube(self, axes=None, axes_coordinates=None, axes_units=None, data_unit=None, **kwargs): From 6ce4c0f3d605f6b26b43bd6fae49a33c6641f860 Mon Sep 17 00:00:00 2001 From: DanRyanIrish Date: Fri, 30 Mar 2018 11:21:55 -0400 Subject: [PATCH 10/13] Move derivation of axes coordinates in NDCube 2D plot case to separate function so it can be used elsewhere. Minor bug found as part of process that was fixed. --- ndcube/mixins/plotting.py | 122 ++++++++++++++++++---------------- ndcube/tests/test_plotting.py | 2 +- 2 files changed, 67 insertions(+), 57 deletions(-) diff --git a/ndcube/mixins/plotting.py b/ndcube/mixins/plotting.py index 775289202..563fd688a 100644 --- a/ndcube/mixins/plotting.py +++ b/ndcube/mixins/plotting.py @@ -1,5 +1,6 @@ from warnings import warn import copy +import datetime import numpy as np import matplotlib as mpl @@ -195,7 +196,7 @@ def _plot_2D_cube(self, axes=None, plot_axis_indices=None, axes_coordinates=None data = np.ma.masked_array(data, self.mask) if axes is None: try: - axes_coord_check = axes_coordinates == [None, None] + axes_coord_check == [None, None] except: axes_coord_check = False if axes_coord_check: @@ -227,54 +228,8 @@ def _plot_2D_cube(self, axes=None, plot_axis_indices=None, axes_coordinates=None ax.imshow(data, **kwargs) else: # Else manually set axes x and y values based on user's input for axes_coordinates. - axes_values = [] - default_labels = [] - for i, plot_axis_index in enumerate(plot_axis_indices): - # If axis coordinate is None, derive axis values from WCS. - if axes_coordinates[plot_axis_index] is None: - if axes_units[plot_axis_index] is None: - # N.B. This assumes axes are independent. Fix this before merging!!! - axis_value = self.axis_world_coords(plot_axis_index) - axes_units[plot_axis_index] = axis_value.unit - axis_value = self.axis_world_coords()[plot_axis_index].value - else: - axis_value = self.axis_world_coords(plot_axis_index).to( - axes_units[plot_axis_index]).value - # Derive default axis label. - default_label = "{0} [{1}]".format( - self.world_axis_physical_types[plot_axis_index], - axes_units[plot_axis_index]) - elif isinstance(axes_coordinates[plot_axis_index], str): - # If axis coordinate is a string, derive axis values from - # corresponding extra coord. - axis_label_text = copy.deepcopy(axes_coordinates[plot_axis_index]) - axis_value = self.extra_coords[axes_coordinates[plot_axis_index]]["value"] - if isinstance(axis_value, u.Quantity): - if axes_units[plot_axis_index] is None: - axes_units[plot_axis_index] = axis_value.unit - axis_value = axis_value.value - else: - if axes_units[plot_axis_index] is not None: - raise TypeError(INVALID_UNIT_SET_MESSAGE) - default_label = "{0} [{1}]".format(axis_label_text, - axes_units[plot_axis_index]) - else: - # Else user must have manually set the axis coordinates. - if isinstance(axes_coordinates[plot_axis_index], u.Quantity): - if axes_units[plot_axis_index] is None: - axes_units[plot_axis_index] = axes_coordinates[plot_axis_index].unit - axis_value = axes_coordinates[plot_axis_index].value - else: - axis_value = axes_coordinates[plot_axis_index].to( - axes_units[plot_axis_index]).value - else: - if axes_units[plot_axis_index] is None: - axis_value = axes_coordinates[plot_axis_index] - else: - raise TypeError(INVALID_UNIT_SET_MESSAGE) - default_label = " [{0}]".format(axes_units[plot_axis_index]) - axes_values.append(axis_value) - default_labels.append(default_label) + new_axes_coordinates, new_axis_units, default_labels = \ + self._derive_axes_coordinates(axes_coordinates, axes_units) # Initialize axes object and set values along axis. fig, ax = plt.subplots(1, 1) # Since we can't assume the x-axis will be uniform, create NonUniformImage @@ -282,17 +237,22 @@ def _plot_2D_cube(self, axes=None, plot_axis_indices=None, axes_coordinates=None if plot_axis_indices[0] < plot_axis_indices[1]: data = data.transpose() im_ax = mpl.image.NonUniformImage( - ax, extent=(axes_values[0][0], axes_values[0][-1], - axes_values[1][0], axes_values[1][-1]), **kwargs) - im_ax.set_data(axes_values[0], axes_values[1], data) + ax, extent=(new_axes_coordinates[plot_axis_indices[0]][0], + new_axes_coordinates[plot_axis_indices[0]][-1], + new_axes_coordinates[plot_axis_indices[1]][0], + new_axes_coordinates[plot_axis_indices[1]][-1]), **kwargs) + im_ax.set_data(new_axes_coordinates[plot_axis_indices[0]], + new_axes_coordinates[plot_axis_indices[1]], data) ax.add_image(im_ax) # Set the limits, labels, etc. of the axes. - xlim = kwargs.pop("xlim", (axes_values[0][0], axes_values[0][-1])) + xlim = kwargs.pop("xlim", (new_axes_coordinates[plot_axis_indices[0]][0], + new_axes_coordinates[plot_axis_indices[0]][-1])) ax.set_xlim(xlim) - ylim = kwargs.pop("xlim", (axes_values[1][0], axes_values[1][-1])) + ylim = kwargs.pop("xlim", (new_axes_coordinates[plot_axis_indices[1]][0], + new_axes_coordinates[plot_axis_indices[1]][-1])) ax.set_ylim(ylim) - xlabel = kwargs.pop("xlabel", default_labels[0]) - ylabel = kwargs.pop("ylabel", default_labels[1]) + xlabel = kwargs.pop("xlabel", default_labels[plot_axis_indices[0]]) + ylabel = kwargs.pop("ylabel", default_labels[plot_axis_indices[1]]) ax.set_xlabel(xlabel) ax.set_ylabel(ylabel) return ax @@ -323,6 +283,7 @@ def _plot_3D_cube(self, plot_axis_indices=None, axes_coordinates=None, same length as the axis which will provide all values for that slider. If None is specified for an axis then the array indices will be used for that axis. + """ # For convenience in inserting dummy variables later, ensure # plot_axis_indices are all positive. @@ -361,6 +322,55 @@ def _plot_3D_cube(self, plot_axis_indices=None, axes_coordinates=None, axis_ranges=axes_coordinates[1], **kwargs) return ax + def _derive_axes_coordinates(self, axes_coordinates, axes_units): + new_axes_coordinates = [] + new_axes_units = [] + default_labels = [] + default_label_text = "" + for i, axis_coordinate in enumerate(axes_coordinates): + # If axis coordinate is None, derive axis values from WCS. + if axis_coordinate is None: + # N.B. This assumes axes are independent. Fix this before merging!!! + new_axis_coordinate = self.axis_world_coords(i) + axis_label_text = self.world_axis_physical_types[i] + elif isinstance(axis_coordinate, str): + # If axis coordinate is a string, derive axis values from + # corresponding extra coord. + new_axis_coordinate = self.extra_coords[axis_coordinate]["value"] + axis_label_text = axis_coordinate + else: + # Else user must have manually set the axis coordinates. + new_axis_coordinate = axis_coordinate + axis_label_text = default_label_text + # If axis coordinate is a Quantity, convert to unit supplied by user. + if isinstance(new_axis_coordinate, u.Quantity): + if axes_units[i] is None: + new_axis_unit = new_axis_coordinate.unit + new_axis_coordinate = new_axis_coordinate.value + else: + new_axis_unit = axes_units[i] + new_axis_coordinate = new_axis_coordinate.to(new_axis_unit).value + else: + if axes_units[i] is None: + new_axis_unit = None + else: + raise TypeError(INVALID_UNIT_SET_MESSAGE) + # Derive default axis label + if type(new_axis_coordinate[0]) is datetime.datetime: + if axis_label_text == default_label_text: + default_label = "{0}".format(new_axis_coordinate[0].strftime("%Y/%m/%d %H:%M")) + else: + default_label = "{0} [{1}]".format( + axis_label_text, new_axis_coordinate[0].strftime("%Y/%m/%d %H:%M")) + else: + default_label = "{0} [{1}]".format(axis_label_text, new_axis_unit) + # Append new coordinates, units and labels to output list. + new_axes_coordinates.append(new_axis_coordinate) + new_axes_units.append(new_axis_unit) + default_labels.append(default_label) + return new_axes_coordinates, new_axes_units, default_labels + + def _support_101_plot_API(plot_axis_indices, axes_coordinates, axes_units, data_unit, kwargs): """Check if user has used old API and convert it to new API.""" diff --git a/ndcube/tests/test_plotting.py b/ndcube/tests/test_plotting.py index 7673ef2f3..86bfe8076 100644 --- a/ndcube/tests/test_plotting.py +++ b/ndcube/tests/test_plotting.py @@ -186,7 +186,7 @@ def test_cube_plot_1D_errors(test_input, test_kwargs, expected_error): @pytest.mark.parametrize("test_input, test_kwargs, expected_values", [ (cube[0], {}, (np.ma.masked_array(cube[0].data, cube[0].mask), "time [min]", "em.wl [m]", - (-0.5, 3.5, 2.5, -0.5))), + (0.4, 1.6, 2e-11, 6e-11))), (cube[0], {"axes_coordinates": ["bye", None], "axes_units": [None, u.cm]}, (np.ma.masked_array(cube[0].data, cube[0].mask), "bye [m]", "em.wl [cm]", From 1ba5b1a9f7ec0a5720614526efc60a4547cc9c26 Mon Sep 17 00:00:00 2001 From: DanRyanIrish Date: Fri, 30 Mar 2018 11:44:58 -0400 Subject: [PATCH 11/13] Enable axes coordinates and units to be specified by user in NDCube 2D animation method. Currently doesnt work though, probably due to bugs in sunpy ImageAnimator. --- ndcube/mixins/plotting.py | 57 +++++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/ndcube/mixins/plotting.py b/ndcube/mixins/plotting.py index 563fd688a..4554827d1 100644 --- a/ndcube/mixins/plotting.py +++ b/ndcube/mixins/plotting.py @@ -6,7 +6,7 @@ import matplotlib as mpl import matplotlib.pyplot as plt import astropy.units as u -from sunpy.visualization.imageanimator import ImageAnimatorWCS +from sunpy.visualization.imageanimator import ImageAnimator, ImageAnimatorWCS import sunpy.visualization.wcsaxes_compat as wcsaxes_compat from ndcube import utils @@ -301,25 +301,42 @@ def _plot_3D_cube(self, plot_axis_indices=None, axes_coordinates=None, data = (self.data * self.unit).to(data_unit).value # Combine data values with mask. data = np.ma.masked_array(data, self.mask) - # If there are missing axes in WCS object, add corresponding dummy axes to data. - if data.ndim < self.wcs.naxis: - new_shape = list(data.shape) - for i in np.arange(self.wcs.naxis)[self.missing_axis[::-1]]: - new_shape.insert(i, 1) - # Also insert dummy coordinates and units. - axes_coordinates.insert(i, None) - axes_units.insert(i, None) - # Iterate plot_axis_indices if neccessary - for j, pai in enumerate(plot_axis_indices): - if pai >= i: - plot_axis_indices[j] = plot_axis_indices[j] + 1 - # Reshape data - data = data.reshape(new_shape) - - ax = ImageAnimatorWCS(data, wcs=self.wcs, image_axes=plot_axis_indices, - unit_x_axis=axes_units[plot_axis_indices[0]], - unit_y_axis=axes_units[plot_axis_indices[1]], - axis_ranges=axes_coordinates[1], **kwargs) + # If axes_coordinates not provided generate an ImageAnimatorWCS plot + # using NDCube's wcs object. + if (axes_coordinates[plot_axis_indices[0]] is None and + axes_coordinates[plot_axis_indices[1]] is None): + # If there are missing axes in WCS object, add corresponding dummy axes to data. + if data.ndim < self.wcs.naxis: + new_shape = list(data.shape) + for i in np.arange(self.wcs.naxis)[self.missing_axis[::-1]]: + new_shape.insert(i, 1) + # Also insert dummy coordinates and units. + axes_coordinates.insert(i, None) + axes_units.insert(i, None) + # Iterate plot_axis_indices if neccessary + for j, pai in enumerate(plot_axis_indices): + if pai >= i: + plot_axis_indices[j] = plot_axis_indices[j] + 1 + # Reshape data + data = data.reshape(new_shape) + # Generate plot + ax = ImageAnimatorWCS(data, wcs=self.wcs, image_axes=plot_axis_indices, + unit_x_axis=axes_units[plot_axis_indices[0]], + unit_y_axis=axes_units[plot_axis_indices[1]], + axis_ranges=axes_coordinates, **kwargs) + # If one of the plot axes is set manually, produce a basic ImageAnimator object. + else: + new_axes_coordinates, new_axes_units, default_labels = \ + self._derive_axes_coordinates(axes_coordinates, axes_units) + # If axis labels not set by user add to kwargs. + if "xlabel" not in kwargs: + kwargs["xlabel"] = default_labels[plot_axis_indices[0]] + if "ylabel" not in kwargs: + kwargs["ylabel"] = default_labels[plot_axis_indices[1]] + ax = ImageAnimator(data, image_axes=plot_axis_indices, + unit_x_axis=new_axes_units[plot_axis_indices[0]], + unit_y_axis=new_axes_units[plot_axis_indices[1]], + axis_ranges=new_axes_coordinates, **kwargs) return ax def _derive_axes_coordinates(self, axes_coordinates, axes_units): From a48106ef5e3cbb691e261e93af763e00a6670f6d Mon Sep 17 00:00:00 2001 From: DanRyanIrish Date: Fri, 30 Mar 2018 19:52:12 -0400 Subject: [PATCH 12/13] Add case to _derive_axes_coordinates() for when axis coordinates are datetimes and removed kwargs not accepted currently when calling ImageAnimator. --- ndcube/mixins/plotting.py | 12 ++++++------ ndcube/tests/test_plotting.py | 6 ++++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/ndcube/mixins/plotting.py b/ndcube/mixins/plotting.py index 4554827d1..f25f76159 100644 --- a/ndcube/mixins/plotting.py +++ b/ndcube/mixins/plotting.py @@ -329,13 +329,7 @@ def _plot_3D_cube(self, plot_axis_indices=None, axes_coordinates=None, new_axes_coordinates, new_axes_units, default_labels = \ self._derive_axes_coordinates(axes_coordinates, axes_units) # If axis labels not set by user add to kwargs. - if "xlabel" not in kwargs: - kwargs["xlabel"] = default_labels[plot_axis_indices[0]] - if "ylabel" not in kwargs: - kwargs["ylabel"] = default_labels[plot_axis_indices[1]] ax = ImageAnimator(data, image_axes=plot_axis_indices, - unit_x_axis=new_axes_units[plot_axis_indices[0]], - unit_y_axis=new_axes_units[plot_axis_indices[1]], axis_ranges=new_axes_coordinates, **kwargs) return ax @@ -367,6 +361,12 @@ def _derive_axes_coordinates(self, axes_coordinates, axes_units): else: new_axis_unit = axes_units[i] new_axis_coordinate = new_axis_coordinate.to(new_axis_unit).value + elif isinstance(new_axis_coordinate[0], datetime.datetime): + axis_label_text = "{0}/sec since {1}".format( + axis_label_text, new_axis_coordinate[0]) + new_axis_coordinate = np.array([(t-new_axis_coordinate[0]).total_seconds() + for t in new_axis_coordinate]) + new_axis_unit = u.s else: if axes_units[i] is None: new_axis_unit = None diff --git a/ndcube/tests/test_plotting.py b/ndcube/tests/test_plotting.py index 86bfe8076..9a4dcea0d 100644 --- a/ndcube/tests/test_plotting.py +++ b/ndcube/tests/test_plotting.py @@ -43,7 +43,8 @@ ('bye', 2, u.Quantity(range(data.shape[2]), unit=u.m)), ('another time', 2, np.array( [datetime.datetime(2000, 1, 1)+datetime.timedelta(minutes=i) - for i in range(data.shape[2])])) + for i in range(data.shape[2])])), + ('array coord', 2, np.arange(100, 100+data.shape[2])) ]) cube_unit = NDCube( @@ -222,7 +223,7 @@ def test_cube_plot_2D(test_input, test_kwargs, expected_values): @pytest.mark.parametrize("test_input, test_kwargs, expected_error", [ - (cube[0], {"axes_coordinates": ["another time", None], "axes_units": [u.cm, None]}, TypeError), + (cube[0], {"axes_coordinates": ["array coord", None], "axes_units": [u.cm, None]}, TypeError), (cube[0], {"axes_coordinates": [np.arange(10, 10+cube[0].data.shape[1]), None], "axes_units": [u.cm, None]}, TypeError), (cube[0], {"data_unit": u.cm}, TypeError) @@ -231,6 +232,7 @@ def test_cube_plot_2D_errors(test_input, test_kwargs, expected_error): with pytest.raises(expected_error): output = test_input.plot(**test_kwargs) + @pytest.mark.parametrize("test_input, test_kwargs, expected_values", [ (cubem, {}, (cubem.data, [np.array([0., 2.]), [0, 3], [0, 4]], "", "")) From 0a7a81d3f14b6f7ae78ab16a75c8f2b069f8d142 Mon Sep 17 00:00:00 2001 From: DanRyanIrish Date: Sun, 8 Apr 2018 17:50:49 -0400 Subject: [PATCH 13/13] PEP8 changes. --- ndcube/mixins/plotting.py | 23 ++++++++++++----------- ndcube/tests/test_plotting.py | 2 +- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/ndcube/mixins/plotting.py b/ndcube/mixins/plotting.py index f25f76159..44c739a4d 100644 --- a/ndcube/mixins/plotting.py +++ b/ndcube/mixins/plotting.py @@ -10,7 +10,7 @@ import sunpy.visualization.wcsaxes_compat as wcsaxes_compat from ndcube import utils -from ndcube.mixins.sequence_plotting import _prep_axes_kwargs, _derive_1D_coordinates_and_units, _determine_sequence_units, _make_1D_sequence_plot +from ndcube.mixins import sequence_plotting __all__ = ['NDCubePlotMixin'] @@ -65,18 +65,18 @@ def plot(self, axes=None, plot_axis_indices=None, axes_coordinates=None, plot_axis_indices, axes_coordinates, axes_units, data_unit, kwargs) # Check kwargs are in consistent formats and set default values if not done so by user. naxis = len(self.dimensions) - plot_axis_indices, axes_coordinates, axes_units = _prep_axes_kwargs( + plot_axis_indices, axes_coordinates, axes_units = sequence_plotting._prep_axes_kwargs( naxis, plot_axis_indices, axes_coordinates, axes_units) if naxis is 1: ax = self._plot_1D_cube(axes, axes_coordinates, - axes_units, data_unit, **kwargs) + axes_units, data_unit, **kwargs) else: if len(plot_axis_indices) == 1: raise NotImplementedError() else: if naxis == 2: ax = self._plot_2D_cube(axes, plot_axis_indices, axes_coordinates, - axes_units, data_unit, **kwargs) + axes_units, data_unit, **kwargs) else: ax = self._plot_3D_cube( plot_axis_indices=plot_axis_indices, axes_coordinates=axes_coordinates, @@ -96,8 +96,8 @@ def _plot_1D_cube(self, axes=None, axes_coordinates=None, axes_units=None, data_ """ # Derive x-axis coordinates and unit from inputs. - x_axis_coordinates, unit_x_axis = _derive_1D_coordinates_and_units(axes_coordinates, - axes_units) + x_axis_coordinates, unit_x_axis = sequence_plotting._derive_1D_coordinates_and_units( + axes_coordinates, axes_units) if x_axis_coordinates is None: # Default is to derive x coords and defaul xlabel from WCS object. xname = self.world_axis_physical_types[0] @@ -154,7 +154,8 @@ def _plot_1D_cube(self, axes=None, axes_coordinates=None, axes_units=None, data_ if yerror is not None: yerror = np.ma.masked_array(yerror, self.mask) # Create plot - fig, ax = _make_1D_sequence_plot(xdata, ydata, yerror, data_unit, default_xlabel, kwargs) + fig, ax = sequence_plotting._make_1D_sequence_plot(xdata, ydata, yerror, + data_unit, default_xlabel, kwargs) return ax def _plot_2D_cube(self, axes=None, plot_axis_indices=None, axes_coordinates=None, @@ -215,12 +216,12 @@ def _plot_2D_cube(self, axes=None, plot_axis_indices=None, axes_coordinates=None ax = wcsaxes_compat.gca_wcs(self.wcs, slices=slice_list) # Set axis labels x_wcs_axis = utils.cube.data_axis_to_wcs_axis(plot_axis_indices[0], - self.missing_axis) + self.missing_axis) ax.set_xlabel("{0} [{1}]".format( self.world_axis_physical_types[plot_axis_indices[0]], self.wcs.wcs.cunit[x_wcs_axis])) y_wcs_axis = utils.cube.data_axis_to_wcs_axis(plot_axis_indices[1], - self.missing_axis) + self.missing_axis) ax.set_ylabel("{0} [{1}]".format( self.world_axis_physical_types[plot_axis_indices[1]], self.wcs.wcs.cunit[y_wcs_axis])) @@ -304,7 +305,7 @@ def _plot_3D_cube(self, plot_axis_indices=None, axes_coordinates=None, # If axes_coordinates not provided generate an ImageAnimatorWCS plot # using NDCube's wcs object. if (axes_coordinates[plot_axis_indices[0]] is None and - axes_coordinates[plot_axis_indices[1]] is None): + axes_coordinates[plot_axis_indices[1]] is None): # If there are missing axes in WCS object, add corresponding dummy axes to data. if data.ndim < self.wcs.naxis: new_shape = list(data.shape) @@ -374,7 +375,7 @@ def _derive_axes_coordinates(self, axes_coordinates, axes_units): raise TypeError(INVALID_UNIT_SET_MESSAGE) # Derive default axis label if type(new_axis_coordinate[0]) is datetime.datetime: - if axis_label_text == default_label_text: + if axis_label_text == default_label_text: default_label = "{0}".format(new_axis_coordinate[0].strftime("%Y/%m/%d %H:%M")) else: default_label = "{0} [{1}]".format( diff --git a/ndcube/tests/test_plotting.py b/ndcube/tests/test_plotting.py index 9a4dcea0d..33ac5f0aa 100644 --- a/ndcube/tests/test_plotting.py +++ b/ndcube/tests/test_plotting.py @@ -100,7 +100,7 @@ ('another time', 2, np.array( [datetime.datetime(2000, 1, 1)+datetime.timedelta(minutes=i) for i in range(data.shape[2])])) - ]) + ]) @pytest.mark.parametrize("test_input, test_kwargs, expected_values", [