Coverage for tests/test_actions.py: 16%

224 statements  

« prev     ^ index     » next       coverage.py v6.4.2, created at 2022-08-06 02:06 -0700

1# This file is part of analysis_tools. 

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/>. 

21 

22import unittest 

23 

24import astropy.units as u 

25import numpy as np 

26import pandas as pd 

27from lsst.analysis.tools.actions.scalar.scalarActions import ( 

28 ApproxFloor, 

29 CountAction, 

30 MeanAction, 

31 MedianAction, 

32 SigmaMadAction, 

33 StdevAction, 

34) 

35from lsst.analysis.tools.actions.vector.calcShapeSize import CalcShapeSize 

36from lsst.analysis.tools.actions.vector.selectors import ( 

37 CoaddPlotFlagSelector, 

38 FlagSelector, 

39 GalaxySelector, 

40 SkyObjectSelector, 

41 SnSelector, 

42 StarSelector, 

43 VectorSelector, 

44) 

45from lsst.analysis.tools.actions.vector.vectorActions import ( 

46 DownselectVector, 

47 ExtinctionCorrectedMagDiff, 

48 FractionalDifference, 

49 LoadVector, 

50 MagColumnNanoJansky, 

51 MagDiff, 

52) 

53 

54 

55class TestScalarActions(unittest.TestCase): 

56 """ "Test ScalarActions""" 

57 

58 def setUp(self): 

59 x = np.arange(100, dtype=float) 

60 x[31] = np.nan 

61 x[41] = np.nan 

62 x[59] = np.nan 

63 y = x**2 

64 self.data = {"r_y": x, "i_y": y} 

65 self.mask = np.zeros(100, dtype=bool) 

66 self.mask[1:50] = 1 

67 

68 def _testScalarActionAlmostEqual(self, cls, truth, maskedTruth): 

69 action = cls(vectorKey="{band}_y") 

70 schema = [col for col, colType in action.getInputSchema()] 

71 self.assertEqual(schema, ["{band}_y"]) 

72 result = action(self.data, band="i") 

73 self.assertAlmostEqual(result, truth) 

74 result = action(self.data, mask=self.mask, band="i") 

75 self.assertAlmostEqual(result, maskedTruth) 

76 

77 def testMedianAction(self): 

78 self._testScalarActionAlmostEqual(MedianAction, 2500, 576) 

79 

80 def testMeanAction(self): 

81 self._testScalarActionAlmostEqual(MeanAction, 3321.9278350515465, 803.8936170212766) 

82 

83 def testStdevAction(self): 

84 self._testScalarActionAlmostEqual(StdevAction, 2984.5855649976297, 733.5989754407842) 

85 

86 def testSigmaMadAction(self): 

87 self._testScalarActionAlmostEqual(SigmaMadAction, 3278.033505115886, 759.0923358748682) 

88 

89 def testCountAction(self): 

90 self._testScalarActionAlmostEqual(CountAction, 97, 47) 

91 

92 def testApproxFloorAction(self): 

93 self._testScalarActionAlmostEqual(ApproxFloor, 9216.0, 2352.5) 

94 

95 

96class TestVectorActions(unittest.TestCase): 

97 """Test VectorActions""" 

98 

99 def setUp(self): 

100 r = np.arange(5) + 1 

101 i = r**2 

102 rFlag = np.ones(5) 

103 rFlag[1] = 0 

104 iFlag = np.ones(5) 

105 iFlag[3] = 0 

106 data = { 

107 "r_vector": r, 

108 "i_vector": i, 

109 "r_flag": rFlag, 

110 "i_flag": iFlag, 

111 "g_vector": 3 * r, 

112 "E(B-V)": r[::-1], 

113 "r_ixx": r**2, 

114 "r_iyy": r[::-1] ** 2, 

115 "r_ixy": r * r[::-1], 

116 "g_iixx": r**2, 

117 "g_iiyy": r[::-1] ** 2, 

118 "g_iixy": r * r[::-1], 

119 } 

120 

121 self.data = pd.DataFrame.from_dict(data) 

122 

123 def _checkSchema(self, action, truth): 

124 schema = sorted([col for col, colType in action.getInputSchema()]) 

125 self.assertEqual(schema, truth) 

126 

127 def testDownselectVector(self): 

128 selector = FlagSelector(selectWhenTrue=["{band}_flag"]) 

129 action = DownselectVector(vectorKey="{band}_vector", selector=selector) 

130 result = action(self.data, band="r") 

131 self._checkSchema(action, ["{band}_flag", "{band}_vector"]) 

132 np.testing.assert_array_equal(result, np.array([1, 3, 4, 5])) 

133 

134 def testMagColumnNanoJansky(self): 

135 truth = [ 

136 31.40006562228223, 

137 29.894915643962324, 

138 29.014459348683918, 

139 28.389765665642418, 

140 27.905215600602137, 

141 ] 

142 action = MagColumnNanoJansky(vectorKey="{band}_vector") 

143 result = action(self.data, band="i") 

144 self._checkSchema(action, ["{band}_vector"]) 

145 np.testing.assert_array_almost_equal(result, truth) 

146 

147 def testFractionalDifference(self): 

148 actionA = LoadVector(vectorKey="{band1}_vector") 

149 actionB = LoadVector(vectorKey="{band2}_vector") 

150 truth = [0.0, -0.5, -0.6666666666666666, -0.75, -0.8] 

151 diff = FractionalDifference(actionA=actionA, actionB=actionB) 

152 result = diff(self.data, band1="r", band2="i") 

153 self._checkSchema(diff, ["{band1}_vector", "{band2}_vector"]) 

154 np.testing.assert_array_almost_equal(result, truth) 

155 

156 def testMagDiff(self): 

157 # Use the same units as the data so that we know the difference exactly 

158 # without conversions. 

159 # testExtinctionCorrectedMagDiff will test the conversions 

160 truth = np.array(2 * self.data["r_vector"]) * u.ABmag 

161 action = MagDiff( 

162 col1="{band1}_vector", 

163 col2="{band2}_vector", 

164 fluxUnits1="mag(AB)", 

165 fluxUnits2="mag(AB)", 

166 returnMillimags=False, 

167 ) 

168 result = action(self.data, band1="g", band2="r") 

169 self._checkSchema(action, ["{band1}_vector", "{band2}_vector"]) 

170 np.testing.assert_array_almost_equal(result, truth.value) 

171 

172 def testExtinctionCorrectedMagDiff(self): 

173 for returnMillimags in (True, False): 

174 # Check that conversions are working properly 

175 magDiff = MagDiff( 

176 col1="{band1}_vector", 

177 col2="{band2}_vector", 

178 fluxUnits1="jansky", 

179 fluxUnits2="jansky", 

180 returnMillimags=returnMillimags, 

181 ) 

182 action = ExtinctionCorrectedMagDiff( 

183 magDiff=magDiff, 

184 band1="g", 

185 band2="r", 

186 ebvCol="E(B-V)", 

187 extinctionCoeffs={"g": 0.2, "r": 1.5}, 

188 ) 

189 

190 result = action(self.data, band1="g", band2="r") 

191 lhs = (np.array(self.data["g_vector"]) * u.jansky).to(u.ABmag) 

192 rhs = (np.array(self.data["r_vector"]) * u.jansky).to(u.ABmag) 

193 diff = lhs - rhs 

194 correction = np.array((0.2 - 1.5) * self.data["E(B-V)"]) * u.mag 

195 if returnMillimags: 

196 diff = diff.to(u.mmag) 

197 correction = correction.to(u.mmag) 

198 truth = diff - correction 

199 self._checkSchema(action, ["E(B-V)", "{band1}_vector", "{band2}_vector"]) 

200 np.testing.assert_array_almost_equal(result, truth.value) 

201 

202 # Test with hard coded bands 

203 magDiff = MagDiff( 

204 col1="g_vector", 

205 col2="r_vector", 

206 fluxUnits1="jansky", 

207 fluxUnits2="jansky", 

208 returnMillimags=False, 

209 ) 

210 action = ExtinctionCorrectedMagDiff( 

211 magDiff=magDiff, 

212 ebvCol="E(B-V)", 

213 extinctionCoeffs={"g": 0.2, "r": 1.5}, 

214 ) 

215 result = action(self.data) 

216 lhs = (np.array(self.data["g_vector"]) * u.jansky).to(u.ABmag) 

217 rhs = (np.array(self.data["r_vector"]) * u.jansky).to(u.ABmag) 

218 diff = lhs - rhs 

219 correction = np.array((0.2 - 1.5) * self.data["E(B-V)"]) * u.mag 

220 truth = diff - correction 

221 self._checkSchema(action, ["E(B-V)", "g_vector", "r_vector"]) 

222 np.testing.assert_array_almost_equal(result, truth.value) 

223 

224 def testCalcShapeSize(self): 

225 xx = self.data["r_ixx"] 

226 yy = self.data["r_iyy"] 

227 xy = self.data["r_ixy"] 

228 

229 # Test determinant with defaults 

230 action = CalcShapeSize() 

231 result = action(self.data, band="r") 

232 schema = [col for col, colType in action.getInputSchema()] 

233 self.assertEqual(sorted(schema), ["{band}_ixx", "{band}_ixy", "{band}_iyy"]) 

234 truth = 0.25 * (xx * yy - xy**2) 

235 np.testing.assert_array_almost_equal(result, truth) 

236 

237 # Test trace with columns specified 

238 action = CalcShapeSize( 

239 colXx="{band}_iixx", 

240 colYy="{band}_iiyy", 

241 colXy="{band}_iixy", 

242 sizeType="trace", 

243 ) 

244 result = action(self.data, band="g") 

245 schema = [col for col, colType in action.getInputSchema()] 

246 self.assertEqual(sorted(schema), ["{band}_iixx", "{band}_iiyy"]) 

247 truth = np.sqrt(0.5 * (xx + yy)) 

248 np.testing.assert_array_almost_equal(result, truth) 

249 

250 

251class TestVectorSelectors(unittest.TestCase): 

252 def setUp(self): 

253 self.size = 20 

254 falseFlags = { 

255 "{band}_psfFlux_flag": [1], 

256 "{band}_pixelFlags_saturatedCenter": [3], 

257 "{band}_extendedness_flag": [5], 

258 "xy_flag": [7], 

259 "i_pixelFlags_edge": [13], 

260 "r_pixelFlags_edge": [15], 

261 "sky_object": [13, 15, 17], 

262 } 

263 

264 trueFlags = { 

265 "detect_isPatchInner": [9], 

266 "detect_isDeblendedSource": [11], 

267 } 

268 

269 flux = np.arange(self.size) * 10 

270 fluxErr = np.ones(self.size) * 0.1 

271 extendedness = np.arange(20) / 20 - 0.1 

272 

273 self.data = { 

274 "r_psfFlux": flux, 

275 "r_psfFluxErr": fluxErr, 

276 "i_cmodelFlux": flux[::-1], 

277 "i_cmodelFluxError": fluxErr[::-1], 

278 "r_cmodelFlux": flux, 

279 "r_cmodelFluxError": fluxErr, 

280 "i_extendedness": extendedness, 

281 "i_extended": extendedness, 

282 } 

283 bands = ("r", "i") 

284 for band in bands: 

285 for flag, bits in falseFlags.items(): 

286 vector = np.zeros(self.size, dtype=bool) 

287 for bit in bits: 

288 vector[bit] = 1 

289 self.data[flag.format(band=band)] = vector 

290 

291 for flag, bits in trueFlags.items(): 

292 vector = np.ones(self.size, dtype=bool) 

293 for bit in bits: 

294 vector[bit] = 0 

295 self.data[flag.format(band=band)] = vector 

296 

297 def _checkSchema(self, action, truth): 

298 schema = [col for col, colType in action.getInputSchema()] 

299 self.assertEqual(sorted(schema), sorted(truth)) 

300 

301 def testFlagSelector(self): 

302 selector = FlagSelector( 

303 selectWhenFalse=["{band}_psfFlux_flag"], selectWhenTrue=["detect_isPatchInner"] 

304 ) 

305 self._checkSchema(selector, ["detect_isPatchInner", "{band}_psfFlux_flag"]) 

306 result = selector(self.data, band="r") 

307 truth = np.ones(self.size, dtype=bool) 

308 truth[1] = False 

309 truth[9] = False 

310 np.testing.assert_array_equal(result, truth) 

311 

312 def testCoaddPlotFlagSelector(self): 

313 # Test defaults 

314 selector = CoaddPlotFlagSelector() 

315 keys = [ 

316 "{band}_psfFlux_flag", 

317 "{band}_pixelFlags_saturatedCenter", 

318 "{band}_extendedness_flag", 

319 "xy_flag", 

320 "detect_isPatchInner", 

321 "detect_isDeblendedSource", 

322 ] 

323 self._checkSchema(selector, keys) 

324 

325 result = selector(self.data) 

326 truth = np.ones(self.size, dtype=bool) 

327 for bit in (1, 3, 5, 7, 9, 11): 

328 truth[bit] = 0 

329 np.testing.assert_array_equal(result, truth) 

330 

331 # Test bands override 

332 selector = CoaddPlotFlagSelector( 

333 bands=["i", "r"], 

334 selectWhenFalse=["{band}_psfFlux_flag"], 

335 selectWhenTrue=["detect_isDeblendedSource"], 

336 ) 

337 self._checkSchema(selector, ["{band}_psfFlux_flag", "detect_isDeblendedSource"]) 

338 result = selector(self.data) 

339 truth = np.ones(self.size, dtype=bool) 

340 for bit in (1, 11): 

341 truth[bit] = 0 

342 np.testing.assert_array_equal(result, truth) 

343 

344 def testSnSelector(self): 

345 # test defaults 

346 selector = SnSelector() 

347 keys = [ 

348 "{band}_psfFlux", 

349 "{band}_psfFluxErr", 

350 ] 

351 self._checkSchema(selector, keys) 

352 result = selector(self.data, bands=["r"]) 

353 truth = np.ones(self.size, dtype=bool) 

354 truth[:6] = 0 

355 np.testing.assert_array_equal(result, truth) 

356 

357 # test overrides 

358 selector = SnSelector( 

359 fluxType="{band}_cmodelFlux", 

360 threshold=200.0, 

361 uncertaintySuffix="Error", 

362 bands=["r", "i"], 

363 ) 

364 keys = [ 

365 "{band}_cmodelFlux", 

366 "{band}_cmodelFluxError", 

367 ] 

368 self._checkSchema(selector, keys) 

369 result = selector(self.data) 

370 truth = np.ones(self.size, dtype=bool) 

371 truth[:3] = 0 

372 truth[-3:] = 0 

373 np.testing.assert_array_equal(result, truth) 

374 

375 def testSkyObjectSelector(self): 

376 # Test default 

377 selector = SkyObjectSelector() 

378 keys = ["{band}_pixelFlags_edge", "sky_object"] 

379 self._checkSchema(selector, keys) 

380 result = selector(self.data) 

381 truth = np.zeros(self.size, dtype=bool) 

382 truth[15] = 1 

383 truth[17] = 1 

384 np.testing.assert_array_equal(result, truth) 

385 

386 # Test overrides 

387 selector = SkyObjectSelector(bands=["i", "r"]) 

388 self._checkSchema(selector, keys) 

389 result = selector(self.data) 

390 truth = np.zeros(self.size, dtype=bool) 

391 truth[17] = 1 

392 np.testing.assert_array_equal(result, truth) 

393 

394 def testStarSelector(self): 

395 # test default 

396 selector = StarSelector() 

397 self._checkSchema(selector, ["{band}_extendedness"]) 

398 result = selector(self.data, band="i") 

399 truth = (self.data["i_extendedness"] >= 0) & (self.data["i_extendedness"] < 0.5) 

400 np.testing.assert_array_almost_equal(result, truth) 

401 

402 # Test overrides 

403 selector = StarSelector(vectorKey="i_extended", extendedness_maximum=0.3) 

404 result = selector(self.data, band="i") 

405 truth = (self.data["i_extendedness"] >= 0) & (self.data["i_extendedness"] < 0.3) 

406 np.testing.assert_array_almost_equal(result, truth) 

407 

408 def testGalaxySelector(self): 

409 # test default 

410 selector = GalaxySelector() 

411 self._checkSchema(selector, ["{band}_extendedness"]) 

412 result = selector(self.data, band="i") 

413 truth = self.data["i_extendedness"] > 0.5 

414 np.testing.assert_array_almost_equal(result, truth) 

415 

416 # Test overrides 

417 selector = GalaxySelector(vectorKey="i_extended", extendedness_minimum=0.3) 

418 result = selector(self.data, band="i") 

419 truth = self.data["i_extendedness"] > 0.3 

420 np.testing.assert_array_almost_equal(result, truth) 

421 

422 def testVectorSelector(self): 

423 selector = VectorSelector(vectorKey="{band}_psfFlux_flag") 

424 self._checkSchema(selector, ["{band}_psfFlux_flag"]) 

425 result = selector(self.data, band="i") 

426 truth = np.zeros(self.size, dtype=bool) 

427 truth[1] = True 

428 np.testing.assert_array_equal(result, truth)