6 from .schemaMapper
import SchemaMapper
7 from .aggregates
import CoordKey
8 from .source
import SourceRecord
13 def __init__(self, schema, dataIdFormat, coordField="coord", idField="id", radius=None,
14 RecordClass=SourceRecord):
15 """Initialize a multi-catalog match. 18 schema -------- schema shared by all catalogs to be included in the match. 19 dataIdFormat -- dict of name: type for all data ID keys (e.g. {"visit":int, "ccd":int}). 20 coordField ---- prefix for _ra and _dec fields that contain the coordinates to use for the match. 21 idField ------- name of the field in schema that contains unique object IDs. 22 radius -------- lsst.geom.Angle; maximum separation for a match. Defaults to 0.5 arcseconds. 23 RecordClass --- type of record (a subclass of lsst.afw.table.BaseRecord) to expect in catalogs 27 radius = 0.5*lsst.geom.arcseconds
29 raise ValueError(
"'radius' argument must be an Angle")
32 self.
mapper.addMinimalSchema(schema,
True)
34 self.
idKey = schema.find(idField).key
36 outSchema = self.
mapper.editOutputSchema()
39 "object", type=numpy.int64, doc=
"Unique ID for joined sources")
40 for name, dataType
in dataIdFormat.items():
42 name, type=dataType, doc=
"'%s' data ID component")
55 self.
table = RecordClass.Table.make(self.
mapper.getOutputSchema())
60 """Create a new result record from the given input record, using the given data ID and object ID 61 to fill in additional columns.""" 62 outputRecord = self.
table.copyRecord(inputRecord, self.
mapper)
64 outputRecord.set(key, dataId[name])
68 def add(self, catalog, dataId):
69 """Add a new catalog to the match, corresponding to the given data ID. 73 for record
in catalog:
79 catalog.sort(self.
idKey)
81 unmatchedIds = {record.get(self.
idKey)
for record
in catalog}
87 for refRecord, newRecord, distance
in matches:
89 if objId
in matchedRefIds:
93 matchedRefIds.add(objId)
94 if newRecord.get(self.
idKey)
in matchedCatIds:
99 matchedCatIds.add(newRecord.get(self.
idKey))
100 unmatchedIds.discard(newRecord.get(self.
idKey))
103 newToObj.setdefault(newRecord.get(self.
idKey),
set()).
add(objId)
108 for objId
in unmatchedIds:
109 newRecord = catalog.find(objId, self.
idKey)
116 """Return the final match catalog, after sorting it by object, copying it to ensure contiguousness, 117 and optionally removing ambiguous matches. 119 After calling finish(), the in-progress state of the matcher is returned to the state it was 120 just after construction, with the exception of the object ID counter (which is not reset). 124 for record
in self.
result:
126 result.append(record)
130 result = result.copy(deep=
True)
138 """A mapping (i.e. dict-like object) that provides convenient operations on the concatenated 139 catalogs returned by a MultiMatch object. 141 A GroupView provides access to a catalog of grouped objects, in which the grouping is indicated by 142 a field for which all records in a group have the same value. Once constructed, it allows operations 143 similar to those supported by SQL "GROUP BY", such as filtering and aggregate calculation. 147 def build(cls, catalog, groupField="object"):
148 """!Construct a GroupView from a concatenated catalog. 150 @param[in] cls (Class; omit this argument, but Doxygen wants it mentioned) 151 @param[in] catalog Input catalog, containing records grouped by a field in which all records 152 in the same group have the same value. Must be sorted by the group field. 153 @param[in] groupField Name or Key for the field that indicates groups. 155 groupKey = catalog.schema.find(groupField).key
156 ids, indices = numpy.unique(catalog.get(groupKey), return_index=
True)
157 groups = numpy.zeros(len(ids), dtype=object)
158 ends =
list(indices[1:]) + [len(catalog)]
159 for n, (i1, i2)
in enumerate(zip(indices, ends)):
161 groups[n] = catalog[
int(i1):
int(i2)]
162 assert (groups[n].get(groupKey) == ids[n]).
all()
163 return cls(catalog.schema, ids, groups)
166 """Direct constructor; most users should call build() instead. 168 This constructor takes the constituent arrays of the object directly, to allow multiple 169 methods for construction. 177 """Return the number of groups""" 181 """Iterate over group field values""" 185 """Return the catalog subset that corresponds to an group field value""" 186 index = numpy.searchsorted(self.
ids, key)
187 if self.
ids[index] != key:
188 raise KeyError(
"Group with ID {0} not found".
format(key))
192 """Return a new GroupView that contains only groups for which the given predicate function 195 The predicate function is called once for each group, and passed a single argument: the subset 196 catalog for that group. 198 mask = numpy.zeros(len(self), dtype=bool)
199 for i
in range(len(self)):
200 mask[i] = predicate(self.
groups[i])
204 """!Run an aggregate function on each group, returning an array with one element for each group. 206 @param[in] function Callable object that computes the aggregate value. If field is None, 207 called with the entire subset catalog as an argument. If field is not 208 None, called with an array view into that field. 209 @param[in] field A string name or Key object that indicates a single field the aggregate 211 @param[in] dtype Data type for the output array. 213 result = numpy.zeros(len(self), dtype=dtype)
214 if field
is not None:
215 key = self.
schema.find(field).key
221 for i
in range(len(self)):
222 result[i] = f(self.
groups[i])
225 def apply(self, function, field=None, dtype=float):
226 """!Run a non-aggregate function on each group, returning an array with one element for each record. 228 @param[in] function Callable object that computes the aggregate value. If field is None, 229 called with the entire subset catalog as an argument. If field is not 230 None, called with an array view into that field. 231 @param[in] field A string name or Key object that indicates a single field the aggregate 233 @param[in] dtype Data type for the output array. 235 result = numpy.zeros(self.
count, dtype=dtype)
236 if field
is not None:
237 key = self.
schema.find(field).key
244 for i
in range(len(self)):
245 next = last + len(self.
groups[i])
246 result[last:next] = f(self.
groups[i])
def getInputSchema(task, butler=None, schema=None)
Obtain the input schema either directly or froma butler reference.
def __init__(self, schema, dataIdFormat, coordField="coord", idField="id", radius=None, RecordClass=SourceRecord)
def where(self, predicate)
def build(cls, catalog, groupField="object")
Construct a GroupView from a concatenated catalog.
std::shared_ptr< FrameSet > append(FrameSet const &first, FrameSet const &second)
Construct a FrameSet that performs two transformations in series.
daf::base::PropertySet * set
A class representing an angle.
def apply(self, function, field=None, dtype=float)
Run a non-aggregate function on each group, returning an array with one element for each record...
template SourceMatchVector matchRaDec(SourceCatalog const &, lsst::geom::Angle, MatchControl const &)
def makeRecord(self, inputRecord, dataId, objId)
def __init__(self, schema, ids, groups)
bool all(CoordinateExpr< N > const &expr) noexcept
Return true if all elements are true.
def finish(self, removeAmbiguous=True)
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
def __getitem__(self, key)
def aggregate(self, function, field=None, dtype=float)
Run an aggregate function on each group, returning an array with one element for each group...
std::vector< SchemaItem< Flag > > * items
def add(self, catalog, dataId)
daf::base::PropertyList * list