Repository: scipy/scipy
File: scipy/stats/_stats_py.py (Python)

Code before:
    References
    ----------
    .. [1] Brunner, E. and Munzel, U. "The nonparametric Benhrens-Fisher
           problem: Asymptotic theory and a small-sample approximation".
           Biometrical Journal. Vol. 42(2000): 17-25.
    .. [2] Neubert, K. and Brunner, E. "A studentized permutation test for the
           non-parametric Behrens-Fisher problem". Computational Statistics and
           Data Analysis. Vol. 51(2007): 5192-5204.

    Examples
    --------
    >>> from scipy import stats
    >>> x1 = [1,2,1,1,1,1,1,1,1,1,2,4,1,1]
    >>> x2 = [3,3,4,3,1,2,3,1,1,5,4]
    >>> w, p_value = stats.brunnermunzel(x1, x2)
    >>> w
    3.1374674823029505
    >>> p_value
    0.0057862086661515377

    """
    # _axis_nan_policy decorator ensures we can work along the last axis
    xp = array_namespace(x, y)
    length_x = x.shape[-1]
    nx = _count_nonmasked(x, axis=-1)
    ny = _count_nonmasked(x, axis=-1)

    rankc = rankdata(xp.concat((x, y), axis=axis), axis=-1)
    rankcx = rankc[..., :length_x]
    rankcy = rankc[..., length_x:]
    rankcx_mean = xp.mean(rankcx, axis=-1, keepdims=True)
    rankcy_mean = xp.mean(rankcy, axis=-1, keepdims=True)
    rankx = rankdata(x, axis=-1)
    ranky = rankdata(y, axis=-1)
    rankx_mean = xp.mean(rankx, axis=-1, keepdims=True)
    ranky_mean = xp.mean(ranky, axis=-1, keepdims=True)

    temp_x = rankcx - rankx - rankcx_mean + rankx_mean
    Sx = xp.vecdot(temp_x, temp_x, axis=-1)
    Sx /= nx - 1
    temp_y = rankcy - ranky - rankcy_mean + ranky_mean
    Sy = xp.vecdot(temp_y, temp_y, axis=-1)
    Sy /= ny - 1

    rankcx_mean = xp.squeeze(rankcx_mean, axis=-1)
    rankcy_mean = xp.squeeze(rankcy_mean, axis=-1)
    wbfn = nx * ny * (rankcy_mean - rankcx_mean)
    wbfn /= (nx + ny) * xp.sqrt(nx * Sx + ny * Sy)

    if distribution == "t":
        df_numer = xp.pow(nx * Sx + ny * Sy, 2.0)

Review comment (suggestion):
```suggestion
    ny = _count_nonmasked(y, axis=-1)
```

Code after:
    Sy = xp.vecdot(temp_y, temp_y, axis=-1)
    Sy /= ny - 1

    rankcx_mean = xp.squeeze(rankcx_mean, axis=-1)
    rankcy_mean = xp.squeeze(rankcy_mean, axis=-1)
    wbfn = nx * ny * (rankcy_mean - rankcx_mean)
    wbfn /= (nx + ny) * xp.sqrt(nx * Sx + ny * Sy)

    if distribution == "t":
        df_numer = xp.pow(nx * Sx + ny * Sy, 2.0)
        df_denom = xp.pow(nx * Sx, 2.0) / (nx - 1)
        df_denom += xp.pow(ny * Sy, 2.0) / (ny - 1)
        df = df_numer / df_denom

        if not is_lazy_array(df_numer) and not is_lazy_array(df_denom) and (
                xp.any(df_numer == 0) and xp.any(df_denom == 0)):
            message = ("p-value cannot be estimated with `distribution='t' "
                       "because degrees of freedom parameter is undefined "
                       "(0/0). Try using `distribution='normal'")
            warnings.warn(message, RuntimeWarning, stacklevel=2)

        distribution = _SimpleStudentT(df)
    elif distribution == "normal":
        distribution = _SimpleNormal()
    else:
        raise ValueError(
            "distribution should be 't' or 'normal'")

    p = _get_pvalue(-wbfn, distribution, alternative, xp=xp)

    return BrunnerMunzelResult(wbfn, p)


@xp_capabilities(cpu_only=True, exceptions=['cupy', 'jax.numpy'],
    reason='Delegation for `special.stdtr` only implemented for CuPy and JAX.')
@_axis_nan_policy_factory(SignificanceResult, kwd_samples=['weights'], paired=True)
def combine_pvalues(pvalues, method='fisher', weights=None, *, axis=0):
    """
    Combine p-values from independent tests that bear upon the same hypothesis.

    These methods are intended only for combining p-values from hypothesis
    tests based upon continuous distributions.

    Each method assumes that under the null hypothesis, the p-values are
    sampled independently and uniformly from the interval [0, 1]. A test
    statistic (different for each method) is computed and a combined
    p-value is calculated based upon the distribution of this test statistic
    under the null hypothesis.

    Parameters
    ----------