23 """Quality Assessment class for Kernel Candidates"""
24 from builtins
import zip
25 from builtins
import object
32 from .
import diffimLib
33 from .utils
import calcCentroid, calcWidth
39 """Class to undertake QA of KernelCandidates after modeling of
40 the Psf-matching kernel. Both directly--fitted diffim (LOCAL)
41 and spatially--interpolated kernel diffim (SPATIAL) metrics
42 are calculated, based on the distribution of residuals in the
43 KernelCandidates stamp.
45 @param nKernelSpatial : Number of terms in the spatial model; needed to initialize per-basis QA arrays
49 "Position of reference object for registration (radians)."))
51 self.fields.append(
afwTable.Field[
"Angle"](
"RegisterResidualBearing",
52 "Angle of residual wrt declination parallel in radians"))
54 self.fields.append(
afwTable.Field[
"Angle"](
"RegisterResidualDistance",
55 "Offset of residual in radians"))
58 for kType
in (
"LOCAL",
"SPATIAL"):
60 commentAndUnit = metricMap[k][
'comment']
61 self.fields.append(
afwTable.Field[metricMap[k][
'type']](k%(kType), *commentAndUnit))
64 "Status of the KernelCandidate"))
66 self.fields.append(
afwTable.Field[
"ArrayD"](
"KernelCoeffValues_LOCAL",
67 "Original basis coefficients",
71 "Evaluation of background model at this point"))
73 self.fields.append(
afwTable.Field[
"F"](
"KCDiffimMseKernel_SPATIAL",
74 "Mean squared error of spatial kernel estimate"))
77 nameList = [
'KCDiffimMean_%s',
'KCDiffimMedian_%s',
'KCDiffimIQR_%s',
'KCDiffimStDev_%s',
78 'KCDiffimKSD_%s',
'KCDiffimKSProb_%s',
'KCDiffimADA2_%s',
'KCDiffimADCrit_%s',
79 'KCDiffimADSig_%s',
'KCDiffimChiSq_%s',
'KCDiffimMseResids_%s',
'KCKernelCentX_%s',
80 'KCKernelCentY_%s',
'KCKernelStdX_%s',
'KCKernelStdY_%s',
'KernelCandidateId_%s']
81 typeList = [
'F',
'F',
'F',
'F',
'F',
'F',
'F',
'ArrayD',
'ArrayD',
'F',
'F',
'F',
83 commentList = [(
"Mean of KernelCandidate diffim",
"sigma"),
84 (
"Median of KernelCandidate diffim",
"sigma"),
85 (
"Inner quartile range of KernelCandidate diffim",
"sigma"),
86 (
"Standard deviation of KernelCandidate diffim",
"sigma"),
87 (
"D from K-S test of diffim pixels relative to Normal", ),
88 (
"Prob from K-S test of diffim pixels relative to Normal",
"likelihood"),
89 (
"Anderson-Darling test statistic of diffim pixels relative to Normal", ),
90 (
"Critical values for the significance levels in KCDiffimADSig. If A2 is greater " +
91 "than this number, hypothesis that the distributions are similar can be rejected.", 5),
92 (
"Anderson-Darling significance levels for the Normal distribution", 5),
93 (
"Reduced chi^2 of the residual.",
"likelihood"),
94 (
"Mean squared error in diffim : Variance + Bias**2",),
95 (
"Centroid in X for this Kernel",
"pixel"),
96 (
"Centroid in Y for this Kernel",
"pixel"),
97 (
"Standard deviation in X for this Kernel",
"pixel"),
98 (
"Standard deviation in Y for this Kernel",
"pixel"),
99 (
"Id for this KernelCandidate",)]
101 for name, mtype, comment
in zip(nameList, typeList, commentList):
102 metricMap[name] = {
'type': mtype,
'comment': comment}
107 """Add the to-be-generated QA keys to the Source schema"""
108 schema = inSourceCatalog.getSchema()
110 psfDef = inSourceCatalog.getPsfFluxDefinition()
111 centroidDef = inSourceCatalog.getCentroidDefinition()
112 shapeDef = inSourceCatalog.getShapeDefinition()
113 for n
in schema.getNames():
114 inKeys.append(schema[n].asKey())
117 schema.addField(field)
119 for source
in inSourceCatalog:
120 rec = outSourceCatalog.addNew()
122 if k.getTypeString() ==
'Coord':
123 rec.setCoord(source.getCoord())
125 setter = getattr(rec,
"set"+k.getTypeString())
126 getter = getattr(source,
"get"+k.getTypeString())
128 outSourceCatalog.definePsfFlux(psfDef)
129 outSourceCatalog.defineCentroid(centroidDef)
130 outSourceCatalog.defineShape(shapeDef)
131 return outSourceCatalog
134 """Calculate the core QA statistics on a difference image"""
136 maskArr = di.getMask().getArray()
139 maskArr &= mask.getPlaneBitMask([
"BAD",
"SAT",
"NO_DATA",
"EDGE"])
142 diArr = ma.array(di.getImage().getArray(), mask=maskArr)
143 varArr = ma.array(di.getVariance().getArray(), mask=maskArr)
146 diArr /= np.sqrt(varArr)
151 median = ma.extras.median(diArr)
154 data = ma.getdata(diArr[~diArr.mask])
155 iqr = np.percentile(data, 75.) - np.percentile(data, 25.)
158 chisq = np.sum(np.power(data, 2.))
164 variance = np.power(data, 2.).mean()
165 mseResids = bias**2 + variance
170 rchisq = chisq/(len(data) - 1 - dof)
173 D, prob = scipy.stats.kstest(data,
'norm')
175 A2, crit, sig = scipy.stats.anderson(data,
'norm')
177 if np.isinf(A2)
or np.isnan(A2):
179 except ZeroDivisionError:
187 return {
"mean": mean,
"stdev": stdev,
"median": median,
"iqr": iqr,
188 "D": D,
"prob": prob,
"A2": A2,
"crit": crit,
"sig": sig,
189 "rchisq": rchisq,
"mseResids": mseResids}
191 def apply(self, candidateList, spatialKernel, spatialBackground, dof=0):
192 """Evaluate the QA metrics for all KernelCandidates in the
193 candidateList; set the values of the metrics in their
194 associated Sources"""
195 for kernelCandidate
in candidateList:
196 source = kernelCandidate.getSource()
197 schema = source.schema
200 if kernelCandidate.getStatus() != afwMath.SpatialCellCandidate.UNKNOWN:
201 kType = getattr(diffimLib.KernelCandidateF,
"ORIG")
202 di = kernelCandidate.getDifferenceImage(kType)
203 kernelValues = kernelCandidate.getKernel(kType).getKernelParameters()
204 kernelValues = np.asarray(kernelValues)
206 lkim = kernelCandidate.getKernelImage(kType)
208 stdx, stdy =
calcWidth(lkim.getArray(), centx, centy)
214 metrics = {
"KCDiffimMean_LOCAL": localResults[
"mean"],
215 "KCDiffimMedian_LOCAL": localResults[
"median"],
216 "KCDiffimIQR_LOCAL": localResults[
"iqr"],
217 "KCDiffimStDev_LOCAL": localResults[
"stdev"],
218 "KCDiffimKSD_LOCAL": localResults[
"D"],
219 "KCDiffimKSProb_LOCAL": localResults[
"prob"],
220 "KCDiffimADA2_LOCAL": localResults[
"A2"],
221 "KCDiffimADCrit_LOCAL": localResults[
"crit"],
222 "KCDiffimADSig_LOCAL": localResults[
"sig"],
223 "KCDiffimChiSq_LOCAL": localResults[
"rchisq"],
224 "KCDiffimMseResids_LOCAL": localResults[
"mseResids"],
225 "KCKernelCentX_LOCAL": centx,
226 "KCKernelCentY_LOCAL": centy,
227 "KCKernelStdX_LOCAL": stdx,
228 "KCKernelStdY_LOCAL": stdy,
229 "KernelCandidateId_LOCAL": kernelCandidate.getId(),
230 "KernelCoeffValues_LOCAL": kernelValues}
232 key = schema[k].asKey()
233 setter = getattr(source,
"set"+key.getTypeString())
234 setter(key, metrics[k])
237 kType = getattr(diffimLib.KernelCandidateF,
"ORIG")
238 lkim = kernelCandidate.getKernelImage(kType)
244 skim = afwImage.ImageD(spatialKernel.getDimensions())
245 spatialKernel.computeImage(skim,
False, kernelCandidate.getXCenter(),
246 kernelCandidate.getYCenter())
248 stdx, stdy =
calcWidth(skim.getArray(), centx, centy)
251 sbg = spatialBackground(kernelCandidate.getXCenter(), kernelCandidate.getYCenter())
252 di = kernelCandidate.getDifferenceImage(sk, sbg)
258 bias = np.mean(skim.getArray())
259 variance = np.mean(np.power(skim.getArray(), 2.))
260 mseKernel = bias**2 + variance
264 metrics = {
"KCDiffimMean_SPATIAL": spatialResults[
"mean"],
265 "KCDiffimMedian_SPATIAL": spatialResults[
"median"],
266 "KCDiffimIQR_SPATIAL": spatialResults[
"iqr"],
267 "KCDiffimStDev_SPATIAL": spatialResults[
"stdev"],
268 "KCDiffimKSD_SPATIAL": spatialResults[
"D"],
269 "KCDiffimKSProb_SPATIAL": spatialResults[
"prob"],
270 "KCDiffimADA2_SPATIAL": spatialResults[
"A2"],
271 "KCDiffimADCrit_SPATIAL": spatialResults[
"crit"],
272 "KCDiffimADSig_SPATIAL": spatialResults[
"sig"],
273 "KCDiffimChiSq_SPATIAL": spatialResults[
"rchisq"],
274 "KCDiffimMseResids_SPATIAL": spatialResults[
"mseResids"],
275 "KCDiffimMseKernel_SPATIAL": mseKernel,
276 "KCKernelCentX_SPATIAL": centx,
277 "KCKernelCentY_SPATIAL": centy,
278 "KCKernelStdX_SPATIAL": stdx,
279 "KCKernelStdY_SPATIAL": stdy,
280 "KernelCandidateId_SPATIAL": kernelCandidate.getId()}
282 key = schema[k].asKey()
283 setter = getattr(source,
"set"+key.getTypeString())
284 setter(key, metrics[k])
286 def aggregate(self, sourceCatalog, metadata, wcsresids, diaSources=None):
287 """Generate aggregate metrics (e.g. total numbers of false
288 positives) from all the Sources in the sourceCatalog"""
289 for source
in sourceCatalog:
290 sourceId = source.getId()
291 if sourceId
in wcsresids:
294 coord, resids = wcsresids[sourceId]
295 key = source.schema[
"RegisterResidualBearing"].asKey()
296 setter = getattr(source,
"set"+key.getTypeString())
297 setter(key, resids[0])
298 key = source.schema[
"RegisterResidualDistance"].asKey()
299 setter = getattr(source,
"set"+key.getTypeString())
300 setter(key, resids[1])
301 key = source.schema[
"RegisterRefPosition"].asKey()
302 setter = getattr(source,
"set"+key.getTypeString())
304 coord.getDec().asRadians()))
306 metadata.add(
"NFalsePositivesTotal", len(diaSources))
310 for source
in diaSources:
311 refId = source.get(
"refMatchId")
312 srcId = source.get(
"srcMatchId")
317 if refId == 0
and srcId == 0:
319 metadata.add(
"NFalsePositivesRefAssociated", nRefMatch)
320 metadata.add(
"NFalsePositivesSrcAssociated", nSrcMatch)
321 metadata.add(
"NFalsePositivesUnassociated", nunmatched)
322 for kType
in (
"LOCAL",
"SPATIAL"):
323 for sName
in (
"KCDiffimMean",
"KCDiffimMedian",
"KCDiffimIQR",
"KCDiffimStDev",
324 "KCDiffimKSProb",
"KCDiffimADSig",
"KCDiffimChiSq",
325 "KCDiffimMseResids",
"KCDiffimMseKernel"):
326 if sName ==
"KCDiffimMseKernel" and kType ==
"LOCAL":
328 kName =
"%s_%s" % (sName, kType)
329 vals = np.array([s.get(kName)
for s
in sourceCatalog])
330 idx = np.isfinite(vals)
331 metadata.add(
"%s_MEAN" % (kName), np.mean(vals[idx]))
332 metadata.add(
"%s_MEDIAN" % (kName), np.median(vals[idx]))
333 metadata.add(
"%s_STDEV" % (kName), np.std(vals[idx]))
Custom catalog class for record/table subclasses that are guaranteed to have an ID, and should generally be sorted by that ID.
A description of a field in a table.
A kernel created from an Image.