Coverage for tests/test_scatterPlot.py: 22%
96 statements
« prev ^ index » next coverage.py v7.2.3, created at 2023-04-21 10:05 +0000
« prev ^ index » next coverage.py v7.2.3, created at 2023-04-21 10:05 +0000
1# This file is part of analysis_drp.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This program is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program. If not, see <https://www.gnu.org/licenses/>.
23import os
24import shutil
25import tempfile
26import unittest
28import lsst.utils.tests
29import matplotlib
30import matplotlib.pyplot as plt
31import numpy as np
32import pandas as pd
33from lsst.analysis.tools.actions.plot.plotUtils import get_and_remove_figure_text
34from lsst.analysis.tools.actions.plot.scatterplotWithTwoHists import (
35 ScatterPlotStatsAction,
36 ScatterPlotWithTwoHists,
37)
38from lsst.analysis.tools.actions.vector.selectors import (
39 GalaxySelector,
40 SnSelector,
41 StarSelector,
42 VectorSelector,
43)
44from lsst.analysis.tools.actions.vector.vectorActions import (
45 ConstantValue,
46 DivideVector,
47 DownselectVector,
48 LoadVector,
49 MagColumnNanoJansky,
50 SubtractVector,
51)
52from lsst.analysis.tools.interfaces import AnalysisPlot
54matplotlib.use("Agg")
56ROOT = os.path.abspath(os.path.dirname(__file__))
57filename_texts_ref = os.path.join(ROOT, "data", "test_scatterPlot_texts.txt")
58path_lines_ref = os.path.join(ROOT, "data", "test_scatterPlot_lines")
61class ScatterPlotWithTwoHistsTaskTestCase(lsst.utils.tests.TestCase):
62 """ScatterPlotWithTwoHistsTask test case."""
64 def setUp(self):
65 self.testDir = tempfile.mkdtemp(dir=ROOT, prefix="test_output")
67 # Set up a quasi-plausible measurement catalog
68 mag = 12.5 + 2.5 * np.log10(np.arange(10, 100000))
69 flux = 10 ** (-0.4 * (mag - (mag[-1] + 1)))
70 rng = np.random.default_rng(0)
71 extendedness = 0.0 + (rng.uniform(size=len(mag)) < 0.99 * (mag - mag[0]) / (mag[-1] - mag[0]))
72 flux_meas = flux + rng.normal(scale=np.sqrt(flux * (1 + extendedness)))
73 flux_err = np.sqrt(flux_meas * (1 + extendedness))
74 good = (flux_meas / np.sqrt(flux * (1 + extendedness))) > 3
75 extendedness = extendedness[good]
76 flux = flux[good]
77 flux_meas = flux_meas[good]
78 flux_err = flux_err[good]
80 # Configure the plot to show observed vs true mags
81 action = ScatterPlotWithTwoHists(
82 xAxisLabel="mag",
83 yAxisLabel="mag meas - ref",
84 magLabel="mag",
85 plotTypes=[
86 "stars",
87 "galaxies",
88 ],
89 xLims=(20, 30),
90 yLims=(-1000, 1000),
91 )
92 plot = AnalysisPlot(produce=action)
94 # Load the relevant columns
95 key_flux = "meas_Flux"
96 plot.process.buildActions.fluxes_meas = LoadVector(vectorKey=key_flux)
97 plot.process.buildActions.fluxes_err = LoadVector(vectorKey=f"{key_flux}Err")
98 plot.process.buildActions.fluxes_ref = LoadVector(vectorKey="ref_Flux")
99 plot.process.buildActions.mags_ref = MagColumnNanoJansky(
100 vectorKey=plot.process.buildActions.fluxes_ref.vectorKey
101 )
103 # Compute the y-axis quantity
104 plot.process.buildActions.diff = SubtractVector(
105 actionA=MagColumnNanoJansky(
106 vectorKey=plot.process.buildActions.fluxes_meas.vectorKey, returnMillimags=True
107 ),
108 actionB=DivideVector(
109 actionA=plot.process.buildActions.mags_ref,
110 actionB=ConstantValue(value=1e-3),
111 ),
112 )
114 # Filter stars/galaxies, storing quantities separately
115 plot.process.buildActions.galaxySelector = GalaxySelector(vectorKey="refExtendedness")
116 plot.process.buildActions.starSelector = StarSelector(vectorKey="refExtendedness")
117 for singular, plural in (("galaxy", "Galaxies"), ("star", "Stars")):
118 setattr(
119 plot.process.filterActions,
120 f"x{plural}",
121 DownselectVector(
122 vectorKey="mags_ref", selector=VectorSelector(vectorKey=f"{singular}Selector")
123 ),
124 )
125 setattr(
126 plot.process.filterActions,
127 f"y{plural}",
128 DownselectVector(vectorKey="diff", selector=VectorSelector(vectorKey=f"{singular}Selector")),
129 )
130 setattr(
131 plot.process.filterActions,
132 f"flux{plural}",
133 DownselectVector(
134 vectorKey="fluxes_meas", selector=VectorSelector(vectorKey=f"{singular}Selector")
135 ),
136 )
137 setattr(
138 plot.process.filterActions,
139 f"flux{plural}Err",
140 DownselectVector(
141 vectorKey="fluxes_err", selector=VectorSelector(vectorKey=f"{singular}Selector")
142 ),
143 )
145 # Compute low/high SN summary stats
146 statAction = ScatterPlotStatsAction(
147 vectorKey=f"y{plural}",
148 fluxType=f"flux{plural}",
149 highSNSelector=SnSelector(fluxType=f"flux{plural}", threshold=50),
150 lowSNSelector=SnSelector(fluxType=f"flux{plural}", threshold=20),
151 )
152 setattr(plot.process.calculateActions, plural.lower(), statAction)
153 plot.produce = action
155 data = {
156 "ref_Flux": flux,
157 key_flux: flux_meas,
158 f"{key_flux}Err": flux_err,
159 "refExtendedness": extendedness,
160 }
162 self.data = pd.DataFrame(data)
163 self.plot = plot
164 plotInfo = {key: "test" for key in ("plotName", "run", "tableName")}
165 plotInfo["bands"] = []
166 self.plotInfo = plotInfo
168 def tearDown(self):
169 if os.path.exists(self.testDir):
170 shutil.rmtree(self.testDir, True)
171 del self.data
172 del self.plot
173 del self.plotInfo
174 del self.testDir
176 def test_ScatterPlotWithTwoHistsTask(self):
177 plt.rcParams.update(plt.rcParamsDefault)
178 result = self.plot(
179 data=self.data,
180 skymap=None,
181 plotInfo=self.plotInfo,
182 )
183 self.assertTrue(isinstance(result, plt.Figure))
185 # Set to true to save plots as PNGs
186 # Use matplotlib.testing.compare.compare_images if needed
187 save_images = False
188 if save_images:
189 result.savefig(os.path.join(ROOT, "data", "test_scatterPlot.png"))
191 texts, lines = get_and_remove_figure_text(result)
192 if save_images:
193 result.savefig(os.path.join(ROOT, "data", "test_scatterPlot_unlabeled.png"))
195 # Set to true to re-generate reference data
196 resave = False
198 # Compare line values
199 for idx, line in enumerate(lines):
200 filename = os.path.join(path_lines_ref, f"line_{idx}.txt")
201 if resave:
202 np.savetxt(filename, line)
203 arr = np.loadtxt(filename)
204 # Differences of order 1e-12 possible between MacOS and Linux
205 # Plots are generally not expected to be that precise
206 # Differences to 1e-3 should not be visible with this test data
207 self.assertFloatsAlmostEqual(arr, line, atol=1e-3, rtol=1e-4)
209 # Ensure that newlines within labels are replaced by a sentinel
210 newline = "\n"
211 newline_replace = "[newline]"
212 # Compare text labels
213 if resave:
214 with open(filename_texts_ref, "w") as f:
215 f.writelines(f"{text.strip().replace(newline, newline_replace)}\n" for text in texts)
217 with open(filename_texts_ref, "r") as f:
218 texts_ref = set(x.strip() for x in f.readlines())
219 texts_set = set(x.strip().replace(newline, newline_replace) for x in texts)
221 self.assertTrue(texts_set.issuperset(texts_ref))
224class MemoryTester(lsst.utils.tests.MemoryTestCase):
225 pass
228def setup_module(module):
229 lsst.utils.tests.init()
232if __name__ == "__main__": 232 ↛ 233line 232 didn't jump to line 233, because the condition on line 232 was never true
233 lsst.utils.tests.init()
234 unittest.main()