Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-04-09 07:48:46

0001 #!/usr/bin/env python
0002 #
0003 # Copyright (c) 2019 Opticks Team. All Rights Reserved.
0004 #
0005 # This file is part of Opticks
0006 # (see https://bitbucket.org/simoncblyth/opticks).
0007 #
0008 # Licensed under the Apache License, Version 2.0 (the "License"); 
0009 # you may not use this file except in compliance with the License.  
0010 # You may obtain a copy of the License at
0011 #
0012 #   http://www.apache.org/licenses/LICENSE-2.0
0013 #
0014 # Unless required by applicable law or agreed to in writing, software 
0015 # distributed under the License is distributed on an "AS IS" BASIS, 
0016 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
0017 # See the License for the specific language governing permissions and 
0018 # limitations under the License.
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         # transients, not persisted
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         #log.info("CFH.__call__ na %d nb %d nv %7.2f " % (na,nb,nv))
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             # shape comparison, normalize bin counts to average 
0113             #log.info("c2shape comparison")
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]   # left edges 
0130         lhabc[:,1] = bn[1:]     # right edges
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     #seq0 = property(lambda self:self.ctx.get("seq0", None))
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    # default is 20
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     ## only reload evts for rehisting
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]   # sets a single line slice
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