Style File と Data File を分離した QDP-like な ROOT での作図

単なる遊びです。Style file にデータの表示形式を書き、データ辞退は Data file に書いて ROOT で図を作る簡易スクリプト。

#!/usr/bin/env python
# Teruaki Enoto 2012-02-03
from optparse import OptionParser
import array
import shlex
import ROOT

stylefile_def = "/Users/enoto/work/program/root/example/PlotXY/stylefile.txt"

class PlotXY():
    def __init__(self, data_array, stylefile, outputpdf):
        self.data_array = data_array
        self.stylefile  = stylefile
        self.outputpdf = outputpdf

    def show(self):
        print "data_array = ", self.data_array
        print "stylefile = ", self.stylefile

    def init_par(self):
        ### initialize parameters 
        self.pars = {"Title" : "str",
                     "Xmin" : "float",
                     "Xmax" : "float",
                     "Xtitle" : "str",
                     "Xlog" : "bool", 
                     "Ymin" : "float",
                     "Ymax" : "float",
                     "Ytitle" : "str",
                     "Ylog" : "bool", 
                     "Format" : "list", 
                     "Style" : " list",
                     "Can_X0" : "int",
                     "Can_Y0" : "int",
                     "Can_Xwid" : "int",
                     "Can_Ywid" : "int",
                     "TitleBorderSize" : "int",
                     "TitleAlign" : "int",
                     "TextFont" : "int",
                     "FrameLineWidth" : "int",
                     "EndErrorSize" : "float"
                     }
        for par in self.pars:
            exec("self.%s = %s('%s')" % (par, self.pars[par], '0'))

    def set_style(self):
        ### open file 
        try:
            f = open(self.stylefile)
        except IOError, err:
            print 'Error: cannot read stylefile (%s):' % (self.stylefile,), err
            quit()

        ### get file content 
        lex = shlex.shlex(f,posix=True)
        lex.whitespace += "="
        lex.whitespace_split = True

        ### read and set parameters
        while True:
            s = lex.get_token()
            if s!=lex.eof:
                val = lex.get_token()
                print s, val
                if s == "Style" or s == "Format":
                    cmd = "self.%s = %s(%s)" % (s, self.pars[s], val)
                elif s == "Xlog" or s == "Ylog":
                    if val == "True":
                        cmd = "self.%s = %s(%s)" % (s, self.pars[s], True)
                    elif val == "False":
                        cmd = "self.%s = %s(%s)" % (s, self.pars[s], False)
                    else:
                        print "Error:: Xlog/Ylog must be bool (True or False)"
                else:
                    cmd = "self.%s = %s('%s')" % (s, self.pars[s], val)
                exec(cmd)
            elif s==lex.eof:
                break
            else:
                print "Error: unexpected error in reading the parfile."
        f.close()

    def read_all(self):        
        self.graph = []
        id = 0 
        for file in self.data_array:
            if self.Format[id] == 0:
                x, y = self.read(file)
                self.graph.append(ROOT.TGraph(len(x), x, y))

            elif self.Format[id] == 1:
                x, y, xe, ye = self.read_err(file)
                self.graph.append(ROOT.TGraphErrors(len(x),
                                                    x, y,
                                                    xe, ye))

            elif self.Format[id] == 2:
                x, y, xeh, xel, yeh, yel = self.read_asymerr(file)
                self.graph.append(ROOT.TGraphAsymmErrors(len(x),
                                                         x, y,
                                                         xeh, xel,
                                                         yeh, yel))
                
            else:
                print "Error:: Format error in style file"
                quit()
            id += 1

    def read(self, file):
        print "...reading file %s..." % file
        x = array.array('f')
        y = array.array('f')
        for line in open(file):
            col = line.split()
            x.append(float(col[0]))
            y.append(float(col[1]))
        return x, y

    def read_err(self, file):
        print "...reading file %s..." % file
        x = array.array('f')
        y = array.array('f')
        xe = array.array('f')
        ye = array.array('f')
        for line in open(file):
            col = line.split()
            x.append(float(col[0]))
            y.append(float(col[1]))
            xe.append(float(col[2]))
            ye.append(float(col[3]))
        return x, y, xe, ye

    def read_asymerr(self, file):
        print "...reading file %s..." % file
        x = array.array('f')
        y = array.array('f')
        xeh = array.array('f')
        xel = array.array('f')
        yeh = array.array('f')
        yel = array.array('f')
        for line in open(file):
            col = line.split()
            x.append(float(col[0]))
            y.append(float(col[1]))
            xeh.append(float(col[2]))
            xel.append(float(col[3]))
            yeh.append(float(col[4]))
            yel.append(float(col[5]))
        return x, y, xeh, xel, yeh, yel

    def plot(self):
        can = ROOT.TCanvas("c1","A Simple Graph with error bars",
                           self.Can_X0, self.Can_Y0, self.Can_Xwid, self.Can_Ywid)
        print self.Xlog, self.Ylog
        can.SetLogx(self.Xlog)
        can.SetLogy(self.Ylog)

        # --- Optional Gstyles ---
        ROOT.gStyle.SetTitleBorderSize(self.TitleBorderSize)
        ROOT.gStyle.SetTitleAlign(self.TitleAlign)
        ROOT.gStyle.SetTextFont(self.TextFont)
        ROOT.gStyle.SetFrameLineWidth(self.FrameLineWidth)
        ROOT.gStyle.SetEndErrorSize(self.EndErrorSize)

        id = 0
        for graph in self.graph:
            if id == 0:
                graph.Draw("AP")
            else:
                graph.Draw("P")

            graph.SetMarkerColor(self.Style[id][0])
            graph.SetLineColor(self.Style[id][0])
            graph.SetMarkerStyle(self.Style[id][1])  
            graph.SetMarkerSize(self.Style[id][2])
            id += 1

        # --- Range & Title Set ---
        self.graph[0].SetTitle(self.Title)
        self.graph[0].GetXaxis().SetLimits(self.Xmin, self.Xmax)
        self.graph[0].GetXaxis().SetTitle(self.Xtitle)
        self.graph[0].SetMinimum(self.Ymin)
        self.graph[0].SetMaximum(self.Ymax)
        self.graph[0].GetYaxis().SetTitle(self.Ytitle)

        can.Print(self.outputpdf)
        
    def run(self):
        self.show()
        self.init_par()
        self.set_style()
        self.read_all()
        self.plot()

#############################
if __name__=="__main__":
#############################

    # --- Interface to command line
    parser = OptionParser()
    parser.add_option("-s", "--stylefile", dest="stylefile", default=stylefile_def, 
                      type="string", action="store", help="style file name")
    parser.add_option("-o", "--outputpdf", dest="outputpdf", default="out.pdf",
                      type="string", action="store", help="output pdf file name")
    (opt, arg) = parser.parse_args()

    # --- run main ---
    if len(arg) == 0:
        print "Error:: No input data_array."
        quit()
    else:
        plot = PlotXY(arg, stylefile=opt.stylefile, outputpdf=opt.outputpdf)
        plot.run()
    quit()

ほんで、Style file

Title = "Title"
Xmin = 0.8
Xmax = 10.0
Xtitle = "Xtitle"
Xlog = True
Ymin = 0.1
Ymax = 4.0
Ytitle = "Ytitle"
Ylog = True
#-------- Format file ---------
# Format 
# 0 : xy
# 1 : xy with x, y errors
# 2 : xy with x, y asimerr
# Style [color, marker, size]
Format = [2,1]
Style = [[2,20,1.1],[3,22,1.1]]
#--------- Other Format ----------
Can_X0 = 200
Can_Y0 = 10
Can_Xwid = 580
Can_Ywid = 450
TitleBorderSize = 0
TitleAlign = 13
TextFont = 132
FrameLineWidth = 1
EndErrorSize = 0.8

Data file は

3.0 2.0 0.2 0.4 0.2 0.1 
5.0 2.0 0.1 0.8 0.1 0.2 
3.4 2.5 0.1 0.7 0.2 0.1
5.6 0.3 0.1 0.8 0.3 0.2

で、図