AwfulPersimmon
AwfulPersimmon

Reputation: 175

Finding min and max values from a dictionary containing tuple values

I have a python dictionary named cdc_year_births.

For cdc_year_births, the keys are the unit (in this case the unit is a year), the values are the number of births in that unit:

print(cdc_year_births)
{2000: 4058814, 2001: 4025933, 2002: 4021726, 2003: 4089950, 1994: 3952767, 
1995: 3899589, 1996: 3891494, 1997: 3880894, 1998: 3941553, 1999: 3959417}

I wrote a function that returns the maximum and minimum years and their births. When I started the function, I thought I'd hard code the max and min unit at 0 and 1000000000, respectively, and then iterate through the dictionary and compare each key's value to those hard coded values; if the conditions were met, I'd replace the max/min unit and the max/min birth.

But if the dictionary I used had negative values or values greater than 1000000000, this function wouldn't work, which is why I had to "load in" some actual values from the dictionary with the first loop, then loop over them again.

I built this function but could not get it to work properly:

def max_min_counts(data):
    max_min = {}
    for key,value in data.items():
        max_min["max"] = key,value
        max_min["min"] = key,value
    for key,value in data.items():
        if value >= max_min["max"]:
            max_min["max"]=key,value
        if value <= max_min["min"]:
            max_min["min"]=key,value
    return max_min

t=max_min_counts(cdc_year_births)
print(t)

It results in TypeError: unorderable types: int() >= tuple() for

if value >= max_min["max"]:

and

if value <= max_min["min"]:

I tried extracting the value from the tuple as described in Finding the max and min in dictionary as tuples python, but could not get this to work.

Can anyone help me make the second, shorter function work or show me how to write a better one?

Thank you very much in advance.

Upvotes: 3

Views: 5896

Answers (4)

cs95
cs95

Reputation: 402814

Your values are 2-tuples. You'll need one further level of indexing to get them to work:

if value >= max_min["max"][1]:

And,

if value <= max_min["min"][1]:

If you want to preset your max/min values, you can use float('inf') and -float('inf'):

max_min["max"] = (-1, -float('inf')) # Smallest value possible.
max_min["min"] = (-1,  float('inf')) #  Largest value possible.

You can do this efficiently using max, min, and operator.itemgetter to avoid a lambda:

from operator import itemgetter
max(cdc_year_births.items(), key=itemgetter(1))
# (2003, 4089950)

min(cdc_year_births.items(), key=itemgetter(1))
# (1997, 3880894)

Here's a slick way to compute the max-min with reduce

from fuctools import reduce

reduce(lambda x, y: x if x[1] > y[1] else y, cdc_year_births.items())
# (2003, 4089950)

reduce(lambda x, y: x if x[1] < y[1] else y, cdc_year_births.items())
# (1997, 3880894)

items() generates a list of tuples out of your dictionary, and the key tells the functions what to compare against when picking the max/min.

Upvotes: 4

YojaGedi
YojaGedi

Reputation: 1

Yeah, I'm up to this exercise too.

Without using max and min functions (we haven't covered them yet in the course material) here's the hard way...

def minimax(dict):
    minimax_dict = {}
    if(len(dict) == 31):
        time = "day_of_month"
    elif(len(dict) == 12):
        time = "month"
    elif(len(dict) == 7):
        time = "day_of_week"
    else:
        time = 'year'
    min_time = "min_" + time
    max_time = "max_" + time
    for item in dict:
        if 'min_count' in minimax_dict:
            if dict[item] < minimax_dict['min_count']:
                minimax_dict['min_count'] = dict[item]
                minimax_dict[min_time] = item
        else:
            minimax_dict['min_count'] = dict[item]
            minimax_dict[min_time] = item
        if 'max_count' in minimax_dict:
            if dict[item] > minimax_dict['max_count']:
                minimax_dict['max_count'] = dict[item]
                minimax_dict[max_time] = item
        else:
            minimax_dict['max_count'] = dict[item]
            minimax_dict[max_time] = item
    return minimax_dict
#here's the test stuff...
min_max_dow_births = minimax(cdc_dow_births)
#min_max_dow_births
min_max_year_births = minimax(cdc_year_births)
#min_max_year_births
min_max_dom_births = minimax(cdc_dom_births)
#min_max_dom_births
min_max_month_births = minimax(cdc_month_births)
#min_max_month_births

Upvotes: 0

wbadart
wbadart

Reputation: 2911

In case you're interested in a more functional programming-oriented solution (or just something with more independent component parts), allow me to suggest the following:

Establish a comparison function between entries

Yes, we can use </> to compare the values as we iterate through the dict, but, as will become evident in a moment, it'll be useful to have something which lets us keep track of the year associated with that number of births.

def comp_births(op, lpair, rpair):
    lyr, lbirths = lpair
    ryr, rbirths = rpair
    return rpair if op(rbirths, lbirths) else lpair

At the end of the day, op will end up being either the numerical greater than or the numerical less than, but adding this tuple business accomplishes our goal of keeping track of the year associated with the number of births. Futher, by factoring op out into a function parameter, rather than hard-coding the operator, we open the door for reusing this code for both the "min" and "max" variations.

Construct your iteratees

Now, all we need to do to create a function that compairs two year/num_births pairs is partially apply our comparison function:

from functools import partial
from operator import gt, lt

get_max = partial(comp_births, gt)
get_min = partial(comp_births, lt)

get_max((2003, 150), (2012, 400))  #=> (2012, 400)

Pipe in your data

So where do we find these year/num_births pairs? Turns out it's just cdc_year_births.items(). And since we're lazy, let's use a function to do the iteration for us (reduce):

from functools import reduce

yr_of_max_births, max_births = reduce(get_max, births.items())
yr_of_min_births, min_births = reduce(get_min, births.items())

demo

Upvotes: 1

Prune
Prune

Reputation: 77850

You need to compare against the value, not the entire tuple:

if value >= max_min["max"][1]:

As for not using the built-in functions, are you averse to using other built-ins? For instance, you could use reduce with a simple function -- x if x[1] < y[1] else y -- to get the minimum of all the entries. You could also sort the entries with x[1] as the key, then take the first and last elements of the sorted list.

Upvotes: 0

Related Questions