Coverage for tests / test_actions.py: 15%

382 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-04-28 09:21 +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 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 # putting guards just in case e1 or e2 are superior to 1, but unlikely. 

453 if abs(e1) >= 1: 

454 e1 = 0 

455 if abs(e2) >= 1: 

456 e2 = 0 

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

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

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

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

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

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

463 return [L[0, 0], L[1, 1], L[0, 1]] 

464 

465 def testCalcRhoStatistics(self): 

466 # just check if runs 

467 rho = CalcRhoStatistics() 

468 rho.treecorr.nbins = 21 

469 rho.treecorr.min_sep = 0.01 

470 rho.treecorr.max_sep = 100.0 

471 rho.treecorr.sep_units = "arcmin" 

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

473 for rho in result: 

474 if rho != "rho3alt": 

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

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

477 else: 

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

479 

480 

481class TestVectorSelectors(unittest.TestCase): 

482 def setUp(self): 

483 self.size = 20 

484 falseFlags = { 

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

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

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

488 "coord_flag": [7], 

489 "i_pixelFlags_edge": [13], 

490 "r_pixelFlags_edge": [15], 

491 "i_pixelFlags_nodata": [14], 

492 "r_pixelFlags_nodata": [16], 

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

494 } 

495 

496 trueFlags = { 

497 "detect_isPatchInner": [9], 

498 "detect_isDeblendedSource": [11], 

499 } 

500 

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

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

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

504 

505 self.data = { 

506 "r_psfFlux": flux, 

507 "r_psfFluxErr": fluxErr, 

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

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

510 "r_cmodelFlux": flux, 

511 "r_cmodelFluxError": fluxErr, 

512 "i_extendedness": extendedness, 

513 "i_extended": extendedness, 

514 } 

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

516 for band in bands: 

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

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

519 for bit in bits: 

520 vector[bit] = 1 

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

522 

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

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

525 for bit in bits: 

526 vector[bit] = 0 

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

528 

529 def _checkSchema(self, action, truth): 

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

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

532 

533 def testFlagSelector(self): 

534 selector = FlagSelector( 

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

536 ) 

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

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

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

540 truth[1] = False 

541 truth[9] = False 

542 np.testing.assert_array_equal(result, truth) 

543 

544 def testCoaddPlotFlagSelector(self): 

545 # Test defaults 

546 selector = CoaddPlotFlagSelector() 

547 keys = [ 

548 "{band}_psfFlux_flag", 

549 "{band}_pixelFlags_saturatedCenter", 

550 "{band}_extendedness_flag", 

551 "sky_object", 

552 "coord_flag", 

553 "detect_isPatchInner", 

554 "detect_isDeblendedSource", 

555 ] 

556 self._checkSchema(selector, keys) 

557 # Specifying a band will format all keys containing band 

558 selector.bands = ["i"] 

559 self._checkSchema(selector, [key.format(band=selector.bands[0]) for key in keys]) 

560 

561 result = selector(self.data) 

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

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

564 truth[bit] = 0 

565 np.testing.assert_array_equal(result, truth) 

566 

567 # Test bands override 

568 selector = CoaddPlotFlagSelector( 

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

570 selectWhenTrue=["detect_isDeblendedSource"], 

571 ) 

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

573 selector.bands = ["i", "r"] 

574 self._checkSchema(selector, ["i_psfFlux_flag", "r_psfFlux_flag", "detect_isDeblendedSource"]) 

575 result = selector(self.data) 

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

577 for bit in (1, 11): 

578 truth[bit] = 0 

579 np.testing.assert_array_equal(result, truth) 

580 

581 def testRangeSelector(self): 

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

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

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

585 truth = [30, 40] 

586 np.testing.assert_array_equal(result, truth) 

587 

588 def testSetSelector(self): 

589 n_values = 3 

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

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

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

593 result = selector(self.data) 

594 truth = np.zeros_like(result) 

595 truth[:n_values] = True 

596 # i_cModelFlux is just r_psfFlux reversed 

597 truth[-n_values:] = True 

598 np.testing.assert_array_equal(result, truth) 

599 

600 def testSnSelector(self): 

601 # test defaults 

602 selector = SnSelector() 

603 keys = [ 

604 "{band}_psfFlux", 

605 "{band}_psfFluxErr", 

606 ] 

607 self._checkSchema(selector, keys) 

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

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

610 truth[:6] = 0 

611 np.testing.assert_array_equal(result, truth) 

612 

613 # test overrides 

614 selector = SnSelector( 

615 fluxType="{band}_cmodelFlux", 

616 threshold=200.0, 

617 uncertaintySuffix="Error", 

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

619 ) 

620 keys = [ 

621 "{band}_cmodelFlux", 

622 "{band}_cmodelFluxError", 

623 ] 

624 self._checkSchema(selector, keys) 

625 result = selector(self.data) 

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

627 truth[:3] = 0 

628 truth[-3:] = 0 

629 np.testing.assert_array_equal(result, truth) 

630 

631 def testSkyObjectSelector(self): 

632 # Test with kwargs 

633 selector = SkyObjectSelector() 

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

635 self._checkSchema(selector, keys) 

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

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

638 truth[15] = 1 

639 truth[17] = 1 

640 np.testing.assert_array_equal(result, truth) 

641 

642 # Test overrides 

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

644 self._checkSchema(selector, keys) 

645 result = selector(self.data) 

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

647 truth[17] = 1 

648 np.testing.assert_array_equal(result, truth) 

649 

650 def testStarSelector(self): 

651 # test default 

652 selector = StarSelector() 

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

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

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

656 np.testing.assert_array_almost_equal(result, truth) 

657 

658 # Test overrides 

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

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

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

662 np.testing.assert_array_almost_equal(result, truth) 

663 

664 def testGalaxySelector(self): 

665 # test default 

666 selector = GalaxySelector() 

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

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

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

670 np.testing.assert_array_almost_equal(result, truth) 

671 

672 # Test overrides 

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

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

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

676 np.testing.assert_array_almost_equal(result, truth) 

677 

678 def testVectorSelector(self): 

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

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

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

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

683 truth[1] = True 

684 np.testing.assert_array_equal(result, truth) 

685 

686 

687class TestKeyedDataActions(unittest.TestCase): 

688 def testCalcRelativeDistances(self): 

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

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

691 # relative distances. 

692 num_visits = 15 

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

694 obj_id = 0 

695 visit_id = range(num_visits) 

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

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

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

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

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

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

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

703 all_ras.append(ra_meas) 

704 all_decs.append(dec_meas) 

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

706 all_visits.append(visit_id) 

707 obj_id += 1 

708 data = pd.DataFrame( 

709 { 

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

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

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

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

714 } 

715 ) 

716 

717 task = CalcRelativeDistances() 

718 res = task(data) 

719 

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

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

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

723 

724 

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

726 lsst.utils.tests.init() 

727 unittest.main()