LSSTApplications  11.0-13-gbb96280,12.1+18,12.1+7,12.1-1-g14f38d3+72,12.1-1-g16c0db7+5,12.1-1-g5961e7a+84,12.1-1-ge22e12b+23,12.1-11-g06625e2+4,12.1-11-g0d7f63b+4,12.1-19-gd507bfc,12.1-2-g7dda0ab+38,12.1-2-gc0bc6ab+81,12.1-21-g6ffe579+2,12.1-21-gbdb6c2a+4,12.1-24-g941c398+5,12.1-3-g57f6835+7,12.1-3-gf0736f3,12.1-37-g3ddd237,12.1-4-gf46015e+5,12.1-5-g06c326c+20,12.1-5-g648ee80+3,12.1-5-gc2189d7+4,12.1-6-ga608fc0+1,12.1-7-g3349e2a+5,12.1-7-gfd75620+9,12.1-9-g577b946+5,12.1-9-gc4df26a+10
LSSTDataManagementBasePackage
_syntax.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 # Copyright 2008-2015 LSST Corporation.
4 #
5 # This product includes software developed by the
6 # LSST Project (http://www.lsst.org/).
7 #
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the LSST License Statement and
19 # the GNU General Public License along with this program. If not,
20 # see <http://www.lsstcorp.org/LegalNotices/>.
21 #
22 """
23 Special Python syntactic sugar for Catalogs and Records.
24 
25 This module is imported by tableLib.py, and should not need to be imported by any other module.
26 I've moved the code out of the .i file here to avoid recompiling when only pure-Python code is
27 changed.
28 """
29 from builtins import zip
30 
31 import fnmatch
32 import re
33 import numpy
34 import collections
35 
36 def Schema_extract(self, *patterns, **kwds):
37  """
38  Extract a dictionary of {<name>: <schema-item>} in which the field names
39  match the given shell-style glob pattern(s).
40 
41  Any number of glob patterns may be passed; the result will be the union of all
42  the result of each glob considered separately.
43 
44  Additional optional arguments may be passed as keywords:
45 
46  regex ------ A regular expression to be used in addition to any glob patterns passed
47  as positional arguments. Note that this will be compared with re.match,
48  not re.search.
49 
50  sub -------- A replacement string template (see re.MatchObject.expand) used to set the
51  dictionary keys of any fields matched by regex. The field name in the
52  SchemaItem is not modified.
53 
54  ordered----- If True, a collections.OrderedDict will be returned instead of a standard
55  dict, with the order corresponding to the definition order of the Schema.
56 
57  """
58  if kwds.pop("ordered", False):
59  d = collections.OrderedDict()
60  else:
61  d = dict()
62  regex = kwds.pop("regex", None)
63  sub = kwds.pop("sub", None)
64  if sub is not None and regex is None:
65  raise ValueError("'sub' keyword argument to extract is invalid without 'regex' argument")
66  if kwds:
67  raise ValueError("Unrecognized keyword arguments for extract: %s" % ", ".join(kwds.keys()))
68  for item in self:
69  trueName = item.field.getName()
70  names = [trueName]
71  for alias, target in self.getAliasMap().items():
72  if trueName.startswith(target):
73  names.append(trueName.replace(target, alias, 1))
74  for name in names:
75  if regex is not None:
76  m = re.match(regex, name)
77  if m is not None:
78  if sub is not None:
79  name = m.expand(sub)
80  d[name] = item
81  continue # continue middle loop so we don't match the same name twice
82  for pattern in patterns:
83  if fnmatch.fnmatchcase(name, pattern):
84  d[name] = item
85  break # break inner loop so we don't match the same name twice
86  return d
87 
88 def BaseRecord_extract(self, *patterns, **kwds):
89  """
90  Extract a dictionary of {<name>: <field-value>} in which the field names
91  match the given shell-style glob pattern(s).
92 
93  Any number of glob patterns may be passed; the result will be the union of all
94  the result of each glob considered separately.
95 
96  Additional optional arguments may be passed as keywords:
97 
98  items ------ The result of a call to self.schema.extract(); this will be used instead
99  of doing any new matching, and allows the pattern matching to be reused
100  to extract values from multiple records. This keyword is incompatible
101  with any position arguments and the regex, sub, and ordered keyword
102  arguments.
103 
104  split ------ If True, fields with named subfields (e.g. points) will be split into
105  separate items in the dict; instead of {"point": lsst.afw.geom.Point2I(2,3)},
106  for instance, you'd get {"point.x": 2, "point.y": 3}.
107  Default is False.
108 
109  regex ------ A regular expression to be used in addition to any glob patterns passed
110  as positional arguments. Note that this will be compared with re.match,
111  not re.search.
112 
113  sub -------- A replacement string (see re.MatchObject.expand) used to set the
114  dictionary keys of any fields matched by regex.
115 
116  ordered----- If True, a collections.OrderedDict will be returned instead of a standard
117  dict, with the order corresponding to the definition order of the Schema.
118  Default is False.
119 
120  """
121  d = kwds.pop("items", None)
122  split = kwds.pop("split", False)
123  if d is None:
124  d = self.schema.extract(*patterns, **kwds).copy()
125  elif kwds:
126  raise ValueError("Unrecognized keyword arguments for extract: %s" % ", ".join(kwds.keys()))
127  for name, schemaItem in list(d.items()): # must use list because we might be adding/deleting elements
128  key = schemaItem.key
129  if split and key.HAS_NAMED_SUBFIELDS:
130  for subname, subkey in zip(key.subfields, key.subkeys):
131  d["%s.%s" % (name, subname)] = self.get(subkey)
132  del d[name]
133  else:
134  d[name] = self.get(schemaItem.key)
135  return d
136 
137 def BaseColumnView_extract(self, *patterns, **kwds):
138  """
139  Extract a dictionary of {<name>: <column-array>} in which the field names
140  match the given shell-style glob pattern(s).
141 
142  Any number of glob patterns may be passed; the result will be the union of all
143  the result of each glob considered separately.
144 
145  Note that extract("*", copy=True) provides an easy way to transform a row-major
146  ColumnView into a possibly more efficient set of contiguous NumPy arrays.
147 
148  This routines unpacks Flag columns into full boolean arrays and covariances into dense
149  (i.e. non-triangular packed) arrays with dimension (N,M,M), where N is the number of
150  records and M is the dimension of the covariance matrix. Fields with named subfields
151  (e.g. points) are always split into separate dictionary items, as is done in
152  BaseRecord.extract(..., split=True). String fields are silently ignored.
153 
154  Additional optional arguments may be passed as keywords:
155 
156  items ------ The result of a call to self.schema.extract(); this will be used instead
157  of doing any new matching, and allows the pattern matching to be reused
158  to extract values from multiple records. This keyword is incompatible
159  with any position arguments and the regex, sub, and ordered keyword
160  arguments.
161 
162  where ------ Any expression that can be passed as indices to a NumPy array, including
163  slices, boolean arrays, and index arrays, that will be used to index
164  each column array. This is applied before arrays are copied when
165  copy is True, so if the indexing results in an implicit copy no
166  unnecessary second copy is performed.
167 
168  copy ------- If True, the returned arrays will be contiguous copies rather than strided
169  views into the catalog. This ensures that the lifetime of the catalog is
170  not tied to the lifetime of a particular catalog, and it also may improve
171  the performance if the array is used repeatedly.
172  Default is False.
173 
174  regex ------ A regular expression to be used in addition to any glob patterns passed
175  as positional arguments. Note that this will be compared with re.match,
176  not re.search.
177 
178  sub -------- A replacement string (see re.MatchObject.expand) used to set the
179  dictionary keys of any fields matched by regex.
180 
181  ordered----- If True, a collections.OrderedDict will be returned instead of a standard
182  dict, with the order corresponding to the definition order of the Schema.
183  Default is False.
184 
185  """
186  copy = kwds.pop("copy", False)
187  where = kwds.pop("where", None)
188  d = kwds.pop("items", None)
189  if d is None:
190  d = self.schema.extract(*patterns, **kwds).copy()
191  elif kwds:
192  raise ValueError("Unrecognized keyword arguments for extract: %s" % ", ".list(kwds.keys()))
193  def processArray(a):
194  if where is not None:
195  a = a[where]
196  if copy:
197  a = numpy.ascontiguousarray(a)
198  return a
199  for name, schemaItem in list(d.items()): # must use list because we might be adding/deleting elements
200  key = schemaItem.key
201  if key.getTypeString() == "String":
202  del d[name]
203  else:
204  d[name] = processArray(self.get(schemaItem.key))
205  return d
206 
207 def BaseCatalog_asAstropy(self, cls=None, copy=False, unviewable="copy"):
208  """!
209  Return an astropy.table.Table (or subclass thereof) view into this catalog.
210 
211  @param[in] cls Table subclass to use; None implies astropy.table.Table itself.
212  Use astropy.table.QTable to get Quantity columns.
213 
214  @param[in] copy Whether to copy data from the LSST catalog to the astropy table.
215  Not copying is usually faster, but can keep memory from being
216  freed if columns are later removed from the Astropy view.
217 
218  @param[in] unviewable One of the following options, indicating how to handle field types
219  (string and Flag) for which views cannot be constructed:
220  - 'copy' (default): copy only the unviewable fields.
221  - 'raise': raise ValueError if unviewable fields are present.
222  - 'skip': do not include unviewable fields in the Astropy Table.
223  This option is ignored if copy=True.
224  """
225  import astropy.table
226  if cls is None:
227  cls = astropy.table.Table
228  if unviewable not in ("copy", "raise", "skip"):
229  raise ValueError("'unviewable' must be one of 'copy', 'raise', or 'skip'")
230  ps = self.getMetadata()
231  meta = ps.toOrderedDict() if ps is not None else None
232  columns = []
233  items = self.schema.extract("*", ordered=True)
234  for name, item in items.items():
235  key = item.key
236  unit = item.field.getUnits() or None # use None instead of "" when empty
237  if key.getTypeString() == "String":
238  if not copy:
239  if unviewable == "raise":
240  raise ValueError("Cannot extract string unless copy=True or unviewable='copy' or 'skip'.")
241  elif unviewable == "skip":
242  continue
243  data = numpy.zeros(len(self), dtype=numpy.dtype((str, key.getSize())))
244  for i, record in enumerate(self):
245  data[i] = record.get(key)
246  elif key.getTypeString() == "Flag":
247  if not copy:
248  if unviewable == "raise":
249  raise ValueError(
250  "Cannot extract packed bit columns unless copy=True or unviewable='copy' or 'skip'."
251  )
252  elif unviewable == "skip":
253  continue
254  data = self.columns.get_bool_array(key)
255  elif key.getTypeString() == "Angle":
256  data = self.columns.get(key)
257  unit = "radian"
258  if copy:
259  data = data.copy()
260  else:
261  data = self.columns.get(key)
262  if copy:
263  data = data.copy()
264  columns.append(
265  astropy.table.Column(
266  data,
267  name=item.field.getName(),
268  unit=unit,
269  description=item.field.getDoc()
270  )
271  )
272  return cls(columns, meta=meta, copy=False)
def BaseCatalog_asAstropy
Return an astropy.table.Table (or subclass thereof) view into this catalog.
Definition: _syntax.py:207