THnSparse variable binning and private data members

Firstly, thank you to those involved in making it. THnSparse is really great. Reduced the memory consumption of my app from 2GB to a tenth of that. Also has done wonders for my processing time.

In making my application I need to specialise it in a few ways, and I’m struggling to do that because its data members are ‘private’ - so I have to hack ROOT source if I want to touch anything.

root.cern.ch/root/html/THnSparse … ta_Members

This contrasts with say, TH1:

root.cern.ch/root/html/TH1.html#TH1:Data_Members

Where they are protected, allowing me to subclass them and change them.

One thing I wanted to use was variable binning - I’m in the process of trying to implement this, but just the fact that the data members are private instead of protected means I have to recompile the whole of ROOT.

Would a patch for variable binning be welcome, and can the data members be made protected?

Regards,

  • Peter

Hi,

why can’t you use THnSparse::GetAxis()->Set(…) for variable binning (preferably before filling :-)? So far I have not found a reason to grant access to the private data members. After all, this is what data encapsulation in OO is about, if I remember correctly :slight_smile:

So let’s keep in touch - if you have a good extension you’d like to put in a subclass maybe we could integrate it in THnSparse directly. If not we can discuss making these members protected. Let me know!

Cheers, Axel.

Just it seems by ROOT convention that generally ‘protected’ data members are used rather than private… I just took a look at several classes and they seem to be this way. (Could be that I just inadvertantly selected the few that had protected members instead of private).

If you’re inheriting from an object, why should you have to access things through Getters() and Setters() ?

Member visibility philosophy aside, I just wanted to make a few extensions as quickly as possible without reading every line of code from THnSparse. I didn’t spot the GetAxis, and once I did notice it I did not think that I would be able to change the axes through it. If the members were protected instead of private it would have been half of the effort. In the end I had to recompile the whole of ROOT (something I can’t do on production boxes) in order to test some quick changes. You’re right though, I could do that particular thing through GetAxis.

There were other modifications that needed to be made, and whilst I could (perhaps) have used the Getters and Setters with the class, it means that instead of copy and pasting from the original code and changing the bits to add the functionality I wanted, I would have to change every instance of a variable access to call a Getter() or Setter() function.

Anyway. All I have added in the end is a new constructor and a small amount of code so that the projections work. (I’ve only done it for 2D projections since that’s all I needed, but it would be easy enough to extend it).

I’m happy to contribute these changes, but they probably need a bit more work for consistency.

I was also wondering - how about the ability to TTree::Draw() onto a THnSparse?

Regards,

  • Peter

Hi,

which constructor? Why were the standard Projection() methods not sufficient for you - what was missing?

For a ROOT class with private data members check e.g. TAxis. It’s a matter of taste, and apparently taste differs between the two of us :slight_smile:

I don’t know yet whether drawing the THnSparse (via TParallelCoords) or the interface to TTree::Draw() is more urgent - but I agree, it would be good to have both. I will post once we have it.

Cheers, Axel.

Here is how I modified the 2D projection:

[code] /*
// y, x looks wrong, but it’s what TH3::Project3D(“xy”) does
TH2D* h = new TH2D(name, title,
GetAxis(yDim)->GetNbins(),
GetAxis(yDim)->GetXmin(), GetAxis(yDim)->GetXmax(),
GetAxis(xDim)->GetNbins(),
GetAxis(xDim)->GetXmin(), GetAxis(xDim)->GetXmax());
*/
TAxis *axisX = GetAxis(xDim), *axisY = GetAxis(yDim);

const TArrayD *xbins = axisX->GetXbins(), *ybins = axisY->GetXbins();

Int_t ilowX = axisX->GetFirst(), ihighX = axisX->GetLast(),
ilowY = axisY->GetFirst(), ihighY = axisY->GetLast();

Double_t lowX = axisX->GetBinLowEdge( ilowX ), highX = axisX->GetBinUpEdge( ihighX ),
lowY = axisY->GetBinLowEdge( ilowY ), highY = axisY->GetBinUpEdge( ihighY );

Int_t nx = ihighX-ilowX+1, ny = ihighY-ilowY+1;

TH2D *h = NULL;

if ( xbins->fN == 0 && ybins->fN == 0 )
{
h = new TH2D( name, title, ny, lowY, highY, nx, lowX, highX );
} else if ( ybins->fN == 0 )
{
h = new TH2D( name, title, ny, lowY, highY, nx, &xbins->fArray[ilowX-1] );
} else if ( xbins->fN == 0 )
{
h = new TH2D( name, title, ny, &ybins->fArray[ilowY-1], nx, lowX, highX );
} else
{
h = new TH2D( name, title, ny, &ybins->fArray[ilowY-1], nx, &xbins->fArray[ilowX-1] );
}[/code]

See hist/TH3.cxx TH3::Project3D() for reference.

The new constructor was only a minor change from the original:

[code]THnSparse::THnSparse(const char* name, const char* title, Int_t dim,
const Int_t* nbins, const Double_t *bins,
Int_t chunksize):
TNamed(name, title), fNdimensions(dim), fChunkSize(chunksize), fFilledBins(0),
fAxes(dim), fEntries(0), fTsumw(0), fTsumw2(-1.), fTsumwx(dim), fTsumwx2(dim),
fCompactCoord(0), fIntegral(0), fIntegralStatus(kNoInt)
{
for (Int_t i = 0; i < fNdimensions; ++i) {
TAxis
axis = new TAxis(nbins[i], bins[i]);
TString name(“axis”);
name += i;
axis->SetName(name);
axis->SetTitle(name);
fAxes.AddAtAndExpand(axis, i);
}
fAxes.SetOwner();

fCompactCoord = new THnSparseCompactBinCoord(dim, nbins);
}
[/code]

It’s a bit of a pain to call though. Would be nice to be able to call it say:

[code]Double_t nbins[] = {2, 3, 4};
Double_n bins[][10] = { {10, 20}, {5, 7, 8}, { 40, 50, 60, 90 } };

THnSparse Test( “test”, “test”, 3, nbins, bins );[/code]

But you can’t do that, of course, you have to write something like:

Double_t _bins[][10] = { {10, 20}, {5, 7, 8}, { 40, 50, 60, 90 } }; Double *bins[] = { _bins[0], _bins[1], _bins[2] };

Unless anyone can think of a neater way to do it. (One way might be to fix the maximum number of bins, and change the constructor to accept “Double_t bins[][nmax]” instead of “Double_t **bins” , but we don’t want to do that.)

Regards,

  • Peter

Any thoughts, Axel?

Also, how far are we off being able to make a THnSparse from a parallel coords plot or from a tree?

  • Peter

Peter,

You will never get a THnSparse out of parallel coordinates. We will implement
a Tree->THnSparse projection in the same way that we have Tree->parallel coordinates.

There are still many developments to exploit the power of THnSparse
-projection from a Tree
-rebinning
-fitting

This is on our todo list, but it is not scheduled for the June release. Projections and fitting will likely be part of the December release.

Rene

Hi Peter,

of course rebinning is already implemented :slight_smile: I agree that the projections should take variable bins into account; I will let you know when it’s in. Thanks for pointing that out!

I don’t like the 2d pointer for the bins. It’s just that C++ cannot handle these kind of arrays properly and we have to live with that. So instead of 2d array floating point literal funkiness I’d prefer to leave it as it is, with explicit calls to TAxis::Set().

Cheers, Axel.

Hi,

digging up a very old thread …: Has the TTree --> THnSparse functionality been implemented? Currently, TTree::Draw() can produce 1d, 2d, … histogram objects, but I couldn’t find a way to make a THn.

Thanks!

Best,
Peter