Coverage for tests/test_astropyTableViews.py: 23%

120 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2022-08-30 02:37 -0700

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/>. 

21 

22""" 

23Tests for Astropy views into afw.table Catalogs 

24 

25Run with: 

26 python test_astropyTableViews.py 

27or 

28 pytest test_astropyTableViews.py 

29""" 

30import unittest 

31import operator 

32 

33import numpy as np 

34import astropy.table 

35import astropy.units 

36 

37import lsst.utils.tests 

38import lsst.geom 

39import lsst.afw.table 

40 

41 

42class AstropyTableViewTestCase(lsst.utils.tests.TestCase): 

43 """Test that we can construct Astropy views to afw.table Catalog objects. 

44 

45 This test case does not yet test the syntax 

46 

47 table = astropy.table.Table(lsst_catalog) 

48 

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 """ 

53 

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) 

82 

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 

92 

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) 

106 

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"]) 

117 

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"]) 

123 

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"]) 

131 

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) 

152 

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"]) 

162 

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") 

168 

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) 

178 

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") 

185 

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) 

211 

212 

213class TestMemory(lsst.utils.tests.MemoryTestCase): 

214 pass 

215 

216 

217def setup_module(module): 

218 lsst.utils.tests.init() 

219 

220 

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()