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

Code before:
        q = np.asarray(q, copy=True)
        q = xpx.at(q, nan_out).set(0)
        p = np.asarray(p, copy=True)
        p = xpx.at(p, nan_out).set(0.5)

    return x, q, p, alternative, axis, keepdims, axis_none, ndim, nan_out


def _quantile_test_postprocess(res, axis, axis_none, keepdims, ndim, nan_out):
    # Reshape per axis/keepdims

    res[nan_out] = -1 if np.issubdtype(res.dtype, np.integer) else np.nan

    if axis_none and keepdims:
        shape = (1,)*(ndim - 1) + res.shape
        res = np.reshape(res, shape)
        axis = -1

    res = np.moveaxis(res, -1, axis)

    if not keepdims:
        res = np.squeeze(res, axis=axis)
    return res[()]


@xp_capabilities(np_only=True)
def quantile_test(x, *, q=0, p=0.5, alternative='two-sided', axis=0, keepdims=None):
    r"""
    Perform a quantile test and compute a confidence interval of the quantile.

    This function tests the null hypothesis that `q` is the value of the
    quantile associated with probability `p` of the population underlying
    sample `x`. For example, with default parameters, it tests that the
    median of the population underlying `x` is zero. The function returns an
    object including the test statistic, a p-value, and a method for computing
    the confidence interval around the quantile.

    Parameters
    ----------
    x : array_like
        A one-dimensional sample.
    q : float, default: 0
        The hypothesized value of the quantile.
    p : float, default: 0.5
        The probability associated with the quantile; i.e. the proportion of
        the population less than `q` is `p`. Must be strictly between 0 and
        1.
    alternative : {'two-sided', 'less', 'greater'}, optional
        Defines the alternative hypothesis.
        The following options are available (default is 'two-sided'):


Review comment (suggestion):
The `statistic` of this function is an order statistic of the data, so it is an integer. This means we can't actually NaN-out the result; instead, we set it to a sentinel value that can never be valid (-1).  Do you agree that's the best we can do? We could raise a `FutureWarning` about making the output floating point, but I wanted to be able to do something now.

`stastic_type` always takes on value `1` or `2` in normal operation, so `-1` is also an appropriate sentinel.

Need to document the part about the sentinel value.

Code after:

    if (keepdims is False) and (p.shape[axis] != 1):
        message = ("`keepdims` may be False only if `p` and `q` are scalars or the "
                   "length of `p` and `q` along `axis` is 1.")
        raise ValueError(message)
    keepdims = (p.shape[axis] != 1) if keepdims is None else keepdims

    x = np.moveaxis(x, axis, -1)
    q = np.moveaxis(q, axis, -1)
    p = np.moveaxis(p, axis, -1)

    nan_out = ((p >= 1) | (p <= 0) | np.isnan(p) | np.isnan(q)
               | np.any(np.isnan(x), axis=-1, keepdims=True))
    if np.any(nan_out):
        # These get NaN-ed out at the end. In the meantime, we want them
        # to pass through calculations without warnings or errors
        q = xpx.at(q, nan_out).set(0, copy=True)
        p = xpx.at(p, nan_out).set(0.5, copy=True)

    return x, q, p, alternative, axis, keepdims, axis_none, ndim, nan_out


def _quantile_test_postprocess(res, axis, axis_none, keepdims, ndim, nan_out):
    # Reshape per axis/keepdims

    res[nan_out] = -1 if np.issubdtype(res.dtype, np.integer) else np.nan

    if axis_none and keepdims:
        shape = (1,)*(ndim - 1) + res.shape
        res = np.reshape(res, shape)
        axis = -1

    res = np.moveaxis(res, -1, axis)

    if not keepdims:
        res = np.squeeze(res, axis=axis)
    return res[()]


@xp_capabilities(np_only=True)
def quantile_test(x, *, q=0, p=0.5, alternative='two-sided', axis=0, keepdims=None):
    r"""
    Perform a quantile test and compute a confidence interval of the quantile.

    This function tests the null hypothesis that `q` is the value of the
    quantile associated with probability `p` of the population underlying
    sample `x`. For example, with default parameters, it tests that the
    median of the population underlying `x` is zero. The function returns an
    object including the test statistic, a p-value, and a method for computing
    the confidence interval around the quantile.