Skip to content

ValueError: The truth value of a Series is ambiguous

Have you written an if statement about a column and had Pandas refuse to answer?

sales = pd.DataFrame({'region': ['North', 'South', 'North'],
                      'revenue': [100, 250, 175]})

if sales['revenue'] > 150:
    print('good month')
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

The comparison itself worked. sales['revenue'] > 150 produced three answers — False, True, True — and if needs exactly one. Pandas will not guess which one you meant, and the message lists the ways to say it.

If you meant "is any row above 150":

(sales['revenue'] > 150).any()
np.True_

If you meant "is every row above 150", use .all() instead. Those two are the whole fix in most cases.

More often, you did not want if at all

The if is usually a habit carried over from writing loops. What people almost always want is the rows themselves, and for that the comparison goes straight into the brackets:

sales[sales['revenue'] > 150]
  region  revenue
1  South      250
2  North      175

No if, no any, no loop. The boolean Series selects the rows where it is True. Once that clicks, a large class of Pandas code gets shorter.

and, or and not are the same problem

Python's and, or and not all ask for a single truth value, so they raise the same error the moment a Series is involved:

sales[(sales['revenue'] > 100) and (sales['region'] == 'North')]
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

Pandas has element-wise versions: & for and, | for or, ~ for not. They compare row by row and give back a Series:

sales[(sales['revenue'] > 100) & (sales['region'] == 'North')]
  region  revenue
2  North      175

The parentheses are not decoration. & binds more tightly than > in Python, so without them the expression is read as sales['revenue'] > (100 & sales['region']) == 'North', and the error you get next will be a stranger one.

The mistakes I see most often

Reaching for .bool() because the message names it. It only works on a Series holding exactly one value, so it answers a different question from the one most people are asking. .any() and .all() are what the situation usually calls for.

Testing a whole frame for emptiness with if df. That raises too. if df.empty is the supported way, and if len(df) reads more plainly to most people.

Mixing and with & in one expression. The & half works element-wise, the and half demands a single value, and the error points at the line rather than the operator.

Practice

See loc in Pandas and query in Pandas for selecting rows by condition, and any in Pandas for reducing a boolean column to one answer.