1

I'm about 2 weeks into Python programming. Tried to search on the forum about the error stated in my title however I still do not understand what is wrong with my code.

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

What I'm trying to do:

  • Insert a defined function into my dictionary which is to determine the threshold level based on the DataFrame ['LocalCurrency'] value.

  • If my DataFrame ['Country'] consist of a key from the dictionary (e.g. Singapore), run the function and place the returning value in a new column called 'Threshold level'.

def convertsgp(x):    
    if x <= 99:
        y = 'Nominal'
    elif x <= 349:
        y = 'Threshold 1'
    elif x <= 1399:
        y = 'Threshold 2'
    elif x > 1400:
        y = 'Threshold 3'
    return y



mydict = {'Singapore': convertsgp }


for i in list(mydict.keys()):
    df.loc[(df['Country']== i, 'Threshold Level'] = mydict [i](df['LocalCurrency'])

Your inputs are greatly appreciated, thank you!

Tom Wojcik
  • 5,471
  • 4
  • 32
  • 44
Zongyi
  • 15
  • 4
  • Does this answer your question? [Truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all()](https://stackoverflow.com/questions/36921951/truth-value-of-a-series-is-ambiguous-use-a-empty-a-bool-a-item-a-any-o) – Tom Wojcik Dec 26 '19 at 10:27
  • @Zongyi hope - I've helped you - if you still have any problems - feel free to ask it. If no - I'll be thankful if you can mark an answer as correct. – Stas Buzuluk Dec 26 '19 at 10:56
  • Edited my answer with a better approach. – Stas Buzuluk Dec 26 '19 at 11:14

2 Answers2

0

Actual error - you see - is raised, because you are trying to use series as a key of the dictionary. To avoid it - you have to get a single element from this series. There are, also, a lot of other problems in your code. You can solve it like this:

import pandas as pd 
data = {
        'Threshold Level':[0, 0, 0, 0, 0], 
        'Country':['Itally', 'Singapore', 'Ukraine', 'USA', 'England'],
        'LocalCurrency': [100, 2000, 392, 3,23 ]
        } 

# Convert the dictionary into DataFrame  
df = pd.DataFrame(data) 

def convertsgp(x):

    if x <= 99:
        y = 'Nominal'
    elif x <= 349:
        y = 'Threshold 1'
    elif x <= 1399:
        y = 'Threshold 2'
    elif x > 1400:
        y = 'Threshold 3'
    return y


mydict = {'Singapore': convertsgp }


for i in list(mydict.keys()):
    local_currency = df.loc[df['Country']== i]['LocalCurrency']
    df.loc[df['Country']== i, 'Threshold Level'] = mydict[i](local_currency[1])

Also - if you don't have all the countries in dict - a better approach is to use apply, like this, it'll work much faster for big DataFrames and, also - it looks better:

import pandas as pd 
data = {
        'ThresholdLevel':[0, 0, 0, 0, 0], 
        'Country':['Itally', 'Singapore', 'Ukraine', 'USA', 'England'],
        'LocalCurrency': [100, 2000, 392, 3,23 ]
        } 

# Convert the dictionary into DataFrame  
df = pd.DataFrame(data) 

def convertsgp(x):
    if x <= 99:
        y = 'Nominal'
    elif x <= 349:
        y = 'Threshold 1'
    elif x <= 1399:
        y = 'Threshold 2'
    elif x > 1400:
        y = 'Threshold 3'
    return y

mydict = {'Singapore': convertsgp}

def apply_corresponding_function(country, value_to_apply_on):

    try: # lets try to get function we need to apply
        function_to_apply = mydict[country] # if there's no such key in dictionaries it'll raise an error
        return function_to_apply(value_to_apply_on) 

    except: # this will be executed, if operations after 'try' raised any errors:
        return False


df['ThresholdLevel'] = df.apply(lambda x: apply_corresponding_function(x['Country'], x['LocalCurrency']), axis =1)

Also, if you have all your countries in dict you may use:

df['Threshold Level'] = mydict[df['Country'].any()](df['LocalCurrency'])

Here's how you can rewrite function and it usage [ added to explain the behaviour of apply function in more details]:

def function_to_be_applied(dataframe_row):

    country = dataframe_row['Country']
    value_to_apply_on = dataframe_row['LocalCurrency']

    try: # lets try to get function we need to apply
        function_to_apply = mydict[country] # if there's no such key in dictionaries it'll raise an error
        return function_to_apply(value_to_apply_on) 

    except: # this will be executed, if operations after 'try' raised any errors:
        return False


df['ThresholdLevel'] = df.apply(function_to_be_applied, axis = 1)
Stas Buzuluk
  • 794
  • 9
  • 19
  • Works like a charm! However, there's function such as lambda and df.method "apply" which I'm not unfamiliar with. Would need to learn more about the mentioned topics. Is it okay that I consult you regarding this code if I have any questions again? – Zongyi Dec 26 '19 at 12:54
  • @Zongyi Sure - it's ok - feel free to ask any question. Apply method - applies a function to each row of DataFrame separately. (https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.apply.html) Lambda functions - is just inline functions, you may think about it as about simple function: `some_func = lambda x: x+2` Is same to: `def some_func(x): return x+2` (https://www.w3schools.com/python/python_lambda.asp) – Stas Buzuluk Dec 26 '19 at 13:03
  • Hi Stas, I just read on the lambda function. It seems like lambda is different way to rewrite a function into a single sentence. But why do we need lambda when we have written a function through `def`? When I remove the lambda from your code, it pops an error saying **'Series' objects are mutable, thus they cannot be hashed**. I'm confused haha. `df['ThresholdLevel'] = df.apply(lambda x: convert_thres(x['Country'], x['LocalCurrency']), axis =1)` VS `df['ThresholdLevel'] = df.apply(convert_thres(df['Country'], df['LocalCurrency']), axis =1)` – Zongyi Dec 27 '19 at 08:05
  • You understand lambda functions properly. For apply - you can think about it, as about iterating over rows and applying function for each. So the input to function you are applying - is an only single row of a dataframe. That's why - lambda works - `x` here - is single row, not an entire DataFrame. I've also added another way you can write and use function - it should be better in explanation of apply behaviour. Check it please, hope it's clear. – Stas Buzuluk Dec 27 '19 at 11:10
0
  1. Pandas is not easy. I’m not saying you shouldn’t use it if you’re only 2 weeks into Python, but you will have a lot of questions about pandas if you’re just starting.
  2. Do not iterate with i over keys. Usually i is used for index. You seem to iterate over a list of countries. Maybe name it country.
  3. You might want to use applymap or apply.
  4. In convertsgp try to add else for readability.
  5. You have a problem in this line df.loc[(df['Country']== i, 'Threshold Level'] = mydict [i](df['LocalCurrency']) There should be no ( after df.loc[ .
Tom Wojcik
  • 5,471
  • 4
  • 32
  • 44
  • Thank you Tom for your advice. As I'm still learning, please pardon me if I'm asking any silly questions. 1) For point 2, what do you mean i is only indexes? How can I better write my code to access the keys in my dictionary? As I'm a heavy Excel user, it is very intuitive for me to see key as the index (Excel header) while the value is its corresponding elements (Excel values below header). Hope you understand what I'm saying haha. 2) For point 4, I'm not sure what to insert for Else statement as I have covered all the values in my argument. What can I do to manage the Else statement? – Zongyi Dec 26 '19 at 13:09
  • By convention you'd use for `i, v in enumerate(lst)`, where `i` stands for index and `v` for value. `el` for list element, `k` for key. If `list(mydict.keys())` is a list of countries, `for country in list(mydict.keys())` makes more sense. Readibility counts. As of else statement, it's just a suggestion. It's good the way you wrote it, but what if `y` wasn't defined for some reason, as it's always defined in if/elif block? IDE would suggest to make this edit. Try using PyCharm and follow its suggestions. Good luck! – Tom Wojcik Dec 26 '19 at 22:49