Repository: scipy/scipy

Issue:
BUG: stats: wrong shape from median_absolute_deviation for arrays with size 0 and an axis.
Currently, `median_absolute_deviation` returns the scalar `nan` if the `size` of the input array is 0, even if an `axis` argument is given.  This is not correct.  The function must respect the reduction-like behavior for the shape of the arrays.  For example, if `x` has shape `(m, n, p)` and `axis=1`, the shape of the result must be `(m, p)`, even if any of `m`, `n`, or `p` are 0.

For example,

```
In [21]: z = np.ones((3, 0))                                                                                            
```
Here, just like `n.sum(z, axis=0)`, the result should be an array with shape (0,), but in the master branch, we incorrectly get the scalar `nan`:

```
In [22]: median_absolute_deviation(z, axis=0)  # from the master branch, not correct!
Out[22]: nan
```
With `axis=1`, the result should be an array with shape (3,).  The values in the array should be the same as the value produced by `median_absolute_deviation([])`, which is `nan`, so the actual result should be `array([nan, nan, nan])`.  But in the master branch, the behavior is again incorrect--instead of getting the array with shape (3,), we get the scalar `nan`:
```
In [23]: median_absolute_deviation(z, axis=1)  # from the master branch, not correct!
Out[23]: nan
```
The above code was function with the current master branch:
```
In [28]: import scipy

In [29]: scipy.__version__
Out[29]: '1.5.0.dev0+69f44c0'
```



Discussion:

Resolving patch:
diff --git a/scipy/stats/stats.py b/scipy/stats/stats.py
--- a/scipy/stats/stats.py
+++ b/scipy/stats/stats.py
@@ -2829,7 +2829,12 @@ def median_absolute_deviation(x, axis=0, center=np.median, scale=1.4826,
 
     # Consistent with `np.var` and `np.std`.
     if not x.size:
-        return np.nan
+        nan_shape = [item for i, item in enumerate(x.shape) if i != axis]
+        nan_array = np.full(nan_shape, np.nan)
+        if not nan_array.size:
+            return np.nan
+        else:
+            return nan_array
 
     contains_nan, nan_policy = _contains_nan(x, nan_policy)
 


Test patch:
diff --git a/scipy/stats/tests/test_stats.py b/scipy/stats/tests/test_stats.py
--- a/scipy/stats/tests/test_stats.py
+++ b/scipy/stats/tests/test_stats.py
@@ -1884,6 +1884,26 @@ def test_mad_empty(self):
         mad = stats.median_absolute_deviation(dat)
         assert_equal(mad, np.nan)
 
+    def test_mad_nan_shape1(self):
+        z = np.ones((3, 0))
+        mad_axis0 = stats.median_absolute_deviation(z, axis=0)
+        assert_equal(mad_axis0, np.nan)
+        mad_axis1 = stats.median_absolute_deviation(z, axis=1)
+        assert_equal(mad_axis1, np.array([np.nan, np.nan, np.nan]))
+        assert_equal(mad_axis1.shape, (3,))
+
+    def test_mad_nan_shape2(self):
+        z = np.ones((3, 0, 2))
+        mad_axis0 = stats.median_absolute_deviation(z, axis=0)
+        assert_equal(mad_axis0, np.nan)
+        mad_axis1 = stats.median_absolute_deviation(z, axis=1)
+        assert_equal(mad_axis1, np.array([[np.nan, np.nan],
+                                          [np.nan, np.nan],
+                                          [np.nan, np.nan]]))
+        assert_equal(mad_axis1.shape, (3, 2))
+        mad_axis2 = stats.median_absolute_deviation(z, axis=2)
+        assert_equal(mad_axis2, np.nan)
+
     def test_mad_nan_propagate(self):
         dat = np.array([2.20, 2.20, 2.4, 2.4, 2.5, 2.7, 2.8, 2.9, 3.03,
                 3.03, 3.10, 3.37, 3.4, 3.4, 3.4, 3.5, 3.6, 3.7, 3.7,