File indexing completed on 2026-04-09 07:48:46
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021 """
0022
0023
0024 CFH random access for debugging::
0025
0026 delta:~ blyth$ ipython -i $(which cfh.py) -- /tmp/blyth/opticks/CFH/concentric/1/TO_BT_BT_BT_BT_SA/0/X
0027 Python 2.7.11 (default, Dec 5 2015, 23:51:51)
0028 Type "copyright", "credits" or "license" for more information.
0029
0030 IPython 1.2.1 -- An enhanced Interactive Python.
0031 ? -> Introduction and overview of IPython's features.
0032 %quickref -> Quick reference.
0033 help -> Python's own help system.
0034 object? -> Details about 'object', use 'object??' for extra details.
0035 /Users/blyth/opticks/ana/cfh.py /tmp/blyth/opticks/CFH/concentric/1/TO_BT_BT_BT_BT_SA/0/X
0036 ['/tmp/blyth/opticks/CFH/concentric/1/TO_BT_BT_BT_BT_SA/0/X']
0037 [2016-11-12 18:42:19,579] p4851 {/Users/blyth/opticks/ana/cfh.py:199} INFO - CFH.load from /tmp/blyth/opticks/CFH/concentric/1/TO_BT_BT_BT_BT_SA/0/X
0038
0039 In [1]: h
0040 Out[1]: X[0]
0041
0042 In [2]: h.bins
0043 Out[2]: array([-76.3726, -61.0981, -45.8235, -30.549 , -15.2745, 0. , 15.2745, 30.549 , 45.8235, 61.0981, 76.3726])
0044
0045
0046 """
0047 import os, sys, json, logging, numpy as np
0048 from opticks.ana.base import json_
0049 from opticks.ana.ctx import Ctx
0050 from opticks.ana.nbase import chi2
0051 from opticks.ana.abstat import ABStat
0052 log = logging.getLogger(__name__)
0053
0054
0055 class CFH(Ctx):
0056 """
0057 Persistable comparison histograms and chi2
0058 The members are numpy arrays and a single ctx dict
0059 allowing simple load/save.
0060
0061 * :google:`chi2 comparison of normalized histograms`
0062 * http://www.hep.caltech.edu/~fcp/statistics/hypothesisTest/PoissonConsistency/PoissonConsistency.pdf
0063 * ~/opticks_refs/PoissonConsistency.pdf
0064
0065 """
0066 NAMES = "lhabc".split()
0067
0068 @classmethod
0069 def load_(cls, ctx):
0070 if type(ctx) is list:
0071 return map(lambda _:cls.load_(_), ctx)
0072 elif type(ctx) is Ctx:
0073 h = CFH(ctx)
0074 h.load()
0075 return h
0076 else:
0077 log.warning("CFH.load_ unexpected ctx %s " % repr(ctx))
0078 return None
0079
0080 def __init__(self, *args, **kwa):
0081
0082 Ctx.__init__(self, *args, **kwa)
0083
0084 self._log = False
0085 self._ylim = None
0086
0087 def __call__(self, bn, av, bv, lab, c2cut=30, c2shape=False):
0088 """
0089 :param bn: bin edges array
0090 :param av: a values array
0091 :param bv: b values array
0092 :param lab: list of two labels for av and bv
0093 :param c2cut: a+b stat requirement to compute chi2
0094 :param c2shape: when True, normalize histogram counts before comparison
0095
0096 Called from AB.rhist
0097 """
0098
0099 na = len(av)
0100 nb = len(bv)
0101 nv = 0.5*float(na + nb)
0102
0103
0104
0105 ahis,_ = np.histogram(av, bins=bn)
0106 bhis,_ = np.histogram(bv, bins=bn)
0107
0108 ah = ahis.astype(np.float32)
0109 bh = bhis.astype(np.float32)
0110
0111 if c2shape:
0112
0113
0114 uah = ah*nv/float(na)
0115 ubh = bh*nv/float(nb)
0116 else:
0117 uah = ah
0118 ubh = bh
0119 pass
0120
0121 c2, c2n, c2c = chi2(uah, ubh, cut=c2cut)
0122
0123 assert len(ahis) == len(bhis) == len(c2)
0124 nval = len(ahis)
0125 assert len(bn) - 1 == nval
0126
0127 lhabc = np.zeros((nval,5), dtype=np.float32)
0128
0129 lhabc[:,0] = bn[0:-1]
0130 lhabc[:,1] = bn[1:]
0131 lhabc[:,2] = uah
0132 lhabc[:,3] = ubh
0133 lhabc[:,4] = c2
0134
0135 self.lhabc = lhabc
0136
0137 meta = {}
0138 meta['nedge'] = "%d" % len(bn)
0139 meta['nval'] = "%d" % nval
0140
0141 meta['c2cut'] = c2cut
0142 meta['c2n'] = c2n
0143 meta['c2c'] = c2c
0144 meta['la'] = lab[0]
0145 meta['lb'] = lab[1]
0146
0147 meta['c2_ymax'] = "10"
0148 meta['logyfac'] = "3."
0149 meta['linyfac'] = "1.3"
0150
0151 self.update(meta)
0152
0153
0154 @classmethod
0155 def c2per_(cls, hh):
0156 """
0157 :param hh: list of CFH instances
0158 :return c2per: combined chi2 float
0159
0160 ::
0161
0162 In [6]: hh = ab.hh
0163
0164 In [7]: c2sums = map(lambda h:h.chi2.sum(), hh )
0165 Out[7]: [11.125423, 0.0, 0.0, 5.8574519, 180.07062, 182.38904, 208.7128, 11.125423]
0166
0167 In [8]: c2nums = map(lambda h:h.c2n, hh )
0168 Out[8]: [19.0, 1.0, 1.0, 4.0, 222.0, 159.0, 218.0, 19.0]
0169
0170 In [12]: sum(c2sums)
0171 Out[12]: 599.28075361251831
0172
0173 In [13]: sum(c2nums)
0174 Out[13]: 643.0
0175
0176 In [14]: sum(c2sums)/sum(c2nums)
0177 Out[14]: 0.93200739286550283
0178
0179 """
0180 if type(hh) is CFH:
0181 hh = [hh]
0182 pass
0183 assert type(hh) is list
0184
0185 c2sums = map(lambda h:h.chi2.sum(), hh )
0186 c2nums = map(lambda h:h.c2n, hh )
0187
0188 s_c2sums = sum(c2sums)
0189 s_c2nums = sum(c2nums)
0190
0191 if s_c2nums > 0:
0192 c2per = s_c2sums/s_c2nums
0193 else:
0194 c2per = 0.
0195 pass
0196 return c2per
0197
0198
0199 ledg = property(lambda self:self.lhabc[:,0])
0200 hedg = property(lambda self:self.lhabc[:,1])
0201 ahis = property(lambda self:self.lhabc[:,2])
0202 bhis = property(lambda self:self.lhabc[:,3])
0203 chi2 = property(lambda self:self.lhabc[:,4])
0204
0205 def _get_bins(self):
0206 """
0207 Recompose bins from lo and hi edges
0208 """
0209 lo = self.ledg
0210 hi = self.hedg
0211
0212 bins = np.zeros(len(lo)+1, dtype=np.float32)
0213 bins[0:-1] = lo
0214 bins[-1] = hi[-1]
0215
0216 return bins
0217 bins = property(_get_bins)
0218
0219
0220 def _get_ndf(self):
0221 ndf = max(self.c2n - 1, 1)
0222 return ndf
0223 ndf = property(_get_ndf)
0224
0225 def _get_c2p(self):
0226 ndf = self.ndf
0227 c2p = self.chi2.sum()/ndf
0228 return c2p
0229 c2p = property(_get_c2p)
0230
0231 def _get_c2label(self):
0232 return "chi2/ndf %4.2f [%d]" % (self.c2p, self.ndf)
0233 c2label = property(_get_c2label)
0234
0235
0236 def _set_log(self, log_=True):
0237 self._log = log_
0238 def _get_log(self):
0239 return self._log
0240 log = property(_get_log, _set_log)
0241
0242 def _get_ylim(self):
0243 if self._ylim is None:
0244 ymin = 1 if self.log else 0
0245 yfac = self.logyfac if self.log else self.linyfac
0246 ymax = max(self.ahis.max(), self.bhis.max())
0247 self._ylim = [ymin,ymax*yfac]
0248 pass
0249 return self._ylim
0250
0251 def _set_ylim(self, _ylim):
0252 self._ylim = _ylim
0253 ylim = property(_get_ylim, _set_ylim)
0254
0255
0256 def _get_ctxstr(self, name, fallback="?"):
0257 return str(self.get(name,fallback))
0258
0259 def _get_ctxfloat(self, name, fallback="0"):
0260 return float(self.get(name,fallback))
0261
0262 def _get_ctxint(self, name, fallback="0"):
0263 return int(self.get(name,fallback))
0264
0265
0266
0267 la = property(lambda self:self._get_ctxstr("la"))
0268 lb = property(lambda self:self._get_ctxstr("lb"))
0269 qwn = property(lambda self:self._get_ctxstr("qwn"))
0270
0271 c2_ymax = property(lambda self:self._get_ctxfloat("c2_ymax"))
0272 logyfac = property(lambda self:self._get_ctxfloat("logyfac"))
0273 linyfac = property(lambda self:self._get_ctxfloat("linyfac"))
0274 c2n = property(lambda self:self._get_ctxfloat("c2n"))
0275 c2c = property(lambda self:self._get_ctxfloat("c2c"))
0276 c2cut = property(lambda self:self._get_ctxfloat("c2cut"))
0277
0278 nedge = property(lambda self:self._get_ctxint("nedge"))
0279 nval = property(lambda self:self._get_ctxint("nval"))
0280 irec = property(lambda self:self._get_ctxint("irec"))
0281
0282 def __repr__(self):
0283 return "%s[%s]" % (self.qwn,self.irec)
0284
0285 def ctxpath(self):
0286 return self.path("ctx.json")
0287
0288 def paths(self):
0289 return [self.ctxpath()] + map(lambda name:self.path(name+".npy"), self.NAMES)
0290
0291 def exists(self):
0292 if self.seq0 is None:
0293 log.warning("CFH.exists can only be used with single line selections")
0294 return False
0295
0296 paths = self.paths()
0297 a = np.zeros(len(paths), dtype=np.bool)
0298 for i,path in enumerate(paths):
0299 a[i] = os.path.exists(path)
0300 return np.all(a[i] == True)
0301
0302 def save(self):
0303 if self.seq0 is None:
0304 log.warning("CFH.save can only be used with single line selections")
0305 return
0306
0307 dir_ = self.dir
0308 log.debug("CFH.save to %s " % dir_)
0309 if not os.path.exists(dir_):
0310 os.makedirs(dir_)
0311 json.dump(dict(self), file(self.ctxpath(),"w") )
0312 for name in self.NAMES:
0313 np.save(self.path(name+".npy"), getattr(self, name))
0314 pass
0315
0316
0317 def load(self):
0318 if self.seq0 is None:
0319 log.warning("CFH.load can only be used with single line selections")
0320 return
0321
0322 dir_ = self.dir
0323 log.debug("CFH.load from %s " % dir_)
0324
0325 exists = os.path.exists(dir_)
0326 if not exists:
0327 log.fatal("CFH.load non existing dir %s " % (dir_) )
0328
0329 assert exists, dir_
0330
0331 js = json_(self.ctxpath())
0332 k = map(str, js.keys())
0333 v = map(str, js.values())
0334
0335 self.update(dict(zip(k,v)))
0336
0337 for name in self.NAMES:
0338 setattr(self, name, np.load(self.path(name+".npy")))
0339 pass
0340
0341
0342
0343 def test_load():
0344 ctx = {'det':"concentric", 'tag':"1", 'qwn':"X", 'irec':"5", 'seq0':"TO_BT_BT_BT_BT_DR_SA" }
0345 h = CFH_.load(ctx)
0346
0347
0348
0349 if __name__ == '__main__':
0350 from opticks.ana.main import opticks_main
0351 ok = opticks_main()
0352 print(ok)
0353
0354 import matplotlib.pyplot as plt
0355 plt.rcParams["figure.max_open_warning"] = 200
0356 plt.ion()
0357
0358
0359 from opticks.ana.ab import AB
0360 from opticks.ana.cfplot import one_cfplot, qwns_plot
0361 print(ok.nargs)
0362
0363
0364 if ok.rehist:
0365 ab = AB(ok)
0366 else:
0367 ab = None
0368 pass
0369
0370
0371 st = ABStat.load(ok)
0372
0373 if ok.chi2sel:
0374 reclabs = st.reclabsel()
0375 elif len(ok.nargs) > 0:
0376 reclabs = [ok.nargs[0]]
0377 else:
0378 reclabs = ["[TO] AB",]
0379 pass
0380
0381 n_reclabs = len(reclabs)
0382 log.info(" n_reclabs : %d " % (n_reclabs))
0383
0384 n_limit = 50
0385 if n_reclabs > n_limit:
0386 log.warning("too many reclabs truncating to %d" % n_limit )
0387 reclabs = reclabs[:n_limit]
0388 pass
0389
0390 for reclab in reclabs:
0391
0392 ctx = Ctx.reclab2ctx_(reclab, det=ok.det, tag=ok.tag)
0393
0394 st[st.st.reclab==reclab]
0395 suptitle = st.suptitle
0396
0397 ctxs = ctx.qsub()
0398 assert len(ctxs) == 8 , ctxs
0399 log.info(" %s " % suptitle)
0400
0401 if ok.rehist:
0402 hh = ab.rhist_(ctxs, rehist=True)
0403 else:
0404 hh = CFH.load_(ctxs)
0405 pass
0406
0407 if len(hh) == 1:
0408 one_cfplot(ok, hh[0])
0409 else:
0410 qwns_plot(ok, hh, suptitle )
0411 pass
0412 pass
0413
0414
0415