Coverage for tests / test_actions.py: 15%

378 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-03-02 02:04 -0800

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 

27 

28import lsst.utils.tests 

29from lsst.analysis.tools.actions.keyedData.calcDistances import CalcRelativeDistances 

30from lsst.analysis.tools.actions.scalar import ( 

31 ApproxFloor, 

32 CountAction, 

33 MeanAction, 

34 MedianAction, 

35 SigmaMadAction, 

36 StdevAction, 

37) 

38from lsst.analysis.tools.actions.vector import ( 

39 CalcBinnedStatsAction, 

40 CalcMomentSize, 

41 CalcRhoStatistics, 

42 ConvertFluxToMag, 

43 DownselectVector, 

44 ExtinctionCorrectedMagDiff, 

45 LoadVector, 

46 MagDiff, 

47) 

48from lsst.analysis.tools.actions.vector.mathActions import ( 

49 AddVector, 

50 ConstantValue, 

51 DivideVector, 

52 FractionalDifference, 

53 Log10Vector, 

54 MultiplyVector, 

55 RaiseFromBaseVector, 

56 RaiseToPowerVector, 

57 SqrtVector, 

58 SquareVector, 

59 SubtractVector, 

60) 

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

62 CoaddPlotFlagSelector, 

63 FlagSelector, 

64 GalaxySelector, 

65 RangeSelector, 

66 SetSelector, 

67 SkyObjectSelector, 

68 SnSelector, 

69 StarSelector, 

70 VectorSelector, 

71) 

72from lsst.analysis.tools.math import sqrt 

73from lsst.pex.config import FieldValidationError 

74 

75 

76class TestScalarActions(unittest.TestCase): 

77 """ "Test ScalarActions""" 

78 

79 def setUp(self): 

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

81 x[31] = np.nan 

82 x[41] = np.nan 

83 x[59] = np.nan 

84 y = x**2 

85 self.data = { 

86 "r_y": x, 

87 "i_y": y, 

88 "z_y": y.reshape((10, 10)), 

89 } 

90 mask = np.zeros(100, dtype=bool) 

91 mask[1:50] = 1 

92 self.mask = { 

93 "r": mask, 

94 "i": mask, 

95 "z": mask.reshape((10, 10)), 

96 } 

97 

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

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

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

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

102 

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

104 self.assertAlmostEqual(result, truth) 

105 

106 result = action(self.data, band="z") 

107 self.assertAlmostEqual(result, truth) 

108 

109 result = action(self.data, mask=self.mask["i"], band="i") 

110 self.assertAlmostEqual(result, maskedTruth) 

111 

112 result = action(self.data, mask=self.mask["z"], band="z") 

113 self.assertAlmostEqual(result, maskedTruth) 

114 

115 def testMedianAction(self): 

116 self._testScalarActionAlmostEqual(MedianAction, 2500, 576) 

117 

118 def testMeanAction(self): 

119 self._testScalarActionAlmostEqual(MeanAction, 3321.9278350515465, 803.8936170212766) 

120 

121 def testStdevAction(self): 

122 self._testScalarActionAlmostEqual(StdevAction, 2984.5855649976297, 733.5989754407842) 

123 

124 def testSigmaMadAction(self): 

125 self._testScalarActionAlmostEqual(SigmaMadAction, 3278.033505115886, 759.0923358748682) 

126 

127 def testCountAction(self): 

128 self._testScalarActionAlmostEqual(CountAction, 97, 47) 

129 

130 def testApproxFloorAction(self): 

131 self._testScalarActionAlmostEqual(ApproxFloor, 9216.0, 2352.5) 

132 

133 

134class TestVectorActions(unittest.TestCase): 

135 """Test VectorActions""" 

136 

137 def setUp(self): 

138 self.size = 5 

139 r = np.arange(float(self.size)) + 1 

140 i = r**2 

141 rFlag = np.ones(self.size) 

142 rFlag[1] = 0 

143 iFlag = np.ones(self.size) 

144 iFlag[3] = 0 

145 data = { 

146 "r_vector": r, 

147 "i_vector": i, 

148 "r_flag": rFlag, 

149 "i_flag": iFlag, 

150 "g_vector": 3 * r, 

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

152 "r_ixx": r**2, 

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

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

155 "g_iixx": r**2, 

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

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

158 } 

159 

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

161 

162 def _checkSchema(self, action, truth): 

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

164 self.assertEqual(schema, truth) 

165 

166 # VectorActions with their own files 

167 

168 def testCalcBinnedStats(self): 

169 selector = RangeSelector(vectorKey="r_vector", minimum=0, maximum=self.size) 

170 prefix = "a_" 

171 stats = CalcBinnedStatsAction(name_prefix=prefix, selector_range=selector, key_vector="r_vector") 

172 result = stats(self.data) 

173 mask = selector(self.data) 

174 values = self.data["r_vector"][mask] 

175 median = np.median(values) 

176 truth = { 

177 stats.name_mask: mask, 

178 stats.name_median: median, 

179 stats.name_sigmaMad: 1.482602218505602 * np.median(np.abs(values - median)), 

180 stats.name_count: np.sum(mask), 

181 stats.name_select_maximum: np.max(values), 

182 stats.name_select_median: median, 

183 stats.name_select_minimum: np.min(values), 

184 "range_maximum": self.size, 

185 "range_minimum": 0, 

186 } 

187 self.assertEqual(list(result.keys()), list(truth.keys())) 

188 

189 np.testing.assert_array_almost_equal(result[stats.name_sigmaMad], truth[stats.name_sigmaMad]) 

190 del truth[stats.name_sigmaMad] 

191 

192 np.testing.assert_array_equal(result[stats.name_mask], truth[stats.name_mask]) 

193 del truth[stats.name_mask] 

194 

195 for key, value in truth.items(): 

196 self.assertEqual(result[key], value, key) 

197 

198 def testCalcMomentSize(self): 

199 xx = self.data["r_ixx"] 

200 yy = self.data["r_iyy"] 

201 xy = self.data["r_ixy"] 

202 

203 # Test determinant with defaults 

204 action = CalcMomentSize() 

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

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

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

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

209 np.testing.assert_array_almost_equal(result, truth) 

210 

211 # Test trace with columns specified 

212 action = CalcMomentSize( 

213 colXx="{band}_iixx", 

214 colYy="{band}_iiyy", 

215 colXy="{band}_iixy", 

216 sizeType="trace", 

217 ) 

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

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

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

221 truth = sqrt(0.5 * (xx + yy)) 

222 np.testing.assert_array_almost_equal(result, truth) 

223 

224 CalcMomentSize(sizeType="trace", colXy=None).validate() 

225 with self.assertRaises(FieldValidationError): 

226 CalcMomentSize(sizeType="determinant", colXy=None).validate() 

227 

228 # def testCalcE(self): TODO: implement 

229 

230 # def testCalcEDiff(self): TODO: implement 

231 

232 # def testCalcE1(self): TODO: implement 

233 

234 # def testCalcE2(self): TODO: implement 

235 

236 # MathActions 

237 

238 def _testMath(self, ActionType, truth, num_vectors: int = 2, compare_exact: bool = False, **kwargs): 

239 letters = ("A", "B") 

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

241 actions = { 

242 f"action{letters[num]}": LoadVector(vectorKey=f"{{band{num+1}}}_vector") 

243 for num in range(num_vectors) 

244 } 

245 action = ActionType(**actions, **kwargs) 

246 kwargs_bands = {f"band{num+1}": bands[num] for num in range(num_vectors)} 

247 result = action(self.data, **kwargs_bands) 

248 self._checkSchema(action, [action.vectorKey for action in actions.values()]) 

249 if compare_exact: 

250 np.testing.assert_array_equal(result, truth) 

251 else: 

252 np.testing.assert_array_almost_equal(result, truth) 

253 

254 def testConstant(self): 

255 truth = [42.0] 

256 action = ConstantValue(value=truth[0]) 

257 self._checkSchema(action, []) 

258 result = action({}) 

259 np.testing.assert_array_equal(result, truth) 

260 

261 def testAdd(self): 

262 truth = [2.0, 6.0, 12.0, 20.0, 30.0] 

263 self._testMath(AddVector, truth, compare_exact=True) 

264 

265 def testSubtract(self): 

266 truth = [0.0, -2.0, -6.0, -12.0, -20.0] 

267 self._testMath(SubtractVector, truth, compare_exact=True) 

268 

269 def testMultiply(self): 

270 truth = [1.0, 8.0, 27.0, 64.0, 125.0] 

271 self._testMath(MultiplyVector, truth, compare_exact=False) 

272 

273 def testDivide(self): 

274 truth = 1 / np.arange(1, 6) 

275 self._testMath(DivideVector, truth, compare_exact=False) 

276 

277 def testSqrt(self): 

278 truth = sqrt(np.arange(1, 6)) 

279 self._testMath(SqrtVector, truth, compare_exact=False, num_vectors=1) 

280 

281 def testSquare(self): 

282 truth = np.arange(1, 6) ** 2 

283 self._testMath(SquareVector, truth, compare_exact=True, num_vectors=1) 

284 

285 def testRaiseFromBase(self): 

286 power = np.arange(1, 6) 

287 for base in (-2.3, 0.6): 

288 truth = base**power 

289 self._testMath(RaiseFromBaseVector, truth, compare_exact=False, base=base, num_vectors=1) 

290 

291 def testRaiseToPower(self): 

292 base = np.arange(1, 6) 

293 for power in (-2.3, 0.6): 

294 truth = base**power 

295 self._testMath(RaiseToPowerVector, truth, compare_exact=False, power=power, num_vectors=1) 

296 

297 def testLog10(self): 

298 truth = np.log10(np.arange(1, 6)) 

299 self._testMath(Log10Vector, truth, compare_exact=False, num_vectors=1) 

300 

301 def testFractionalDifference(self): 

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

303 self._testMath(FractionalDifference, truth, compare_exact=False) 

304 

305 # Basic vectorActions 

306 

307 # def testLoadVector(self): TODO: implement 

308 

309 def testDownselectVector(self): 

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

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

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

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

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

315 

316 # def testMultiCriteriaDownselectVector(self): TODO: implement 

317 

318 # Astronomical vectorActions 

319 

320 # def testCalcSn(self): TODO: implement 

321 

322 def testConvertFluxToMag(self): 

323 truth = [ 

324 31.4, 

325 29.89485002168, 

326 29.0143937264, 

327 28.38970004336, 

328 27.90514997832, 

329 ] 

330 action = ConvertFluxToMag(vectorKey="{band}_vector") 

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

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

333 np.testing.assert_array_almost_equal(result, truth) 

334 

335 # def testConvertUnits(self): TODO: implement 

336 

337 def testMagDiff(self): 

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

339 # without conversions. 

340 # testExtinctionCorrectedMagDiff will test the conversions 

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

342 action = MagDiff( 

343 col1="{band1}_vector", 

344 col2="{band2}_vector", 

345 fluxUnits1="mag(AB)", 

346 fluxUnits2="mag(AB)", 

347 returnMillimags=False, 

348 ) 

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

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

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

352 

353 def testExtinctionCorrectedMagDiff(self): 

354 for returnMillimags in (True, False): 

355 # Check that conversions are working properly 

356 magDiff = MagDiff( 

357 col1="{band1}_vector", 

358 col2="{band2}_vector", 

359 fluxUnits1="jansky", 

360 fluxUnits2="jansky", 

361 returnMillimags=returnMillimags, 

362 ) 

363 action = ExtinctionCorrectedMagDiff( 

364 magDiff=magDiff, 

365 band1="g", 

366 band2="r", 

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

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

369 ) 

370 

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

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

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

374 diff = lhs - rhs 

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

376 if returnMillimags: 

377 diff = diff.to(u.mmag) 

378 correction = correction.to(u.mmag) 

379 truth = diff - correction 

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

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

382 

383 # Test with hard coded bands 

384 magDiff = MagDiff( 

385 col1="g_vector", 

386 col2="r_vector", 

387 fluxUnits1="jansky", 

388 fluxUnits2="jansky", 

389 returnMillimags=False, 

390 ) 

391 action = ExtinctionCorrectedMagDiff( 

392 magDiff=magDiff, 

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

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

395 ) 

396 result = action(self.data) 

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

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

399 diff = lhs - rhs 

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

401 truth = diff - correction 

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

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

404 

405 # def testRAcosDec(self): TODO: implement 

406 

407 # Statistical vectorActions 

408 

409 # def testPerGroupStatistic(self): TODO: implement 

410 

411 # def testResidualWithPerGroupStatistic(self): TODO: implement 

412 

413 

414class TestVectorRhoStats(unittest.TestCase): 

415 """Test Rho stats""" 

416 

417 def setUp(self): 

418 # generate data just for testCalcRhoStatistics. 

419 np.random.seed(42) 

420 sizeRho = 1000 

421 size_src = np.random.normal(scale=1e-3, size=sizeRho) 

422 e1_src = np.random.normal(scale=1e-3, size=sizeRho) 

423 e2_src = np.random.normal(scale=1e-3, size=sizeRho) 

424 

425 size_psf = np.random.normal(scale=1e-3, size=sizeRho) 

426 e1_psf = np.random.normal(scale=1e-3, size=sizeRho) 

427 e2_psf = np.random.normal(scale=1e-3, size=sizeRho) 

428 

429 src_data = np.array( 

430 [self.getMatrixElements(size, e1, e2) for size, e1, e2 in zip(size_src, e1_src, e2_src)] 

431 ) 

432 psf_data = np.array( 

433 [self.getMatrixElements(size, e1, e2) for size, e1, e2 in zip(size_psf, e1_psf, e2_psf)] 

434 ) 

435 

436 dataRhoStats = { 

437 "coord_ra": np.random.uniform(-120, 120, sizeRho), 

438 "coord_dec": np.random.uniform(-120, 120, sizeRho), 

439 "r_ixx": src_data[:, 0], 

440 "r_iyy": src_data[:, 1], 

441 "r_ixy": src_data[:, 2], 

442 "r_ixxPSF": psf_data[:, 0], 

443 "r_iyyPSF": psf_data[:, 1], 

444 "r_ixyPSF": psf_data[:, 2], 

445 } 

446 

447 self.dataRhoStats = pd.DataFrame.from_dict(dataRhoStats) 

448 

449 # Needed for testCalcRhoStatistics. 

450 @staticmethod 

451 def getMatrixElements(size, e1, e2): 

452 # puting guards just in case e1 or e2 are 

453 # supprior to 1, but unlikely. 

454 if abs(e1) >= 1: 

455 e1 = 0 

456 if abs(e2) >= 1: 

457 e2 = 0 

458 e = sqrt(e1**2 + e2**2) 

459 q = (1 - e) / (1 + e) 

460 phi = 0.5 * np.arctan2(e2, e1) 

461 rot = np.array([[np.cos(phi), np.sin(phi)], [-np.sin(phi), np.cos(phi)]]) 

462 ell = np.array([[size**2, 0], [0, (size * q) ** 2]]) 

463 L = np.dot(rot.T, ell.dot(rot)) 

464 return [L[0, 0], L[1, 1], L[0, 1]] 

465 

466 def testCalcRhoStatistics(self): 

467 

468 # just check if runs 

469 rho = CalcRhoStatistics() 

470 rho.treecorr.nbins = 21 

471 rho.treecorr.min_sep = 0.01 

472 rho.treecorr.max_sep = 100.0 

473 rho.treecorr.sep_units = "arcmin" 

474 result = rho(self.dataRhoStats, band="r") 

475 for rho in result: 

476 if rho != "rho3alt": 

477 self.assertEqual(np.sum(np.isfinite(result[rho].xip)), len(result[rho].xip)) 

478 self.assertEqual(np.sum(np.isfinite(result[rho].xim)), len(result[rho].xim)) 

479 else: 

480 self.assertEqual(np.sum(np.isfinite(result[rho].xi)), len(result[rho].xi)) 

481 

482 

483class TestVectorSelectors(unittest.TestCase): 

484 def setUp(self): 

485 self.size = 20 

486 falseFlags = { 

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

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

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

490 "coord_flag": [7], 

491 "i_pixelFlags_edge": [13], 

492 "r_pixelFlags_edge": [15], 

493 "i_pixelFlags_nodata": [14], 

494 "r_pixelFlags_nodata": [16], 

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

496 } 

497 

498 trueFlags = { 

499 "detect_isPatchInner": [9], 

500 "detect_isDeblendedSource": [11], 

501 } 

502 

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

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

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

506 

507 self.data = { 

508 "r_psfFlux": flux, 

509 "r_psfFluxErr": fluxErr, 

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

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

512 "r_cmodelFlux": flux, 

513 "r_cmodelFluxError": fluxErr, 

514 "i_extendedness": extendedness, 

515 "i_extended": extendedness, 

516 } 

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

518 for band in bands: 

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

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

521 for bit in bits: 

522 vector[bit] = 1 

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

524 

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

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

527 for bit in bits: 

528 vector[bit] = 0 

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

530 

531 def _checkSchema(self, action, truth): 

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

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

534 

535 def testFlagSelector(self): 

536 selector = FlagSelector( 

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

538 ) 

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

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

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

542 truth[1] = False 

543 truth[9] = False 

544 np.testing.assert_array_equal(result, truth) 

545 

546 def testCoaddPlotFlagSelector(self): 

547 # Test defaults 

548 # Bands needs to be set to something otherwise it 

549 # will crash as the default value looks it up 

550 # elsewhere. 

551 selector = CoaddPlotFlagSelector(bands=["i"]) 

552 keys = [ 

553 "{band}_psfFlux_flag", 

554 "{band}_pixelFlags_saturatedCenter", 

555 "{band}_extendedness_flag", 

556 "sky_object", 

557 "coord_flag", 

558 "detect_isPatchInner", 

559 "detect_isDeblendedSource", 

560 ] 

561 self._checkSchema(selector, keys) 

562 

563 result = selector(self.data) 

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

565 for bit in (1, 3, 5, 7, 9, 11, 13, 15, 17): 

566 truth[bit] = 0 

567 np.testing.assert_array_equal(result, truth) 

568 

569 # Test bands override 

570 selector = CoaddPlotFlagSelector( 

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

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

573 selectWhenTrue=["detect_isDeblendedSource"], 

574 ) 

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

576 result = selector(self.data) 

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

578 for bit in (1, 11): 

579 truth[bit] = 0 

580 np.testing.assert_array_equal(result, truth) 

581 

582 def testRangeSelector(self): 

583 selector = RangeSelector(vectorKey="r_psfFlux", minimum=np.nextafter(20, 30), maximum=50) 

584 self._checkSchema(selector, ["r_psfFlux"]) 

585 result = self.data["r_psfFlux"][selector(self.data)] 

586 truth = [30, 40] 

587 np.testing.assert_array_equal(result, truth) 

588 

589 def testSetSelector(self): 

590 n_values = 3 

591 values = self.data["r_psfFlux"][:n_values] 

592 selector = SetSelector(vectorKeys=("r_psfFlux", "i_cmodelFlux"), values=values) 

593 self._checkSchema(selector, ("r_psfFlux", "i_cmodelFlux")) 

594 result = selector(self.data) 

595 truth = np.zeros_like(result) 

596 truth[:n_values] = True 

597 # i_cModelFlux is just r_psfFlux reversed 

598 truth[-n_values:] = True 

599 np.testing.assert_array_equal(result, truth) 

600 

601 def testSnSelector(self): 

602 # test defaults 

603 selector = SnSelector() 

604 keys = [ 

605 "{band}_psfFlux", 

606 "{band}_psfFluxErr", 

607 ] 

608 self._checkSchema(selector, keys) 

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

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

611 truth[:6] = 0 

612 np.testing.assert_array_equal(result, truth) 

613 

614 # test overrides 

615 selector = SnSelector( 

616 fluxType="{band}_cmodelFlux", 

617 threshold=200.0, 

618 uncertaintySuffix="Error", 

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

620 ) 

621 keys = [ 

622 "{band}_cmodelFlux", 

623 "{band}_cmodelFluxError", 

624 ] 

625 self._checkSchema(selector, keys) 

626 result = selector(self.data) 

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

628 truth[:3] = 0 

629 truth[-3:] = 0 

630 np.testing.assert_array_equal(result, truth) 

631 

632 def testSkyObjectSelector(self): 

633 # Test default 

634 selector = SkyObjectSelector() 

635 keys = ["{band}_pixelFlags_edge", "{band}_pixelFlags_nodata", "sky_object"] 

636 self._checkSchema(selector, keys) 

637 result = selector(self.data) 

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

639 truth[15] = 1 

640 truth[17] = 1 

641 np.testing.assert_array_equal(result, truth) 

642 

643 # Test overrides 

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

645 self._checkSchema(selector, keys) 

646 result = selector(self.data) 

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

648 truth[17] = 1 

649 np.testing.assert_array_equal(result, truth) 

650 

651 def testStarSelector(self): 

652 # test default 

653 selector = StarSelector() 

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

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

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

657 np.testing.assert_array_almost_equal(result, truth) 

658 

659 # Test overrides 

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

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

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

663 np.testing.assert_array_almost_equal(result, truth) 

664 

665 def testGalaxySelector(self): 

666 # test default 

667 selector = GalaxySelector() 

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

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

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

671 np.testing.assert_array_almost_equal(result, truth) 

672 

673 # Test overrides 

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

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

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

677 np.testing.assert_array_almost_equal(result, truth) 

678 

679 def testVectorSelector(self): 

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

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

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

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

684 truth[1] = True 

685 np.testing.assert_array_equal(result, truth) 

686 

687 

688class TestKeyedDataActions(unittest.TestCase): 

689 def testCalcRelativeDistances(self): 

690 # To test CalcRelativeDistances, make a matched visit catalog with 

691 # objects in a box slightly larger than the annulus used in calculating 

692 # relative distances. 

693 num_visits = 15 

694 scatter_in_degrees = (5 * u.milliarcsecond).to(u.degree).value 

695 obj_id = 0 

696 visit_id = range(num_visits) 

697 all_ras, all_decs, all_objs, all_visits = [], [], [], [] 

698 for ra in np.linspace(0, 6, 10): 

699 for dec in np.linspace(0, 6, 10): 

700 ra_degrees = (ra * u.arcmin).to(u.degree).value 

701 dec_degrees = (dec * u.arcmin).to(u.degree).value 

702 ra_meas = ra_degrees + np.random.rand(num_visits) * scatter_in_degrees 

703 dec_meas = dec_degrees + np.random.rand(num_visits) * scatter_in_degrees 

704 all_ras.append(ra_meas) 

705 all_decs.append(dec_meas) 

706 all_objs.append(np.ones(num_visits) * obj_id) 

707 all_visits.append(visit_id) 

708 obj_id += 1 

709 data = pd.DataFrame( 

710 { 

711 "coord_ra": np.concatenate(all_ras), 

712 "coord_dec": np.concatenate(all_decs), 

713 "obj_index": np.concatenate(all_objs), 

714 "visit": np.concatenate(all_visits), 

715 } 

716 ) 

717 

718 task = CalcRelativeDistances() 

719 res = task(data) 

720 

721 self.assertNotEqual(res["AMx"], np.nan) 

722 self.assertNotEqual(res["ADx"], np.nan) 

723 self.assertNotEqual(res["AFx"], np.nan) 

724 

725 

726if __name__ == "__main__": 726 ↛ 727line 726 didn't jump to line 727 because the condition on line 726 was never true

727 lsst.utils.tests.init() 

728 unittest.main()