[FIXED] “ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all()”

pandas Series is a one-dimensional labelled data structure that may store texts, numbers, and even other Python objects. It is one of the fundamental data structure in pandas for holding one-dimensional data and is constructed on top of numpy array. In a lot of situation, you may need to use it in conjunction with boolean operations, and, or, or not. You may encounter the following error while doing so : ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

This article is going to show you the reason behind "ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all()" error, as well as possible ways to fix it.

ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all()

What does ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all() really means?

Every time you run an expression with operands and operators, the Python tries to evaluate individual values to boolean. This is what called "truthy" or "falsy" values. A "truthy" value will satisfy the check performed by if or while statements, or operators.

In Python, all values are considered "truthy" except for the following, which are "falsy":

  • None
  • False
  • 0
  • 0.0
  • 0j
  • decimal.Decimal(0)
  • fraction.Fraction(0, 1)
  • [] – an empty list
  • {} – an empty dict
  • () – an empty tuple
  • '' – an empty str
  • b'' – an empty bytes
  • set() – an empty set
  • an empty range, like range(0)
  • objects for which
    • obj.__bool__() returns False
    • obj.__len__() returns 0

Now, to be able to define whether a Series is truthy or falsy is tricky. A Series can contains both True and False values, Python doesn’t know which value to use, meaning that the series has an ambiguous truth value. It would cause so much confusion if we provide a default truthy/valsy value for a Series object. Look at the example below to see what I mean:

In [1]: if pd.Series([False, True, False]):
   ...:     print("The Series is true")
   ...: 
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-3-fa1755415a60> in <module>
----> 1 if pd.Series([False, True, False]):
      2     print("The Series is true")
      3 

~/.local/share/virtualenvs/testing-p6fO7ldL/lib/python3.8/site-packages/pandas/core/generic.py in __nonzero__(self)
   1535     @final
   1536     def __nonzero__(self):
-> 1537         raise ValueError(
   1538             f"The truth value of a {type(self).__name__} is ambiguous. "
   1539             "Use a.empty, a.bool(), a.item(), a.any() or a.all()."

ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

Avoid "ValueError: The truth value of a Series is ambiguous"

Now that you know that a Series cannot hold a "truthy" or "falsy" value, depending on what you want to do, you can use one of these attributes/methods : Series.empty, Series.bool(), Series.item(), Series.any() or Series.all(). You can also use bitwise and/or (| and &) to compare two Series element-by-element.

Compare two Series of the same size using bitwise operations

Let’s say s1 and s2 are two Series with the same size, s1|s2 does a bitwise OR and s1&s2 does a bitwise AND. This is useful if the Series contains only boolean or numerical values.

In [13]: s1 = pd.Series([1, 2, 3, 4])

In [14]: s2 = pd.Series([5, 6, 7, 8])

In [15]: s1|s2
Out[15]: 
0     5
1     6
2     7
3    12
dtype: int64

In [16]: s1&s2
Out[16]: 
0    1
1    2
2    3
3    0
dtype: int64

Alternatively, you can import numpy and use its logical_or and logical_and methods.

In [35]: import numpy as np
In [36]: np.logical_and(s1, s2)
Out[36]: 
0    True
1    True
2    True
3    True
dtype: bool

In [37]: 

In [37]: np.logical_or(s1, s2)
Out[37]: 
0    True
1    True
2    True
3    True
dtype: bool

Filter DataFrame column values based on a condition

Let’s say you have a DataFrame like the ones below, and you want to get the rows that is priced below 50000.

import pandas as pd

df = pd.DataFrame.from_dict({
    'manufacturer': ['HP', 'DELL', 'LENOVO', 'MSI'], 
    'processor_i': ['3', '5', '7', '5'],
    'price': [30000, 45000, 80000, 55000],
})

Normally, new users would extract the price column and put it into an if/else block of code, but it will produce the "ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all()" error.

In [1]: if df['price'] < 40000:
    ...:     print(df)
    ...: 
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-21-4411513fbfb9> in <module>
----> 1 if df['price'] < 40000:
      2     print(df)
      3 

~/.local/share/virtualenvs/testing-p6fO7ldL/lib/python3.8/site-packages/pandas/core/generic.py in __nonzero__(self)
   1535     @final
   1536     def __nonzero__(self):
-> 1537         raise ValueError(
   1538             f"The truth value of a {type(self).__name__} is ambiguous. "
   1539             "Use a.empty, a.bool(), a.item(), a.any() or a.all()."

ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

What you should do instead is applying the condition directly into the DataFrame by putting it into square brackets.

In [23]: df[df['price'] < 50000]
Out[23]: 
  manufacturer processor_i  price
0           HP           3  30000
1         DELL           5  45000

Use Series.any() to evaluate Series values

Series.any() is kind of similar to Python’s any(). It returns True if any element in the Series is True or "truthy", and returns False otherwise.

In [26]: df = pd.DataFrame.from_dict({
    ...:     'manufacturer': ['HP', 'DELL', 'LENOVO', 'MSI'],
    ...:     'processor_i': ['3', '5', '7', '5'],
    ...:     'price': [30000, 45000, 80000, 55000],
    ...: })

In [27]: if (df['price'] < 50000).any():
    ...:     print(df)
    ...: 
  manufacturer processor_i  price
0           HP           3  30000
1         DELL           5  45000
2       LENOVO           7  80000
3          MSI           5  55000

Use Series.all() to obtain truthy value from Series

Series.all() and Python all() works the same way. The function return True if all element in the Series is True or "truthy", and returns False otherwise. Below is how we can apply it to our example.

In [29]: if (df['price'] < 100000).all():
    ...:     print(df)
    ...: 
  manufacturer processor_i  price
0           HP           3  30000
1         DELL           5  45000
2       LENOVO           7  80000
3          MSI           5  55000

Use Series.empty, Series.bool() and Series.item()

If you are doing stuff with loops and conditions, Series.empty, Series.bool() and Series.item() can be helpful.

For example, if you just want to check if series contains more than zero elements, then you can use .empty.

>>> series_x = pd.Series([])
>>> print(series_x.empty)
True

.bool() is used when there is only one element in the series and in boolean type, whereas .item() return the only element of single item series.

>>> series_x = pd.Series([0])
>>> series_y = pd.Series([1])
>>> series_z = pd.Series([True])
>>> series_u = pd.Series([False])
>>> series_v = pd.Series([50])
>>> series_w = pd.Series([50, 60])

>>> print(series_x.bool())
Output: Error! Works with boolean values only

>>> print(series_z.bool())
True

>>> print(series_u.bool())
False

>>> print(series_x.item())
0

>>> print(series_w.item())
Output: Error! Works with single value series only

>>> print(series_z.item())
True

Conclusion

We hope that the explanation above helps you understand how "ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all()" error happened, as well as how to avoid seeing it in the future. If you notice any problem with the article, don’t hesitate to let us know in the comments below.

Click to rate this post!
[Total: 12 Average: 4.8]

Leave a Comment