23 __all__ = [
"KernelCandidateQa"]
32 from .
import diffimLib
33 from .utils
import calcCentroid, calcWidth
37 """Quality Assessment class for Kernel Candidates""" 40 """Class to undertake QA of KernelCandidates after modeling of 41 the Psf-matching kernel. Both directly--fitted diffim (LOCAL) 42 and spatially--interpolated kernel diffim (SPATIAL) metrics 43 are calculated, based on the distribution of residuals in the 44 KernelCandidates stamp. 48 nKernelSpatial : `int` 49 Number of terms in the spatial model; needed to initialize per-basis QA arrays 53 "RegisterRefPosition",
54 "Position of reference object for registration (radians)."))
57 "Angle of residual wrt declination parallel in radians"))
60 "Offset of residual in radians"))
63 for kType
in (
"LOCAL",
"SPATIAL"):
65 commentAndUnit = metricMap[k][
'comment']
69 "Status of the KernelCandidate"))
72 "Original basis coefficients",
76 "Evaluation of background model at this point"))
79 "Mean squared error of spatial kernel estimate"))
82 nameList = [
'KCDiffimMean_%s',
'KCDiffimMedian_%s',
'KCDiffimIQR_%s',
'KCDiffimStDev_%s',
83 'KCDiffimKSD_%s',
'KCDiffimKSProb_%s',
'KCDiffimADA2_%s',
'KCDiffimADCrit_%s',
84 'KCDiffimADSig_%s',
'KCDiffimChiSq_%s',
'KCDiffimMseResids_%s',
'KCKernelCentX_%s',
85 'KCKernelCentY_%s',
'KCKernelStdX_%s',
'KCKernelStdY_%s',
'KernelCandidateId_%s']
86 typeList = [
'F',
'F',
'F',
'F',
'F',
'F',
'F',
'ArrayD',
'ArrayD',
'F',
'F',
'F',
89 (
"Mean of KernelCandidate diffim",
"sigma"),
90 (
"Median of KernelCandidate diffim",
"sigma"),
91 (
"Inner quartile range of KernelCandidate diffim",
"sigma"),
92 (
"Standard deviation of KernelCandidate diffim",
"sigma"),
93 (
"D from K-S test of diffim pixels relative to Normal", ),
94 (
"Prob from K-S test of diffim pixels relative to Normal",
"likelihood"),
95 (
"Anderson-Darling test statistic of diffim pixels relative to Normal", ),
96 (
"Critical values for the significance levels in KCDiffimADSig. If A2 is greater " +
97 "than this number, hypothesis that the distributions are similar can be rejected.", 5),
98 (
"Anderson-Darling significance levels for the Normal distribution", 5),
99 (
"Reduced chi^2 of the residual.",
"likelihood"),
100 (
"Mean squared error in diffim : Variance + Bias**2",),
101 (
"Centroid in X for this Kernel",
"pixel"),
102 (
"Centroid in Y for this Kernel",
"pixel"),
103 (
"Standard deviation in X for this Kernel",
"pixel"),
104 (
"Standard deviation in Y for this Kernel",
"pixel"),
105 (
"Id for this KernelCandidate",),
108 for name, mtype, comment
in zip(nameList, typeList, commentList):
109 metricMap[name] = {
'type': mtype,
'comment': comment}
114 """Add the to-be-generated QA keys to the Source schema""" 115 schema = inSourceCatalog.getSchema()
117 psfDef = inSourceCatalog.schema.getAliasMap().get(
"slot_PsfFlux")
118 centroidDef = inSourceCatalog.getCentroidDefinition()
119 shapeDef = inSourceCatalog.getShapeDefinition()
120 for n
in schema.getNames():
121 inKeys.append(schema[n].asKey())
124 schema.addField(field)
126 for source
in inSourceCatalog:
127 rec = outSourceCatalog.addNew()
129 if k.getTypeString() ==
'Coord':
130 rec.setCoord(source.getCoord())
132 setter = getattr(rec,
"set"+k.getTypeString())
133 getter = getattr(source,
"get"+k.getTypeString())
135 outSourceCatalog.definePsfFlux(psfDef)
136 outSourceCatalog.defineCentroid(centroidDef)
137 outSourceCatalog.defineShape(shapeDef)
138 return outSourceCatalog
140 def _calculateStats(self, di, dof=0.):
141 """Calculate the core QA statistics on a difference image""" 143 maskArr = di.getMask().getArray()
146 maskArr &= mask.getPlaneBitMask([
"BAD",
"SAT",
"NO_DATA",
"EDGE"])
149 diArr = ma.array(di.getImage().getArray(), mask=maskArr)
150 varArr = ma.array(di.getVariance().getArray(), mask=maskArr)
153 diArr /= np.sqrt(varArr)
158 median = ma.extras.median(diArr)
161 data = ma.getdata(diArr[~diArr.mask])
162 iqr = np.percentile(data, 75.) - np.percentile(data, 25.)
165 chisq = np.sum(np.power(data, 2.))
171 variance = np.power(data, 2.).mean()
172 mseResids = bias**2 + variance
177 rchisq = chisq/(len(data) - 1 - dof)
180 D, prob = scipy.stats.kstest(data,
'norm')
182 A2, crit, sig = scipy.stats.anderson(data,
'norm')
184 if np.isinf(A2)
or np.isnan(A2):
186 except ZeroDivisionError:
194 return {
"mean": mean,
"stdev": stdev,
"median": median,
"iqr": iqr,
195 "D": D,
"prob": prob,
"A2": A2,
"crit": crit,
"sig": sig,
196 "rchisq": rchisq,
"mseResids": mseResids}
198 def apply(self, candidateList, spatialKernel, spatialBackground, dof=0):
199 """Evaluate the QA metrics for all KernelCandidates in the 200 candidateList; set the values of the metrics in their 201 associated Sources""" 202 for kernelCandidate
in candidateList:
203 source = kernelCandidate.getSource()
204 schema = source.schema
207 if kernelCandidate.getStatus() != afwMath.SpatialCellCandidate.UNKNOWN:
208 kType = getattr(diffimLib.KernelCandidateF,
"ORIG")
209 di = kernelCandidate.getDifferenceImage(kType)
210 kernelValues = kernelCandidate.getKernel(kType).getKernelParameters()
211 kernelValues = np.asarray(kernelValues)
213 lkim = kernelCandidate.getKernelImage(kType)
215 stdx, stdy =
calcWidth(lkim.getArray(), centx, centy)
221 metrics = {
"KCDiffimMean_LOCAL": localResults[
"mean"],
222 "KCDiffimMedian_LOCAL": localResults[
"median"],
223 "KCDiffimIQR_LOCAL": localResults[
"iqr"],
224 "KCDiffimStDev_LOCAL": localResults[
"stdev"],
225 "KCDiffimKSD_LOCAL": localResults[
"D"],
226 "KCDiffimKSProb_LOCAL": localResults[
"prob"],
227 "KCDiffimADA2_LOCAL": localResults[
"A2"],
228 "KCDiffimADCrit_LOCAL": localResults[
"crit"],
229 "KCDiffimADSig_LOCAL": localResults[
"sig"],
230 "KCDiffimChiSq_LOCAL": localResults[
"rchisq"],
231 "KCDiffimMseResids_LOCAL": localResults[
"mseResids"],
232 "KCKernelCentX_LOCAL": centx,
233 "KCKernelCentY_LOCAL": centy,
234 "KCKernelStdX_LOCAL": stdx,
235 "KCKernelStdY_LOCAL": stdy,
236 "KernelCandidateId_LOCAL": kernelCandidate.getId(),
237 "KernelCoeffValues_LOCAL": kernelValues}
239 key = schema[k].asKey()
240 setter = getattr(source,
"set"+key.getTypeString())
241 setter(key, metrics[k])
244 kType = getattr(diffimLib.KernelCandidateF,
"ORIG")
245 lkim = kernelCandidate.getKernelImage(kType)
251 skim = afwImage.ImageD(spatialKernel.getDimensions())
252 spatialKernel.computeImage(skim,
False, kernelCandidate.getXCenter(),
253 kernelCandidate.getYCenter())
255 stdx, stdy =
calcWidth(skim.getArray(), centx, centy)
258 sbg = spatialBackground(kernelCandidate.getXCenter(), kernelCandidate.getYCenter())
259 di = kernelCandidate.getDifferenceImage(sk, sbg)
265 bias = np.mean(skim.getArray())
266 variance = np.mean(np.power(skim.getArray(), 2.))
267 mseKernel = bias**2 + variance
271 metrics = {
"KCDiffimMean_SPATIAL": spatialResults[
"mean"],
272 "KCDiffimMedian_SPATIAL": spatialResults[
"median"],
273 "KCDiffimIQR_SPATIAL": spatialResults[
"iqr"],
274 "KCDiffimStDev_SPATIAL": spatialResults[
"stdev"],
275 "KCDiffimKSD_SPATIAL": spatialResults[
"D"],
276 "KCDiffimKSProb_SPATIAL": spatialResults[
"prob"],
277 "KCDiffimADA2_SPATIAL": spatialResults[
"A2"],
278 "KCDiffimADCrit_SPATIAL": spatialResults[
"crit"],
279 "KCDiffimADSig_SPATIAL": spatialResults[
"sig"],
280 "KCDiffimChiSq_SPATIAL": spatialResults[
"rchisq"],
281 "KCDiffimMseResids_SPATIAL": spatialResults[
"mseResids"],
282 "KCDiffimMseKernel_SPATIAL": mseKernel,
283 "KCKernelCentX_SPATIAL": centx,
284 "KCKernelCentY_SPATIAL": centy,
285 "KCKernelStdX_SPATIAL": stdx,
286 "KCKernelStdY_SPATIAL": stdy,
287 "KernelCandidateId_SPATIAL": kernelCandidate.getId()}
289 key = schema[k].asKey()
290 setter = getattr(source,
"set"+key.getTypeString())
291 setter(key, metrics[k])
293 def aggregate(self, sourceCatalog, metadata, wcsresids, diaSources=None):
294 """Generate aggregate metrics (e.g. total numbers of false 295 positives) from all the Sources in the sourceCatalog""" 296 for source
in sourceCatalog:
297 sourceId = source.getId()
298 if sourceId
in wcsresids:
301 coord, resids = wcsresids[sourceId]
302 key = source.schema[
"RegisterResidualBearing"].asKey()
303 setter = getattr(source,
"set"+key.getTypeString())
304 setter(key, resids[0])
305 key = source.schema[
"RegisterResidualDistance"].asKey()
306 setter = getattr(source,
"set"+key.getTypeString())
307 setter(key, resids[1])
308 key = source.schema[
"RegisterRefPosition"].asKey()
309 setter = getattr(source,
"set"+key.getTypeString())
311 coord.getDec().asRadians()))
313 metadata.add(
"NFalsePositivesTotal", len(diaSources))
317 for source
in diaSources:
318 refId = source.get(
"refMatchId")
319 srcId = source.get(
"srcMatchId")
324 if refId == 0
and srcId == 0:
326 metadata.add(
"NFalsePositivesRefAssociated", nRefMatch)
327 metadata.add(
"NFalsePositivesSrcAssociated", nSrcMatch)
328 metadata.add(
"NFalsePositivesUnassociated", nunmatched)
329 for kType
in (
"LOCAL",
"SPATIAL"):
330 for sName
in (
"KCDiffimMean",
"KCDiffimMedian",
"KCDiffimIQR",
"KCDiffimStDev",
331 "KCDiffimKSProb",
"KCDiffimADSig",
"KCDiffimChiSq",
332 "KCDiffimMseResids",
"KCDiffimMseKernel"):
333 if sName ==
"KCDiffimMseKernel" and kType ==
"LOCAL":
335 kName =
"%s_%s" % (sName, kType)
336 vals = np.array([s.get(kName)
for s
in sourceCatalog])
337 idx = np.isfinite(vals)
338 metadata.add(
"%s_MEAN" % (kName), np.mean(vals[idx]))
339 metadata.add(
"%s_MEDIAN" % (kName), np.median(vals[idx]))
340 metadata.add(
"%s_STDEV" % (kName), np.std(vals[idx]))
def addToSchema(self, inSourceCatalog)
std::shared_ptr< FrameSet > append(FrameSet const &first, FrameSet const &second)
Construct a FrameSet that performs two transformations in series.
def _calculateStats(self, di, dof=0.)
def __init__(self, nKernelSpatial)
A description of a field in a table.
def aggregate(self, sourceCatalog, metadata, wcsresids, diaSources=None)
def apply(self, candidateList, spatialKernel, spatialBackground, dof=0)
def calcWidth(arr, centx, centy)
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects...
A kernel created from an Image.