From 35f1eafc60a7f671fb6051093315667f34e4e7a2 Mon Sep 17 00:00:00 2001 From: Jeff Reback Date: Sat, 22 Aug 2015 17:49:29 -0400 Subject: [PATCH] DEPR: remove auto broadcasting with a time-series, xref #2304 --- doc/source/whatsnew/v0.17.0.txt | 30 +++++++++++++ pandas/core/frame.py | 11 +---- pandas/sparse/tests/test_sparse.py | 15 ++++--- pandas/tests/test_frame.py | 67 +++++++++++++++--------------- pandas/tests/test_series.py | 8 ++-- 5 files changed, 79 insertions(+), 52 deletions(-) diff --git a/doc/source/whatsnew/v0.17.0.txt b/doc/source/whatsnew/v0.17.0.txt index 599e030c3f919..1f723a9c533fe 100644 --- a/doc/source/whatsnew/v0.17.0.txt +++ b/doc/source/whatsnew/v0.17.0.txt @@ -37,6 +37,7 @@ Highlights include: - Support for ``Series.dt.strftime`` to generate formatted strings for datetime-likes, see :ref:`here ` - Development installed versions of pandas will now have ``PEP440`` compliant version strings (:issue:`9518`) - Support for reading SAS xport files, see :ref:`here ` +- Removal of the automatic TimeSeries broadcasting, deprecated since 0.8.0, see :ref:`here ` Check the :ref:`API Changes ` and :ref:`deprecations ` before updating. @@ -673,6 +674,35 @@ Removal of prior version deprecations/changes - Removal of ``na_last`` parameters from ``Series.order()`` and ``Series.sort()``, in favor of ``na_position``, xref (:issue:`5231`) - Remove of ``percentile_width`` from ``.describe()``, in favor of ``percentiles``. (:issue:`7088`) - Removal of ``colSpace`` parameter from ``DataFrame.to_string()``, in favor of ``col_space``, circa 0.8.0 version. +- Removal of automatic time-series broadcasting (:issue:`2304`) + + .. ipython :: python + + np.random.seed(1234) + df = DataFrame(np.random.randn(5,2),columns=list('AB'),index=date_range('20130101',periods=5)) + df + + Previously + + .. code-block:: python + + In [3]: df + df.A + FutureWarning: TimeSeries broadcasting along DataFrame index by default is deprecated. + Please use DataFrame. to explicitly broadcast arithmetic operations along the index + + Out[3]: + A B + 2013-01-01 0.942870 -0.719541 + 2013-01-02 2.865414 1.120055 + 2013-01-03 -1.441177 0.166574 + 2013-01-04 1.719177 0.223065 + 2013-01-05 0.031393 -2.226989 + + Current + + .. ipython :: python + + df.add(df.A,axis='index') .. _whatsnew_0170.performance: diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 6f134f0181fe0..a9979b4eb3810 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -3354,16 +3354,7 @@ def _combine_series_infer(self, other, func, level=None, fill_value=None): return self._constructor(data=self._series, index=self.index, columns=self.columns) - # teeny hack because one does DataFrame + TimeSeries all the time - if self.index.is_all_dates and other.index.is_all_dates: - warnings.warn(("TimeSeries broadcasting along DataFrame index " - "by default is deprecated. Please use " - "DataFrame. to explicitly broadcast arithmetic " - "operations along the index"), - FutureWarning) - return self._combine_match_index(other, func, level=level, fill_value=fill_value) - else: - return self._combine_match_columns(other, func, level=level, fill_value=fill_value) + return self._combine_match_columns(other, func, level=level, fill_value=fill_value) def _combine_match_index(self, other, func, level=None, fill_value=None): left, right = self.align(other, join='outer', axis=0, level=level, copy=False) diff --git a/pandas/sparse/tests/test_sparse.py b/pandas/sparse/tests/test_sparse.py index 103f3992f950a..1a76e0f3c641f 100644 --- a/pandas/sparse/tests/test_sparse.py +++ b/pandas/sparse/tests/test_sparse.py @@ -1189,14 +1189,19 @@ def _compare_to_dense(a, b, da, db, op): frame['A'].reindex(fidx[::2]), SparseSeries([], index=[])] - for op in ops: + for op in opnames: _compare_to_dense(frame, frame[::2], frame.to_dense(), - frame[::2].to_dense(), op) + frame[::2].to_dense(), getattr(operator, op)) + + # 2304, no auto-broadcasting for i, s in enumerate(series): + f = lambda a, b: getattr(a,op)(b,axis='index') _compare_to_dense(frame, s, frame.to_dense(), - s.to_dense(), op) - _compare_to_dense(s, frame, s.to_dense(), - frame.to_dense(), op) + s.to_dense(), f) + + # rops are not implemented + #_compare_to_dense(s, frame, s.to_dense(), + # frame.to_dense(), f) # cross-sectional operations series = [frame.xs(fidx[0]), diff --git a/pandas/tests/test_frame.py b/pandas/tests/test_frame.py index 022594e296c2a..9687d9b742126 100644 --- a/pandas/tests/test_frame.py +++ b/pandas/tests/test_frame.py @@ -6039,46 +6039,47 @@ def test_combineSeries(self): #added = self.mixed_int + (100*series).astype('int32') #_check_mixed_int(added, dtype = dict(A = 'int32', B = 'float64', C = 'int32', D = 'int64')) - # TimeSeries - buf = StringIO() - tmp = sys.stderr - sys.stderr = buf - try: - ts = self.tsframe['A'] - added = self.tsframe + ts - - for key, col in compat.iteritems(self.tsframe): - result = col + ts - assert_series_equal(added[key], result, check_names=False) - self.assertEqual(added[key].name, key) - if col.name == ts.name: - self.assertEqual(result.name, 'A') - else: - self.assertTrue(result.name is None) + # TimeSeries + ts = self.tsframe['A'] + + # 10890 + # we no longer allow auto timeseries broadcasting + # and require explict broadcasting + added = self.tsframe.add(ts, axis='index') + + for key, col in compat.iteritems(self.tsframe): + result = col + ts + assert_series_equal(added[key], result, check_names=False) + self.assertEqual(added[key].name, key) + if col.name == ts.name: + self.assertEqual(result.name, 'A') + else: + self.assertTrue(result.name is None) - smaller_frame = self.tsframe[:-5] - smaller_added = smaller_frame + ts + smaller_frame = self.tsframe[:-5] + smaller_added = smaller_frame.add(ts, axis='index') - self.assertTrue(smaller_added.index.equals(self.tsframe.index)) + self.assertTrue(smaller_added.index.equals(self.tsframe.index)) - smaller_ts = ts[:-5] - smaller_added2 = self.tsframe + smaller_ts - assert_frame_equal(smaller_added, smaller_added2) + smaller_ts = ts[:-5] + smaller_added2 = self.tsframe.add(smaller_ts, axis='index') + assert_frame_equal(smaller_added, smaller_added2) - # length 0 - result = self.tsframe + ts[:0] + # length 0, result is all-nan + result = self.tsframe.add(ts[:0], axis='index') + expected = DataFrame(np.nan,index=self.tsframe.index,columns=self.tsframe.columns) + assert_frame_equal(result, expected) - # Frame is length 0 - result = self.tsframe[:0] + ts - self.assertEqual(len(result), 0) + # Frame is all-nan + result = self.tsframe[:0].add(ts, axis='index') + expected = DataFrame(np.nan,index=self.tsframe.index,columns=self.tsframe.columns) + assert_frame_equal(result, expected) - # empty but with non-empty index - frame = self.tsframe[:1].reindex(columns=[]) - result = frame * ts - self.assertEqual(len(result), len(ts)) - finally: - sys.stderr = tmp + # empty but with non-empty index + frame = self.tsframe[:1].reindex(columns=[]) + result = frame.mul(ts,axis='index') + self.assertEqual(len(result), len(ts)) def test_combineFunc(self): result = self.frame * 2 diff --git a/pandas/tests/test_series.py b/pandas/tests/test_series.py index 3567c98e71bce..8f241bdc25d8b 100644 --- a/pandas/tests/test_series.py +++ b/pandas/tests/test_series.py @@ -4515,10 +4515,10 @@ def test_operators_frame(self): # rpow does not work with DataFrame df = DataFrame({'A': self.ts}) - tm.assert_almost_equal(self.ts + self.ts, (self.ts + df)['A']) - tm.assert_almost_equal(self.ts ** self.ts, (self.ts ** df)['A']) - tm.assert_almost_equal(self.ts < self.ts, (self.ts < df)['A']) - tm.assert_almost_equal(self.ts / self.ts, (self.ts / df)['A']) + tm.assert_almost_equal(self.ts + self.ts, self.ts + df['A']) + tm.assert_almost_equal(self.ts ** self.ts, self.ts ** df['A']) + tm.assert_almost_equal(self.ts < self.ts, self.ts < df['A']) + tm.assert_almost_equal(self.ts / self.ts, self.ts / df['A']) def test_operators_combine(self): def _check_fill(meth, op, a, b, fill_value=0):