Skip to content

Commit dce05cf

Browse files
committed
Merge pull request #4738 from jreback/bool_single
API: allow single element boolean Series to improve on numpy behavior( related GH4657)
2 parents ec77315 + fe97e1a commit dce05cf

File tree

8 files changed

+94
-14
lines changed

8 files changed

+94
-14
lines changed

doc/source/basics.rst

+11-2
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ Boolean Reductions
219219

220220
.. _basics.reductions:
221221

222-
Furthermore, you can apply the reduction functions: ``any()`` and ``all()`` to provide a
222+
Furthermore, you can apply the reductions: ``empty``, ``any()``, ``all()``, and ``bool()`` to provide a
223223
way to summarize these results.
224224

225225
.. ipython:: python
@@ -233,7 +233,7 @@ You can reduce to a final boolean value.
233233
234234
(df>0).any().any()
235235
236-
Finally you can test if a pandas object is empty, via the ``empty`` property.
236+
You can test if a pandas object is empty, via the ``empty`` property.
237237

238238
.. ipython:: python
239239
@@ -262,6 +262,15 @@ Finally you can test if a pandas object is empty, via the ``empty`` property.
262262
ValueError: The truth value of an array is ambiguous. Use a.empty, a.any() or a.all().
263263
264264
265+
To evaluate single-element pandas objects in a boolean context, use the method ``.bool()``:
266+
267+
.. ipython:: python
268+
269+
Series([True]).bool()
270+
Series([False]).bool()
271+
DataFrame([[True]]).bool()
272+
DataFrame([[False]]).bool()
273+
265274
See :ref:`gotchas<gotchas.truth>` for a more detailed discussion.
266275

267276

doc/source/gotchas.rst

+9
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,15 @@ or return if ``any`` value is ``True``.
5959
print("I am any")
6060
>>> I am any
6161
62+
To evaluate single-element pandas objects in a boolean context, use the method ``.bool()``:
63+
64+
.. ipython:: python
65+
66+
Series([True]).bool()
67+
Series([False]).bool()
68+
DataFrame([[True]]).bool()
69+
DataFrame([[False]]).bool()
70+
6271
See :ref:`boolean reductions<basics.reductions>` for more examples.
6372

6473
Bitwise boolean

doc/source/release.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ API Changes
241241

242242
- Infer and downcast dtype if ``downcast='infer'`` is passed to ``fillna/ffill/bfill`` (:issue:`4604`)
243243
- ``__nonzero__`` for all NDFrame objects, will now raise a ``ValueError``, this reverts back to (:issue:`1073`, :issue:`4633`)
244-
behavior.
244+
behavior. Add ``.bool()`` method to ``NDFrame`` objects to facilitate evaluating of single-element boolean Series
245245
- ``DataFrame.update()`` no longer raises a ``DataConflictError``, it now
246246
will raise a ``ValueError`` instead (if necessary) (:issue:`4732`)
247247
- ``Series.isin()`` and ``DataFrame.isin()`` now raise a ``TypeError`` when

doc/source/v0.13.0.txt

+16-2
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,14 @@ API changes
5555
index.set_names(["bob", "cranberry"], inplace=True)
5656

5757
- Infer and downcast dtype if ``downcast='infer'`` is passed to ``fillna/ffill/bfill`` (:issue:`4604`)
58+
- Remove deprecated ``Factor`` (:issue:`3650`)
59+
- Remove deprecated ``set_printoptions/reset_printoptions`` (:issue:``3046``)
60+
- Remove deprecated ``_verbose_info`` (:issue:`3215`)
5861
- ``__nonzero__`` for all NDFrame objects, will now raise a ``ValueError``, this reverts back to (:issue:`1073`, :issue:`4633`)
59-
behavior. See :ref:`gotchas<gotchas.truth>` for a more detailed discussion.
62+
behavior. Added the ``.bool()`` method to ``NDFrame`` objects to facilitate evaluating of single-element boolean Series
63+
See :ref:`gotchas<gotchas.truth>` for a more detailed discussion.
6064

61-
This prevent behaviors like (which will now all raise ``ValueError``)
65+
This prevents behaviors like (which will now all raise ``ValueError``)
6266

6367
.. code-block:: python
6468

@@ -68,6 +72,16 @@ API changes
6872
df1 and df2
6973
s1 and s2
7074

75+
76+
To evaluate single-element pandas objects in a boolean context, use the method ``.bool()``:
77+
78+
.. ipython:: python
79+
80+
Series([True]).bool()
81+
Series([False]).bool()
82+
DataFrame([[True]]).bool()
83+
DataFrame([[False]]).bool()
84+
7185
- All non-Index NDFrames (``Series``, ``DataFrame``, ``Panel``, ``Panel4D``,
7286
``SparsePanel``, etc.), now support the entire set of arithmetic operators
7387
and arithmetic flex methods (add, sub, mul, etc.). ``SparsePanel`` does not

pandas/core/generic.py

+16-1
Original file line numberDiff line numberDiff line change
@@ -596,10 +596,25 @@ def empty(self):
596596
return not all(len(self._get_axis(a)) > 0 for a in self._AXIS_ORDERS)
597597

598598
def __nonzero__(self):
599-
raise ValueError("The truth value of an array is ambiguous. Use a.empty, a.item(), a.any() or a.all().")
599+
raise ValueError("The truth value of a {0} is ambiguous. "
600+
"Use a.empty, a.bool(), a.item(), a.any() or a.all().".format(self.__class__.__name__))
600601

601602
__bool__ = __nonzero__
602603

604+
def bool(self):
605+
""" Return the bool of a single element PandasObject
606+
This must be a boolean scalar value, either True or False
607+
608+
Raise a ValueError if the PandasObject does not have exactly
609+
1 element, or that element is not boolean """
610+
v = self.squeeze()
611+
if isinstance(v, (bool,np.bool_)):
612+
return bool(v)
613+
elif np.isscalar(v):
614+
raise ValueError("bool cannot act on a non-boolean single element {0}".format(self.__class__.__name__))
615+
616+
self.__nonzero__()
617+
603618
def __abs__(self):
604619
return self.abs()
605620

pandas/core/series.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import operator
99
import types
10+
import warnings
1011

1112
from numpy import nan, ndarray
1213
import numpy as np
@@ -913,7 +914,6 @@ def to_string(self, buf=None, na_rep='NaN', float_format=None,
913914
"""
914915

915916
if nanRep is not None: # pragma: no cover
916-
import warnings
917917
warnings.warn("nanRep is deprecated, use na_rep", FutureWarning)
918918
na_rep = nanRep
919919

pandas/io/tests/test_pytables.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -2662,18 +2662,18 @@ def test_select_dtypes(self):
26622662
df = DataFrame(np.random.randn(5,2), columns =['A','B'])
26632663
df['object'] = 'foo'
26642664
df.ix[4:5,'object'] = 'bar'
2665-
df['bool'] = df['A'] > 0
2665+
df['boolv'] = df['A'] > 0
26662666
_maybe_remove(store, 'df')
26672667
store.append('df', df, data_columns = True)
26682668

2669-
expected = df[df.bool == True].reindex(columns=['A','bool'])
2669+
expected = df[df.boolv == True].reindex(columns=['A','boolv'])
26702670
for v in [True,'true',1]:
2671-
result = store.select('df', Term('bool == %s' % str(v)), columns = ['A','bool'])
2671+
result = store.select('df', Term('boolv == %s' % str(v)), columns = ['A','boolv'])
26722672
tm.assert_frame_equal(expected, result)
26732673

2674-
expected = df[df.bool == False ].reindex(columns=['A','bool'])
2674+
expected = df[df.boolv == False ].reindex(columns=['A','boolv'])
26752675
for v in [False,'false',0]:
2676-
result = store.select('df', Term('bool == %s' % str(v)), columns = ['A','bool'])
2676+
result = store.select('df', Term('boolv == %s' % str(v)), columns = ['A','boolv'])
26772677
tm.assert_frame_equal(expected, result)
26782678

26792679
# integer index

pandas/tests/test_generic.py

+35-2
Original file line numberDiff line numberDiff line change
@@ -205,11 +205,31 @@ def test_get_numeric_data_preserve_dtype(self):
205205

206206
def test_nonzero_single_element(self):
207207

208+
# allow single item via bool method
208209
s = Series([True])
209-
self.assertRaises(ValueError, lambda : bool(s))
210+
self.assert_(s.bool() is True)
210211

211212
s = Series([False])
212-
self.assertRaises(ValueError, lambda : bool(s))
213+
self.assert_(s.bool() is False)
214+
215+
# single item nan to raise
216+
for s in [ Series([np.nan]), Series([pd.NaT]), Series([True]), Series([False]) ]:
217+
self.assertRaises(ValueError, lambda : bool(s))
218+
219+
for s in [ Series([np.nan]), Series([pd.NaT])]:
220+
self.assertRaises(ValueError, lambda : s.bool())
221+
222+
# multiple bool are still an error
223+
for s in [Series([True,True]), Series([False, False])]:
224+
self.assertRaises(ValueError, lambda : bool(s))
225+
self.assertRaises(ValueError, lambda : s.bool())
226+
227+
# single non-bool are an error
228+
for s in [Series([1]), Series([0]),
229+
Series(['a']), Series([0.0])]:
230+
self.assertRaises(ValueError, lambda : bool(s))
231+
self.assertRaises(ValueError, lambda : s.bool())
232+
213233

214234
class TestDataFrame(unittest.TestCase, Generic):
215235
_typ = DataFrame
@@ -220,6 +240,19 @@ def test_rename_mi(self):
220240
index=MultiIndex.from_tuples([("A",x) for x in ["a","B","c"]]))
221241
result = df.rename(str.lower)
222242

243+
def test_nonzero_single_element(self):
244+
245+
# allow single item via bool method
246+
df = DataFrame([[True]])
247+
self.assert_(df.bool() is True)
248+
249+
df = DataFrame([[False]])
250+
self.assert_(df.bool() is False)
251+
252+
df = DataFrame([[False, False]])
253+
self.assertRaises(ValueError, lambda : df.bool())
254+
self.assertRaises(ValueError, lambda : bool(df))
255+
223256
def test_get_numeric_data_preserve_dtype(self):
224257

225258
# get the numeric data

0 commit comments

Comments
 (0)