Coverage for tests/test_astropyTableViews.py: 21%
120 statements
« prev ^ index » next coverage.py v7.1.0, created at 2023-02-05 17:50 -0800
« prev ^ index » next coverage.py v7.1.0, created at 2023-02-05 17:50 -0800
1# This file is part of afw.
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/>.
22"""
23Tests for Astropy views into afw.table Catalogs
25Run with:
26 python test_astropyTableViews.py
27or
28 pytest test_astropyTableViews.py
29"""
30import unittest
31import operator
33import numpy as np
34import astropy.table
35import astropy.units
37import lsst.utils.tests
38import lsst.geom
39import lsst.afw.table
42class AstropyTableViewTestCase(lsst.utils.tests.TestCase):
43 """Test that we can construct Astropy views to afw.table Catalog objects.
45 This test case does not yet test the syntax
47 table = astropy.table.Table(lsst_catalog)
49 which is made available by BaseCatalog.__astropy_table__, as this will not
50 be available until Astropy 1.2 is released. However, this simply
51 delegates to BaseCatalog.asAstropy, which can also be called directly.
52 """
54 def setUp(self):
55 schema = lsst.afw.table.Schema()
56 self.k1 = schema.addField(
57 "a1", type=np.float64, units="meter", doc="a1 (meter)")
58 self.k2 = schema.addField("a2", type=np.int32, doc="a2 (unitless)")
59 self.k3 = schema.addField(
60 "a3", type="ArrayF", size=3, units="count", doc="a3 (array, counts)")
61 self.k4 = schema.addField("a4", type="Flag", doc="a4 (flag)")
62 self.k5 = lsst.afw.table.CoordKey.addFields(
63 schema, "a5", "a5 coordinate")
64 self.k6 = schema.addField("a6", type=str, size=8, doc="a6 (str)")
65 self.catalog = lsst.afw.table.BaseCatalog(schema)
66 self.data = [
67 {
68 "a1": 5.0, "a2": 3, "a3": np.array([0.5, 0.0, -0.5], dtype=np.float32),
69 "a4": True, "a5_ra": 45.0*lsst.geom.degrees, "a5_dec": 30.0*lsst.geom.degrees,
70 "a6": "bubbles"
71 },
72 {
73 "a1": 2.5, "a2": 7, "a3": np.array([1.0, 0.5, -1.5], dtype=np.float32),
74 "a4": False, "a5_ra": 25.0*lsst.geom.degrees, "a5_dec": -60.0*lsst.geom.degrees,
75 "a6": "pingpong"
76 },
77 ]
78 for d in self.data:
79 record = self.catalog.addNew()
80 for k, v in d.items():
81 record.set(k, v)
83 def tearDown(self):
84 del self.k1
85 del self.k2
86 del self.k3
87 del self.k4
88 del self.k5
89 del self.k6
90 del self.catalog
91 del self.data
93 def testQuantityColumn(self):
94 """Test that a column with units is handled as expected by Table and QTable.
95 """
96 v1 = self.catalog.asAstropy(cls=astropy.table.Table, unviewable="skip")
97 self.assertEqual(v1["a1"].unit, astropy.units.Unit("m"))
98 self.assertFloatsAlmostEqual(v1["a1"], self.catalog["a1"])
99 self.assertNotIsInstance(v1["a1"], astropy.units.Quantity)
100 v2 = self.catalog.asAstropy(
101 cls=astropy.table.QTable, unviewable="skip")
102 self.assertEqual(v2["a1"].unit, astropy.units.Unit("m"))
103 self.assertFloatsAlmostEqual(
104 v2["a1"]/astropy.units.Quantity(self.catalog["a1"]*100, "cm"), 1.0)
105 self.assertIsInstance(v2["a1"], astropy.units.Quantity)
107 def testUnitlessColumn(self):
108 """Test that a column without units is handled as expected by Table and QTable.
109 """
110 v1 = self.catalog.asAstropy(cls=astropy.table.Table, unviewable="skip")
111 self.assertEqual(v1["a2"].unit, None)
112 self.assertFloatsAlmostEqual(v1["a2"], self.catalog["a2"])
113 v2 = self.catalog.asAstropy(
114 cls=astropy.table.QTable, unviewable="skip")
115 self.assertEqual(v2["a2"].unit, None)
116 self.assertFloatsAlmostEqual(v2["a2"], self.catalog["a2"])
118 def testArrayColumn(self):
119 """Test that an array column appears as a 2-d array with the expected shape.
120 """
121 v = self.catalog.asAstropy(unviewable="skip")
122 self.assertFloatsAlmostEqual(v["a3"], self.catalog["a3"])
124 def testFlagColumn(self):
125 """Test that Flag columns can be viewed if copy=True or unviewable="copy".
126 """
127 v1 = self.catalog.asAstropy(unviewable="copy")
128 np.testing.assert_array_equal(v1["a4"], self.catalog["a4"])
129 v2 = self.catalog.asAstropy(copy=True)
130 np.testing.assert_array_equal(v2["a4"], self.catalog["a4"])
132 def testCoordColumn(self):
133 """Test that Coord columns appears as a pair of columns with correct angle units.
134 """
135 v1 = self.catalog.asAstropy(cls=astropy.table.Table, unviewable="skip")
136 self.assertFloatsAlmostEqual(v1["a5_ra"], self.catalog["a5_ra"])
137 self.assertEqual(v1["a5_ra"].unit, astropy.units.Unit("rad"))
138 self.assertNotIsInstance(v1["a5_ra"], astropy.units.Quantity)
139 self.assertFloatsAlmostEqual(v1["a5_dec"], self.catalog["a5_dec"])
140 self.assertEqual(v1["a5_dec"].unit, astropy.units.Unit("rad"))
141 self.assertNotIsInstance(v1["a5_dec"], astropy.units.Quantity)
142 v2 = self.catalog.asAstropy(
143 cls=astropy.table.QTable, unviewable="skip")
144 self.assertFloatsAlmostEqual(v2["a5_ra"]/astropy.units.Quantity(self.catalog["a5_ra"], unit="rad"),
145 1.0)
146 self.assertEqual(v2["a5_ra"].unit, astropy.units.Unit("rad"))
147 self.assertIsInstance(v2["a5_ra"], astropy.units.Quantity)
148 self.assertFloatsAlmostEqual(v2["a5_dec"]/astropy.units.Quantity(self.catalog["a5_dec"], unit="rad"),
149 1.0)
150 self.assertEqual(v2["a5_dec"].unit, astropy.units.Unit("rad"))
151 self.assertIsInstance(v2["a5_dec"], astropy.units.Quantity)
153 def testStringColumn(self):
154 """Test that string columns can be viewed if copy=True or unviewable='copy'.
155 """
156 v1 = self.catalog.asAstropy(unviewable="copy")
157 self.assertEqual(v1["a6"][0], self.data[0]["a6"])
158 self.assertEqual(v1["a6"][1], self.data[1]["a6"])
159 v2 = self.catalog.asAstropy(copy=True)
160 self.assertEqual(v2["a6"][0], self.data[0]["a6"])
161 self.assertEqual(v2["a6"][1], self.data[1]["a6"])
163 def testRaiseOnUnviewable(self):
164 """Test that we can't view this table without copying, since it has Flag and String columns.
165 """
166 self.assertRaises(ValueError, self.catalog.asAstropy,
167 copy=False, unviewable="raise")
169 def testNoUnnecessaryCopies(self):
170 """Test that fields that aren't Flag or String are not copied when copy=False (the default).
171 """
172 v1 = self.catalog.asAstropy(unviewable="copy")
173 v1["a2"][0] = 4
174 self.assertEqual(self.catalog[0]["a2"], 4)
175 v2 = self.catalog.asAstropy(unviewable="skip")
176 v2["a2"][1] = 10
177 self.assertEqual(self.catalog[1]["a2"], 10)
179 def testUnviewableSkip(self):
180 """Test that we can skip unviewable columns.
181 """
182 v1 = self.catalog.asAstropy(unviewable="skip")
183 self.assertRaises(KeyError, operator.getitem, v1, "a4")
184 self.assertRaises(KeyError, operator.getitem, v1, "a6")
186 def testVariableLengthArray(self):
187 """Variable-length arrays have 0-size in the schema, and can't be made
188 into an astropy view, but should be gracefully skipped."""
189 schema = lsst.afw.table.Schema()
190 schema.addField("a1", type=np.float64, units="meter", doc="a1 (meter)")
191 schema.addField("array1", doc="integer", units="arcsecond", type="ArrayI", size=0)
192 schema.addField("array2", doc="double-precision", units="count", type="ArrayD")
193 schema.addField("arrayOk", doc="not-variable-length", units="meter", type="ArrayD", size=3)
194 catalog = lsst.afw.table.BaseCatalog(schema)
195 catalog.addNew()
196 with self.assertRaises(ValueError) as cm:
197 catalog.asAstropy(unviewable="raise")
198 self.assertIn("Cannot extract variable-length array fields", str(cm.exception))
199 v1 = catalog.asAstropy(unviewable="skip")
200 # NOTE: Just check that the columns are included or not in the output:
201 # the above tests do a good job checking that "safe" arrays work.
202 self.assertIn("a1", v1.columns)
203 self.assertNotIn("array1", v1.columns)
204 self.assertNotIn("array2", v1.columns)
205 self.assertIn("arrayOk", v1.columns)
206 v1 = catalog.asAstropy(unviewable="copy")
207 self.assertIn("a1", v1.columns)
208 self.assertNotIn("array1", v1.columns)
209 self.assertNotIn("array2", v1.columns)
210 self.assertIn("arrayOk", v1.columns)
213class TestMemory(lsst.utils.tests.MemoryTestCase):
214 pass
217def setup_module(module):
218 lsst.utils.tests.init()
221if __name__ == "__main__": 221 ↛ 222line 221 didn't jump to line 222, because the condition on line 221 was never true
222 lsst.utils.tests.init()
223 unittest.main()