Adding numbers in graph

Hi,

This question has probably been answered before but I couldn’t find it.
What is the easy way to put numbers above each point in a TGraphErrors? So, I have a vector of numbers the same size of the entries in a TGraph and I want to draw them. I have done this before by hand, ie, first seeing where the points were and then making a small function to draw them.
Is there some automatic way of doing this, a function that given a tgrapherrors and the value of the error at each point knows where it should print the numbers?

Thanks

We have that for histograms. See: root.cern.ch/root/html534/THistPainter.html#HP15

But not for TGraphs.

The position of the text for each “number” is known: it is the X and Y vectors.
A small loop should should make it in an automatic way.

What “numbers” do you want to “put above each point” ?

Can you provide a small example showing what you have already ?

Hi couet,

Sure, I can post a small example. So for example i have a vector with the number of entries that contributed to each point in the graph. (and aux_th2 is an auxiliary TH2D on top of which I draw everything)
Then I do:

[code] graph->Draw(“PL”);
TText* text[num_bins];
double margin_bottom=0.1, margin_top=0.1, margin_left=0.1, margin_right=0.1;
gPad->SetBottomMargin(margin_bottom);
gPad->SetRightMargin(margin_top);
gPad->SetTopMargin(margin_left);
gPad->SetLeftMargin(margin_right);

double max_x=aux_th2.GetXaxis()->GetXmax();
double min_x=aux_th2.GetXaxis()->GetXmin();
double max_y=aux_th2.GetYaxis()->GetXmax();
double min_y=aux_th2.GetYaxis()->GetXmin();

for (int ibin = 0; ibin < num_bins; ibins++){

double x, y, ey;
graph->GetPoint(ibin,x,y);
ey=graph->GetErrorY(ibin);  

//draw numbers starting at the bottom of the th2d ( 1-margin_bottom) and 
//going only up to the top (1.-margin_bottom-margin_up) and add a margin 
//so it doesn't plot on top of the line
double perc_y = (1-margin_bottom)-(1.-margin_bottom-margin_up)*(max_y-(y+ey))/(max_y-min_y)+0.02;

//same thing but this time the margin is for the length of the number
//future: make it length dependent	
double perc_x = (1-margin_left)-(1.-margin_left-margin_right)*(max_x-x)/(max_x-min_x)-0.02; 
      
text[ibin]=new TText(perc_x,perc_y, int_to_charvec(entries[ibin]) );	     
text[ibin]->SetNDC(true);   
text[ibin]->Draw(); 

}[/code]

So this does more or less what I wanted, but things aren’t well aligned.
And, I have seen this recently in presentations (perhaps in profiles, I am not sure), and some even had “two levels” for the numbers if they were too big to be side by side, so I thought that maybe there was a easy way of doing it.

Thanks

Edit:

Actually in the link you sent i just noticed this option in TProfile:
profile->Draw(“TEXT0E”);
and I think I can use this to do what I wanted, i was just looking in the wrong place. Thanks!

Thanks for the example. Can you send me one which I can run and modify ?
The one you sent does not defined the graph.
It will be more helpful for you.

Hi,

Sure, this one runs, but is very crude, if you have some bins that have 1000 entries and the other has 5, this will not work very well:

#include <stdio.h>    
#include <stdlib.h>   
#include <time.h> 
#include <algorithm>
#include <iostream> 

const int num_bins=5;
int mypow(int a, int b);
//should be dependent on text length (at least)
double padding_text=0.02;

void numbers_in_graph(){

  //could make a much more interesting example obviously, 
  //but I can't think of a simpler one that is usable in practice

  double x[num_bins], e_y[num_bins];
  for(int i=0; i<num_bins; i++){
	x[i]=(double)i+0.5;
	e_y[i]=pow(2,i);  
  }
  
  srand (time(NULL));
  double y[num_bins]={0}, n[num_bins]={0};
  
  for(int i=0; i<1000; i++){
    double rand_tmp=2.*(rand()/(double)RAND_MAX);
    int bin = rand()%num_bins;
    y[bin]+=rand_tmp;
    n[bin]++;
  }
  
  double max_th2 = y[0];
  double min_th2 = y[0];
  for (i = 1; i < num_bins; i++) {
	max_th2=std::max(y[i],max_th2);	
	min_th2=std::min(y[i],min_th2);
  }

  TH2F* aux_th2=new TH2F("aux_th2","",1,0,num_bins,1,0.75*min_th2,1.25*max_th2);
  aux_th2->SetStats(0);
  TGraphErrors* graph=new TGraphErrors(num_bins, x, y, 0, e_y);
  
  TCanvas* canvas=new TCanvas("canvas","",600,480);
  canvas->cd();
  double margin_bottom=0.1, margin_top=0.1, margin_left=0.1, margin_right=0.1;
  gPad->SetBottomMargin(margin_bottom);
  gPad->SetRightMargin(margin_top);
  gPad->SetTopMargin(margin_left);
  gPad->SetLeftMargin(margin_right);   
  
  aux_th2->Draw();
  graph->Draw("PL");  
  
  
  //The reason this is written in this convoluted manner 
  //(GetPoint instead of using defined vectors, etc)
  //is because it was inside a function
  TText* text[num_bins];
     
  double max_x=aux_th2->GetXaxis()->GetXmax();
  double min_x=aux_th2->GetXaxis()->GetXmin();
  double max_y=aux_th2->GetYaxis()->GetXmax();
  double min_y=aux_th2->GetYaxis()->GetXmin();
                                        
  for (int ibin = 0; ibin < num_bins; ibin++){

    double xp, yp, eyp;
    graph->GetPoint(ibin,xp,yp);
    eyp=graph->GetErrorY(ibin);  

    //draw numbers starting at the bottom of the th2d ( 1-margin_bottom) and 
    //going only up to the top (1.-margin_bottom-margin_up) and add a margin 
    //so it doesn't plot on top of the line
    double perc_y = (1-margin_bottom)-(1.-margin_bottom-margin_top)*(max_y-(yp+eyp))/(max_y-min_y)+padding_text;
    
    //same thing but this time the margin is for the length of the number
    //future: make it length dependent   
    double perc_x = (1-margin_left)-(1.-margin_left-margin_right)*(max_x-xp)/(max_x-min_x)-padding_text; 
       
    if(perc_x>1 || perc_y>1 || perc_x<0 || perc_y<0){
      cout << "error gets out of frame -> redefine th2" << endl;   
      continue;
    }    
          
    int num=n[ibin];
	if(num>0){
	  const int ni=int(log(num)/log(10)+1);
	  char* num_string=new char[ni];
	  num_string[ni]='\0';
	  for (int ii=ni-1, j=0; ii>=0; ii--, j++){
		num_string[j]=int(num%int(mypow(10, ii+1)))/int(mypow(10, ii))+'0';
	  }
      text[ibin]=new TText(perc_x,perc_y,num_string);  
	}
    else
	  text[ibin]=new TText(perc_x,perc_y,"0");
       
    text[ibin]->SetNDC(true);   
    text[ibin]->Draw(); 
     
  }

  return;

}

int mypow(int a, int b)
{
    if (b==0) return 1;
    if (a==0) return 0;
    return a*mypow(a, b-1);
}

Yes the way you do it is a bit complex but it is general enough… Nothing is hardcoded.
May be the X position of the text(s) should be equal to the X position of the point(s) in the graph
and you should use a text alignment centered. See:
root.cern.ch/root/html/TAttText.html#T1

Yes, I should be doing that, thanks!

Also, if and when you find the time, I think it might be useful to have an example somewhere in the site of how to do this (ideally easier than the one i posted).
I think other people might find it useful, at least when you do 10 fits of a parameter (for different energies for example) and want to understand how the errors relate to statistics. The TGraph class offers much more functionality for this than a tprofile (assym, errors in x, bent bars, etc) but is missing an easy way to plot numbers.

That’s a good point. I have added this exemple in the ROOT tutorials.
It is the following (note the use of TExec which allows an interactive update of the text position and value when you move a point):

// Draw a graph with text attached to each point.
// The text is drawn in a TExec function, therefore if the text is
// moved interactively, it will be automatically updated.
// Author: Olivier Couet
void graphtext() {
   TCanvas *c = new TCanvas("c","A Simple Graph Example with Text",700,500);
   c->SetGrid();

   const Int_t n = 10;
   TGraph *gr = new TGraph(n);
   gr->SetTitle("A Simple Graph Example with Text");
   gr->SetMarkerStyle(20);
   TExec *ex = new TExec("ex","drawtext();");
   gr->GetListOfFunctions()->Add(ex);

   Double_t x, y;
   for (Int_t i=0;i<n;i++) {
      x = i*0.1;
      y = 10*sin(x+0.2);
      gr->SetPoint(i,x,y);

   }
   gr->Draw("ALP");
}

void drawtext()
{
   Int_t i,n;
   Double_t x,y;
   TLatex *l;

   TGraph *g = (TGraph*)gPad->GetListOfPrimitives()->FindObject("Graph");
   n = g->GetN();
   for (i=1; i<n; i++) {
      g->GetPoint(i,x,y);
      l = new TLatex(x,y+0.2,Form("%4.2f",y));
      l->SetTextSize(0.025);
      l->SetTextFont(42);
      l->SetTextAlign(21);
      l->Paint();
   }
}