Repository: numpy/numpy
Pull request: DOC: Update documentation of np.clip
<!-- Please be sure you are following the instructions in the dev guidelines
http://www.numpy.org/devdocs/dev/development_workflow.html
-->

<!-- We'd appreciate it if your commit message is properly formatted
http://www.numpy.org/devdocs/dev/development_workflow.html#writing-the-commit-message
-->

Fixes #15061 by informing users and  updating the documentation of np.clip

Problem statement:
np.clip: change in precedence between minimum and maximum when maximum < minimum
Greetings!

First, thanks to the NumPy community for your efforts.

Second, In NumPy 1.16.5 I observe the following:

```python
>>> import numpy as np
>>> np.clip(2, 3, 0)
3
```

In NumPy 1.17.4, I get a different result:

```python
>>> import numpy as np
>>> np.clip(2, 3, 0)
0
```
I see that the difference has to do with which constraint gets precedence. Was this an intentional change in precedence then?


### Reproducing code example:

```python
import numpy as np
assert np.clip(2, 3, 0), 3
```

### Numpy/Python version information:

```python
>>> import sys, numpy; print(numpy.__version__, sys.version)
1.17.4 3.7.4 (default, Dec  3 2019, 11:37:16) 
[GCC 5.4.0 20160609]
```

Hints from issue discussion:
As from the documentation of the function:
`No check is performed to ensure ``a_min < a_max``.`
Thanks for your response. I did see that. But the docs also say that "Equivalent to but faster than `np.maximum(a_min, np.minimum(a, a_max))`" These are no longer equivalent:

```python
>>> np.maximum(3, np.minimum(2, 0))
3
>>> np.clip(2, 3, 0)
0
```
Sounds like the doc is wrong.

Note that in the past the behavior was not as simple as you make it out to be - I believe the behavior swapped if you make the inputs arrays rather than scalars
Marking it as documentation, because at the moment I do not feel that we should undo the general clip changes. But if more concerns come up, I may change my mind.
For reference, #12519 was the change to `clip`
We knew that some of this stuff was going to happen, right? I remember a lot of confusing back-and-forth with the `hypothesis` tests & it was just really hard to get everything perfect for pathological corner cases & move forward. 

Some people also wanted an `Exception` raised because it was a little crazy to deal with `min > max`, but then performance concerns & can't easily put the handling in the inner loops and so on.

Eric can correct me if I'm wrong, but I believe the broader motivation was Travis' proposal: https://github.com/numpy/numpy/issues/12514

So maybe change in behavior for pathological cases was considered "ok" on balance to empower that broader improvent to NumPy?
Many thanks for considering this issue and providing the background to the change.

(FYI: I only noticed the change as I happened to have a StaticFrame test that was exercising the condition of maximum less than minimum.)

Commit message: DOC: Update documentation of np.clip

Patch to review:
diff --git a/numpy/core/fromnumeric.py b/numpy/core/fromnumeric.py
index f09f2a465d0e..9f55e104ce7a 100644
--- a/numpy/core/fromnumeric.py
+++ b/numpy/core/fromnumeric.py
@@ -2033,7 +2033,8 @@ def clip(a, a_min, a_max, out=None, **kwargs):
     is specified, values smaller than 0 become 0, and values larger
     than 1 become 1.
 
-    Equivalent to but faster than ``np.maximum(a_min, np.minimum(a, a_max))``.
+    Equivalent to but faster than ``np.maximum(a_min, np.minimum(a, a_max))``
+    only if ``a_min < a_max`` else returns ``a_max``.
     No check is performed to ensure ``a_min < a_max``.
 
     Parameters


Code review comments:
diff_hunk: @@ -2033,7 +2033,8 @@ def clip(a, a_min, a_max, out=None, **kwargs):
     is specified, values smaller than 0 become 0, and values larger
     than 1 become 1.
 
-    Equivalent to but faster than ``np.maximum(a_min, np.minimum(a, a_max))``.
+    Equivalent to but faster than ``np.maximum(a_min, np.minimum(a, a_max))``
+    only if ``a_min < a_max`` else returns ``a_max``.
path: numpy/core/fromnumeric.py
text: @user1:
I'd be surprised if it were this straightforward, but happy to be wrong :)

I though it was more like if `amin > amax`, strange things can happen depending on the nature of the input.

@user2:
Maybe simply "Assumes `a_min < a_max`"

@user3:
I think it actually _is_ this straightforward (whereas it didn't use to be). Bring deliberately vague here is probably good anyway though.