Repository: pandas-dev/pandas

Issue:
Can't shift an empty dataframe with pandas 0.14.1 and numpy 1.7.1
We have some code which filters a dataframe, then shifts the result. However, this dataframe may end up being empty. In pandas 0.13, this works without problem:

```
In [61]: df = DataFrame(columns=['foo'])

In [62]: df.shift(-1)
Out[62]: 
Empty DataFrame
Columns: [foo]
Index: []

[0 rows x 1 columns]
```

However, pandas 0.14 throws a division by zero error:

```
In [1]: from pandas import *

In [2]: df = DataFrame(columns=['foo'])

In [3]: df.shift(-1)
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-3-6aa009807b04> in <module>()
----> 1 df.shift(-1)

/users/is/whughes/pyenvs/da497516f84bbd5b/lib/python2.7/site-packages/pandas/core/generic.pyc in shift(self, periods, freq, axis, **kwds)
   3288         block_axis = self._get_block_manager_axis(axis)
   3289         if freq is None and not len(kwds):
-> 3290             new_data = self._data.shift(periods=periods, axis=block_axis)
   3291         else:
   3292             return self.tshift(periods, freq, **kwds)

/users/is/whughes/pyenvs/da497516f84bbd5b/lib/python2.7/site-packages/pandas/core/internals.pyc in shift(self, **kwargs)
   2226 
   2227     def shift(self, **kwargs):
-> 2228         return self.apply('shift', **kwargs)
   2229 
   2230     def fillna(self, **kwargs):

/users/is/whughes/pyenvs/da497516f84bbd5b/lib/python2.7/site-packages/pandas/core/internals.pyc in apply(self, f, axes, filter, do_integrity_check, **kwargs)
   2190                                                  copy=align_copy)
   2191 
-> 2192             applied = getattr(b, f)(**kwargs)
   2193 
   2194             if isinstance(applied, list):

/users/is/whughes/pyenvs/da497516f84bbd5b/lib/python2.7/site-packages/pandas/core/internals.pyc in shift(self, periods, axis)
    789             new_values = new_values.T
    790             axis = new_values.ndim - axis - 1
--> 791         new_values = np.roll(new_values, periods, axis=axis)
    792         axis_indexer = [ slice(None) ] * self.ndim
    793         if periods > 0:

/users/is/whughes/pyenvs/da497516f84bbd5b/lib/python2.7/site-packages/numpy/core/numeric.pyc in roll(a, shift, axis)
   1145         n = a.shape[axis]
   1146         reshape = False
-> 1147     shift %= n
   1148     indexes = concatenate((arange(n-shift,n),arange(n-shift)))
   1149     res = a.take(indexes, axis)

ZeroDivisionError: integer division or modulo by zero
```

The behaviour is correct with numpy 1.8 however.



Discussion:
yep that changed in 0.14/0.14.1 to optimize. must not have a test for it. welcome a pull-request.


Resolving patch:
diff --git a/pandas/core/internals.py b/pandas/core/internals.py
--- a/pandas/core/internals.py
+++ b/pandas/core/internals.py
@@ -817,7 +817,10 @@ def shift(self, periods, axis=0):
         if f_ordered:
             new_values = new_values.T
             axis = new_values.ndim - axis - 1
-        new_values = np.roll(new_values, periods, axis=axis)
+
+        if np.prod(new_values.shape):
+            new_values = np.roll(new_values, periods, axis=axis)
+
         axis_indexer = [ slice(None) ] * self.ndim
         if periods > 0:
             axis_indexer[axis] = slice(None,periods)


Test patch:
diff --git a/pandas/tests/test_frame.py b/pandas/tests/test_frame.py
--- a/pandas/tests/test_frame.py
+++ b/pandas/tests/test_frame.py
@@ -9610,6 +9610,13 @@ def test_shift_bool(self):
                        columns=['high', 'low'])
         assert_frame_equal(rs, xp)
 
+    def test_shift_empty(self):
+        # Regression test for #8019
+        df = DataFrame({'foo': []})
+        rs = df.shift(-1)
+
+        assert_frame_equal(df, rs)
+
     def test_tshift(self):
         # PeriodIndex
         ps = tm.makePeriodFrame()