root icon indicating copy to clipboard operation
root copied to clipboard

Graph: non correct display of error bars when exceeding y-range

Open romainbqt opened this issue 1 year ago • 6 comments

Check duplicate issues.

  • [ ] Checked for duplicates

Description

Dear ROOT developpers,

I would like to report some bugs that I observed when displaying some TGraph errors (please find the reproducers code provided below). Here I used TGraphAsymmErrors.

  • The first issue testErrorGraph_v1 is I am creating a frame to display a graph with error but controling the y-range for the display (see screen shot below).

    • The points have increasing errors bars and some should go beyond that y-range (starting from x=4) however the end of error bars are displayed on the x-bottom and top axis. So it looks like it is the total error bar while it is indeed not. It would be nice to not have those vertical lines if the error bars exceed the y-range used for the plot.

    • For the rightest most point (x=9) I have put the data point on purpose to be below the ymin value and you can see the weird display too.

    image

  • The second issue testErrorGraph_v2 is related to a very weird interplay between TPad::SetBottomMargin() function and using TPad::SetLogy() function

    • If one sets the bottom margin to be 0 with pad.SetBottomMargin(0) here is the plot
      • Again the error bars starting from x>=3 should go beyond ymin=0.1.
      • The last point which I set to be negative is displayed on the x-axis while it should indeed not be displayed since ymin= 0.1.

    image

    • If one sets the bottom margin of the pad to be different from 0 with e.g. pad.SetBottomMargin(0.1), notice the last point at x=9 disappear with a white space. If I increase the marker size you can see that this white space increases as shown in the two screen shots below image
      image

--> I believe the best would be to not have that point or white space displayed in any case

Many thanks in advance for the fix that would be super helpful, In the meantime I have found some very hacky workaround (not very useful here I just played with several ways of displaying the information to arrive to what I want), Best, Romain

Reproducer

Below are the two codes that allow reprocuding the errors

testErrorGraph_v1

import ROOT 
from array import array

ROOT.gROOT.SetBatch(True)
ROOT.gStyle.SetEndErrorSize(10)

nPoints = 10
# Set ymin and ymax on purpose so that some error bars should be go beyon this range 
ymax = 4
ymin = 0

# Data x, y
data_x = array('d', list(range(nPoints)))
data_y = array('d', [2.]*nPoints)

#  x and y up/down errors 
# Increasing y-error for each points
unc_y_lo = array('d', [0.5*(i+1) for i in range(nPoints)])
unc_y_hi = array('d', [0.5*(i+1) for i in range(nPoints)])
unc_x_lo = array('d', [0]*nPoints)
unc_x_hi = array('d', [0]*nPoints)

# Make last point be out of range w.r.t to the zoom 
data_y[-1] = ymin-1

canv = ROOT.TCanvas("c","c")

canv.cd()
pad = ROOT.TPad("pad", "pad", 0.1, 0.1, 0.9, 0.9)
pad.Draw()
pad.cd()

xminFrame = min(data_x) - 0.5 
xmaxFrame = max(data_x) + 0.5 
yminFrame = ymin
ymaxFrame = ymax 

drawframe = pad.DrawFrame(xminFrame, yminFrame, xmaxFrame, ymaxFrame)

gr = ROOT.TGraphAsymmErrors(nPoints, data_x, data_y, unc_x_lo, unc_x_hi, unc_y_lo, unc_y_hi)
gr.SetMarkerColor(2)
gr.SetLineColor(ROOT.kRed)
gr.SetMarkerStyle(20)
gr.SetMarkerColor(ROOT.kBlue)
gr.SetMarkerSize(1.5)
gr.SetLineWidth(3)
gr.Draw("same PE0")

canv.Print("testErrorGraph_v1.pdf")

testErrorGraph_v2

import ROOT 
from array import array

ROOT.gROOT.SetBatch(True)
ROOT.gStyle.SetEndErrorSize(10)

nPoints = 10
# Set ymin and ymax on purpose so that some error bars should be go beyon this range 
ymax = 4
# Set ymin > 0 since here we use log scale 
ymin = 0.1

# Data x, y
data_x = array('d', list(range(nPoints)))
data_y = array('d', [2.]*nPoints)

#  x and y up/down errors 
# Increasing y-error for each points
unc_y_lo = array('d', [0.5*(i+1) for i in range(nPoints)])
unc_y_hi = array('d', [0.5*(i+1) for i in range(nPoints)])
unc_x_lo = array('d', [0]*nPoints)
unc_x_hi = array('d', [0]*nPoints)

# Make last point negative 
data_y[-1] = -1

canv = ROOT.TCanvas("c","c")

canv.cd()
pad = ROOT.TPad("pad", "pad", 0.1, 0.1, 0.9, 0.9)
pad.Draw()
pad.cd()

# Make the plot with bottom margin = 0 and something different than 0 
# observe the difference for the last point 
pad.SetBottomMargin(0.)
#pad.SetBottomMargin(0.1)
pad.SetLogy()

xminFrame = min(data_x) - 0.5 
xmaxFrame = max(data_x) + 0.5 
yminFrame = ymin
ymaxFrame = ymax 

drawframe = pad.DrawFrame(xminFrame, yminFrame, xmaxFrame, ymaxFrame)

gr = ROOT.TGraphAsymmErrors(nPoints, data_x, data_y, unc_x_lo, unc_x_hi, unc_y_lo, unc_y_hi)
gr.SetMarkerColor(2)
gr.SetLineColor(ROOT.kRed)
gr.SetMarkerStyle(20)
gr.SetMarkerColor(ROOT.kBlue)
gr.SetMarkerSize(3)
gr.SetLineWidth(3)
gr.Draw("same PE0")

canv.Print("testErrorGraph_v2.pdf")

ROOT version

I think all versions are affected but I tested it with ROOT 6.30/02 and 6.30/00

Installation method

Build from source

Operating system

Linux, MacOs

Additional context

No response

romainbqt avatar Jul 19 '24 14:07 romainbqt

Thanks for your report. I will check. (note: the option "same" does not exist fro graph drawing)

couet avatar Jul 23 '24 11:07 couet

I have the fix for the 1st issue. It is not only in TGRaphAssymErrors but also in TGraphErrors and TGraphBentErrors and also for the drawing options > |> || []

couet avatar Jul 23 '24 12:07 couet

The 2nd one seems more tricky as your y scale has negative values which is always tricky with log scale. I will first make a PR for the first one and once merged I will tackle the 2nd issue.

couet avatar Jul 23 '24 12:07 couet

This PR fixes the 1st issue: https://github.com/root-project/root/pull/16093

couet avatar Jul 23 '24 12:07 couet

To make it easier to debug interactively I made this C version of the 2nd issue:

void testErrorGraph_v2() {
   gStyle->SetEndErrorSize(10);
   auto c = new TCanvas();
   c->Divide(3,1);


   c->SetGridx();
   c->SetGridy();
   const int n = 10;
   Double_t x[n]   = {0,1,2,3,4,5,6,7,8,9};
   Double_t y[n]   = {2,2,2,2,2,2,2,2,2,-4};
   Double_t exl[n] = {0,0,0,0,0,0,0,0,0,0};
   Double_t eyl[n] = {1,2,3,4,5,6,7,8,9,10};
   Double_t exh[n] = {0,0,0,0,0,0,0,0,0,0};
   Double_t eyh[n] = {1,2,3,4,5,6,7,8,9,10};
   auto gr = new TGraphAsymmErrors(n,x,y,exl,exh,eyl,eyh);
   gr->SetLineColor(kRed);
   gr->SetMarkerStyle(20);
   gr->SetMarkerColor(kBlue);
   gr->SetMarkerSize(1.5);
   gr->SetLineWidth(3);

   c->cd(1);
   gPad->DrawFrame(-1,0.1,10,4);
   gr->Draw("E0P");

   c->cd(2)->SetLogy();
   gPad->DrawFrame(-1,0.1,10,4);
   gr->Draw("E0P");

   c->cd(3)->SetLogy();
   c->cd(4)->SetBottomMargin(0.);
   gPad->DrawFrame(-1,0.1,10,4);
   gr->Draw("E0P");
}
Screenshot 2024-08-16 at 14 51 37

couet avatar Jul 23 '24 14:07 couet

Many thanks @couet for the fix of the first issue!

romainbqt avatar Jul 26 '24 14:07 romainbqt

I took another look at the second "issue." In this case, the plot in Y-log scale is invalid because some points are negative along the Y-axis. The graph rendering does "its best," but handling y=-1 will always be approximate. There is no obvious clean fix without compromising the entire code and generating potential bad side effects. In a way, this spurious plot indicates that something is wrong with that data point, and in reality, something is indeed wrong as the Y value is negative.

couet avatar May 16 '25 14:05 couet

Just in case - canvas displayed correctly in web mode:

Image

linev avatar May 16 '25 14:05 linev

Hi @couet, @linev,

It appears this issue is closed, but wasn't yet added to a project. Please add upcoming versions that will include the fix, or 'not applicable' otherwise.

Sincerely, :robot:

github-actions[bot] avatar May 19 '25 06:05 github-actions[bot]