FillN not working with pyroot

Hello rooters,
If TH1::FillN() is not working with numpy arrays, this may be the cause of your error. If a 1-D slice of a numpy array is taken, the dimension from which the slice is taken from matters. Even if the slice is reduced to a 1-D numpy array and has the shape (N,), whether or not this is from rank ‘m’ will make the difference between FillN succeeding or crashing. I have demonstrated this bug in the following MWE.

import numpy as np
import ROOT

A = np.random.normal(loc=0.0, scale=1.0, size=(1000,))
weights = np.ones(1000)

th1d_A = ROOT.TH1D('test_th1_A', 'Test', 100, -1.0, 1.0)
th1d_A.FillN(A.shape[0], A, weights)

B = np.random.normal(loc=0.0, scale=1.0, size=(1000,2))
B_slice_copy = np.ndarray.copy(B[:,0])

th1d_B = ROOT.TH1D('test_th1_B', 'Test', 100, -1.0, 1.0)
th1d_B.FillN(B_slice_copy.shape[0], B_slice_copy, weights)

B_slice_transp = B_slice_transp = np.random.normal(loc=0.0, scale=1.0, size=(2,1000))[1,:]
th1d_C = ROOT.TH1D('test_th1_C', 'Test', 100, -1.0, 1.0)

th1d_C.FillN(B_slice_transp.shape[0], B_slice_transp, weights)

B_slice = B[:,1]

th1d_D = ROOT.TH1D('test_th1_D', 'Test', 100, -1.0, 1.0)
th1d_D.FillN(B_slice.shape[0], B_slice, weights)

What is happening here? Example A works fine as per the pyroot tutorial.
Example B will work because a new slot of memory is allocated, and the python pointer is pointing at this new memory. Example C works because it is sliced from dimension 0, i.e. [m,:]. Example D fails however because it uses a slice from dimension 2. This is extremely hard to catch because as I’ve shown, the two array slices will show up with the exact same shape and type as far as the python interpreter is concerned. The output is the following:

<class 'numpy.ndarray'>
(1000, 2)
<class 'numpy.ndarray'>
Traceback (most recent call last):
  File "~/Projects/Experimental/", line 30, in <module>
    th1d_D.FillN(B_slice.shape[0], B_slice, weights)
TypeError: none of the 2 overloaded methods succeeded. Full details:
  void TH1::FillN(int ntimes, const double* x, const double* w, int stride = 1) =>
    TypeError: could not convert argument 2 (could not convert argument to buffer or nullptr)
  void TH1::FillN(int, const double*, const double*, const double*, int) =>
    TypeError: takes at least 5 arguments (3 given)

ROOT Version: ROOT Version: 6.22/08
Platform: Built for linuxx8664gcc on Apr 14 2021, 11:53:00

For reference, this is the latest root build on conda-forge: ::

Compiler: Not Provided

The problem is that when you are doing a slice you are actually making a view. Maybe is possible to get the correct pointer in that case to be passed to FillN, but I am not sure.
As a workaround please copy the sliced array before passing to FillN (for example using np.copy)


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