def getChargeSystematic(analysis,channel,runPeriod,**kwargs):
    '''A function to simplify plotting multiple channels and run periods.'''
    mass = kwargs.pop('mass',500)
    myCut = kwargs.pop('myCut','1')
    scaleFactor = kwargs.pop('scaleFactor','event.pu_weight*event.lep_scale*event.trig_scale')
    loglevel = kwargs.pop('loglevel','INFO')
    for key, value in kwargs.iteritems():
        logger.warning("Unrecognized parameter '" + key + "' = " + str(value))
        return 0

    logging.info("%s:%s:%iTeV: Mass: %i" % (analysis,channel,runPeriod,mass))
    numleps = {
        'Hpp2l': 2,
        'Z'    : 2,
        'TT'   : 2,
        'WZ'   : 3,
        'WZ_W' : 1,
        'WZ_Dijet': 1,
        'Hpp3l': 3,
        'Hpp4l': 4,
    }
    nl = numleps[analysis]
    ntuples = 'ntuples/%s_%sTeV_%s' % (analysis,runPeriod,channel)
    saves = '%s_%s_%sTeV' % (analysis,channel,runPeriod)
    sigMap = getSigMap(nl,mass)
    intLumiMap = getIntLumiMap()
    channelBackground = getChannelBackgrounds(runPeriod)

    finalStates, leptons = getChannels(nl)
    mergeDict = getMergeDict(runPeriod)
    cutFlowMap = {}
    cutFlowMap[channel] = defineCutFlowMap(channel,finalStates,mass)

    plotter = Plotter(channel,ntupleDir=ntuples,saveDir=saves,period=runPeriod,mergeDict=mergeDict,scaleFactor=scaleFactor,loglevel=loglevel,rootName='charge_sys')
    plotter.initializeBackgroundSamples([sigMap[runPeriod][x] for x in channelBackground[channel]])
    plotter.initializeSignalSamples([sigMap[runPeriod]['Sig']])
    plotter.initializeDataSamples([sigMap[runPeriod]['data']])
    plotter.setIntLumi(intLumiMap[runPeriod])

    systMap = {}
    for numElec in range(nl+1):
        logging.info('%s:%s:%iTeV: Num electrons: %i' % (analysis, channel, runPeriod, numElec))
        systMap[numElec] = {}
        states = [x for x in finalStates if x.count('e')==numElec]
        chanCuts = ['channel=="{0}"'.format(x) for x in states]
        chanCut = ' || '.join(chanCuts)
        cut = '{0} && ({1})'.format(myCut,chanCut)
        sig_num = plotter.getSignalEntries(cut,customScale='event.pu_weight*event.lep_scale*event.trig_scale*event.charge_uncertainty')
        bg_num = plotter.getBackgroundEntries(cut,customScale='event.pu_weight*event.lep_scale*event.trig_scale*event.charge_uncertainty')
        data_num = plotter.getDataEntries(cut,customScale='charge_uncertainty')
        sig_den = plotter.getSignalEntries(cut)
        bg_den = plotter.getBackgroundEntries(cut)
        data_den = plotter.getDataEntries(cut)
        sig_syst = sig_num/sig_den if sig_den else 1.
        bg_syst = bg_num/bg_den if bg_den else 1.
        data_syst = data_num/data_den if data_den else 1.
        systMap[numElec]['sig'] = sig_syst
        systMap[numElec]['bg'] = bg_syst
        systMap[numElec]['data'] = data_syst

    return systMap
def main(argv=None):
    loglevel = getattr(logging,'INFO')
    logging.basicConfig(format='%(asctime)s.%(msecs)03d %(levelname)s %(name)s: %(message)s', level=loglevel, datefmt='%Y-%m-%d %H:%M:%S')
    logger = logging.getLogger(__name__)
    nl = 2
    mass = 500
    analysis = 'Hpp2l'
    runPeriod = 8
    channel = 'Charge'
    ntuples = 'ntuples/%s_%iTeV_%s' % (analysis,runPeriod,channel)
    saves = '%s_%s_%sTeV' % (analysis,channel,runPeriod)
    sigMap = getSigMap(nl,mass)
    intLumiMap = getIntLumiMap()
    channelBackground = getChannelBackgrounds(runPeriod)
    finalStates, leptons = getChannels(nl)
    mergeDict = getMergeDict(runPeriod)
    logger.info('Load plotter')
    plotter = Plotter(channel,ntupleDir=ntuples,saveDir=saves,period=runPeriod,mergeDict=mergeDict,rootName='chargeId')
    plotter.initializeBackgroundSamples([sigMap[runPeriod]['Z']])
    #plotter.initializeBackgroundSamples([sigMap[runPeriod][x] for x in channelBackground[channel]])
    plotter.initializeDataSamples([sigMap[runPeriod]['data']])
    plotter.setIntLumi(intLumiMap[runPeriod])
    plotMode = 'plotMCData'
    plotMethod = getattr(plotter,plotMode)
    results = {'e': {}, 'm': {}}
    myCut = 'finalstate.met<20.'
    samesign = 'l1.Chg==l2.Chg && {0}'.format(myCut)
    oppsign = 'l1.Chg!=l2.Chg && {0}'.format(myCut)
    logger.info('Calculate values')
    ptBins = [10.,20.,30.,40.,60.,100.,200.]
    etaBins = {
        'e': [0,1.479,2.5],
        'm': [0,1.2,2.4],
    }
    for l in ['e']:
        flv = 'z1Flv=="{0}" && fabs(z1.mass-91.1876)<10.'.format(l+l)
        results[l] = {'ss':{},'os':{},'unc':{},}

        for p in range(len(ptBins)-1):
            logger.info('{0}: pT {1:f}-{2:f}'.format(l,ptBins[p],ptBins[p+1]))
            # tag first lepton, probe second, both in same eta region
            ptcut = 'l2.Pt>={0:f} && l2.Pt<{1:f}'.format(ptBins[p],ptBins[p+1])

            for e in range(len(etaBins[l])-1):
                logger.info('{0}: eta {1:f}-{2:f}'.format(l,etaBins[l][e],etaBins[l][e+1]))
                etacut = 'fabs(l1.Eta)>={0:f} && fabs(l1.Eta)<{1:f} && fabs(l2.Eta)>={0:f} && fabs(l2.Eta)<{1:f}'.format(etaBins[l][e],etaBins[l][e+1])

                mc = 'mc{0}{1}'.format(p,e)
                data = 'data{0}{1}'.format(p,e)
                thisCut = '{0} && {1} && {2} && {3}'.format(samesign,flv,ptcut,etacut)
                results[l]['ss'][mc] = plotter.getBackgroundEntries(thisCut,doError=True)
                results[l]['ss'][data] = plotter.getDataEntries(thisCut,doError=True)
                logger.info('{0}: Same sign: MC: {1:f} +/- {2:f}; Data: {3:f} +/- {4:f}'.format(l,results[l]['ss'][mc][0],results[l]['ss'][mc][1],results[l]['ss'][data][0],results[l]['ss'][data][1]))
                plotMethod('z1.mass', [60,60,120],   'z1Mass_samesign_{0}_{1}_{2}'.format(l,p,e),      yaxis='Events/1.0 GeV', xaxis='M_{\\ell^{+}\\ell^{+}} (GeV)',     legendpos=43,logy=0,cut=thisCut)

                thisCut = '{0} && {1} && {2} && {3}'.format(oppsign,flv,ptcut,etacut)
                results[l]['os'][mc] = plotter.getBackgroundEntries(thisCut,doError=True)
                results[l]['os'][data] = plotter.getDataEntries(thisCut,doError=True)
                logger.info('{0}: Opposite sign: MC: {1:f} +/- {2:f}; Data: {3:f} +/- {4:f}'.format(l,results[l]['os'][mc][0],results[l]['os'][mc][1],results[l]['os'][data][0],results[l]['os'][data][1]))
                plotMethod('z1.mass', [60,60,120],   'z1Mass_oppositesign_{0}_{1}_{2}'.format(l,p,e),  yaxis='Events/1.0 GeV', xaxis='M_{\\ell^{+}\\ell^{-}} (GeV)',     legendpos=43,logy=0,cut=thisCut)

                mcnum = results[l]['ss'][mc]
                mcdenom = getSum(results[l]['ss'][mc],results[l]['os'][mc])
                mcratio = getRatio(mcnum,mcdenom)
                results[l]['unc'][mc] = mcratio
                datanum = results[l]['ss'][data]
                datadenom = getSum(results[l]['ss'][data],results[l]['os'][data])
                dataratio = getRatio(datanum,datadenom)
                results[l]['unc'][data] = dataratio
                ratio = getRatio(results[l]['unc'][data],results[l]['unc'][mc])
                logger.info('{0}: Uncertainty: MC: {1:f} +/- {2:f}; Data: {3:f} +/- {4:f}; Data/MC: {5:f} +/- {6:f}'.format(l,results[l]['unc'][mc][0],results[l]['unc'][mc][1],results[l]['unc'][data][0],results[l]['unc'][data][1],ratio[0],ratio[1]))

    print json.dumps(results,sort_keys=True,indent=4)