Coverage for tests / test_actions.py: 15%

378 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-05-05 18:53 +0000

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 lsst.utils.tests 

26import numpy as np 

27import pandas as pd 

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

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

30 ApproxFloor, 

31 CountAction, 

32 MeanAction, 

33 MedianAction, 

34 SigmaMadAction, 

35 StdevAction, 

36) 

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

38 CalcBinnedStatsAction, 

39 CalcMomentSize, 

40 CalcRhoStatistics, 

41 ConvertFluxToMag, 

42 DownselectVector, 

43 ExtinctionCorrectedMagDiff, 

44 LoadVector, 

45 MagDiff, 

46) 

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

48 AddVector, 

49 ConstantValue, 

50 DivideVector, 

51 FractionalDifference, 

52 Log10Vector, 

53 MultiplyVector, 

54 RaiseFromBaseVector, 

55 RaiseToPowerVector, 

56 SqrtVector, 

57 SquareVector, 

58 SubtractVector, 

59) 

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

61 CoaddPlotFlagSelector, 

62 FlagSelector, 

63 GalaxySelector, 

64 RangeSelector, 

65 SetSelector, 

66 SkyObjectSelector, 

67 SnSelector, 

68 StarSelector, 

69 VectorSelector, 

70) 

71from lsst.analysis.tools.math import sqrt 

72from lsst.pex.config import FieldValidationError 

73 

74 

75class TestScalarActions(unittest.TestCase): 

76 """ "Test ScalarActions""" 

77 

78 def setUp(self): 

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

80 x[31] = np.nan 

81 x[41] = np.nan 

82 x[59] = np.nan 

83 y = x**2 

84 self.data = { 

85 "r_y": x, 

86 "i_y": y, 

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

88 } 

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

90 mask[1:50] = 1 

91 self.mask = { 

92 "r": mask, 

93 "i": mask, 

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

95 } 

96 

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

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

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

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

101 

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

103 self.assertAlmostEqual(result, truth) 

104 

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

106 self.assertAlmostEqual(result, truth) 

107 

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

109 self.assertAlmostEqual(result, maskedTruth) 

110 

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

112 self.assertAlmostEqual(result, maskedTruth) 

113 

114 def testMedianAction(self): 

115 self._testScalarActionAlmostEqual(MedianAction, 2500, 576) 

116 

117 def testMeanAction(self): 

118 self._testScalarActionAlmostEqual(MeanAction, 3321.9278350515465, 803.8936170212766) 

119 

120 def testStdevAction(self): 

121 self._testScalarActionAlmostEqual(StdevAction, 2984.5855649976297, 733.5989754407842) 

122 

123 def testSigmaMadAction(self): 

124 self._testScalarActionAlmostEqual(SigmaMadAction, 3278.033505115886, 759.0923358748682) 

125 

126 def testCountAction(self): 

127 self._testScalarActionAlmostEqual(CountAction, 97, 47) 

128 

129 def testApproxFloorAction(self): 

130 self._testScalarActionAlmostEqual(ApproxFloor, 9216.0, 2352.5) 

131 

132 

133class TestVectorActions(unittest.TestCase): 

134 """Test VectorActions""" 

135 

136 def setUp(self): 

137 self.size = 5 

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

139 i = r**2 

140 rFlag = np.ones(self.size) 

141 rFlag[1] = 0 

142 iFlag = np.ones(self.size) 

143 iFlag[3] = 0 

144 data = { 

145 "r_vector": r, 

146 "i_vector": i, 

147 "r_flag": rFlag, 

148 "i_flag": iFlag, 

149 "g_vector": 3 * r, 

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

151 "r_ixx": r**2, 

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

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

154 "g_iixx": r**2, 

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

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

157 } 

158 

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

160 

161 def _checkSchema(self, action, truth): 

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

163 self.assertEqual(schema, truth) 

164 

165 # VectorActions with their own files 

166 

167 def testCalcBinnedStats(self): 

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

169 prefix = "a_" 

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

171 result = stats(self.data) 

172 mask = selector(self.data) 

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

174 median = np.median(values) 

175 truth = { 

176 stats.name_mask: mask, 

177 stats.name_median: median, 

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

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

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

181 stats.name_select_median: median, 

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

183 "range_maximum": self.size, 

184 "range_minimum": 0, 

185 } 

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

187 

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

189 del truth[stats.name_sigmaMad] 

190 

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

192 del truth[stats.name_mask] 

193 

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

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

196 

197 def testCalcMomentSize(self): 

198 xx = self.data["r_ixx"] 

199 yy = self.data["r_iyy"] 

200 xy = self.data["r_ixy"] 

201 

202 # Test determinant with defaults 

203 action = CalcMomentSize() 

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

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

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

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

208 np.testing.assert_array_almost_equal(result, truth) 

209 

210 # Test trace with columns specified 

211 action = CalcMomentSize( 

212 colXx="{band}_iixx", 

213 colYy="{band}_iiyy", 

214 colXy="{band}_iixy", 

215 sizeType="trace", 

216 ) 

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

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

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

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

221 np.testing.assert_array_almost_equal(result, truth) 

222 

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

224 with self.assertRaises(FieldValidationError): 

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

226 

227 # def testCalcE(self): TODO: implement 

228 

229 # def testCalcEDiff(self): TODO: implement 

230 

231 # def testCalcE1(self): TODO: implement 

232 

233 # def testCalcE2(self): TODO: implement 

234 

235 # MathActions 

236 

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

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

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

240 actions = { 

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

242 for num in range(num_vectors) 

243 } 

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

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

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

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

248 if compare_exact: 

249 np.testing.assert_array_equal(result, truth) 

250 else: 

251 np.testing.assert_array_almost_equal(result, truth) 

252 

253 def testConstant(self): 

254 truth = [42.0] 

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

256 self._checkSchema(action, []) 

257 result = action({}) 

258 np.testing.assert_array_equal(result, truth) 

259 

260 def testAdd(self): 

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

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

263 

264 def testSubtract(self): 

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

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

267 

268 def testMultiply(self): 

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

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

271 

272 def testDivide(self): 

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

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

275 

276 def testSqrt(self): 

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

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

279 

280 def testSquare(self): 

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

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

283 

284 def testRaiseFromBase(self): 

285 power = np.arange(1, 6) 

286 for base in (-2.3, 0.6): 

287 truth = base**power 

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

289 

290 def testRaiseToPower(self): 

291 base = np.arange(1, 6) 

292 for power in (-2.3, 0.6): 

293 truth = base**power 

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

295 

296 def testLog10(self): 

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

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

299 

300 def testFractionalDifference(self): 

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

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

303 

304 # Basic vectorActions 

305 

306 # def testLoadVector(self): TODO: implement 

307 

308 def testDownselectVector(self): 

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

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

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

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

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

314 

315 # def testMultiCriteriaDownselectVector(self): TODO: implement 

316 

317 # Astronomical vectorActions 

318 

319 # def testCalcSn(self): TODO: implement 

320 

321 def testConvertFluxToMag(self): 

322 truth = [ 

323 31.4, 

324 29.89485002168, 

325 29.0143937264, 

326 28.38970004336, 

327 27.90514997832, 

328 ] 

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

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

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

332 np.testing.assert_array_almost_equal(result, truth) 

333 

334 # def testConvertUnits(self): TODO: implement 

335 

336 def testMagDiff(self): 

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

338 # without conversions. 

339 # testExtinctionCorrectedMagDiff will test the conversions 

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

341 action = MagDiff( 

342 col1="{band1}_vector", 

343 col2="{band2}_vector", 

344 fluxUnits1="mag(AB)", 

345 fluxUnits2="mag(AB)", 

346 returnMillimags=False, 

347 ) 

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

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

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

351 

352 def testExtinctionCorrectedMagDiff(self): 

353 for returnMillimags in (True, False): 

354 # Check that conversions are working properly 

355 magDiff = MagDiff( 

356 col1="{band1}_vector", 

357 col2="{band2}_vector", 

358 fluxUnits1="jansky", 

359 fluxUnits2="jansky", 

360 returnMillimags=returnMillimags, 

361 ) 

362 action = ExtinctionCorrectedMagDiff( 

363 magDiff=magDiff, 

364 band1="g", 

365 band2="r", 

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

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

368 ) 

369 

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

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

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

373 diff = lhs - rhs 

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

375 if returnMillimags: 

376 diff = diff.to(u.mmag) 

377 correction = correction.to(u.mmag) 

378 truth = diff - correction 

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

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

381 

382 # Test with hard coded bands 

383 magDiff = MagDiff( 

384 col1="g_vector", 

385 col2="r_vector", 

386 fluxUnits1="jansky", 

387 fluxUnits2="jansky", 

388 returnMillimags=False, 

389 ) 

390 action = ExtinctionCorrectedMagDiff( 

391 magDiff=magDiff, 

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

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

394 ) 

395 result = action(self.data) 

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

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

398 diff = lhs - rhs 

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

400 truth = diff - correction 

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

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

403 

404 # def testRAcosDec(self): TODO: implement 

405 

406 # Statistical vectorActions 

407 

408 # def testPerGroupStatistic(self): TODO: implement 

409 

410 # def testResidualWithPerGroupStatistic(self): TODO: implement 

411 

412 

413class TestVectorRhoStats(unittest.TestCase): 

414 """Test Rho stats""" 

415 

416 def setUp(self): 

417 # generate data just for testCalcRhoStatistics. 

418 np.random.seed(42) 

419 sizeRho = 1000 

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

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

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

423 

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

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

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

427 

428 src_data = np.array( 

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

430 ) 

431 psf_data = np.array( 

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

433 ) 

434 

435 dataRhoStats = { 

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

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

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

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

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

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

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

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

444 } 

445 

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

447 

448 # Needed for testCalcRhoStatistics. 

449 @staticmethod 

450 def getMatrixElements(size, e1, e2): 

451 # putting guards just in case e1 or e2 are superior to 1, but unlikely. 

452 if abs(e1) >= 1: 

453 e1 = 0 

454 if abs(e2) >= 1: 

455 e2 = 0 

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

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

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

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

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

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

462 return [L[0, 0], L[1, 1], L[0, 1]] 

463 

464 def testCalcRhoStatistics(self): 

465 # just check if runs 

466 rho = CalcRhoStatistics() 

467 rho.treecorr.nbins = 21 

468 rho.treecorr.min_sep = 0.01 

469 rho.treecorr.max_sep = 100.0 

470 rho.treecorr.sep_units = "arcmin" 

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

472 for rho in result: 

473 if rho != "rho3alt": 

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

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

476 else: 

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

478 

479 

480class TestVectorSelectors(unittest.TestCase): 

481 def setUp(self): 

482 self.size = 20 

483 falseFlags = { 

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

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

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

487 "coord_flag": [7], 

488 "i_pixelFlags_edge": [13], 

489 "r_pixelFlags_edge": [15], 

490 "i_pixelFlags_nodata": [14], 

491 "r_pixelFlags_nodata": [16], 

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

493 } 

494 

495 trueFlags = { 

496 "detect_isPatchInner": [9], 

497 "detect_isDeblendedSource": [11], 

498 } 

499 

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

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

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

503 

504 self.data = { 

505 "r_psfFlux": flux, 

506 "r_psfFluxErr": fluxErr, 

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

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

509 "r_cmodelFlux": flux, 

510 "r_cmodelFluxError": fluxErr, 

511 "i_extendedness": extendedness, 

512 "i_extended": extendedness, 

513 } 

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

515 for band in bands: 

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

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

518 for bit in bits: 

519 vector[bit] = 1 

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

521 

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

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

524 for bit in bits: 

525 vector[bit] = 0 

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

527 

528 def _checkSchema(self, action, truth): 

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

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

531 

532 def testFlagSelector(self): 

533 selector = FlagSelector( 

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

535 ) 

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

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

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

539 truth[1] = False 

540 truth[9] = False 

541 np.testing.assert_array_equal(result, truth) 

542 

543 def testCoaddPlotFlagSelector(self): 

544 # Test defaults 

545 # Bands needs to be set to something otherwise it 

546 # will crash as the default value looks it up 

547 # elsewhere. 

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

549 keys = [ 

550 "{band}_psfFlux_flag", 

551 "{band}_pixelFlags_saturatedCenter", 

552 "{band}_extendedness_flag", 

553 "sky_object", 

554 "coord_flag", 

555 "detect_isPatchInner", 

556 "detect_isDeblendedSource", 

557 ] 

558 self._checkSchema(selector, keys) 

559 

560 result = selector(self.data) 

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

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

563 truth[bit] = 0 

564 np.testing.assert_array_equal(result, truth) 

565 

566 # Test bands override 

567 selector = CoaddPlotFlagSelector( 

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

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

570 selectWhenTrue=["detect_isDeblendedSource"], 

571 ) 

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

573 result = selector(self.data) 

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

575 for bit in (1, 11): 

576 truth[bit] = 0 

577 np.testing.assert_array_equal(result, truth) 

578 

579 def testRangeSelector(self): 

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

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

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

583 truth = [30, 40] 

584 np.testing.assert_array_equal(result, truth) 

585 

586 def testSetSelector(self): 

587 n_values = 3 

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

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

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

591 result = selector(self.data) 

592 truth = np.zeros_like(result) 

593 truth[:n_values] = True 

594 # i_cModelFlux is just r_psfFlux reversed 

595 truth[-n_values:] = True 

596 np.testing.assert_array_equal(result, truth) 

597 

598 def testSnSelector(self): 

599 # test defaults 

600 selector = SnSelector() 

601 keys = [ 

602 "{band}_psfFlux", 

603 "{band}_psfFluxErr", 

604 ] 

605 self._checkSchema(selector, keys) 

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

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

608 truth[:6] = 0 

609 np.testing.assert_array_equal(result, truth) 

610 

611 # test overrides 

612 selector = SnSelector( 

613 fluxType="{band}_cmodelFlux", 

614 threshold=200.0, 

615 uncertaintySuffix="Error", 

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

617 ) 

618 keys = [ 

619 "{band}_cmodelFlux", 

620 "{band}_cmodelFluxError", 

621 ] 

622 self._checkSchema(selector, keys) 

623 result = selector(self.data) 

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

625 truth[:3] = 0 

626 truth[-3:] = 0 

627 np.testing.assert_array_equal(result, truth) 

628 

629 def testSkyObjectSelector(self): 

630 # Test with kwargs 

631 selector = SkyObjectSelector() 

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

633 self._checkSchema(selector, keys) 

634 result = selector(self.data, bands=["i"]) 

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

636 truth[15] = 1 

637 truth[17] = 1 

638 np.testing.assert_array_equal(result, truth) 

639 

640 # Test overrides 

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

642 self._checkSchema(selector, keys) 

643 result = selector(self.data) 

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

645 truth[17] = 1 

646 np.testing.assert_array_equal(result, truth) 

647 

648 def testStarSelector(self): 

649 # test default 

650 selector = StarSelector() 

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

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

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

654 np.testing.assert_array_almost_equal(result, truth) 

655 

656 # Test overrides 

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

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

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

660 np.testing.assert_array_almost_equal(result, truth) 

661 

662 def testGalaxySelector(self): 

663 # test default 

664 selector = GalaxySelector() 

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

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

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

668 np.testing.assert_array_almost_equal(result, truth) 

669 

670 # Test overrides 

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

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

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

674 np.testing.assert_array_almost_equal(result, truth) 

675 

676 def testVectorSelector(self): 

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

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

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

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

681 truth[1] = True 

682 np.testing.assert_array_equal(result, truth) 

683 

684 

685class TestKeyedDataActions(unittest.TestCase): 

686 def testCalcRelativeDistances(self): 

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

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

689 # relative distances. 

690 num_visits = 15 

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

692 obj_id = 0 

693 visit_id = range(num_visits) 

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

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

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

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

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

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

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

701 all_ras.append(ra_meas) 

702 all_decs.append(dec_meas) 

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

704 all_visits.append(visit_id) 

705 obj_id += 1 

706 data = pd.DataFrame( 

707 { 

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

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

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

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

712 } 

713 ) 

714 

715 task = CalcRelativeDistances() 

716 res = task(data) 

717 

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

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

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

721 

722 

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

724 lsst.utils.tests.init() 

725 unittest.main()