From a8d43d3571bca113f74aca53c31b4bfd73417219 Mon Sep 17 00:00:00 2001 From: Melanie Simet Date: Fri, 22 Jun 2018 19:19:30 -0600 Subject: [PATCH 1/4] Add an option to have ScatterPlotSysTests do histograms for dense data (#94) --- stile/sys_tests.py | 109 +++++++++++++++++++++++++++++----------- tests/test_sys_tests.py | 28 +++++++++++ 2 files changed, 108 insertions(+), 29 deletions(-) create mode 100644 tests/test_sys_tests.py diff --git a/stile/sys_tests.py b/stile/sys_tests.py index 077306b..07faf0b 100644 --- a/stile/sys_tests.py +++ b/stile/sys_tests.py @@ -1849,7 +1849,8 @@ class BaseScatterPlotSysTest(SysTest): short_name = 'scatterplot' def __call__(self, array, x_field, y_field, yerr_field, z_field=None, residual=False, per_ccd_stat=None, xlabel=None, ylabel=None, zlabel=None, color="", - lim=None, equal_axis=False, linear_regression=False, reference_line=None): + lim=None, equal_axis=False, linear_regression=False, reference_line=None, + histogram=False, histogram_n_bins=40, histogram_cmap='Blues'): """ Draw a scatter plot and return a :class:`matplotlib.figure.Figure` object. This method has a bunch of options for controlling appearance of a plot, which is @@ -1895,6 +1896,15 @@ def __call__(self, array, x_field, y_field, yerr_field, z_field=None, residual=F ``x=y`` is drawn. If ``reference_line == 'zero'``, ``y=0`` is drawn. A user-specific function can be used by passing an object which has an attribute :func:`__call__` and returns a 1-d Numpy array. + :param histogram: Plot a 2-d histogram (instead of plotting each point individually) + for crowded plots. Setting `histogram=True` will cause any data + given for `z` to be ignored, and any uncertainty on `y` will not be + plotted (though it will still be used to compute a trendline). + [default: False] + :param histogram_n_bins: The number of bins along *each* axis for the histogram; ignored if + `histogram=False`. [default: 40] + :param histogram_cmap: The matplotlib colormap used for the histogram; ignored if + `histogram=False`. [default: 'Blues'] :returns: a :class:`matplotlib.figure.Figure` object """ if per_ccd_stat: @@ -1939,7 +1949,9 @@ def __call__(self, array, x_field, y_field, yerr_field, z_field=None, residual=F return self.scatterPlot(x, y, yerr, z, xlabel=xlabel, ylabel=ylabel, color=color, lim=lim, equal_axis=False, - linear_regression=True, reference_line=reference_line) + linear_regression=True, reference_line=reference_line, + histogram=histogram, histogram_n_bins=histogram_n_bins, + histogram_cmap=histogram_cmap) def getData(self): """ @@ -1951,7 +1963,8 @@ def getData(self): return self.data def scatterPlot(self, x, y, yerr=None, z=None, xlabel=None, ylabel=None, zlabel=None, color="", - lim=None, equal_axis=False, linear_regression=False, reference_line=None): + lim=None, equal_axis=False, linear_regression=False, reference_line=None, + histogram=False, histogram_n_bins=40, histogram_cmap='Blues'): """ Draw a scatter plot and return a :class:`matplotlib.figure.Figure` object. This method has a bunch of options for controlling appearance of a plot, which is @@ -1991,11 +2004,20 @@ def scatterPlot(self, x, y, yerr=None, z=None, xlabel=None, ylabel=None, zlabel= A user-specific function can be used by passing an object which has an attribute :func:`__call__` and returns a 1-d Numpy array. [default: False] + :param histogram: Plot a 2-d histogram (instead of plotting each point individually) + for crowded plots. Setting `histogram=True` will cause any data + given for `z` to be ignored, and any uncertainty on `y` will not be + plotted (though it will still be used to compute a trendline). + [default: False] + :param histogram_n_bins: The number of bins along *each* axis for the histogram; ignored if + `histogram=False`. [default: 40] + :param histogram_cmap: The matplotlib colormap used for the histogram; ignored if + `histogram=False`. [default: 'Blues'] :returns: a :class:`matplotlib.figure.Figure` object """ fig = plt.figure() ax = fig.add_subplot(1, 1, 1) - + print histogram, "histogram" # mask data with nan. Emit a warning if an array has nan in it. x_isnan = numpy.isnan(x) y_isnan = numpy.isnan(y) @@ -2012,6 +2034,8 @@ def scatterPlot(self, x, y, yerr=None, z=None, xlabel=None, ylabel=None, zlabel= % (numpy.sum(yerr_isnan), len(yerr_isnan))) sel = numpy.logical_and(sel, numpy.invert(yerr_isnan)) if z is not None: + if histogram: + warnings.warn('Plotting a histogram - z (color) data will be ignored.') z_isnan = numpy.isnan(z) if numpy.sum(z_isnan) != 0: warnings.warn('There are %s nans in z, out of %s.' @@ -2050,19 +2074,24 @@ def scatterPlot(self, x, y, yerr=None, z=None, xlabel=None, ylabel=None, zlabel= ylim = None # plot - if z is None: - if yerr is None: - p = ax.plot(x, y, ".%s" % color) + if not histogram: + if z is None: + if yerr is None: + p = ax.plot(x, y, ".%s" % color) + else: + p = ax.errorbar(x, y, yerr, fmt=".%s" % color) + # store color for latter use + used_color = p[0].get_color() else: - p = ax.errorbar(x, y, yerr, fmt=".%s" % color) - # store color for latter use - used_color = p[0].get_color() + if yerr is not None: + plt.errorbar(x, y, yerr=yerr, linestyle="None", color="k", zorder=0) + plt.scatter(x, y, c=z, zorder=1) + cb = plt.colorbar() + used_color = "b" else: - if yerr is not None: - plt.errorbar(x, y, yerr=yerr, linestyle="None", color="k", zorder=0) - plt.scatter(x, y, c=z, zorder=1) + plt.hist2d(x, y, bins=histogram_n_bins, cmap=histogram_cmap) cb = plt.colorbar() - used_color = "b" + used_color = color # make axes ticks equal to each other if specified if equal_axis: @@ -2132,7 +2161,7 @@ def scatterPlot(self, x, y, yerr=None, z=None, xlabel=None, ylabel=None, zlabel= ax.set_xlabel(xlabel) if ylabel is not None: ax.set_ylabel(ylabel) - if zlabel is not None: + if zlabel is not None and not histogram: cb.set_label(zlabel) fig.tight_layout() @@ -2231,13 +2260,17 @@ class ScatterPlotStarVsPSFG1SysTest(BaseScatterPlotSysTest): objects_list = ['star PSF'] required_quantities = [('g1', 'g1_err', 'psf_g1')] - def __call__(self, array, per_ccd_stat=None, color='', lim=None): + def __call__(self, array, per_ccd_stat=None, color='', lim=None, + histogram=False, histogram_n_bins=40, histogram_cmap='Blues'): + print "lalala", histogram return super(ScatterPlotStarVsPSFG1SysTest, self).__call__(array, 'psf_g1', 'g1', 'g1_err', residual=False, per_ccd_stat=per_ccd_stat, xlabel=r'$g^{\rm PSF}_1$', ylabel=r'$g^{\rm star}_1$', color=color, lim=lim, equal_axis=False, linear_regression=True, - reference_line='one-to-one') + reference_line='one-to-one', + histogram=histogram, histogram_n_bins=histogram_n_bins, + histogram_cmap=histogram_cmap) class ScatterPlotStarVsPSFG2SysTest(BaseScatterPlotSysTest): @@ -2249,13 +2282,16 @@ class ScatterPlotStarVsPSFG2SysTest(BaseScatterPlotSysTest): objects_list = ['star PSF'] required_quantities = [('g2', 'g2_err', 'psf_g2')] - def __call__(self, array, per_ccd_stat=None, color='', lim=None): + def __call__(self, array, per_ccd_stat=None, color='', lim=None, + histogram=False, histogram_n_bins=40, histogram_cmap='Blues'): return super(ScatterPlotStarVsPSFG2SysTest, self).__call__(array, 'psf_g2', 'g2', 'g2_err', residual=False, per_ccd_stat=per_ccd_stat, xlabel=r'$g^{\rm PSF}_2$', ylabel=r'$g^{\rm star}_2$', color=color, lim=lim, equal_axis=False, linear_regression=True, - reference_line='one-to-one') + reference_line='one-to-one', + histogram=histogram, histogram_n_bins=histogram_n_bins, + histogram_cmap=histogram_cmap) class ScatterPlotStarVsPSFSigmaSysTest(BaseScatterPlotSysTest): @@ -2267,14 +2303,17 @@ class ScatterPlotStarVsPSFSigmaSysTest(BaseScatterPlotSysTest): objects_list = ['star PSF'] required_quantities = [('sigma', 'sigma_err', 'psf_sigma')] - def __call__(self, array, per_ccd_stat=None, color='', lim=None): + def __call__(self, array, per_ccd_stat=None, color='', lim=None, + histogram=False, histogram_n_bins=40, histogram_cmap='Blues'): return super(ScatterPlotStarVsPSFSigmaSysTest, self).__call__(array, 'psf_sigma', 'sigma', 'sigma_err', residual=False, per_ccd_stat=per_ccd_stat, xlabel=r'$\sigma^{\rm PSF}$ [arcsec]', ylabel=r'$\sigma^{\rm star}$ [arcsec]', color=color, lim=lim, equal_axis=False, - linear_regression=True, reference_line='one-to-one') + linear_regression=True, reference_line='one-to-one', + histogram=histogram, histogram_n_bins=histogram_n_bins, + histogram_cmap=histogram_cmap) class ScatterPlotResidualVsPSFG1SysTest(BaseScatterPlotSysTest): @@ -2286,13 +2325,16 @@ class ScatterPlotResidualVsPSFG1SysTest(BaseScatterPlotSysTest): objects_list = ['star PSF'] required_quantities = [('g1', 'g1_err', 'psf_g1')] - def __call__(self, array, per_ccd_stat=None, color='', lim=None): + def __call__(self, array, per_ccd_stat=None, color='', lim=None, + histogram=False, histogram_n_bins=40, histogram_cmap='Blues'): return super(ScatterPlotResidualVsPSFG1SysTest, self).__call__(array, 'psf_g1', 'g1', 'g1_err', residual=True, per_ccd_stat=per_ccd_stat, xlabel=r'$g^{\rm PSF}_1$', ylabel=r'$g^{\rm star}_1 - g^{\rm PSF}_1$', color=color, lim=lim, equal_axis=False, - linear_regression=True, reference_line='zero') + linear_regression=True, reference_line='zero', + histogram=histogram, histogram_n_bins=histogram_n_bins, + histogram_cmap=histogram_cmap) class ScatterPlotResidualVsPSFG2SysTest(BaseScatterPlotSysTest): @@ -2304,13 +2346,16 @@ class ScatterPlotResidualVsPSFG2SysTest(BaseScatterPlotSysTest): objects_list = ['star PSF'] required_quantities = [('g2', 'g2_err', 'psf_g2')] - def __call__(self, array, per_ccd_stat=None, color='', lim=None): + def __call__(self, array, per_ccd_stat=None, color='', lim=None, + histogram=False, histogram_n_bins=40, histogram_cmap='Blues'): return super(ScatterPlotResidualVsPSFG2SysTest, self).__call__(array, 'psf_g2', 'g2', 'g2_err', residual=True, per_ccd_stat=per_ccd_stat, xlabel=r'$g^{\rm PSF}_2$', ylabel=r'$g^{\rm star}_2 - g^{\rm PSF}_2$', color=color, lim=lim, equal_axis=False, - linear_regression=True, reference_line='zero') + linear_regression=True, reference_line='zero', + histogram=histogram, histogram_n_bins=histogram_n_bins, + histogram_cmap=histogram_cmap) class ScatterPlotResidualVsPSFSigmaSysTest(BaseScatterPlotSysTest): @@ -2322,14 +2367,17 @@ class ScatterPlotResidualVsPSFSigmaSysTest(BaseScatterPlotSysTest): objects_list = ['star PSF'] required_quantities = [('sigma', 'sigma_err', 'psf_sigma')] - def __call__(self, array, per_ccd_stat=None, color='', lim=None): + def __call__(self, array, per_ccd_stat=None, color='', lim=None, + histogram=False, histogram_n_bins=40, histogram_cmap='Blues'): return super(ScatterPlotResidualVsPSFSigmaSysTest, self).__call__(array, 'psf_sigma', 'sigma', 'sigma_err', residual=True, per_ccd_stat=per_ccd_stat, xlabel=r'$\sigma^{\rm PSF}$ [arcsec]', ylabel=r'$\sigma^{\rm star} - \sigma^{\rm PSF}$ [arcsec]', color=color, lim=lim, equal_axis=False, - linear_regression=True, reference_line='zero') + linear_regression=True, reference_line='zero', + histogram=histogram, histogram_n_bins=histogram_n_bins, + histogram_cmap=histogram_cmap) class ScatterPlotResidualSigmaVsPSFMagSysTest(BaseScatterPlotSysTest): @@ -2341,7 +2389,8 @@ class ScatterPlotResidualSigmaVsPSFMagSysTest(BaseScatterPlotSysTest): objects_list = ['star PSF'] required_quantities = [('sigma', 'sigma_err', 'psf_sigma', 'mag_inst')] - def __call__(self, array, per_ccd_stat='None', color='', lim=None): + def __call__(self, array, per_ccd_stat='None', color='', lim=None, + histogram=False, histogram_n_bins=40, histogram_cmap='Blues'): self.per_ccd_stat = None if per_ccd_stat == 'None' else per_ccd_stat import numpy.lib.recfunctions use_array = numpy.copy(array) @@ -2360,5 +2409,7 @@ def __call__(self, array, per_ccd_stat='None', color='', lim=None): ylabel= r'$(\sigma^{\rm star} - \sigma^{\rm PSF})/\sigma^{\rm PSF}$', color=color, lim=lim, equal_axis=False, - linear_regression=True, reference_line='zero') + linear_regression=True, reference_line='zero', + histogram=histogram, histogram_n_bins=histogram_n_bins, + histogram_cmap=histogram_cmap) diff --git a/tests/test_sys_tests.py b/tests/test_sys_tests.py new file mode 100644 index 0000000..3f134e4 --- /dev/null +++ b/tests/test_sys_tests.py @@ -0,0 +1,28 @@ +import numpy +import unittest + +try: + import stile +except ImportError: + sys.path.append('..') + import stile + +class TestSysTests(unittest.TestCase): + def test_histogram_scatterplot(self): + source_data = stile.ReadASCIITable('../examples/example_source_catalog.dat', + fields={'id': 0, 'ra': 1, 'dec': 2, 'z': 3, 'g1': 4, 'g2': 5}) + data = numpy.rec.fromarrays([source_data['ra'], source_data['dec'], source_data['g1'], + source_data['g2'], source_data['g1']+0.1*source_data['ra'], source_data['g2']+0.05*source_data['dec'], + numpy.ones_like(source_data['ra']), numpy.ones_like(source_data['ra'])+0.02, + source_data['g1']*0.05, source_data['g2']*0.05, source_data['g1']*0.05, + source_data['g2']*0.05], + names = ['x', 'y', 'g1', 'g2', 'psf_g1', 'psf_g2', 'sigma', 'psf_sigma', + 'g1_err', 'g2_err', 'psf_g1_err', 'psf_g2_err']) + obj = stile.ScatterPlotSysTest('StarVsPSFG1') + obj(data, color='g').savefig('p1.png') + obj(data, histogram=True, color='g').savefig('p2.png') + + +if __name__=='__main__': + unittest.main() + From 5066e773996178561576ded32fe11a677f5772f1 Mon Sep 17 00:00:00 2001 From: Melanie Simet Date: Fri, 22 Jun 2018 19:20:48 -0600 Subject: [PATCH 2/4] Remove spurious diagnostic from test_sys_tests (#94) --- tests/test_sys_tests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_sys_tests.py b/tests/test_sys_tests.py index 3f134e4..afb8dfb 100644 --- a/tests/test_sys_tests.py +++ b/tests/test_sys_tests.py @@ -19,8 +19,8 @@ def test_histogram_scatterplot(self): names = ['x', 'y', 'g1', 'g2', 'psf_g1', 'psf_g2', 'sigma', 'psf_sigma', 'g1_err', 'g2_err', 'psf_g1_err', 'psf_g2_err']) obj = stile.ScatterPlotSysTest('StarVsPSFG1') - obj(data, color='g').savefig('p1.png') - obj(data, histogram=True, color='g').savefig('p2.png') + obj(data, color='g') + obj(data, histogram=True, color='g') if __name__=='__main__': From 97048f8f03f50bd578977e201648503f424d5647 Mon Sep 17 00:00:00 2001 From: Melanie Simet Date: Fri, 13 Jul 2018 17:57:14 -0700 Subject: [PATCH 3/4] Update changelog (#94) --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d07c664..2f14871 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +7/13/18: Add 2D histogram option for ScatterPlotSysTest (issue #94) 7/11/18: Update code to python3 3/17/16: Update documentation to Sphinx standard and add documentation build files (issue #17) 2/17/16: Changes to correlation function plots & documentation (issue #77) From 6c70cd5c0045afc745d049467d7e819d62243291 Mon Sep 17 00:00:00 2001 From: Melanie Simet Date: Fri, 13 Jul 2018 17:58:00 -0700 Subject: [PATCH 4/4] Remove print statements used in testing (#94) --- stile/sys_tests.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/stile/sys_tests.py b/stile/sys_tests.py index 9cde27d..020ca92 100644 --- a/stile/sys_tests.py +++ b/stile/sys_tests.py @@ -2024,7 +2024,6 @@ def scatterPlot(self, x, y, yerr=None, z=None, xlabel=None, ylabel=None, zlabel= """ fig = plt.figure() ax = fig.add_subplot(1, 1, 1) - print histogram, "histogram" # mask data with nan. Emit a warning if an array has nan in it. x_isnan = numpy.isnan(x) y_isnan = numpy.isnan(y) @@ -2269,7 +2268,6 @@ class ScatterPlotStarVsPSFG1SysTest(BaseScatterPlotSysTest): def __call__(self, array, per_ccd_stat=None, color='', lim=None, histogram=False, histogram_n_bins=40, histogram_cmap='Blues'): - print "lalala", histogram return super(ScatterPlotStarVsPSFG1SysTest, self).__call__(array, 'psf_g1', 'g1', 'g1_err', residual=False, per_ccd_stat=per_ccd_stat, xlabel=r'$g^{\rm PSF}_1$',