Question about the scaling factor in ROOT’s weighted Poisson log-likelihood (WLS fit)

Hi ROOT experts,

I have been studying the implementation of the weighted log-likelihood fit (“WL” option) in ROOT (v6.36.04), and I would like to understand the statistical motivation behind one part of the implementation.

From the source code, ROOT defines the effective scaling factor as

s_i = sigma_i^2 / y_i

and

n_eff = y_i / s_i = y_i^2 / sigma_i^2
mu_eff = mu_i / s_i

which gives

l_i = s_i * [ n_eff * log(n_eff / mu_eff) + mu_eff - n_eff ]

I have independently implemented this expression and verified numerically that it reproduces ROOT’s MinFCN exactly for my weighted histogram fits.

While studying this implementation, I became curious about the role of the final scaling factor.

Once the effective quantities are defined as

n_eff = y_i / s_i
mu_eff = mu_i / s_i

it seems natural to consider the effective Poisson deviance

l_i = n_eff * log(n_eff / mu_eff) + mu_eff - n_eff by itself.

However, ROOT instead minimizes

l_i = s_i * [ n_eff * log(n_eff / mu_eff) + mu_eff - n_eff ]

While studying this implementation, I became curious about the role of the final scaling factor.

The effective count formalism is also discussed in the paper G. Böhm and G. Zech, Statistics of weighted Poisson events and its applications.

In a section on the Scaled Poisson Distribution (SPD), it is introduced for parameter estimation, where the effective quantities are defined in essentially the same way as in ROOT.

However, the likelihood in the paper does not appear to include the additional scaling factor s_i that multiplies the effective Poisson deviance.

So my question is:

Why does ROOT include the final scaling factor s_i?

  1. If the effective quantities (n_eff and mu_eff) are already introduced, why is the effective Poisson deviance by itself not sufficient?

  2. Would omitting the factor s_i lead to an incorrect likelihood or estimator? If so, could you explain what statistical property would be lost?

If there is a derivation, technical note, or publication explaining the origin of this factor, I would greatly appreciate a reference.

tagging @jonas @StephanH

I saw it, but I haven’t found the source code yet. :sweat_smile:

@Byungchul_Yu which code are you talking about specifically? There’s at least three places that deal with Poisson likelihoods.

Thanks! @StephanH I’m referring to the same implementation discussed in this earlier ROOT Forum thread:

https://root-forum.cern.ch/t/weighted-log-likelihood-estimation-for-low-statistics-binned-histogram/58191

@moneta Lorenzo’s reply there includes the relevant source code and a detailed explanation of how the weighted likelihood is implemented. My current question is a follow-up to that discussion: I understand how ROOT evaluates the expression numerically, but I’m trying to understand the statistical motivation for the scaling factor.

Hello @Byungchul_Yu ,

@jonas and me had a look, but for the moment, we can’t explain the factor. Jonas will check more in the coming days.

Hi @jonas, @moneta and @StephanH,

Just following up on this, as the topic will close automatically soon.
Have you had a chance to look into the statistical motivation behind the scaling factor s_i?
Any updates or insights would be greatly appreciated!

Hello, @Byungchul_Yu !

I took a closer look at the implementation of TH1::Fit method when option "WL" is specified.
The relevant code is inside ROOT::Fit::FitUtil::EvaluatePoissonLogL.
I will try to address your questions.

Math derivation

Firstly, let’s map variables from the code to analytical quantities using your designations.

// FitUtil.cxx

// bin content: y_i = sum w
auto y = *data.ValuePtr(i);

// model prediction: mu_i
fval = func(x, p);

// standard deviation: sigma_i = sqrt(sum w^2)
double error = data.Error(i); 

// effective weight or scale factor: s_i = sigma_i^2 / y_i
weight = (error * error) / y;

Following Böhm & Zech, a weighted bin content Y_i distribution can be modeled via a Scaled Poisson Distribution (SPD):

Y_i = s_{i, \text{true}} X_i, \, X_i \sim \text{Pois}(n_\text{eff,true})

where bin statistics are

y_i = \sum_{j} w_j, \quad \sigma_i^2 = \sum_{j} w_j^2

The estimated effective parameters are:

s_i = \frac{\sigma_i^2}{y_i},\quad n_\text{eff} = \frac{y_i^2}{\sigma_i^2} = \frac{y_i}{s_i},\quad \mu_\text{eff} = \frac{\mu_i}{s_i}

where \mu_i is model prediction for bin content.

The effective Poisson log-likelihood (dropping factorial term) is

l(\mu_\text{eff} \mid n_\text{eff}) = n_\text{eff} \log(\mu_\text{eff}) - \mu_\text{eff}

The Poisson deviance is

\Delta l = l(n_\text{eff} \mid n_\text{eff}) - l(\mu_\text{eff} \mid n_\text{eff}) = n_\text{eff} \log\frac{n_\text{eff}}{\mu_\text{eff}} - n_\text{eff} + \mu_\text{eff}

We can factor out s_i^{-1} by substituting back n_\text{eff} = y_i/s_i,\, \mu_\text{eff} = \mu_i/s_i

\begin{align*} \Delta l &= \frac{y_i}{s_i} \log\frac{y_i}{\mu_i} - \frac{y_i}{s_i} + \frac{\mu_i}{s_i} =\\ &= \frac{1}{s_i} \times \left( -y_i \log\frac{\mu_i}{y_i} + \mu_i - y_i \right) \end{align*}

Now compare to ROOT::Fit::FitUtil::EvaluatePoissonLogL implementation:

// FitUtil.cxx
// parts of log-likelihood calculation
nloglike -= weight * y * ( ROOT::Math::Util::EvalLog(fval/y) );
nloglike += weight  *  ( fval - y);

Since \mathtt{weight} = s_i, ROOT computes

\Delta l_\text{ROOT} = s_i \times \left( -y_i \log\frac{\mu_i}{y_i} + \mu_i - y_i \right)

In the limit (y_i - \mu_i) \ll \mu_i the correct deviance yields weighted Pearson’s \chi^2:

\Delta l \approx s_i^{-1} \left( -y_i \left(- \frac{y_i - \mu_i}{\mu_i} \right ) + \mu_i - y_i \right) = \frac{y_i}{\sigma_i^2} \times \frac{(y_i - \mu_i)^2}{\mu_i} \approx \frac{(y_i - \mu_i)^2}{\sigma_i^2}

For ROOT’s implementation, the limit becomes

\Delta l_\text{ROOT} \approx \frac{\sigma_i^2}{y_i} \times \frac{(y_i - \mu_i)^2}{\mu_i}

This inverts statistical weights: for example, large effective weights (s_i > 1) indicate overdispersion compared to Poisson distribution.
Such bins should have a reduced impact on objective function to minimize, but instead it is amplified.

Addressing questions

To sum up, regarding your first question, effective Poisson deviance is sufficient.

Regarding your second question, dropping s_i^{-1} factor (not s_i) yields basic Poisson deviance which disregards the variance of the weights inside bin.
It will produce biased point estimates with increased variance.
Also it will likely underestimate or overestimate error bounds if s_i > 1 or s_i < 1 over all bins.
Notably, I see that ROOT applies sandwich correction to covariance matrix in Fitter::ApplyWeightCorrection which fixes error bounds, but this doesn’t fix bias and increased variance of point estimates.

Possible bug

@StephanH and @jonas ,
I suspect that in the implementation inside ROOT::Fit::FitUtil::EvaluatePoissonLogL might be wrong.
Instead of

// FitUtil.cxx
// parts of log-likelihood calculation
nloglike -= weight * y * ( ROOT::Math::Util::EvalLog(fval/y) );
nloglike += weight  *  ( fval - y);

it should be

// FitUtil.cxx
// parts of log-likelihood calculation
nloglike -= (1.0/weight) * y * ( ROOT::Math::Util::EvalLog(fval/y) );
nloglike += (1.0/weight)  *  ( fval - y);

This behavior with multiplying log-likelihood with wrong prefactor appears both in v6.34.04 and v6.40.02.
If this is truly unintended, it might seriously affect fit convergence and quality in case of large weight variances inside bins.
Please let me know if I am missing something important in ROOT’s implementation.

For a reference, I suggest looking there:

Important: in the link they define scaling factor differently:

s_\text{scikit} = \frac{\sum w}{\sum w^2}

while convention in this topic is

s = \frac{\sum w^2}{\sum w}