From a79f4d6a12d8884b54a49f1f7c811b08a6796dc4 Mon Sep 17 00:00:00 2001 From: Eric Chlebek Date: Tue, 17 Apr 2012 10:22:50 -0700 Subject: [PATCH 1/2] BUG Deprecating use of bool() on DataFrame, GH #1069 Calling bool() on DataFrame raises issues described in #1069. This change causes DataFrame to raise a ValueError when called with bool(), and instead provides a new property, dataframe.empty. --- pandas/core/frame.py | 14 +++++++++----- pandas/sparse/frame.py | 2 +- pandas/sparse/tests/test_sparse.py | 2 +- pandas/stats/plm.py | 2 +- pandas/tests/test_frame.py | 27 +++++++++++++++++---------- 5 files changed, 29 insertions(+), 18 deletions(-) diff --git a/pandas/core/frame.py b/pandas/core/frame.py index 280f2fbf2fb0d..d3e800b133e19 100644 --- a/pandas/core/frame.py +++ b/pandas/core/frame.py @@ -449,9 +449,12 @@ def shape(self): #---------------------------------------------------------------------- # Class behavior + @property + def empty(self): + return not (len(self.columns) > 0 and len(self.index) > 0) + def __nonzero__(self): - # e.g. "if frame: ..." - return len(self.columns) > 0 and len(self.index) > 0 + raise ValueError("Cannot call bool() on DataFrame.") def _need_info_repr_(self): """ @@ -2652,8 +2655,9 @@ def _combine_match_columns(self, other, func, fill_value=None): columns=left.columns, copy=False) def _combine_const(self, other, func): - if not self: + if self.empty: return self + result_values = func(self.values, other) if not isinstance(result_values, np.ndarray): @@ -2691,10 +2695,10 @@ def combine(self, other, func, fill_value=None): ------- result : DataFrame """ - if not other: + if other.empty: return self.copy() - if not self: + if self.empty: return other.copy() this, other = self.align(other, copy=False) diff --git a/pandas/sparse/frame.py b/pandas/sparse/frame.py index 06008dde09b82..f40e7508d2aeb 100644 --- a/pandas/sparse/frame.py +++ b/pandas/sparse/frame.py @@ -415,7 +415,7 @@ def _combine_frame(self, other, func, fill_value=None, level=None): if level is not None: raise NotImplementedError - if not self and not other: + if self.empty and other.empty: return SparseDataFrame(index=new_index) new_data = {} diff --git a/pandas/sparse/tests/test_sparse.py b/pandas/sparse/tests/test_sparse.py index 6ffa82f6658e7..1241c0032a807 100644 --- a/pandas/sparse/tests/test_sparse.py +++ b/pandas/sparse/tests/test_sparse.py @@ -870,7 +870,7 @@ def _compare_to_dense(a, b, da, db, op): def test_op_corners(self): empty = self.empty + self.empty - self.assert_(not empty) + self.assert_(empty.empty) foo = self.frame + self.empty self.assert_(isinstance(foo.index, DatetimeIndex)) diff --git a/pandas/stats/plm.py b/pandas/stats/plm.py index f5a077c1a9bb2..cee6ee5a76e82 100644 --- a/pandas/stats/plm.py +++ b/pandas/stats/plm.py @@ -149,7 +149,7 @@ def _filter_data(self): x = data_long.filter(x_names) y = data_long['__y__'] - if self._weights: + if self._weights is not None and not self._weights.empty: weights = data_long['__weights__'] else: weights = None diff --git a/pandas/tests/test_frame.py b/pandas/tests/test_frame.py index 8837fb3a3888a..9d77dc0f65a07 100644 --- a/pandas/tests/test_frame.py +++ b/pandas/tests/test_frame.py @@ -1975,17 +1975,17 @@ def test_get_agg_axis(self): self.assertRaises(Exception, self.frame._get_agg_axis, 2) def test_nonzero(self): - self.assertFalse(self.empty) + self.assertTrue(self.empty.empty) - self.assert_(self.frame) - self.assert_(self.mixed_frame) + self.assertFalse(self.frame.empty) + self.assertFalse(self.mixed_frame.empty) # corner case df = DataFrame({'A' : [1., 2., 3.], 'B' : ['a', 'b', 'c']}, index=np.arange(3)) del df['A'] - self.assert_(df) + self.assertFalse(df.empty) def test_repr(self): buf = StringIO() @@ -2329,7 +2329,7 @@ def test_combineFrame(self): self.assert_(np.isnan(empty_plus.values).all()) empty_empty = self.empty + self.empty - self.assert_(not empty_empty) + self.assertTrue(empty_empty.empty) # out of order reverse = self.frame.reindex(columns=self.frame.columns[::-1]) @@ -3360,7 +3360,7 @@ def test_reindex(self): # length zero newFrame = self.frame.reindex([]) - self.assert_(not newFrame) + self.assert_(newFrame.empty) self.assertEqual(len(newFrame.columns), len(self.frame.columns)) # length zero with columns reindexed with non-empty index @@ -3415,7 +3415,7 @@ def test_reindex_columns(self): # length zero newFrame = self.frame.reindex(columns=[]) - self.assert_(not newFrame) + self.assert_(newFrame.empty) def test_reindex_fill_value(self): df = DataFrame(np.random.randn(10, 4)) @@ -3738,10 +3738,10 @@ def test_apply(self): # empty applied = self.empty.apply(np.sqrt) - self.assert_(not applied) + self.assert_(applied.empty) applied = self.empty.apply(np.mean) - self.assert_(not applied) + self.assert_(applied.empty) no_rows = self.frame[:0] result = no_rows.apply(lambda x: x.mean()) @@ -5121,13 +5121,20 @@ def test_stale_cached_series_bug_473(self): self.assert_(isnull(Y['g']['c'])) def test_index_namedtuple(self): + # Skipping until 1026 is properly resolved + raise nose.SkipTest from collections import namedtuple IndexType = namedtuple("IndexType", ["a", "b"]) idx1 = IndexType("foo", "bar") idx2 = IndexType("baz", "bof") index = Index([idx1, idx2], name="composite_index") df = DataFrame([(1, 2), (3, 4)], index=index, columns=["A", "B"]) - self.assertEqual(df.ix[IndexType("foo", "bar")], (1, 2)) + print df.ix[IndexType("foo", "bar")]["A"] + self.assertEqual(df.ix[IndexType("foo", "bar")]["A"], 1) + + def test_bool_raises_value_error_1069(self): + df = DataFrame([1, 2, 3]) + self.failUnlessRaises(ValueError, lambda: bool(df)) if __name__ == '__main__': # unittest.main() From de427aaffef958e64b9436cb76900705e7a9ae76 Mon Sep 17 00:00:00 2001 From: Eric Chlebek Date: Tue, 17 Apr 2012 13:32:20 -0700 Subject: [PATCH 2/2] Prevent tuple sublcass instances from being recognized as "list-like". Close #1026 --- pandas/core/indexing.py | 5 ++++- pandas/tests/test_frame.py | 1 - 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pandas/core/indexing.py b/pandas/core/indexing.py index 0b4e83f13ac51..e668069760574 100644 --- a/pandas/core/indexing.py +++ b/pandas/core/indexing.py @@ -486,7 +486,10 @@ def _is_label_like(key): def _is_list_like(obj): - return np.iterable(obj) and not isinstance(obj, basestring) + # Consider namedtuples to be not list like as they are useful as indices + return (np.iterable(obj) + and not isinstance(obj, basestring) + and not (isinstance(obj, tuple) and type(obj) is not tuple)) def _need_slice(obj): diff --git a/pandas/tests/test_frame.py b/pandas/tests/test_frame.py index 9d77dc0f65a07..9daeaec3c4422 100644 --- a/pandas/tests/test_frame.py +++ b/pandas/tests/test_frame.py @@ -5122,7 +5122,6 @@ def test_stale_cached_series_bug_473(self): def test_index_namedtuple(self): # Skipping until 1026 is properly resolved - raise nose.SkipTest from collections import namedtuple IndexType = namedtuple("IndexType", ["a", "b"]) idx1 = IndexType("foo", "bar")