Coverage for tests / test_actions.py: 15%
378 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-15 00:23 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-15 00:23 +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 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
75class TestScalarActions(unittest.TestCase):
76 """ "Test ScalarActions"""
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 }
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"])
102 result = action(self.data, band="i")
103 self.assertAlmostEqual(result, truth)
105 result = action(self.data, band="z")
106 self.assertAlmostEqual(result, truth)
108 result = action(self.data, mask=self.mask["i"], band="i")
109 self.assertAlmostEqual(result, maskedTruth)
111 result = action(self.data, mask=self.mask["z"], band="z")
112 self.assertAlmostEqual(result, maskedTruth)
114 def testMedianAction(self):
115 self._testScalarActionAlmostEqual(MedianAction, 2500, 576)
117 def testMeanAction(self):
118 self._testScalarActionAlmostEqual(MeanAction, 3321.9278350515465, 803.8936170212766)
120 def testStdevAction(self):
121 self._testScalarActionAlmostEqual(StdevAction, 2984.5855649976297, 733.5989754407842)
123 def testSigmaMadAction(self):
124 self._testScalarActionAlmostEqual(SigmaMadAction, 3278.033505115886, 759.0923358748682)
126 def testCountAction(self):
127 self._testScalarActionAlmostEqual(CountAction, 97, 47)
129 def testApproxFloorAction(self):
130 self._testScalarActionAlmostEqual(ApproxFloor, 9216.0, 2352.5)
133class TestVectorActions(unittest.TestCase):
134 """Test VectorActions"""
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 }
159 self.data = pd.DataFrame.from_dict(data)
161 def _checkSchema(self, action, truth):
162 schema = sorted([col for col, colType in action.getInputSchema()])
163 self.assertEqual(schema, truth)
165 # VectorActions with their own files
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()))
188 np.testing.assert_array_almost_equal(result[stats.name_sigmaMad], truth[stats.name_sigmaMad])
189 del truth[stats.name_sigmaMad]
191 np.testing.assert_array_equal(result[stats.name_mask], truth[stats.name_mask])
192 del truth[stats.name_mask]
194 for key, value in truth.items():
195 self.assertEqual(result[key], value, key)
197 def testCalcMomentSize(self):
198 xx = self.data["r_ixx"]
199 yy = self.data["r_iyy"]
200 xy = self.data["r_ixy"]
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)
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)
223 CalcMomentSize(sizeType="trace", colXy=None).validate()
224 with self.assertRaises(FieldValidationError):
225 CalcMomentSize(sizeType="determinant", colXy=None).validate()
227 # def testCalcE(self): TODO: implement
229 # def testCalcEDiff(self): TODO: implement
231 # def testCalcE1(self): TODO: implement
233 # def testCalcE2(self): TODO: implement
235 # MathActions
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)
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)
260 def testAdd(self):
261 truth = [2.0, 6.0, 12.0, 20.0, 30.0]
262 self._testMath(AddVector, truth, compare_exact=True)
264 def testSubtract(self):
265 truth = [0.0, -2.0, -6.0, -12.0, -20.0]
266 self._testMath(SubtractVector, truth, compare_exact=True)
268 def testMultiply(self):
269 truth = [1.0, 8.0, 27.0, 64.0, 125.0]
270 self._testMath(MultiplyVector, truth, compare_exact=False)
272 def testDivide(self):
273 truth = 1 / np.arange(1, 6)
274 self._testMath(DivideVector, truth, compare_exact=False)
276 def testSqrt(self):
277 truth = sqrt(np.arange(1, 6))
278 self._testMath(SqrtVector, truth, compare_exact=False, num_vectors=1)
280 def testSquare(self):
281 truth = np.arange(1, 6) ** 2
282 self._testMath(SquareVector, truth, compare_exact=True, num_vectors=1)
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)
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)
296 def testLog10(self):
297 truth = np.log10(np.arange(1, 6))
298 self._testMath(Log10Vector, truth, compare_exact=False, num_vectors=1)
300 def testFractionalDifference(self):
301 truth = [0.0, -0.5, -0.6666666666666666, -0.75, -0.8]
302 self._testMath(FractionalDifference, truth, compare_exact=False)
304 # Basic vectorActions
306 # def testLoadVector(self): TODO: implement
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]))
315 # def testMultiCriteriaDownselectVector(self): TODO: implement
317 # Astronomical vectorActions
319 # def testCalcSn(self): TODO: implement
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)
334 # def testConvertUnits(self): TODO: implement
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)
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 )
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)
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)
404 # def testRAcosDec(self): TODO: implement
406 # Statistical vectorActions
408 # def testPerGroupStatistic(self): TODO: implement
410 # def testResidualWithPerGroupStatistic(self): TODO: implement
413class TestVectorRhoStats(unittest.TestCase):
414 """Test Rho stats"""
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)
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)
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 )
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 }
446 self.dataRhoStats = pd.DataFrame.from_dict(dataRhoStats)
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]]
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))
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 }
495 trueFlags = {
496 "detect_isPatchInner": [9],
497 "detect_isDeblendedSource": [11],
498 }
500 flux = np.arange(self.size) * 10
501 fluxErr = np.ones(self.size) * 0.1
502 extendedness = np.arange(20) / 20 - 0.1
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
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
528 def _checkSchema(self, action, truth):
529 schema = [col for col, colType in action.getInputSchema()]
530 self.assertEqual(sorted(schema), sorted(truth))
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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 )
715 task = CalcRelativeDistances()
716 res = task(data)
718 self.assertNotEqual(res["AMx"], np.nan)
719 self.assertNotEqual(res["ADx"], np.nan)
720 self.assertNotEqual(res["AFx"], np.nan)
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()