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()
38 "object", type=numpy.int64, doc=
"Unique ID for joined sources")
39 for name, dataType
in dataIdFormat.items():
41 name, type=dataType, doc=
"'%s' data ID component")
54 self.
table = RecordClass.Table.make(self.
mapper.getOutputSchema())
59 """Create a new result record from the given input record, using the given data ID and object ID 60 to fill in additional columns.""" 61 outputRecord = self.
table.copyRecord(inputRecord, self.
mapper)
63 outputRecord.set(key, dataId[name])
67 def add(self, catalog, dataId):
68 """Add a new catalog to the match, corresponding to the given data ID. 72 for record
in catalog:
78 catalog.sort(self.
idKey)
80 unmatchedIds = {record.get(self.
idKey)
for record
in catalog}
86 for refRecord, newRecord, distance
in matches:
88 if objId
in matchedRefIds:
92 matchedRefIds.add(objId)
93 if newRecord.get(self.
idKey)
in matchedCatIds:
98 matchedCatIds.add(newRecord.get(self.
idKey))
99 unmatchedIds.discard(newRecord.get(self.
idKey))
102 newToObj.setdefault(newRecord.get(self.
idKey),
set()).
add(objId)
107 for objId
in unmatchedIds:
108 newRecord = catalog.find(objId, self.
idKey)
115 """Return the final match catalog, after sorting it by object, copying it to ensure contiguousness, 116 and optionally removing ambiguous matches. 118 After calling finish(), the in-progress state of the matcher is returned to the state it was 119 just after construction, with the exception of the object ID counter (which is not reset). 123 for record
in self.
result:
125 result.append(record)
129 result = result.copy(deep=
True)
137 """A mapping (i.e. dict-like object) that provides convenient operations on the concatenated 138 catalogs returned by a MultiMatch object. 140 A GroupView provides access to a catalog of grouped objects, in which the grouping is indicated by 141 a field for which all records in a group have the same value. Once constructed, it allows operations 142 similar to those supported by SQL "GROUP BY", such as filtering and aggregate calculation. 146 def build(cls, catalog, groupField="object"):
147 """!Construct a GroupView from a concatenated catalog. 149 @param[in] cls (Class; omit this argument, but Doxygen wants it mentioned) 150 @param[in] catalog Input catalog, containing records grouped by a field in which all records 151 in the same group have the same value. Must be sorted by the group field. 152 @param[in] groupField Name or Key for the field that indicates groups. 154 groupKey = catalog.schema.find(groupField).key
155 ids, indices = numpy.unique(catalog.get(groupKey), return_index=
True)
156 groups = numpy.zeros(len(ids), dtype=object)
157 ends =
list(indices[1:]) + [len(catalog)]
158 for n, (i1, i2)
in enumerate(zip(indices, ends)):
160 groups[n] = catalog[
int(i1):
int(i2)]
161 assert (groups[n].get(groupKey) == ids[n]).
all()
162 return cls(catalog.schema, ids, groups)
165 """Direct constructor; most users should call build() instead. 167 This constructor takes the constituent arrays of the object directly, to allow multiple 168 methods for construction. 176 """Return the number of groups""" 180 """Iterate over group field values""" 184 """Return the catalog subset that corresponds to an group field value""" 185 index = numpy.searchsorted(self.
ids, key)
186 if self.
ids[index] != key:
187 raise KeyError(
"Group with ID {0} not found".
format(key))
191 """Return a new GroupView that contains only groups for which the given predicate function 194 The predicate function is called once for each group, and passed a single argument: the subset 195 catalog for that group. 197 mask = numpy.zeros(len(self), dtype=bool)
198 for i
in range(len(self)):
199 mask[i] = predicate(self.
groups[i])
203 """!Run an aggregate function on each group, returning an array with one element for each group. 205 @param[in] function Callable object that computes the aggregate value. If field is None, 206 called with the entire subset catalog as an argument. If field is not 207 None, called with an array view into that field. 208 @param[in] field A string name or Key object that indicates a single field the aggregate 210 @param[in] dtype Data type for the output array. 212 result = numpy.zeros(len(self), dtype=dtype)
213 if field
is not None:
214 key = self.
schema.find(field).key
220 for i
in range(len(self)):
221 result[i] = f(self.
groups[i])
224 def apply(self, function, field=None, dtype=float):
225 """!Run a non-aggregate function on each group, returning an array with one element for each record. 227 @param[in] function Callable object that computes the aggregate value. If field is None, 228 called with the entire subset catalog as an argument. If field is not 229 None, called with an array view into that field. 230 @param[in] field A string name or Key object that indicates a single field the aggregate 232 @param[in] dtype Data type for the output array. 234 result = numpy.zeros(self.
count, dtype=dtype)
235 if field
is not None:
236 key = self.
schema.find(field).key
243 for i
in range(len(self)):
244 next = last + len(self.
groups[i])
245 result[last:next] = f(self.
groups[i])
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