Repository: numpy/numpy

Issue:
BUG: ma: Several functions in the ma module return None when given a scalar.
Here's an example, using `ma.atleast_1d`:

```
In [4]: np.__version__
Out[4]: '1.8.0.dev-31a5501'
```

When given a scalar, the regular version of `atleast_1d` returns an array:

```
In [5]: x = np.atleast_1d(1.0)

In [6]: x
Out[6]: array([ 1.])
```

`ma.atleast_1d` returns `None`:

```
In [7]: x = np.ma.atleast_1d(1.0)

In [8]: print x
None
```

`ma.atleast_1d` and several other functions in the `ma` module are defined using the class `_fromnxfunction` (defined in numpy/ma/extras.py). In the `__call__` method of this class, in the case where `len(args) == 1`, the code handles the cases where the single argument is an ndarray, a tuple or list.  If the single argument is not one of these, the code falls through to the end without returning anything.

The non-masked versions of these accept a scalar, so the masked version should too:

```
atleast_1d
atleast_2d
atleast_3d
diagflat
```



Discussion:

Resolving patch:
diff --git a/numpy/ma/extras.py b/numpy/ma/extras.py
--- a/numpy/ma/extras.py
+++ b/numpy/ma/extras.py
@@ -270,6 +270,10 @@ def __call__(self, *args, **params):
                 _d = func(tuple([np.asarray(a) for a in x]), **params)
                 _m = func(tuple([getmaskarray(a) for a in x]), **params)
                 return masked_array(_d, mask=_m)
+            else:
+                _d = func(np.asarray(x), **params)
+                _m = func(getmaskarray(x), **params)
+                return masked_array(_d, mask=_m)
         else:
             arrays = []
             args = list(args)


Test patch:
diff --git a/numpy/ma/tests/test_extras.py b/numpy/ma/tests/test_extras.py
--- a/numpy/ma/tests/test_extras.py
+++ b/numpy/ma/tests/test_extras.py
@@ -23,11 +23,12 @@
     nomask, ones, zeros, count
     )
 from numpy.ma.extras import (
-    atleast_2d, mr_, dot, polyfit, cov, corrcoef, median, average, unique,
-    setxor1d, setdiff1d, union1d, intersect1d, in1d, ediff1d,
-    apply_over_axes, apply_along_axis, compress_nd, compress_rowcols,
+    atleast_1d, atleast_2d, atleast_3d, mr_, dot, polyfit, cov, corrcoef,
+    median, average, unique, setxor1d, setdiff1d, union1d, intersect1d, in1d,
+    ediff1d, apply_over_axes, apply_along_axis, compress_nd, compress_rowcols,
     mask_rowcols, clump_masked, clump_unmasked, flatnotmasked_contiguous,
-    notmasked_contiguous, notmasked_edges, masked_all, masked_all_like
+    notmasked_contiguous, notmasked_edges, masked_all, masked_all_like,
+    diagflat
     )
 import numpy.ma.extras as mae
 
@@ -1147,6 +1148,25 @@ def test_atleast2d(self):
         assert_equal(a.shape, (3,))
         assert_equal(a.mask.shape, a.data.shape)
 
+    def test_shape_scalar(self):
+        # the atleast and diagflat function should work with scalars
+        # GitHub issue #3367
+        b = atleast_1d(1.0)
+        assert_equal(b.shape, (1, ))
+        assert_equal(b.mask.shape, b.data.shape)
+
+        b = atleast_2d(1.0)
+        assert_equal(b.shape, (1, 1))
+        assert_equal(b.mask.shape, b.data.shape)
+
+        b = atleast_3d(1.0)
+        assert_equal(b.shape, (1, 1, 1))
+        assert_equal(b.mask.shape, b.data.shape)
+
+        b = diagflat(1.0)
+        assert_equal(b.shape, (1, 1))
+        assert_equal(b.mask.shape, b.data.shape)
+
 
 if __name__ == "__main__":
     run_module_suite()