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

Code before:

    axis_none = axis is None
    ndim = max(x.ndim, p.ndim)
    if axis_none:
        x = np.ravel(x)
        q = np.ravel(q)
        p = np.ravel(p)
        axis = 0
    elif np.iterable(axis) or int(axis) != axis:
        message = "`axis` must be an integer or None."
        raise ValueError(message)
    elif (axis >= ndim) or (axis < -ndim):
        message = "`axis` is not compatible with the shapes of the inputs."
        raise ValueError(message)
    axis = int(axis)

    alternatives = {'two-sided', 'less', 'greater'}
    message = f"`alternative` must be one of {alternatives}"
    if alternative not in alternatives:
        raise ValueError(message)

    if keepdims not in {None, True, False}:
        message = "If specified, `keepdims` must be True or False."
        raise ValueError(message)

    x = np.sort(x, axis=axis, stable=False)
    q, p = np.broadcast_arrays(q, p)  # length along axis matters for q and p
    x, q, p = _broadcast_arrays((x, q, p), axis=axis)

    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 = 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


Review comment (suggestion):
`stable` not in NumPy < 2.0. We can add it back when this is translated to array API.
```suggestion
    x = np.sort(x, axis=axis)
```

Code after:
def quantile_test_iv(x, q, p, alternative, axis, keepdims):

    x = np.atleast_1d(x)
    message = '`x` must be an array of numbers.'
    if not np.issubdtype(x.dtype, np.number):
        raise ValueError(message)

    q = np.array(q)[()]
    message = "`q` must be a scalar or array of numbers."
    if not np.issubdtype(q.dtype, np.number):
        raise ValueError(message)

    p = np.array(p)[()]
    message = "`p` must be a scalar or array of floats."
    if not np.issubdtype(p.dtype, np.inexact):
        raise ValueError(message)

    axis_none = axis is None
    ndim = max(x.ndim, p.ndim)
    if axis_none:
        x = np.ravel(x)
        q = np.ravel(q)
        p = np.ravel(p)
        axis = 0
    elif np.iterable(axis) or int(axis) != axis:
        message = "`axis` must be an integer or None."
        raise ValueError(message)
    elif (axis >= ndim) or (axis < -ndim):
        message = "`axis` is not compatible with the shapes of the inputs."
        raise ValueError(message)
    axis = int(axis)

    alternatives = {'two-sided', 'less', 'greater'}
    message = f"`alternative` must be one of {alternatives}"
    if alternative not in alternatives:
        raise ValueError(message)

    if keepdims not in {None, True, False}:
        message = "If specified, `keepdims` must be True or False."
        raise ValueError(message)

    x = np.sort(x, axis=axis)
    q, p = np.broadcast_arrays(q, p)  # length along axis matters for q and p
    x, q, p = _broadcast_arrays((x, q, p), axis=axis)

    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