4 from .tableLib
import SchemaMapper, CoordKey, SourceRecord
8 def __init__(self, schema, dataIdFormat, coordField="coord", idField="id", radius=None,
9 RecordClass=SourceRecord):
10 """Initialize a multi-catalog match.
13 schema -------- schema shared by all catalogs to be included in the match.
14 dataIdFormat -- dict of name: type for all data ID keys (e.g. {"visit":int, "ccd":int}).
15 coordField ---- prefix for _ra and _dec fields that contain the coordinates to use for the match.
16 idField ------- name of the field in schema that contains unique object IDs.
17 radius -------- lsst.afw.geom.Angle; maximum separation for a match. Defaults to 0.5 arcseconds.
18 RecordClass --- type of record (a subclass of lsst.afw.table.BaseRecord) to expect in catalogs
22 radius = 0.5*lsst.afw.geom.arcseconds
24 raise ValueError(
"'radius' argument must be an Angle")
27 self.mapper.addMinimalSchema(schema,
True)
29 self.
idKey = schema.find(idField).key
31 outSchema = self.mapper.editOutputSchema()
32 self.
objectKey = outSchema.addField(
"object", type=numpy.int64, doc=
"Unique ID for joined sources")
33 for name, dataType
in dataIdFormat.iteritems():
34 self.
dataIdKeys[name] = outSchema.addField(name, type=dataType, doc=
"'%s' data ID component")
46 self.
table = RecordClass.Table.make(self.mapper.getOutputSchema())
51 """Create a new result record from the given input record, using the given data ID and object ID
52 to fill in additional columns."""
53 outputRecord = self.table.copyRecord(inputRecord, self.
mapper)
54 for name, key
in self.dataIdKeys.iteritems():
55 outputRecord.set(key, dataId[name])
59 def add(self, catalog, dataId):
60 """Add a new catalog to the match, corresponding to the given data ID.
64 for record
in catalog:
67 self.
reference = self.result.copy(deep=
False)
74 newById = {record.get(self.
idKey): record
for record
in catalog}
78 for refRecord, newRecord, distance
in matches:
80 if objById.pop(objId,
None)
is None:
83 self.ambiguous.add(objId)
84 if newById.pop(newRecord.get(self.
idKey),
None)
is None:
87 self.ambiguous.add(objId)
90 newToObj.setdefault(newRecord.get(self.
idKey), set()).
add(objId)
92 self.result.append(self.
makeRecord(newRecord, dataId, objId))
95 for newRecord
in newById.itervalues():
98 self.result.append(resultRecord)
99 self.reference.append(resultRecord)
102 """Return the final match catalog, after sorting it by object, copying it to ensure contiguousness,
103 and optionally removing ambiguous matches.
105 After calling finish(), the in-progress state of the matcher is returned to the state it was
106 just after construction, with the exception of the object ID counter (which is not reset).
109 result = self.table.Catalog(self.
table)
110 for record
in self.
result:
112 result.append(record)
116 result = result.copy(deep=
True)
124 """A mapping (i.e. dict-like object) that provides convenient operations on the concatenated
125 catalogs returned by a MultiMatch object.
127 A GroupView provides access to a catalog of grouped objects, in which the grouping is indicated by
128 a field for which all records in a group have the same value. Once constructed, it allows operations
129 similar to those supported by SQL "GROUP BY", such as filtering and aggregate calculation.
133 def build(cls, catalog, groupField="object"):
134 """!Construct a GroupView from a concatenated catalog.
136 @param[in] catalog Input catalog, containing records grouped by a field in which all records
137 in the same group have the same value. Must be sorted by the group field.
138 @param[in] groupField Name or Key for the field that indicates groups.
140 groupKey = catalog.schema.find(groupField).key
141 ids, indices = numpy.unique(catalog.get(groupKey), return_index=
True)
142 groups = numpy.zeros(len(ids), dtype=object)
143 ends = list(indices[1:]) + [len(catalog)]
144 for n, (i1, i2)
in enumerate(zip(indices, ends)):
145 groups[n] = catalog[i1:i2]
146 assert (groups[n].get(groupKey) == ids[n]).
all()
147 return cls(catalog.schema, ids, groups)
150 """Direct constructor; most users should call build() instead.
152 This constructor takes the constituent arrays of the object directly, to allow multiple
153 methods for construction.
161 """Return the number of groups"""
165 """Iterate over group field values"""
169 """Return the catalog subset that corresponds to an group field value"""
170 index = numpy.searchsorted(self.
ids, key)
171 if self.
ids[index] != key:
172 raise KeyError(
"Group with ID {0} not found".
format(key))
176 """Return a new GroupView that contains only groups for which the given predicate function
179 The predicate function is called once for each group, and passed a single argument: the subset
180 catalog for that group.
182 mask = numpy.zeros(len(self), dtype=bool)
183 for i
in xrange(len(self)):
184 mask[i] = predicate(self.
groups[i])
188 """!Run an aggregate function on each group, returning an array with one element for each group.
190 @param[in] function Callable object that computes the aggregate value. If field is None,
191 called with the entire subset catalog as an argument. If field is not
192 None, called with an array view into that field.
193 @param[in] field A string name or Key object that indicates a single field the aggregate
195 @param[in] dtype Data type for the output array.
197 result = numpy.zeros(len(self), dtype=float)
198 if field
is not None:
199 key = self.schema.find(field).key
200 f =
lambda cat: function(cat.get(key))
203 for i
in xrange(len(self)):
204 result[i] = f(self.
groups[i])
207 def apply(self, function, field=None, dtype=float):
208 """!Run a non-aggregate function on each group, returning an array with one element for each record.
210 @param[in] function Callable object that computes the aggregate value. If field is None,
211 called with the entire subset catalog as an argument. If field is not
212 None, called with an array view into that field.
213 @param[in] field A string name or Key object that indicates a single field the aggregate
215 @param[in] dtype Data type for the output array.
217 result = numpy.zeros(self.
count, dtype=float)
218 if field
is not None:
219 key = self.schema.find(field).key
220 f =
lambda cat: function(cat.get(key))
224 for i
in xrange(len(self)):
225 next = last + len(self.
groups[i])
226 result[last:next] = f(self.
groups[i])
bool all(CoordinateExpr< N > const &expr)
Return true if all elements are true.
def aggregate
Run an aggregate function on each group, returning an array with one element for each group...
boost::enable_if< typename ExpressionTraits< Scalar >::IsScalar, Scalar >::type sum(Scalar const &scalar)
def build
Construct a GroupView from a concatenated catalog.
def apply
Run a non-aggregate function on each group, returning an array with one element for each record...
std::vector< Match< typename Cat::Record, typename Cat::Record > > matchRaDec(Cat const &cat, Angle radius, bool symmetric=true)
A FunctorKey used to get or set celestial coordiantes from a pair of Angle keys.