Passing an array from PyROOT to ROOT

I am writing a PyROOT script to create a histogram showing the trigger rate per run.

Since I only have a finite number of runs & they are non-consecutive, I would like the bins in the histogram to exactly correspond to the run numbers. As I understand it, I should be able to do this by creating a histogram using this constructor:

Unfortunately, when I pass the construtor a Python array for the parameter “xbins”, I get the following errors:

[quote]Error in TAxis::TAxis::Set: bins must be in increasing order
Error in TAxis::TAxis::Set: bins must be in increasing order
TCanvas::MakeDefCanvas: created default TCanvas with name c1
Error in TCanvas::Range: illegal world coordinates range: x1=167472.001386, y1=-0.131250, x2=-18608.001386, y2=1.181250
Error in TCanvas::RangeAxis: illegal axis coordinates range: xmin=148864.000000, ymin=0.000000, xmax=0.000000, ymax=1.050000
Error in TGraphPainter::PaintGraphHist: X must be in increasing order[/quote]

What am I doing wrong? How does one correctly pass the xbins parameter from a Python script to ROOT?

Below is a simpler version of my script (with many fewer runs), which I used to produce the exact errors above.

[code]#! /usr/bin/env python
import sys
from ROOT import *
from array import *

gSystem.Load(“libStoppedHSCPAnalysis”)

Open root file, get TTree

infileName = "hltanalysis.root"
infile = TFile(infileName)
tree = infile.Get(“stoppedHSCPTree/StoppedHSCPTree”)

Create histogram of trigger rate vs. run#

runArray = array(‘d’,[148864, 149291, 146436, 148822, 149063])
histogram = TH1F(“hist”, “HLT Rate”, len(runArray), runArray)

Set bin labels to match run numbers

histogram.SetBit(TH1.kCanRebin);
for i in xrange(1,len(runArray)):
histogram.GetXaxis().SetBinLabel(i,str(runArray[i]))

Draw Histogram

tree.Draw(“events.run>>hist”)
[/code]

Hi rodenm,

[quote=“rodenm”]

[code]

Create histogram of trigger rate vs. run#

runArray = array(‘d’,[148864, 149291, 146436, 148822, 149063])
histogram = TH1F(“hist”, “HLT Rate”, len(runArray), runArray)
[/code][/quote]
Two problems:
[ul]
[li] You’ll kick yourself, but your bin boundaries aren’t in ascending order i.e. runArray[2] > runArray[1][/li]
[li] You need to define the top bin edge, so the number of bins is len(runArray)-1[/li][/ul]

Without explicitly fixing the hardcoded list, this example works:

# Create histogram of trigger rate vs. run# 
bins = sorted([148864, 149291, 146436, 148822, 149063])
runArray = array('d',bins)
histogram = TH1F("hist", "HLT Rate", len(runArray)-1, runArray)
  1. Wow, example fail! The longer list in my real script gets sorted by I carelessly used the unsorted version to make the post above.

  2. THANK YOU. I did not realize that it was defining the top bin edge. That may have taken years to figure out.

Follow-up question: Now my histogram is binned correctly & the labels are set, when I draw the histogram, it looks terrible. ROOT is trying space the bins out according to their value, where as I would like them evenly spaced along the x-axis (such that each bin takes up the same amount of horizontal space). Ideas?

Ah, this is a different issue, I realise I had assumed that you were wanting some sort of versus-time equivalent, and admit I didn’t draw it to see if it was sensible. In this case, you actually do want N bins for N runs.

I don’t know if there is a better way, but I’d do it like so:

#!/usr/bin/env python
from ROOT import TH1F

bins = sorted([148864, 149291, 146436, 148822, 149063])

# Create with the number of bins you want, and some arbitrary range (0-1 here)
hist = TH1F("hist", "HLT Rate", len(bins), 0, 1)

# Fill some random values so we can see
hist.FillRandom("gaus")

# Set the bin labels to the text that we want
for bin in range(len(bins)):
  binlabel = str(bins[bin])
  # Set the bin label; +1 to convert between sane and root index schemes
  hist.GetXaxis().SetBinLabel(bin+1, binlabel)

you have to be a little careful when filling, but I don’t know another way to do this. Unfortunately, it’d probably get a little crowded with lots of run numbers; AFAIK there is no way to get ROOT to rotate the labels without setting the size to zero and using your own TLatex’s.

Oops, I realised that I was showing you how to do something that you had partially done in the first post; I ignored the rest of the code and concentrated on the error.

Without looping over the tree and manually selecting the events, I’d probably do something like this to get the data out and draw it:

[code]#!/usr/bin/env python
from ROOT import TH1F, TFile
from array import array

gSystem.Load(“libStoppedHSCPAnalysis”)

Open root file, get TTree

infileName = "hltanalysis.root"
infile = TFile(infileName)
tree = infile.Get(“stoppedHSCPTree/StoppedHSCPTree”)

The bins we want to use

bins = sorted([148864, 149291, 146436, 148822, 149063])
Nbins = len(bins)

Make the array bounds, but add one extra run for the last bin edge

runArray = array(‘d’,bins + [bins[-1]+1])

Now build the histogram with these bins

hist = TH1F(“hist”, “HLT Rate Temporary”, Nbins, runArray)

Fill the histogram, without drawing

tree.Draw(“events.run>>hist”, “”, “goff”)

Create a display histogram with the number of bins you want,

and some arbitrary range (0-1 here)

displayhist = TH1F(“displayhist”, “HLT Rate”, Nbins, 0, 1)

Loop over every bin

for bin in range(Nbins):

Copy like-for-like from the temporary storage

displayhist.SetBinContent(bin+1, hist.GetBinContent(bin+1) )

Set the bin label; +1 to convert between sane and root index schemes

hist.GetXaxis().SetBinLabel(bin+1, str(bins[bin]))

Finally, draw the display histogram

displayhist.Draw()
[/code]

In essence; use the run-x binned histogram to fill, and then copy these values over to a histogram with linear bin spacing for the drawing. I haven’t tested it, because I don’t have your data files; but if it has a small error or two I’m sure you can weed them out - the general principle should be here.