Repository: scipy/scipy
File: scipy/stats/tests/test_correlation.py (Python)

Code before:
from scipy import stats
from scipy.stats._axis_nan_policy import SmallSampleWarning


class TestChatterjeeXi:
    @pytest.mark.parametrize('case', [
        dict(y_cont=True, statistic=-0.303030303030303, pvalue=0.9351329808526656),
        dict(y_cont=False, statistic=0.07407407407407396, pvalue=0.3709859367123997)])
    def test_against_R_XICOR(self, case):
        # Test against R package XICOR, e.g.
        # library(XICOR)
        # options(digits=16)
        # x = c(0.11027287231363914, 0.8154770102474279, 0.7073943466920335,
        #       0.6651317324378386, 0.6905752850115503, 0.06115250587536558,
        #       0.5209906494474178, 0.3155763519785274, 0.18405731803625924,
        #       0.8613557911541495)
        # y = c(0.8402081904493103, 0.5946972833914318, 0.23481606164114155,
        #       0.49754786197715384, 0.9146460831206026, 0.5848057749217579,
        #       0.7620801065573549, 0.31410063302647495, 0.7935620302236199,
        #       0.5423085761365468)
        # xicor(x, y, ties=FALSE, pvalue=TRUE)

        rng = np.random.default_rng(25982435982346983)
        x = rng.random(size=10)

        y = (rng.random(size=10) if case['y_cont']
             else rng.integers(0, 5, size=10))
        res = stats.chatterjeexi(x, y, y_continuous=case['y_cont'])

        assert_allclose(res.statistic, case['statistic'])
        assert_allclose(res.pvalue, case['pvalue'])

    @pytest.mark.parametrize('y_continuous', (False, True))
    def test_permutation_asymptotic(self, y_continuous):
        # XICOR doesn't seem to perform the permutation test as advertised, so
        # compare the result of a permutation test against an asymptotic test.
        rng = np.random.default_rng(2524579827426)
        n = np.floor(rng.uniform(100, 150)).astype(int)
        shape = (2, n)
        x = rng.random(size=shape)
        y = (rng.random(size=shape) if y_continuous
             else rng.integers(0, 10, size=shape))
        method = stats.PermutationMethod(rng=rng)
        res = stats.chatterjeexi(x, y, method=method,
                                 y_continuous=y_continuous, axis=-1)
        ref = stats.chatterjeexi(x, y, y_continuous=y_continuous, axis=-1)
        np.testing.assert_allclose(res.statistic, ref.statistic, rtol=1e-15)
        np.testing.assert_allclose(res.pvalue, ref.pvalue, rtol=2e-2)

    def test_input_validation(self):
        rng = np.random.default_rng(25932435798274926)

Review comment (bug):
I'm always skeptical, but this was indeed needed locally to avoid failures with NumPy 1.26.4 + array API testing active.
Does the full array API testsuite for SciPy actually pass for you locally with 1.26.4? I see some issues with torch and array-api-strict in that configuration, not sure that is surprising though, and not connected to this PR I don't think (I see failures on main as well--maybe a similar shim is needed in more places, though I think also `asarray()` call signature stuff was popping up).

Code after:
from scipy._lib._array_api_no_0d import xp_assert_close
from scipy import stats
from scipy.stats._axis_nan_policy import SmallSampleWarning


@make_xp_test_case(stats.chatterjeexi)
class TestChatterjeeXi:
    @pytest.mark.parametrize('case', [
        dict(y_cont=True, statistic=-0.303030303030303, pvalue=0.9351329808526656),
        dict(y_cont=False, statistic=0.07407407407407396, pvalue=0.3709859367123997)])
    @pytest.mark.parametrize('dtype', ['float32', 'float64', None])
    def test_against_R_XICOR(self, case, dtype, xp):
        # Test against R package XICOR, e.g.
        # library(XICOR)
        # options(digits=16)
        # x = c(0.11027287231363914, 0.8154770102474279, 0.7073943466920335,
        #       0.6651317324378386, 0.6905752850115503, 0.06115250587536558,
        #       0.5209906494474178, 0.3155763519785274, 0.18405731803625924,
        #       0.8613557911541495)
        # y = c(0.8402081904493103, 0.5946972833914318, 0.23481606164114155,
        #       0.49754786197715384, 0.9146460831206026, 0.5848057749217579,
        #       0.7620801065573549, 0.31410063302647495, 0.7935620302236199,
        #       0.5423085761365468)
        # xicor(x, y, ties=FALSE, pvalue=TRUE)
        if dtype == 'float32' and np.__version__ < "2":
            pytest.skip("Scalar dtypes only respected after NEP 50.")
        dtype = xp_default_dtype(xp) if dtype is None else getattr(xp, dtype)
        rng = np.random.default_rng(25982435982346983)
        x = rng.random(size=10)
        y = (rng.random(size=10) if case['y_cont']
             else rng.integers(0, 5, size=10))

        x, y = xp.asarray(x, dtype=dtype), xp.asarray(y, dtype=dtype)
        res = stats.chatterjeexi(x, y, y_continuous=case['y_cont'])

        xp_assert_close(res.statistic, xp.asarray(case['statistic'], dtype=dtype))
        xp_assert_close(res.pvalue, xp.asarray(case['pvalue'], dtype=dtype))

    @pytest.mark.parametrize('y_continuous', (False, True))
    def test_permutation_asymptotic(self, y_continuous):
        # XICOR doesn't seem to perform the permutation test as advertised, so
        # compare the result of a permutation test against an asymptotic test.
        rng = np.random.default_rng(2524579827426)
        n = np.floor(rng.uniform(100, 150)).astype(int)
        shape = (2, n)
        x = rng.random(size=shape)
        y = (rng.random(size=shape) if y_continuous
             else rng.integers(0, 10, size=shape))
        method = stats.PermutationMethod(rng=rng)
        res = stats.chatterjeexi(x, y, method=method,
                                 y_continuous=y_continuous, axis=-1)