Coverage for tests / test_actions.py: 15%
382 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-05 08:45 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-05 08:45 +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/>.
22import unittest
24import astropy.units as u
25import numpy as np
26import pandas as pd
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
76class TestScalarActions(unittest.TestCase):
77 """ "Test ScalarActions"""
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 }
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"])
103 result = action(self.data, band="i")
104 self.assertAlmostEqual(result, truth)
106 result = action(self.data, band="z")
107 self.assertAlmostEqual(result, truth)
109 result = action(self.data, mask=self.mask["i"], band="i")
110 self.assertAlmostEqual(result, maskedTruth)
112 result = action(self.data, mask=self.mask["z"], band="z")
113 self.assertAlmostEqual(result, maskedTruth)
115 def testMedianAction(self):
116 self._testScalarActionAlmostEqual(MedianAction, 2500, 576)
118 def testMeanAction(self):
119 self._testScalarActionAlmostEqual(MeanAction, 3321.9278350515465, 803.8936170212766)
121 def testStdevAction(self):
122 self._testScalarActionAlmostEqual(StdevAction, 2984.5855649976297, 733.5989754407842)
124 def testSigmaMadAction(self):
125 self._testScalarActionAlmostEqual(SigmaMadAction, 3278.033505115886, 759.0923358748682)
127 def testCountAction(self):
128 self._testScalarActionAlmostEqual(CountAction, 97, 47)
130 def testApproxFloorAction(self):
131 self._testScalarActionAlmostEqual(ApproxFloor, 9216.0, 2352.5)
134class TestVectorActions(unittest.TestCase):
135 """Test VectorActions"""
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 }
160 self.data = pd.DataFrame.from_dict(data)
162 def _checkSchema(self, action, truth):
163 schema = sorted([col for col, colType in action.getInputSchema()])
164 self.assertEqual(schema, truth)
166 # VectorActions with their own files
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()))
189 np.testing.assert_array_almost_equal(result[stats.name_sigmaMad], truth[stats.name_sigmaMad])
190 del truth[stats.name_sigmaMad]
192 np.testing.assert_array_equal(result[stats.name_mask], truth[stats.name_mask])
193 del truth[stats.name_mask]
195 for key, value in truth.items():
196 self.assertEqual(result[key], value, key)
198 def testCalcMomentSize(self):
199 xx = self.data["r_ixx"]
200 yy = self.data["r_iyy"]
201 xy = self.data["r_ixy"]
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)
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)
224 CalcMomentSize(sizeType="trace", colXy=None).validate()
225 with self.assertRaises(FieldValidationError):
226 CalcMomentSize(sizeType="determinant", colXy=None).validate()
228 # def testCalcE(self): TODO: implement
230 # def testCalcEDiff(self): TODO: implement
232 # def testCalcE1(self): TODO: implement
234 # def testCalcE2(self): TODO: implement
236 # MathActions
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)
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)
261 def testAdd(self):
262 truth = [2.0, 6.0, 12.0, 20.0, 30.0]
263 self._testMath(AddVector, truth, compare_exact=True)
265 def testSubtract(self):
266 truth = [0.0, -2.0, -6.0, -12.0, -20.0]
267 self._testMath(SubtractVector, truth, compare_exact=True)
269 def testMultiply(self):
270 truth = [1.0, 8.0, 27.0, 64.0, 125.0]
271 self._testMath(MultiplyVector, truth, compare_exact=False)
273 def testDivide(self):
274 truth = 1 / np.arange(1, 6)
275 self._testMath(DivideVector, truth, compare_exact=False)
277 def testSqrt(self):
278 truth = sqrt(np.arange(1, 6))
279 self._testMath(SqrtVector, truth, compare_exact=False, num_vectors=1)
281 def testSquare(self):
282 truth = np.arange(1, 6) ** 2
283 self._testMath(SquareVector, truth, compare_exact=True, num_vectors=1)
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)
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)
297 def testLog10(self):
298 truth = np.log10(np.arange(1, 6))
299 self._testMath(Log10Vector, truth, compare_exact=False, num_vectors=1)
301 def testFractionalDifference(self):
302 truth = [0.0, -0.5, -0.6666666666666666, -0.75, -0.8]
303 self._testMath(FractionalDifference, truth, compare_exact=False)
305 # Basic vectorActions
307 # def testLoadVector(self): TODO: implement
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]))
316 # def testMultiCriteriaDownselectVector(self): TODO: implement
318 # Astronomical vectorActions
320 # def testCalcSn(self): TODO: implement
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)
335 # def testConvertUnits(self): TODO: implement
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)
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 )
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)
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)
405 # def testRAcosDec(self): TODO: implement
407 # Statistical vectorActions
409 # def testPerGroupStatistic(self): TODO: implement
411 # def testResidualWithPerGroupStatistic(self): TODO: implement
414class TestVectorRhoStats(unittest.TestCase):
415 """Test Rho stats"""
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)
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)
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 )
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 }
447 self.dataRhoStats = pd.DataFrame.from_dict(dataRhoStats)
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]]
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))
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 }
496 trueFlags = {
497 "detect_isPatchInner": [9],
498 "detect_isDeblendedSource": [11],
499 }
501 flux = np.arange(self.size) * 10
502 fluxErr = np.ones(self.size) * 0.1
503 extendedness = np.arange(20) / 20 - 0.1
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
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
529 def _checkSchema(self, action, truth):
530 schema = [col for col, colType in action.getInputSchema()]
531 self.assertEqual(sorted(schema), sorted(truth))
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)
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])
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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 )
717 task = CalcRelativeDistances()
718 res = task(data)
720 self.assertNotEqual(res["AMx"], np.nan)
721 self.assertNotEqual(res["ADx"], np.nan)
722 self.assertNotEqual(res["AFx"], np.nan)
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()