Create and fill a TH2F using arrays for binning

Dear co-rooters,

I am trying to create a TH2F from an ascii file that can be found [here] (https://cernbox.cern.ch/index.php/s/kXJg0T5CvxZlrsQ)

( 1st column = x_bin, 2nd column = y_bin, 3rd column = bin_content, 4th column = bin_content_uncertainty )

My code to create this histogram and fill is the following

#include "/afs/cern.ch/user/a/astamato/public/lib_thanos/c_header.h"
#include "/afs/cern.ch/user/a/astamato/public/lib_thanos/root_header.h"

void vasilis2root(){

//Define variables
	int x_bins = 241, y_bins = 1000, n_bins = x_bins*y_bins;
	double x_step = 0.05, y_step=0.1;
	float x[x_bins], y[y_bins], n[n_bins], dn[n_bins];
	float X, X_0, Y, Y_0, N, dN;
	int bin;
	X_0 = 0.;
	Y_0 = 0.;	
//Fill the arrays with values
	for (int i=0; i<x_bins; i++){
		x[i]=pow(10,-3.+i*x_step);
		cout << i << ". x value = " << x[i] << endl;
	}
	y[0]=0.1;
	for (int i=1; i<y_bins; i++){
		y[i]=y[i-1]+y_step;
		cout << i << ". y value = " << y[i] << endl;
	}
//Read the ascii file that contains the data
	std::ifstream file ("lambda.dat");
	if(!file){
		cout << "File doesn't exist" << endl;
	}
	int kk = 0;
	while(!file.eof()){
		file >> X >> Y >> N >> dN;
		n[kk]=N;
		dn[kk]=dN;
		kk++;
		//cout << i << ". N value = " << n[i] << " +/- " << dn[i] << endl;
	}
	TH2F *lambda = new TH2F("lambda", "Resolution Function", x_bins, x, y_bins, y);
	/*for (int xx=1; xx<=lambda->GetNbinsX(); xx++){
		for (int yy=1; yy<=lambda->GetNbinsY(); yy++){
			bin = lambda->FindBin(xx,yy);
			lambda->AddBinContent(bin, n[bin]);
		}
	}*/
	lambda->Draw("colz");
}

When running this code and printing out the values of the arrays, everything seems to be in increasing order. However when trying to plot it, I get the following error


> 998. y value = 99.899
> 999. y value = 99.999
> Warning in <TROOT::Append>: Replacing existing TH1: lambda (Potential memory leak).
> Error in <TAxis::TAxis::Set>: bins must be in increasing order
> Error in <TAxis::TAxis::Set>: bins must be in increasing order
> Info in <TCanvas::MakeDefCanvas>:  created default TCanvas with name c1
> Error in <TCanvas::Range>: illegal world coordinates range: x1=0.001125, y1=0.112375, x2=-0.000125, y2=-0.011375
> Error in <TCanvas::RangeAxis>: illegal axis coordinates range: xmin=0.001000, ymin=0.100000, xmax=0.000000, ymax=0.001000
> root [41] Error in <TCanvas::Range>: illegal world coordinates range: x1=0.001125, y1=0.112375, x2=-0.000125, y2=-0.011375
> Error in <TCanvas::RangeAxis>: illegal axis coordinates range: xmin=0.001000, ymin=0.100000, xmax=0.000000, ymax=0.001000

But I don’t seem to be able to understand why!

Any idea would be very helpful!
Thanks in advance!

The number of bin bounds needs to be 1 larger than the number of bins. If you have just one bin, you need a lower and upper bound, i.e. the bin bounds array needs to be size 2.

In your code, you have x_bins = 241 and float x[x_bins]. x and y are too short.

Also, why are you using variable binning at all, you seem to have fixed bin width. (Edit: what kind of x binning are you trying to achieve with 10^(-3 + n*0.05)?That does not match the input file)

For y binning, you repeat floating point additions for the bounds. Not a good idea, floating point errors will accumulate this way.

Also, ifstream loops should look like this:
while (file >> X >> Y >> N >> dN) { ... }
Calls to eof in the beginning are correct in Pascal like languages, but usually incorrect in C++ (eof gets only set AFTER you have tried to read past the end of the file). Your loop here is incorrect.

I don’t understand what all these variables are for anyway, just create the histogram, loop over all lines and fill a bin for every line you read.

I suggest something like this:

#include <TH2F.h>
#include <fstream>
#include <iostream>

void test() {

    // Define variables
    int x_bins = 241, y_bins = 1000;
    double x_step = 0.05, y_step = 0.1;
    double x_low = -3;
    double y_low = 0;

    auto lambda = new TH2F("lambda", "Resolution Function", 
                           x_bins, x_low, x_low + x_bins * x_step, 
                           y_bins, y_low, y_low + y_bins * y_step);

    std::ifstream file("lambda.dat");
    if (!file.is_open()) {
        std::cout << "Cannot open file\n";
        return;
    }
    double X, Y, N, dN;
    while (file >> X >> Y >> N >> dN) {
        auto bin = lambda->FindBin(X + x_step/2, Y + y_step/2);
        lambda->SetBinContent(bin, N);
        lambda->SetBinError(bin, dN);
    }
    if (!file.eof()) { 
        std::cout << "Error before the eof was reached\n";
        return;
    }
    lambda->Draw("colz");
}

The result:

Thank you for your answer!
The reason that I tried to do it with arrays is because the 1st column in the file is the exponent i.e. -3 means 1e-3.

So I tried converting the exponents by using

#include <TH2F.h>
#include <fstream>
#include <iostream>

void test(){

// Define variables
    int x_bins = 241, y_bins = 1000;
    double x_step = pow(10,-2.95)-pow(10,-3.) , y_step = 0.1;
    double x_low = pow(10,-3);
    double y_low = 0;

    TH2F *lambda = new TH2F("lambda", "Resolution Function", 
                           x_bins, x_low, pow(10,9.), 
                           y_bins, y_low, y_low + y_bins * y_step);

    std::ifstream file("lambda.dat");
    if (!file.is_open()) {
        std::cout << "Cannot open file\n";
        return;
    }
    double X, Y, N, dN;
    while (file >> X >> Y >> N >> dN) {
        int bin = lambda->FindBin(pow(10,X) + x_step/2, Y + y_step/2);
        lambda->SetBinContent(bin, N);
        lambda->SetBinError(bin, dN);
    }
    if (!file.eof()) { 
        std::cout << "Error before the eof was reached\n";
        return;
    }
    lambda->Draw("colz");
}

but it didn’t work.

https://imgur.com/a/BmH9k

Any idea on how to have the x axis values raised to the power of 10 the 1st column?

Thanks in advance!

ah, got it! Then you really need variable binning - or just label the x axis as log(…) :wink:

Like this?

#include <TH2F.h>
#include <fstream>
#include <iostream>

void test() {

    // Define variables
    constexpr int x_bins = 241, y_bins = 1000;
    double x_step = 0.05, y_step = 0.1;
    double x_low = -3;
    double y_low = 0;
    
    std::array<double, x_bins + 1> x_bounds;
    for (int i = 0; i <= x_bins; ++i)
        x_bounds[i] = pow(10, x_low + i * x_step);

    auto lambda = new TH2F("lambda", "Resolution Function", 
                           x_bins, &x_bounds[0],
                           y_bins, y_low, y_low + y_bins * y_step);

    std::ifstream file("lambda.dat");
    if (!file.is_open()) {
        std::cout << "Cannot open file\n";
        return;
    }
    double X, Y, N, dN;
    while (file >> X >> Y >> N >> dN) {
        auto bin = lambda->FindBin(pow(10, X + x_step/2), Y + y_step/2);
        lambda->SetBinContent(bin, N);
        lambda->SetBinError(bin, dN);
    }
    if (!file.eof()) { 
        std::cout << "Error before the eof was reached\n";
        return;
    }
    lambda->Draw("colz");
}

This works!!!
Thank you very much!!!

Just a small comment.
This code works with root 6, I suppose.
I am using root 5.34 so just for the record I am pasting a version that works in root 5.34!

// Define variables
    int x_bins = 241, y_bins = 1000;
    double x_step = 0.05, y_step = 0.1;
    double x_low = -3;
    double y_low = 0;
    
    double x_bounds[x_bins+1];
    for (int i = 0; i <= x_bins; ++i)
        x_bounds[i] = pow(10, x_low + i * x_step);

    TH2F *lambda = new TH2F("lambda", "Resolution Function", 
                           x_bins, &x_bounds[0],
                           y_bins, y_low, y_low + y_bins * y_step);

    std::ifstream file("lambda_half.dat");
    if (!file.is_open()) {
        std::cout << "Cannot open file\n";
        return;
    }
    double X, Y, N, dN;
    while (file >> X >> Y >> N >> dN) {
        int bin = lambda->FindBin(pow(10, X + x_step/2), Y + y_step/2);
        lambda->SetBinContent(bin, N);
        lambda->SetBinError(bin, dN);
    }
    if (!file.eof()) { 
        std::cout << "Error before the eof was reached\n";
        return;
    }

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.