TProfile::Chi2Test is scale invariant

This is a follow-up to Chi2Test using TProfile , which ended up with [hist] Enable chi2 tests for TProfile by hageboeck · Pull Request #19930 · root-project/root · GitHub enabling computation of chi2 for TProfile.

I think there is another issue. The computation uses the TH1 implementation with WW option. This means that the statistic is exactly invariant under rescaling one of the two profiles.

For two profiles, I believe this is not what users expect.

Example with ROOT 6.40.02:

import ROOT

p1 = ROOT.TProfile("p1", "p1", 3, 0, 3)
p2 = ROOT.TProfile("p2", "p2", 3, 0, 3)

for i, mean in enumerate([10.0, 20.0, 30.0]):
    for _ in range(50):
        p1.Fill(i + 0.5, mean - 1.0)        # bin i of p1: mean, spread 1
        p1.Fill(i + 0.5, mean + 1.0)
        p2.Fill(i + 0.5, 2 * (mean - 1.0))  # bin i of p2: exactly twice, spread 2
        p2.Fill(i + 0.5, 2 * (mean + 1.0))

print("bin        p1                 p2")
for i in range(1, 4):
    print("  %d   %6.2f +- %.4f    %6.2f +- %.4f"
          % (i, p1.GetBinContent(i), p1.GetBinError(i),
             p2.GetBinContent(i), p2.GetBinError(i)))

print()
print("Chi2Test(p2, 'CHI2') = %.6g" % p1.Chi2Test(p2, "CHI2"))
print("Chi2Test(p2)         = %.6g" % p1.Chi2Test(p2))

chi2 = sum((p1.GetBinContent(i) - p2.GetBinContent(i)) ** 2
           / (p1.GetBinError(i) ** 2 + p2.GetBinError(i) ** 2)
           for i in range(1, 4))
print()
print("bin by bin, sum (y1-y2)^2/(e1^2+e2^2) = %.6g for 3 d.o.f. -> p = %.3g"
      % (chi2, ROOT.TMath.Prob(chi2, 3)))

Output

bin        p1                 p2
  1    10.00 +- 0.1000     20.00 +- 0.2000
  2    20.00 +- 0.1000     40.00 +- 0.2000
  3    30.00 +- 0.1000     60.00 +- 0.2000

Chi2Test(p2, 'CHI2') = 0
Chi2Test(p2)         = 1

bin by bin, sum (y1-y2)^2/(e1^2+e2^2) = 28000 for 3 d.o.f. -> p = 0

@StephanH maybe again for you?

Hello @wiso,

sorry for picking this up only now. I went back to the papers describing WW Chi^2 tests, and indeed, the test statistic is defined as
image

Where W is the sum of weights of the full histogram and w are the weights per bin. If you set histo_2 = 2*histo_1, the numerator becomes:
W_1*2*w_1i - 2*W_1 * w_1i which is always zero.

How we choose or treat the errors doesn’t even matter. This results from the fact that the paper arXiv:physics/0605123 (2006) defines compatibility as having the same relative probabilities in each bin, independent of an overall scale factor.

Long story short, we can use the test to check if the (normalised) distributions are compatible, but it doesn’t test for scale differences. Now the question is if that’s acceptable?
We could extend the documentation and maybe even add a warning if scale differences are detected.

Update: But the more I read the docs, the clearer it gets that scale invariance is a desired property of the test. We should probably write this down explicitly, but then we still have to decide what to do about the case of profile-profile comparison.

Hello @StephanH , thank you for your reply. I am not sure to understand what you mean:

we can use the test to check if the (normalised) distributions are compatible

we are not comparing distributions (histograms), we are comparing TProfiles. Have a look at my example in my original post: two completely different TProfiles give a fully compatible p-value.

For profile-profile I would either compute the comparison of the means — χ² = Σ (y₁−y₂)²/(e₁²+e₂²), ndf = bins with a positive error on both sides

Hello again,

what I meant to say is “this test is only good if we don’t care about normalisation”. As you already pointed out, that’s not very useful for the profiles.

I have a question about your suggested formula though, since when summing the two errors like that, the result might not be Chi2-distributed (as in a square of a normally-distributed random variable). That would hold if one of the two means didn’t have an error. Is that also the case when summing the squares of the errors?

I think what might apply better here is using a two-sample Student t-test for the compatibility of sample means. So what I would try to do is to compute t-test statistics in each bin, and sum them such that we can use a Chi2 probability distribution.

Sure, a chi2 test is not the best choice. A t-test would be better. The problem is the name of the function, Chi2Test. By the way, right now, I don’t think the function is useful for a TProfile.

Hi,

I went through the current TProfile::Chi2Test() implementation and the underlying TH1::Chi2Test(..., "WW") code path. The scale invariance is not a numerical accident: it follows directly from the statistical hypothesis implemented by the weighted-histogram test.

For two profiles, let

m_{1i},\;m_{2i}

be the bin means returned by the profiles, and let

e_{1i},\;e_{2i}

be the corresponding standard errors of the mean.

TProfile::Chi2Test() currently forces the comparison of two TProfile objects through

TH1::Chi2Test(..., "WW")

and the weighted-weighted test uses, bin by bin,

\chi^2_{\mathrm{WW}} = \sum_i \frac{ \left( S_1m_{2i}-S_2m_{1i} \right)^2 }{ S_1^2e_{2i}^2 + S_2^2e_{1i}^2 },

where

S_1=\sum_i m_{1i}, \qquad S_2=\sum_i m_{2i}.

Define

r=\frac{S_2}{S_1}.

Then the same statistic can be written as

\boxed{ \chi^2_{\mathrm{WW}} = \sum_i \frac{ \left( m_{2i}-r\,m_{1i} \right)^2 }{ e_{2i}^2+r^2e_{1i}^2 } }.

This makes the source of the scale invariance explicit.

If the second profile is a global rescaling of the first,

m_{2i}=c\,m_{1i},

with correspondingly scaled errors

e_{2i}=|c|e_{1i},

then

S_2=cS_1

and therefore

r=c.

Hence every numerator vanishes:

m_{2i}-r\,m_{1i} = c\,m_{1i}-c\,m_{1i} = 0,

so that

\boxed{ \chi^2_{\mathrm{WW}}=0 }

for any non-zero global scale factor c.

So the present TProfile::Chi2Test() is not testing

H_0:\quad \mu_{1i}=\mu_{2i}

bin by bin.

It is effectively testing a different hypothesis:

H_0:\quad \mu_{2i}=c\,\mu_{1i}

for an overall normalization factor.

That hypothesis makes sense for a histogram homogeneity test, where one often wants to compare shapes independently of the total normalization, but it is not the usual equality test for profile means.


For two independent profiles, the natural profile-to-profile test is instead based directly on the difference of the estimated means:

\Delta_i=m_{1i}-m_{2i}.

Under

H_0:\quad \mu_{1i}=\mu_{2i},

and assuming independent profile estimates,

\operatorname{Var}(\Delta_i) = e_{1i}^2+e_{2i}^2.

Therefore the standardized residual is

r_i = \frac{ m_{1i}-m_{2i} }{ \sqrt{e_{1i}^2+e_{2i}^2} },

and the corresponding chi-square statistic is

\boxed{ \chi^2_{\mathrm{profile}} = \sum_i \frac{ \left( m_{1i}-m_{2i} \right)^2 }{ e_{1i}^2+e_{2i}^2 } }.

For independent approximately Gaussian bin-mean estimates, this is the direct test of profile equality.

If k valid bins are compared and no additional common parameter is fitted, the natural number of degrees of freedom is

\boxed{\mathrm{ndf}=k},

not k-1.

The loss of one degree of freedom in the weighted-histogram homogeneity test comes from the normalization constraint. That constraint is not part of the ordinary equality-of-profile-means problem.


For the example

m_1=(10,20,30),
m_2=(20,40,60),

with

e_1=(0.1,0.1,0.1),

and

e_2=(0.2,0.2,0.2),

the current weighted-histogram path gives

\chi^2_{\mathrm{WW}}=0,

because the second profile is exactly twice the first.

But the direct profile-equality statistic gives

\chi^2_{\mathrm{profile}} = \frac{(10-20)^2}{0.1^2+0.2^2} + \frac{(20-40)^2}{0.1^2+0.2^2} + \frac{(30-60)^2}{0.1^2+0.2^2}.

Since

0.1^2+0.2^2=0.05,

this becomes

\chi^2_{\mathrm{profile}} = \frac{100+400+900}{0.05} = \boxed{28000},

with

\boxed{\mathrm{ndf}=3}.

So the two procedures are answering fundamentally different statistical questions.


There are also two additional consequences of reusing TH1::Chi2Test("WW") for profiles.

First, the histogram code treats

\sum_i m_i=0

as an empty histogram condition.

For a TProfile, however, a profile such as

(-1,+1)

can be perfectly well populated and have finite uncertainties, while its bin means sum to zero. A zero sum of profile means is therefore not an emptiness criterion.

Second, the weighted-histogram implementation reduces the degrees of freedom when both bin contents are zero. For a profile,

m_{1i}=m_{2i}=0

does not imply that the bin contains no observations. Both profiles may contain many entries whose estimated means happen to be zero.

So the mismatch is not limited to scale invariance; it comes from applying histogram-content semantics to quantities that, in a TProfile, represent estimated conditional means.


A cleaner implementation would therefore separate the two use cases.

For TProfile versus TProfile, the default equality test could use

\chi^2 = \sum_i \frac{ (m_{1i}-m_{2i})^2 }{ e_{1i}^2+e_{2i}^2 },

with bins included according to the validity of their profile estimates rather than according to histogram-count semantics.

More generally, if full covariance information is available, the same test is

\boxed{ \chi^2 = \Delta^{T} C_{\Delta}^{-1} \Delta },

with

\Delta = m_1-m_2.

For independent profiles,

C_{\Delta} = C_1+C_2.

If the two profiles are built from correlated samples, then the cross-covariance terms must also be included:

C_{\Delta} = C_1+C_2-C_{12}-C_{21}.

An explicitly named shape-only or scale-free comparison could still use a normalization-fitted statistic if that is desired, but it should be distinguished from the default test of equality of profile means.

So the observed scale invariance is mathematically consistent with the current "WW" implementation, but it also shows that the current TProfile::Chi2Test() is testing a different null hypothesis from the one a user would normally expect when comparing two profiles.