Set User range on TGraph not working for x-axis , but OK for y-axis

I am using ROOT version 6.30/08 on Ubuntu 20.
I draw several objects on a canvas (TGraph for data plus others for ellipse and lines). I want to set my own drawing limits using SetRangeUser. While the y axis scales correctly, the x-axis does not use the desired limits. I have read the connected topics in the forum, but I am not able the get it right. Here is my python code, which I execute via “pyroot mycode,py”

#define my limits for the plot
xlow = 120
xhigh = 220
ylow = 90
yhigh = 190

Create a ROOT TCanvas

c3 = ROOT.TCanvas(“c3”, “PCA Scatter Plot”, 2000, 1500)
c3.DrawFrame(xlow, ylow, xhigh, yhigh);
graphl = ROOT.TGraph(lenl, x_data, y_data)
graphl.SetTitle(“PCA components 1 vs 2”)
graphl.SetMarkerSize(msiz)
graphl.SetMarkerColor(4)
graphl.SetMarkerStyle(20)

Set titles and ranges for X and Y axes

graphl.GetXaxis().SetTitle(“Principal Component 1”)
graphl.GetYaxis().SetTitle(“Principal Component 2”)
graphl.Draw(“AP”)
graphl.GetXaxis().SetRangeUser(xlow, xhigh)
graphl.GetYaxis().SetRangeUser(ylow, yhigh)

Draw the error ellipse

ellipse_data.Draw(“L SAME”)
linex.Draw(“L SAME”)
liney.Draw(“L SAME”)
c3.SetGrid()
c3.Update()

All objects are drawn, but the x-scale is chosen by ROOT, ignoring my input.

Probably your limits are larger than the values in TGraph, and SetRangeUser cannot go “outside” those values. In that case, try with SetLimits instead (note that this is ok for TGraph, not so much for histograms). See

and

Dear Dastudillo,

indeed I like to set limits which are smaller/larger than the values of the data in TGraph. This is true for both the x and y axis. In addition I am drawing an ellipse defining a confidence interval (from a PCA analysis). Some regions of the ellipse indeed go beyond the data point from TGraph, so I want to use my own plotting intervals.

The problem was:

  • using ->GetXaxis()->SetRangeUser(xlow, xhigh) and ->GetYaxis()->SetRangeUser(ylow, yhigh) sets the correct limits for the y axis but not for the x axis

Can you provide a small piece of code we can run showing the problem ?

Dear Danilo,

yes, I will provide a stripped down sample code around noon. It will contain also two data files, one for the PCA data, and one for the confidence ellipse. I hope I can append these two files.

Best

Christian

1 Like

I see you are using c3.DrawFrame(xlow, ylow, xhigh, yhigh).
This should draw frame with necessary axes ranges.
After this you should draw graph as graphl.Draw(“P”) - excluding “A” from the option.

Hello Danilo,

if I use the DrawFrame option only (+ “A” instead of “AP”), it will not
display all the graphic elements, e.g. the axes and data points. The
ellipse is displayed.
Appended you find my code and the two files for data and ellipse. With
the present ranges (no DataFrame and only SetRange) it displays wrong x
and y scales.
I tested that with the option “SetRangeUser” and “SetLimits” all is fine.

Thanks for further comments,

best

Christian

(Attachment ErrEllipse.json is missing)

(attachments)

pca_Test.pdf (16.5 KB)
DPCAtest.py (4.58 KB)
PCAData.txt (13.2 KB)

You did not do what was suggested (plot the frame and then the graph without “A”). Cleaning up and reducing your script:

#-------------------------------------------------------------------------------
msiz = 1.3        # 0.4   / 1.3
bsiz = 4.4       # 1.65  / 4.4
#------------------------------------------------------------------------------

xlow = 120
xhigh = 220
ylow = 90
yhigh = 190
zlow = 340
zhigh = 420

x_data, y_data, z_data = array('d'), array('d'), array('d')

with open('PCAData.txt', mode='r', encoding='iso-8859-1') as csv_file:
    csv_reader = csv.reader(csv_file, delimiter=',')
    for row in csv_reader:
        #print("input : ", row)
        #print(" row[0]: ", row[0]," row[1]: ", row[1]," row[2]: ", row[2], )
        x_data.append(float(row[0]))
        y_data.append(float(row[1]))
        z_data.append(float(row[2]))

lenl = len(x_data)
print(" length of data array : ", lenl)

c3 = ROOT.TCanvas("c3", "PCA Scatter Plot", 200, 10, 800, 600)
c3.DrawFrame(xlow, ylow, xhigh, yhigh)

graphl = ROOT.TGraph(lenl)
for i in range(lenl):
    graphl.SetPoint(i, x_data[i], y_data[i])

graphl.Draw("P")  # "A" for axes, "L" for lines, "P" for markers
graphl.SetMarkerSize(msiz)
graphl.SetMarkerColor(4)
graphl.SetMarkerStyle(20)
graphl.SetTitle("PCA components 1 vs 2, Llamas")

graphl.GetXaxis().SetTitle("Principal Component 1")
graphl.GetYaxis().SetTitle("Principal Component 2")

# Ellipse
el1 = ROOT.TEllipse(145,175,25,40)
el1.Draw()  # no need for "L SAME"

c3.SetGrid()
c3.Update()

# Keep the canvas open
input("Press Enter to exit...")

gives this, as wanted:

Dear Danilo,

sorry, I just interchanged “A” and “P” in the “graphl.Draw(” ") statement. When I do it right, the display ranges come out correctly, using only the statement “DrawFrame(xlow,ylow,xhigh,yhigh)” and not “SetRange” etc. The trick then really is to not allow the “A” (=axis) as an argument in graphl.Draw() and only supply “P” for the data points. An elegant solution, which makes perfect sense, but not easy to guess for the casual user.

Thank you very much for your support!

Best

Christian

Dear Danilo,

sorry to bug you again in this matter: With the omission of “A” in the statement graphl(“P”) the scaling is perfect, but the title and axis labels are no more printed (I took exactly your code below). Any suggestion?

Thanks and best wishes.

Christian

To draw titles, do:

h = c3.DrawFrame(xlow, ylow, xhigh, yhigh, "graph title")
h.GetXaxis().SetTitle("Principal Component 1")
h.GetYaxis().SetTitle("Principal Component 2")

Thank you very much, it works !