Skip to content

Commit

Permalink
BUG: Series ops with object dtype may incorrectly fail
Browse files Browse the repository at this point in the history
closes #13043
closes #13072
  • Loading branch information
sinhrks authored and jreback committed May 11, 2016
1 parent 2a99394 commit 4aa6323
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 4 deletions.
14 changes: 14 additions & 0 deletions doc/source/whatsnew/v0.18.2.txt
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,22 @@ Bug Fixes



- Bug in ``Series`` arithmetic raises ``TypeError`` if it contains datetime-like as ``object`` dtype (:issue:`13043`)



- Bug in ``NaT`` - ``Period`` raises ``AttributeError`` (:issue:`13071`)
- Bug in ``Period`` addition raises ``TypeError`` if ``Period`` is on right hand side (:issue:`13069`)
- Bug in ``pd.set_eng_float_format()`` that would prevent NaN's from formatting (:issue:`11981`)












24 changes: 20 additions & 4 deletions pandas/core/ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from pandas.tslib import iNaT
from pandas.compat import bind_method
import pandas.core.missing as missing
import pandas.algos as _algos
import pandas.core.algorithms as algos
from pandas.core.common import (is_list_like, notnull, isnull,
_values_from_object, _maybe_match_name,
Expand Down Expand Up @@ -600,6 +601,21 @@ def na_op(x, y):
result = missing.fill_zeros(result, x, y, name, fill_zeros)
return result

def safe_na_op(lvalues, rvalues):
try:
return na_op(lvalues, rvalues)
except Exception:
if isinstance(rvalues, ABCSeries):
if is_object_dtype(rvalues):
# if dtype is object, try elementwise op
return _algos.arrmap_object(rvalues,
lambda x: op(lvalues, x))
else:
if is_object_dtype(lvalues):
return _algos.arrmap_object(lvalues,
lambda x: op(x, rvalues))
raise

def wrapper(left, right, name=name, na_op=na_op):

if isinstance(right, pd.DataFrame):
Expand Down Expand Up @@ -638,17 +654,17 @@ def wrapper(left, right, name=name, na_op=na_op):
if ridx is not None:
rvalues = algos.take_1d(rvalues, ridx)

arr = na_op(lvalues, rvalues)

return left._constructor(wrap_results(arr), index=index,
result = wrap_results(safe_na_op(lvalues, rvalues))
return left._constructor(result, index=index,
name=name, dtype=dtype)
else:
# scalars
if (hasattr(lvalues, 'values') and
not isinstance(lvalues, pd.DatetimeIndex)):
lvalues = lvalues.values

return left._constructor(wrap_results(na_op(lvalues, rvalues)),
result = wrap_results(safe_na_op(lvalues, rvalues))
return left._constructor(result,
index=left.index, name=left.name,
dtype=dtype)

Expand Down
62 changes: 62 additions & 0 deletions pandas/tseries/tests/test_period.py
Original file line number Diff line number Diff line change
Expand Up @@ -4151,6 +4151,68 @@ def test_intercept_astype_object(self):
result = df.values.squeeze()
self.assertTrue((result[:, 0] == expected.values).all())

def test_ops_series_timedelta(self):
# GH 13043
s = pd.Series([pd.Period('2015-01-01', freq='D'),
pd.Period('2015-01-02', freq='D')], name='xxx')
self.assertEqual(s.dtype, object)

exp = pd.Series([pd.Period('2015-01-02', freq='D'),
pd.Period('2015-01-03', freq='D')], name='xxx')
tm.assert_series_equal(s + pd.Timedelta('1 days'), exp)
tm.assert_series_equal(pd.Timedelta('1 days') + s, exp)

tm.assert_series_equal(s + pd.tseries.offsets.Day(), exp)
tm.assert_series_equal(pd.tseries.offsets.Day() + s, exp)

def test_ops_series_period(self):
# GH 13043
s = pd.Series([pd.Period('2015-01-01', freq='D'),
pd.Period('2015-01-02', freq='D')], name='xxx')
self.assertEqual(s.dtype, object)

p = pd.Period('2015-01-10', freq='D')
# dtype will be object because of original dtype
exp = pd.Series([9, 8], name='xxx', dtype=object)
tm.assert_series_equal(p - s, exp)
tm.assert_series_equal(s - p, -exp)

s2 = pd.Series([pd.Period('2015-01-05', freq='D'),
pd.Period('2015-01-04', freq='D')], name='xxx')
self.assertEqual(s2.dtype, object)

exp = pd.Series([4, 2], name='xxx', dtype=object)
tm.assert_series_equal(s2 - s, exp)
tm.assert_series_equal(s - s2, -exp)

def test_ops_frame_period(self):
# GH 13043
df = pd.DataFrame({'A': [pd.Period('2015-01', freq='M'),
pd.Period('2015-02', freq='M')],
'B': [pd.Period('2014-01', freq='M'),
pd.Period('2014-02', freq='M')]})
self.assertEqual(df['A'].dtype, object)
self.assertEqual(df['B'].dtype, object)

p = pd.Period('2015-03', freq='M')
# dtype will be object because of original dtype
exp = pd.DataFrame({'A': np.array([2, 1], dtype=object),
'B': np.array([14, 13], dtype=object)})
tm.assert_frame_equal(p - df, exp)
tm.assert_frame_equal(df - p, -exp)

df2 = pd.DataFrame({'A': [pd.Period('2015-05', freq='M'),
pd.Period('2015-06', freq='M')],
'B': [pd.Period('2015-05', freq='M'),
pd.Period('2015-06', freq='M')]})
self.assertEqual(df2['A'].dtype, object)
self.assertEqual(df2['B'].dtype, object)

exp = pd.DataFrame({'A': np.array([4, 4], dtype=object),
'B': np.array([16, 16], dtype=object)})
tm.assert_frame_equal(df2 - df, exp)
tm.assert_frame_equal(df - df2, -exp)


if __name__ == '__main__':
import nose
Expand Down
32 changes: 32 additions & 0 deletions pandas/tseries/tests/test_timedeltas.py
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,38 @@ def test_ops_series(self):
tm.assert_series_equal(expected, td * other)
tm.assert_series_equal(expected, other * td)

def test_ops_series_object(self):
# GH 13043
s = pd.Series([pd.Timestamp('2015-01-01', tz='US/Eastern'),
pd.Timestamp('2015-01-01', tz='Asia/Tokyo')],
name='xxx')
self.assertEqual(s.dtype, object)

exp = pd.Series([pd.Timestamp('2015-01-02', tz='US/Eastern'),
pd.Timestamp('2015-01-02', tz='Asia/Tokyo')],
name='xxx')
tm.assert_series_equal(s + pd.Timedelta('1 days'), exp)
tm.assert_series_equal(pd.Timedelta('1 days') + s, exp)

# object series & object series
s2 = pd.Series([pd.Timestamp('2015-01-03', tz='US/Eastern'),
pd.Timestamp('2015-01-05', tz='Asia/Tokyo')],
name='xxx')
self.assertEqual(s2.dtype, object)
exp = pd.Series([pd.Timedelta('2 days'), pd.Timedelta('4 days')],
name='xxx')
tm.assert_series_equal(s2 - s, exp)
tm.assert_series_equal(s - s2, -exp)

s = pd.Series([pd.Timedelta('01:00:00'), pd.Timedelta('02:00:00')],
name='xxx', dtype=object)
self.assertEqual(s.dtype, object)

exp = pd.Series([pd.Timedelta('01:30:00'), pd.Timedelta('02:30:00')],
name='xxx')
tm.assert_series_equal(s + pd.Timedelta('00:30:00'), exp)
tm.assert_series_equal(pd.Timedelta('00:30:00') + s, exp)

def test_compare_timedelta_series(self):
# regresssion test for GH5963
s = pd.Series([timedelta(days=1), timedelta(days=2)])
Expand Down

0 comments on commit 4aa6323

Please sign in to comment.