Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Panel tshift 4853 #4864

Merged
merged 2 commits into from
Sep 18, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/source/release.rst
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,7 @@ Bug Fixes
single column and passing a list for ``ascending``, the argument for
``ascending`` was being interpreted as ``True`` (:issue:`4839`,
:issue:`4846`)
- Fixed ``Panel.tshift`` not working. Added `freq` support to ``Panel.shift`` (:issue:`4853`)

pandas 0.12.0
-------------
Expand Down
20 changes: 20 additions & 0 deletions pandas/core/datetools.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,23 @@
isBusinessDay = BDay().onOffset
isMonthEnd = MonthEnd().onOffset
isBMonthEnd = BMonthEnd().onOffset

def _resolve_offset(freq, kwds):
if 'timeRule' in kwds or 'offset' in kwds:
offset = kwds.get('offset', None)
offset = kwds.get('timeRule', offset)
if isinstance(offset, compat.string_types):
offset = getOffset(offset)
warn = True
else:
offset = freq
warn = False

if warn:
import warnings
warnings.warn("'timeRule' and 'offset' parameters are deprecated,"
" please use 'freq' instead",
FutureWarning)

return offset

49 changes: 0 additions & 49 deletions pandas/core/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -3497,55 +3497,6 @@ def diff(self, periods=1):
new_data = self._data.diff(periods)
return self._constructor(new_data)

def shift(self, periods=1, freq=None, **kwds):
"""
Shift the index of the DataFrame by desired number of periods with an
optional time freq

Parameters
----------
periods : int
Number of periods to move, can be positive or negative
freq : DateOffset, timedelta, or time rule string, optional
Increment to use from datetools module or time rule (e.g. 'EOM')

Notes
-----
If freq is specified then the index values are shifted but the data
if not realigned

Returns
-------
shifted : DataFrame
"""
from pandas.core.series import _resolve_offset

if periods == 0:
return self

offset = _resolve_offset(freq, kwds)

if isinstance(offset, compat.string_types):
offset = datetools.to_offset(offset)

if offset is None:
indexer = com._shift_indexer(len(self), periods)
new_data = self._data.shift(indexer, periods)
elif isinstance(self.index, PeriodIndex):
orig_offset = datetools.to_offset(self.index.freq)
if offset == orig_offset:
new_data = self._data.copy()
new_data.axes[1] = self.index.shift(periods)
else:
msg = ('Given freq %s does not match PeriodIndex freq %s' %
(offset.rule_code, orig_offset.rule_code))
raise ValueError(msg)
else:
new_data = self._data.copy()
new_data.axes[1] = self.index.shift(periods, offset)

return self._constructor(new_data)

#----------------------------------------------------------------------
# Function application

Expand Down
73 changes: 68 additions & 5 deletions pandas/core/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
import pandas.core.indexing as indexing
from pandas.core.indexing import _maybe_convert_indices
from pandas.tseries.index import DatetimeIndex
from pandas.tseries.period import PeriodIndex
from pandas.core.internals import BlockManager
import pandas.core.common as com
import pandas.core.datetools as datetools
from pandas import compat, _np_version_under1p7
from pandas.compat import map, zip, lrange
from pandas.core.common import (isnull, notnull, is_list_like,
Expand Down Expand Up @@ -2667,7 +2669,40 @@ def cummin(self, axis=None, skipna=True):
result = np.minimum.accumulate(y, axis)
return self._wrap_array(result, self.axes, copy=False)

def tshift(self, periods=1, freq=None, **kwds):
def shift(self, periods=1, freq=None, axis=0, **kwds):
"""
Shift the index of the DataFrame by desired number of periods with an
optional time freq

Parameters
----------
periods : int
Number of periods to move, can be positive or negative
freq : DateOffset, timedelta, or time rule string, optional
Increment to use from datetools module or time rule (e.g. 'EOM')

Notes
-----
If freq is specified then the index values are shifted but the data
if not realigned

Returns
-------
shifted : DataFrame
"""
if periods == 0:
return self

if freq is None and not len(kwds):
block_axis = self._get_block_manager_axis(axis)
indexer = com._shift_indexer(len(self), periods)
new_data = self._data.shift(indexer, periods, axis=block_axis)
else:
return self.tshift(periods, freq, **kwds)

return self._constructor(new_data)

def tshift(self, periods=1, freq=None, axis=0, **kwds):
"""
Shift the time index, using the index's frequency if available

Expand All @@ -2677,6 +2712,8 @@ def tshift(self, periods=1, freq=None, **kwds):
Number of periods to move, can be positive or negative
freq : DateOffset, timedelta, or time rule string, default None
Increment to use from datetools module or time rule (e.g. 'EOM')
axis : int or basestring
Corresponds to the axis that contains the Index

Notes
-----
Expand All @@ -2686,19 +2723,45 @@ def tshift(self, periods=1, freq=None, **kwds):

Returns
-------
shifted : Series
shifted : NDFrame
"""
from pandas.core.datetools import _resolve_offset

index = self._get_axis(axis)
if freq is None:
freq = getattr(self.index, 'freq', None)
freq = getattr(index, 'freq', None)

if freq is None:
freq = getattr(self.index, 'inferred_freq', None)
freq = getattr(index, 'inferred_freq', None)

if freq is None:
msg = 'Freq was not given and was not set in the index'
raise ValueError(msg)

return self.shift(periods, freq, **kwds)

if periods == 0:
return self

offset = _resolve_offset(freq, kwds)

if isinstance(offset, compat.string_types):
offset = datetools.to_offset(offset)

block_axis = self._get_block_manager_axis(axis)
if isinstance(index, PeriodIndex):
orig_offset = datetools.to_offset(index.freq)
if offset == orig_offset:
new_data = self._data.copy()
new_data.axes[block_axis] = index.shift(periods)
else:
msg = ('Given freq %s does not match PeriodIndex freq %s' %
(offset.rule_code, orig_offset.rule_code))
raise ValueError(msg)
else:
new_data = self._data.copy()
new_data.axes[block_axis] = index.shift(periods, offset)

return self._constructor(new_data)

def truncate(self, before=None, after=None, copy=True):
"""Function truncate a sorted DataFrame / Series before and/or after
Expand Down
22 changes: 16 additions & 6 deletions pandas/core/internals.py
Original file line number Diff line number Diff line change
Expand Up @@ -758,17 +758,27 @@ def diff(self, n):
new_values = com.diff(self.values, n, axis=1)
return [make_block(new_values, self.items, self.ref_items, ndim=self.ndim, fastpath=True)]

def shift(self, indexer, periods):
def shift(self, indexer, periods, axis=0):
""" shift the block by periods, possibly upcast """

new_values = self.values.take(indexer, axis=1)
new_values = self.values.take(indexer, axis=axis)
# convert integer to float if necessary. need to do a lot more than
# that, handle boolean etc also
new_values, fill_value = com._maybe_upcast(new_values)
if periods > 0:
new_values[:, :periods] = fill_value

# 1-d
if self.ndim == 1:
if periods > 0:
new_values[:periods] = fill_value
else:
new_values[periods:] = fill_value

# 2-d
else:
new_values[:, periods:] = fill_value
if periods > 0:
new_values[:, :periods] = fill_value
else:
new_values[:, periods:] = fill_value
return [make_block(new_values, self.items, self.ref_items, ndim=self.ndim, fastpath=True)]

def eval(self, func, other, raise_on_error=True, try_cast=False):
Expand Down Expand Up @@ -1547,7 +1557,7 @@ def fillna(self, value, inplace=False, downcast=None):
values = self.values if inplace else self.values.copy()
return [ self.make_block(values.get_values(value), fill_value=value) ]

def shift(self, indexer, periods):
def shift(self, indexer, periods, axis=0):
""" shift the block by periods """

new_values = self.values.to_dense().take(indexer)
Expand Down
8 changes: 7 additions & 1 deletion pandas/core/panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -1017,7 +1017,7 @@ def count(self, axis='major'):

return self._wrap_result(result, axis)

def shift(self, lags, axis='major'):
def shift(self, lags, freq=None, axis='major'):
"""
Shift major or minor axis by specified number of leads/lags. Drops
periods right now compared with DataFrame.shift
Expand All @@ -1036,6 +1036,9 @@ def shift(self, lags, axis='major'):
major_axis = self.major_axis
minor_axis = self.minor_axis

if freq:
return self.tshift(lags, freq, axis=axis)

if lags > 0:
vslicer = slice(None, -lags)
islicer = slice(lags, None)
Expand All @@ -1058,6 +1061,9 @@ def shift(self, lags, axis='major'):
return self._constructor(values, items=items, major_axis=major_axis,
minor_axis=minor_axis)

def tshift(self, periods=1, freq=None, axis='major', **kwds):
return super(Panel, self).tshift(periods, freq, axis, **kwds)

def truncate(self, before=None, after=None, axis='major'):
"""Function truncates a sorted Panel before and/or after some
particular values on the requested axis
Expand Down
78 changes: 0 additions & 78 deletions pandas/core/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,6 @@
_np_version_under1p6 = LooseVersion(_np_version) < '1.6'
_np_version_under1p7 = LooseVersion(_np_version) < '1.7'

_SHOW_WARNINGS = True

class _TimeOp(object):
"""
Wrapper around Series datetime/time/timedelta arithmetic operations.
Expand Down Expand Up @@ -2917,62 +2915,6 @@ def last_valid_index(self):
#----------------------------------------------------------------------
# Time series-oriented methods

def shift(self, periods=1, freq=None, copy=True, **kwds):
"""
Shift the index of the Series by desired number of periods with an
optional time offset

Parameters
----------
periods : int
Number of periods to move, can be positive or negative
freq : DateOffset, timedelta, or offset alias string, optional
Increment to use from datetools module or time rule (e.g. 'EOM')

Returns
-------
shifted : Series
"""
if periods == 0:
return self.copy()

offset = _resolve_offset(freq, kwds)

if isinstance(offset, compat.string_types):
offset = datetools.to_offset(offset)

def _get_values():
values = self.values
if copy:
values = values.copy()
return values

if offset is None:
dtype, fill_value = _maybe_promote(self.dtype)
new_values = pa.empty(len(self), dtype=dtype)

if periods > 0:
new_values[periods:] = self.values[:-periods]
new_values[:periods] = fill_value
elif periods < 0:
new_values[:periods] = self.values[-periods:]
new_values[periods:] = fill_value

return self._constructor(new_values, index=self.index, name=self.name)
elif isinstance(self.index, PeriodIndex):
orig_offset = datetools.to_offset(self.index.freq)
if orig_offset == offset:
return self._constructor(
_get_values(), self.index.shift(periods),
name=self.name)
msg = ('Given freq %s does not match PeriodIndex freq %s' %
(offset.rule_code, orig_offset.rule_code))
raise ValueError(msg)
else:
return self._constructor(_get_values(),
index=self.index.shift(periods, offset),
name=self.name)

def asof(self, where):
"""
Return last good (non-NaN) value in TimeSeries if value is NaN for
Expand Down Expand Up @@ -3317,26 +3259,6 @@ def _try_cast(arr, take_fast_path):

return subarr

def _resolve_offset(freq, kwds):
if 'timeRule' in kwds or 'offset' in kwds:
offset = kwds.get('offset', None)
offset = kwds.get('timeRule', offset)
if isinstance(offset, compat.string_types):
offset = datetools.getOffset(offset)
warn = True
else:
offset = freq
warn = False

if warn and _SHOW_WARNINGS: # pragma: no cover
import warnings
warnings.warn("'timeRule' and 'offset' parameters are deprecated,"
" please use 'freq' instead",
FutureWarning)

return offset


# backwards compatiblity
TimeSeries = Series

Expand Down
2 changes: 1 addition & 1 deletion pandas/sparse/series.py
Original file line number Diff line number Diff line change
Expand Up @@ -605,7 +605,7 @@ def shift(self, periods, freq=None, **kwds):
"""
Analogous to Series.shift
"""
from pandas.core.series import _resolve_offset
from pandas.core.datetools import _resolve_offset

offset = _resolve_offset(freq, kwds)

Expand Down
Loading