Repository: pandas-dev/pandas

Issue:
BUG: Panel.apply (2d) doesn't work when function returns a scalar of NumPy datatype in 0.16.1
Panel.apply (2d) doesn't work when function returns a scalar of NumPy datatype in 0.16.1.

test code is as follows:

```
import pandas
import numpy

panel = pandas.Panel(numpy.random.randn(5, 5, 5))

panel.apply(lambda df: 0, axis=[1, 2]) # OK

panel.apply(lambda df: 0.0, axis=[1, 2]) # OK

panel.apply(lambda df: numpy.int64(0), axis=[1, 2]) # NG

panel.apply(lambda df: numpy.float64(0.0), axis=[1, 2]) # NG
```

result is:

```
yIn [1]: import pandas

In [2]: import numpy

In [3]: panel = pandas.Panel(numpy.random.randn(5, 5, 5))

In [4]: panel.apply(lambda df: 0, axis=[1, 2]) # OK
Out[4]:
0    0
1    0
2    0
3    0
4    0
dtype: int64

In [5]: panel.apply(lambda df: 0.0, axis=[1, 2]) # OK
Out[5]:
0    0
1    0
2    0
3    0
4    0
dtype: float64

In [6]: panel.apply(lambda df: numpy.int64(0), axis=[1, 2]) # NG
---------------------------------------------------------------------------
PandasError                               Traceback (most recent call last)
<ipython-input-6-fa750da7769d> in <module>()
----> 1 panel.apply(lambda df: numpy.int64(0), axis=[1, 2]) # NG

C:\Anaconda3\lib\site-packages\pandas\core\panel.py in apply(self, func, axis, **kwargs)
    967         # 2d-slabs
    968         if isinstance(axis, (tuple,list)) and len(axis) == 2:
--> 969             return self._apply_2d(f, axis=axis)
    970
    971         axis = self._get_axis_number(axis)

C:\Anaconda3\lib\site-packages\pandas\core\panel.py in _apply_2d(self, func, axis)
   1066             results.append((e,obj))
   1067
-> 1068         return self._construct_return_type(dict(results))
   1069
   1070     def _reduce(self, op, name, axis=0, skipna=True, numeric_only=None,

C:\Anaconda3\lib\site-packages\pandas\core\panel.py in _construct_return_type(self, result, axes)
   1122
   1123         raise PandasError('invalid _construct_return_type [self->%s] '
-> 1124                           '[result->%s]' % (self, result))
   1125
   1126     def _wrap_result(self, result, axis):

PandasError: invalid _construct_return_type [self-><class 'pandas.core.panel.Panel'>
Dimensions: 5 (items) x 5 (major_axis) x 5 (minor_axis)
Items axis: 0 to 4
Major_axis axis: 0 to 4
Minor_axis axis: 0 to 4] [result->{0: 0, 1: 0, 2: 0, 3: 0, 4: 0}]

In [7]: panel.apply(lambda df: numpy.float64(0.0), axis=[1, 2]) # NG
---------------------------------------------------------------------------
PandasError                               Traceback (most recent call last)
<ipython-input-7-e7ea1dec29c2> in <module>()
----> 1 panel.apply(lambda df: numpy.float64(0.0), axis=[1, 2]) # NG

C:\Anaconda3\lib\site-packages\pandas\core\panel.py in apply(self, func, axis, **kwargs)
    967         # 2d-slabs
    968         if isinstance(axis, (tuple,list)) and len(axis) == 2:
--> 969             return self._apply_2d(f, axis=axis)
    970
    971         axis = self._get_axis_number(axis)

C:\Anaconda3\lib\site-packages\pandas\core\panel.py in _apply_2d(self, func, axis)
   1066             results.append((e,obj))
   1067
-> 1068         return self._construct_return_type(dict(results))
   1069
   1070     def _reduce(self, op, name, axis=0, skipna=True, numeric_only=None,

C:\Anaconda3\lib\site-packages\pandas\core\panel.py in _construct_return_type(self, result, axes)
   1122
   1123         raise PandasError('invalid _construct_return_type [self->%s] '
-> 1124                           '[result->%s]' % (self, result))
   1125
   1126     def _wrap_result(self, result, axis):

PandasError: invalid _construct_return_type [self-><class 'pandas.core.panel.Panel'>
Dimensions: 5 (items) x 5 (major_axis) x 5 (minor_axis)
Items axis: 0 to 4
Major_axis axis: 0 to 4
Minor_axis axis: 0 to 4] [result->{0: 0.0, 1: 0.0, 2: 0.0, 3: 0.0, 4: 0.0}]

In [8]:
```



Discussion:
[This](https://github.com/pydata/pandas/blob/master/pandas/core/panel.py#L1096) should be

 `ndim = 0 if lib.isscalar(list(compat.itervalues(result))[0]) else ndim + 1`

then remove the logic right below

pull-requests welcome!


Resolving patch:
diff --git a/pandas/core/panel.py b/pandas/core/panel.py
--- a/pandas/core/panel.py
+++ b/pandas/core/panel.py
@@ -1093,14 +1093,10 @@ def _construct_return_type(self, result, axes=None):
         # need to assume they are the same
         if ndim is None:
             if isinstance(result,dict):
-                ndim = getattr(list(compat.itervalues(result))[0],'ndim',None)
-
-                # a saclar result
-                if ndim is None:
-                    ndim = 0
+                ndim = getattr(list(compat.itervalues(result))[0],'ndim',0)
 
                 # have a dict, so top-level is +1 dim
-                else:
+                if ndim != 0:
                     ndim += 1
 
         # scalar


Test patch:
diff --git a/pandas/tests/test_panel.py b/pandas/tests/test_panel.py
--- a/pandas/tests/test_panel.py
+++ b/pandas/tests/test_panel.py
@@ -1233,6 +1233,25 @@ def test_apply_slabs(self):
         expected = p.sum(0)
         assert_frame_equal(result,expected)
 
+    def test_apply_no_or_zero_ndim(self):
+        # GH10332
+        self.panel = Panel(np.random.rand(5, 5, 5))
+
+        result_int = self.panel.apply(lambda df: 0, axis=[1, 2])
+        result_float = self.panel.apply(lambda df: 0.0, axis=[1, 2])
+        result_int64 = self.panel.apply(lambda df: np.int64(0), axis=[1, 2])
+        result_float64 = self.panel.apply(lambda df: np.float64(0.0),
+                                          axis=[1, 2])
+
+        expected_int = expected_int64 = Series([0] * 5)
+        expected_float = expected_float64 = Series([0.0] * 5)
+
+        assert_series_equal(result_int, expected_int)
+        assert_series_equal(result_int64, expected_int64)
+        assert_series_equal(result_float, expected_float)
+        assert_series_equal(result_float64, expected_float64)
+
+
     def test_reindex(self):
         ref = self.panel['ItemB']