Repository: pandas-dev/pandas

Issue:
Panel.shift does not respect dtypes 13.1
Because the 13.1 shift uses `.values`, it will upcast mixed dtypes to object. 

``` python
import pandas as pd
import numpy as np
import pandas.util.testing as tm


data = [('item '+ch, tm.makeMixedDataFrame()) for ch in list('abcde')]
data = dict(data)
mixed_panel = pd.Panel.from_dict(data, orient='minor')
shifted = mixed_panel.shift(1)
tm.assert_series_equal(mixed_panel.dtypes, shifted.dtypes) #fails
```

Have PR for this.



Discussion:

Resolving patch:
diff --git a/pandas/core/generic.py b/pandas/core/generic.py
--- a/pandas/core/generic.py
+++ b/pandas/core/generic.py
@@ -3227,6 +3227,42 @@ def shift(self, periods=1, freq=None, axis=0, **kwds):
 
         return self._constructor(new_data).__finalize__(self)
 
+    def slice_shift(self, periods=1, axis=0, **kwds):
+        """
+        Equivalent to `shift` without copying data. The shifted data will
+        not include the dropped periods and the shifted axis will be smaller
+        than the original.
+
+        Parameters
+        ----------
+        periods : int
+            Number of periods to move, can be positive or negative
+
+        Notes
+        -----
+        While the `slice_shift` is faster than `shift`, you may pay for it
+        later during alignment.
+
+        Returns
+        -------
+        shifted : same type as caller
+        """
+        if periods == 0:
+            return self
+
+        if periods > 0:
+            vslicer = slice(None, -periods)
+            islicer = slice(periods, None)
+        else:
+            vslicer = slice(-periods, None)
+            islicer = slice(None, periods)
+
+        new_obj = self._slice(vslicer, axis=axis)
+        shifted_axis = self._get_axis(axis)[islicer]
+        new_obj.set_axis(axis, shifted_axis)
+
+        return new_obj.__finalize__(self)
+
     def tshift(self, periods=1, freq=None, axis=0, **kwds):
         """
         Shift the time index, using the index's frequency if available
diff --git a/pandas/core/panel.py b/pandas/core/panel.py
--- a/pandas/core/panel.py
+++ b/pandas/core/panel.py
@@ -1154,7 +1154,8 @@ def count(self, axis='major'):
     @deprecate_kwarg(old_arg_name='lags', new_arg_name='periods')
     def shift(self, periods=1, freq=None, axis='major'):
         """
-        Shift major or minor axis by specified number of leads/lags.
+        Shift major or minor axis by specified number of leads/lags. Drops
+        periods right now compared with DataFrame.shift
 
         Parameters
         ----------
@@ -1171,7 +1172,7 @@ def shift(self, periods=1, freq=None, axis='major'):
         if axis == 'items':
             raise ValueError('Invalid axis')
 
-        return super(Panel, self).shift(periods, freq=freq, axis=axis)
+        return super(Panel, self).slice_shift(periods, axis=axis)
 
     def tshift(self, periods=1, freq=None, axis='major', **kwds):
         return super(Panel, self).tshift(periods, freq, axis, **kwds)


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
@@ -21,7 +21,8 @@
                                  assert_almost_equal,
                                  ensure_clean,
                                  assertRaisesRegexp,
-                                 makeCustomDataframe as mkdf
+                                 makeCustomDataframe as mkdf,
+                                 makeMixedDataFrame
     )
 import pandas.core.panel as panelm
 import pandas.util.testing as tm
@@ -1652,10 +1653,17 @@ def test_shift(self):
 
         # negative numbers, #2164
         result = self.panel.shift(-1)
-        expected = Panel(dict((i, f.shift(-1))
+        expected = Panel(dict((i, f.shift(-1)[:-1])
                               for i, f in compat.iteritems(self.panel)))
         assert_panel_equal(result, expected)
 
+        # mixed dtypes #6959
+        data = [('item '+ch, makeMixedDataFrame()) for ch in list('abcde')]
+        data = dict(data)
+        mixed_panel = Panel.from_dict(data, orient='minor')
+        shifted = mixed_panel.shift(1)
+        assert_series_equal(mixed_panel.dtypes, shifted.dtypes)
+
     def test_tshift(self):
         # PeriodIndex
         ps = tm.makePeriodPanel()