26 from ._schemaMapper
import SchemaMapper
27 from ._table
import CoordKey, SourceRecord
31 """Initialize a multi-catalog match.
35 schema : `lsst.afw.table.Schema`
36 Schema shared by all catalogs to be included in the match.
38 Set of name: type for all data ID keys (e.g. {"visit":int,
40 coordField : `str`, optional
41 Prefix for _ra and _dec fields that contain the
42 coordinates to use for the match.
43 idField : `str`, optional
44 Name of the field in schema that contains unique object
46 radius : `lsst.geom.Angle`, optional
47 Maximum separation for a match. Defaults to 0.5 arcseconds.
48 RecordClass : `lsst.afw.table.BaseRecord`
49 Type of record to expect in catalogs to be matched.
52 def __init__(self, schema, dataIdFormat, coordField="coord", idField="id", radius=None,
53 RecordClass=SourceRecord):
55 radius = 0.5*lsst.geom.arcseconds
57 raise ValueError(
"'radius' argument must be an Angle")
60 self.
mappermapper.addMinimalSchema(schema,
True)
61 self.
coordKeycoordKey = CoordKey(schema[coordField])
62 self.
idKeyidKey = schema.find(idField).key
64 outSchema = self.
mappermapper.editOutputSchema()
67 "object", type=numpy.int64, doc=
"Unique ID for joined sources")
68 for name, dataType
in dataIdFormat.items():
69 self.
dataIdKeysdataIdKeys[name] = outSchema.addField(
70 name, type=dataType, doc=
"'%s' data ID component")
83 self.
tabletable = RecordClass.Table.make(self.
mappermapper.getOutputSchema())
88 """Create a new result record from the given input record, using the
89 given data ID and object ID to fill in additional columns.
93 inputRecord : `lsst.afw.table.source.sourceRecord`
94 Record to use as the reference for the new result.
95 dataId : `DataId` or `dict`
96 Data id describing the data.
98 Object id of the object to be added.
102 outputRecord : `lsst.afw.table.source.sourceRecord`
103 Newly generated record.
105 outputRecord = self.
tabletable.copyRecord(inputRecord, self.
mappermapper)
107 outputRecord.set(key, dataId[name])
108 outputRecord.set(self.
objectKeyobjectKey, objId)
111 def add(self, catalog, dataId):
112 """Add a new catalog to the match, corresponding to the given data ID.
113 The new catalog is appended to the `self.result` and
114 `self.reference` catalogs.
118 catalog : `lsst.afw.table.base.Catalog`
119 Catalog to be added to the match result.
120 dataId : `DataId` or `dict`
121 Data id for the catalog to be added.
123 if self.
resultresult
is None:
125 for record
in catalog:
127 record, dataId, objId=self.
nextObjIdnextObjId))
131 catalog.sort(self.
idKeyidKey)
133 unmatchedIds = {record.get(self.
idKeyidKey)
for record
in catalog}
137 matchedRefIds =
set()
138 matchedCatIds =
set()
139 for refRecord, newRecord, distance
in matches:
140 objId = refRecord.get(self.
objectKeyobjectKey)
141 if objId
in matchedRefIds:
145 matchedRefIds.add(objId)
146 if newRecord.get(self.
idKeyidKey)
in matchedCatIds:
151 matchedCatIds.add(newRecord.get(self.
idKeyidKey))
152 unmatchedIds.discard(newRecord.get(self.
idKeyidKey))
155 newToObj.setdefault(newRecord.get(self.
idKeyidKey),
set()).
add(objId)
160 for objId
in unmatchedIds:
161 newRecord = catalog.find(objId, self.
idKeyidKey)
168 """Return the final match catalog, after sorting it by object, copying
169 it to ensure contiguousness, and optionally removing ambiguous
172 After calling finish(), the in-progress state of the matcher
173 is returned to the state it was just after construction, with
174 the exception of the object ID counter (which is not reset).
178 removeAmbiguous : `bool`, optional
179 Should ambiguous matches be removed from the match
180 catalog? Defaults to True.
184 result : `lsst.afw.table.base.Catalog`
185 Final match catalog, sorted by object.
189 for record
in self.
resultresult:
191 result.append(record)
193 result = self.
resultresult
195 result = result.copy(deep=
True)
203 """A mapping (i.e. dict-like object) that provides convenient
204 operations on the concatenated catalogs returned by a MultiMatch
207 A GroupView provides access to a catalog of grouped objects, in
208 which the grouping is indicated by a field for which all records
209 in a group have the same value. Once constructed, it allows
210 operations similar to those supported by SQL "GROUP BY", such as
211 filtering and aggregate calculation.
215 schema : `lsst.afw.table.Schema`
216 Catalog schema to use for the grouped object catalog.
218 List of identifying keys for the groups in the catalog.
220 List of catalog subsets associated with each key in ids.
224 def build(cls, catalog, groupField="object"):
225 """Construct a GroupView from a concatenated catalog.
229 catalog : `lsst.afw.table.base.Catalog`
230 Input catalog, containing records grouped by a field in
231 which all records in the same group have the same value.
232 Must be sorted by the group field.
233 groupField : `str`, optional
234 Name or Key for the field that indicates groups. Defaults
239 groupCatalog : `lsst.afw.table.multiMatch.GroupView`
240 Constructed GroupView from the input concatenated catalog.
242 groupKey = catalog.schema.find(groupField).key
243 ids, indices = numpy.unique(catalog.get(groupKey), return_index=
True)
244 groups = numpy.zeros(len(ids), dtype=object)
245 ends =
list(indices[1:]) + [len(catalog)]
246 for n, (i1, i2)
in enumerate(zip(indices, ends)):
247 groups[n] = catalog[i1:i2]
248 assert (groups[n].get(groupKey) == ids[n]).
all()
249 return cls(catalog.schema, ids, groups)
258 return len(self.
idsids)
264 index = numpy.searchsorted(self.
idsids, key)
265 if self.
idsids[index] != key:
266 raise KeyError(
"Group with ID {0} not found".
format(key))
267 return self.
groupsgroups[index]
270 """Return a new GroupView that contains only groups for which the
271 given predicate function returns True.
273 The predicate function is called once for each group, and
274 passed a single argument: the subset catalog for that group.
279 Function to identify which groups should be included in
284 outGroupView : `lsst.afw.table.multiMatch.GroupView`
285 Subset GroupView containing only groups that match the
288 mask = numpy.zeros(len(self), dtype=bool)
289 for i
in range(len(self)):
290 mask[i] = predicate(self.
groupsgroups[i])
294 """Run an aggregate function on each group, returning an array with
295 one element for each group.
300 Callable object that computes the aggregate value. If
301 `field` is None, called with the entire subset catalog as an
302 argument. If `field` is not None, called with an array view
304 field : `str`, optional
305 A string name or Key object that indicates a single field the aggregate
308 Data type of the output array.
312 result : Array of `dtype`
313 Aggregated values for each group.
315 result = numpy.zeros(len(self), dtype=dtype)
316 if field
is not None:
317 key = self.
schemaschema.find(field).key
320 return function(cat.get(key))
323 for i
in range(len(self)):
324 result[i] = f(self.
groupsgroups[i])
327 def apply(self, function, field=None, dtype=float):
328 """Run a non-aggregate function on each group, returning an array with
329 one element for each record.
334 Callable object that computes the aggregate value. If field is None,
335 called with the entire subset catalog as an argument. If field is not
336 None, called with an array view into that field.
338 A string name or Key object that indicates a single field the aggregate
341 Data type for the output array.
345 result : `numpy.array` of `dtype`
346 Result of the function calculated on an element-by-element basis.
348 result = numpy.zeros(self.
countcount, dtype=dtype)
349 if field
is not None:
350 key = self.
schemaschema.find(field).key
353 return function(cat.get(key))
357 for i
in range(len(self)):
358 next = last + len(self.
groupsgroups[i])
359 result[last:next] = f(self.
groupsgroups[i])
std::vector< SchemaItem< Flag > > * items
def where(self, predicate)
def aggregate(self, function, field=None, dtype=float)
def build(cls, catalog, groupField="object")
def __init__(self, schema, ids, groups)
def apply(self, function, field=None, dtype=float)
def __getitem__(self, key)
def __init__(self, schema, dataIdFormat, coordField="coord", idField="id", radius=None, RecordClass=SourceRecord)
def add(self, catalog, dataId)
def finish(self, removeAmbiguous=True)
def makeRecord(self, inputRecord, dataId, objId)
A class representing an angle.
daf::base::PropertyList * list
daf::base::PropertySet * set
std::shared_ptr< FrameSet > append(FrameSet const &first, FrameSet const &second)
Construct a FrameSet that performs two transformations in series.
std::vector< Match< typename Cat1::Record, typename Cat2::Record > > matchRaDec(Cat1 const &cat1, Cat2 const &cat2, lsst::geom::Angle radius, MatchControl const &mc=MatchControl())
Compute all tuples (s1,s2,d) where s1 belings to cat1, s2 belongs to cat2 and d, the distance between...
bool all(CoordinateExpr< N > const &expr) noexcept
Return true if all elements are true.
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
def getInputSchema(task, butler=None, schema=None)
Obtain the input schema either directly or froma butler reference.