I need some help using a dictionary as a function parameter - python

I need to replicate this same function but instead of having a list as a parameter I need a dictionary. The idea is that the calculation done by the function is done with the values, and the function returns the keys.
def funcion(dic, Sum):
Subset = []
def f(dic, i, Sum):
if i >= len(dic): return 1 if Sum == 0 else 0
count = f(dic, i + 1, Sum)
count += f(dic, i + 1, Sum - dic[i])
return count
for i, x in enumerate(dic):
if f(dic, i + 1, Sum - x) > 0:
Subset.append(x)
Sum -= x
return Subset
The function works if I enter (300, 200,100,400). But i need to use as an input something like {1:300 , 2:200 , 3:100, 4:400 }
So the calculation is done with the values, but it returns the keys that match the condition.
Im trying working with dic.keys() and dic.values() but its not working. Could you help me?
Thank u so much.

Your code isn't working with your dictionary because it's expecting to be able to index into dic with numeric indexes starting at 0 and going up to len(dic)-1. However, you've given your dictionary keys that start at 1 and go to len(dic). That means you need to change things up.
The first change is in the recursive f function, where you need the base case to trigger on i > len(dic) rather than using the >= comparison.
The next change in in the loop that calls f. Rather than using enumerate, which will generate indexes starting at 0 (and pair them with the keys of the dictionary, which is what you get when you directly iterate on it), you probably want to do something else.
Now, ideally, you'd want to iterate on dic.items(), which would give you index, value pairs just like your code expects. But depending on how the dictionary gets built, that might iterate over the values in a different order than you expect. In recent versions of Python, dictionaries maintain the order their keys were added in, so if you're creating the dictionary with {1:300, 2:200, 3:100, 4:400 }, you'll get the right order, but a mostly-equivalent dictionary like {3:100, 4:400, 1:300, 2:200 } would give its results in a different order.
So if you need to be resilient against dictionaries that don't have their keys in the right order, you probably want to directly generate the 1-len(dict) keys yourself with range, and then index to get the x value inside the loop:
for i in range(1, len(dic)+1): # Generate the keys directly from a range
x = dic[i] # and do the indexing manually.
if f(dic, i + 1, Sum - x) > 0: # The rest of the loop is the same as before.
Subset.append(x)
Sum -= x

Related

Python Find the mean school assignment - What is a loop?

I have been working on this assignment for about 2 weeks and have nothing done. I am a starter at coding and my teacher is really not helping me with it. She redirects me to her videos that I have to learn from every time and will not directly tell or help me on how I can do it. Here are the instructions to the assignment (said in a video, but made it into text.
Find the mean
Create a program that finds the mean of a list of numbers.
Iterate through it, and instead of printing each item, you want to add them together.
Create a new variable inside of that, that takes the grand total when you add things together,
And then you have to divide it by the length of your array, for python/java script you’ll need to use the method that lets you know the length of your list.
Bonus point for kids who can find the median, to do that you need to sort your list and then you need to remove items from the right and the left until you only have one left
All you’re doing is you need to create a variable that is your list
Create another variable that is a empty one at the moment and be a number
Iterate through your list and add each of the numbers to the variable you created
Then divide the number by the number of items that you had in the list.
Here's what I've done so far.
num = [1, 2, 3, 4, 5, 6];
total = 0;
total = (num[0] + total)
total = (num[1] + total)
total = (num[2] + total)
total = (num[3] + total)
total = (num[4] + total)
total = (num[5] + total)
print(total)
However, she tells me I need to shorten down the total = (num[_] + total) parts into a loop. Here is how she is telling me to do a loop in one of her videos.
for x in ____: print(x)
or
for x in range(len(___)): print (x+1, ____[x])
there is also
while i < len(___):
print(___[i])
i = i + 1
I really don't understand how to do this so explain it like I'm a total noob.
First of all, loops in python are of two types.
while: a while loop executes the code in a body until a condition is true. For example:
i = 0
while(i < 5):
i = i + 1
executes i = i + 1 until i < 5 is true, meaning that when i will be equal to 5 the loop will terminate because its condition becomes false.
for: a for loop in python iterates over the items of any sequence, from the first to the last, and execute its body at each iteration.
Note: in both cases, by loop body I mean the indented code, in the example above the body is i = i + 5.
Iterating over a list. You can iterate over a list:
Using an index
As each position of the array is indexed with a positive number from 0 to the length of the array minus 1, you can access the positions of the array with an incremental index. So, for example:
i = 0
while i < len(arr):
print(arr[i])
i = i + 1
will access arr[0] in the first iteration, arr[1] in the second iteration and so on, up to arr[len(arr)-1] in the last iteration. Then, when i is further incremented, i = len(arr) and so the condition in the while loop (i < arr[i]) becomes false. So the loop is broken.
Using an iterator
I won't go in the details of how an iterator works under the surface since it may be too much to absorb for a beginner. However, what matters to you is the following. In Python you can use an iterator to write the condition of a for loop, as your teacher showed you in the example:
for x in arr:
print(x)
An iterator is intuitively an object that iterates over something that has the characteristic of being "iterable". Lists are not the only iterable elements in python, however they are probably the most important to know. Using an iterator on a list allows you to access in order all the elements of the list. The value of the element of the list is stored in the variable x at each iteration. Therefore:
iter 1: x = arr[0]
iter 2: x = arr[1]
...
iter len(arr)-1: x = arr[len(arr)-1]
Once all the elements of the list are accessed, the loop terminates.
Note: in python, the function range(n) creates an "iterable" from 0 to n-1, so the for loop
for i in range(len(arr)):
print(arr[i])
uses an iterator to create the sequence of values stored in i and then i is in turn used on the array arr to access its elements positionally.
Summing the elements. If you understand what I explained to you, it should be straightforward to write a loop to sum all the elements of a list. You initialize a variable sum=0 before the loop. Then, you add the element accessed as we saw above at each iteration to the variable sum. It will be something like:
sum = 0
for x in arr:
sum = sum + x
I will let you write an equivalent code with the other two methods I showed you and do the other points of the assignment by yourself. I am sure that once you'll understand how it works you'll be fine. I hope to have answered your question.
She wants you to loop through the list.
Python is really nice makes this easier than other languages.
I have an example below that is close to what you need but I do not want to do your homework for you.
listName = [4,8,4,7,84]
for currentListValue in listName:
#Do your calculating here...
#Example: tempVar = tempVar + (currentListValue * 2)
as mentioned in the comments w3schools is a good reference for python.

Python 2.7: Add zeros to the size difference between lists of a multi-value dictionary and sum it

I have a multi-valued dictionary and I want to sum whatever the values are there which can be done with
for key, value in nai_dictionary.items():
nai_sum_rate.setdefault('%s' %(key), []).append(np.sum(rate_nai[key], axis=0))
for any nai_sum_rate dictionary, as suggested in this webpage. However, often times the length of the value lists is not the same.
This is the test data on which I'm testing right now
time = np.array([1,2,3,4,5,6])
test_dict['a'] = np.array([1,2,3,4,5,6]), np.array([1,2,3,4]), np.array([1,2,3,4,5,6])
What I want to do now, is to put a 0 in place of missing values for (in this case) the second array where there is no value against the time variable values and add them together.
In my previous testings, before the nai_sum_rate was a list instead of a dictionary I used the answer provided in this link to sort out the problem with the reference variable time. I've been trying with the dictionary but to no avail.
The expected sum is
3, 6, 9, 12, 10, 12
I solved the problem myself, here is the code that I came up with
for key, value in nai_dictionary.items():
i=0
while i < len(rate_nai[key]):
for key, value in nai_dictionary.items():
for index, values in enumerate(rate_nai[key]):
if len(time) < len(values):
diff = len(values) - len(time)
rate_nai[values] = values[0:np.array(values).size-diff]
elif len(time) > len(values):
diff = len(time) - len(values)
rate_nai['%s' %(key)].pop(index)
rate_nai.setdefault(
'%s' %(key)).append(np.append(np.array(values), np.zeros(diff)))
i+=1
It can tackle the problem with any number of values that have a lesser length than the reference variable time.

MemoryError using numeric range as dict index (Inefficient)

I have a need to define numeric ranges as a dictionary index such as:
SCHEDULE = {
(0, 5000): 1,
(5001, 22500): 2,
(22501, 999999999): 3
}
I search it by this function:
def range_index(table, val):
new_table = {k: v for tup, v in table.items() for k in range(tup[0], tup[1]+1)}
return new_table.get(int(val)) # int() is used to deal with floats.
which works good as long as the range isn't too big. The last entry in SCHEDULE which is 999999999 causes Python to throw MemoryError. If I decrease it to a smaller number, it's fine.
This obviously means we are building this whole table from the ranges. How can this be re-worked so that the entire ranges aren't enumerated for each search?
This is a job for an order-based data structure, not a hash-based data structure like a dict. Hashes are good for equality. They don't do range tests.
Your table should be a pair of lists. The first is sorted and represents range endpoints, and the second represents values associated with each range:
# I don't have enough information to give these better names.
endpoints = [0, 5001, 22501, 1000000000]
values = [1, 2, 3]
To find a value, perform a binary search for the index in the first list and look up the corresponding value in the second. You can use bisect for the binary search:
import bisect
def lookup(endpoints, values, key):
index = bisect.bisect_right(endpoints, key) - 1
if index < 0 or index >= len(values):
raise KeyError('{!r} is out of range'.format(key))
return values[index]
You can do a next on generator with a default value as 0 to handle StopIteration:
def range_index(table, val):
return next((v for k, v in table.items() if k[0] <= int(val) <= k[1]), 0)
This uses the usual less than, greater than checks to find the range of val and get the value corresponding.
Advantages:
No new dictionary creation for every search.
Exits immediately when the condition is satisfied.
Iterate over SCHEDULE and return the first value where val is in the associated range.
category = next(category
for (start, stop), category in SCHEDULE.items()
if val in range(start, stop + 1))
It would be a bit faster if you started off with a dict of ranges, not of tuples. It would be even faster if you made SCHEDULE into a binary tree, and did a binary search on it instead of a linear one. But this is good enough for majority of cases.
This assumes your SCHEDULE is exhaustive, and you'll get a StopIteration error if you submit a val that is not covered by any of the ranges, to signify a programmer error. If you wish an else value, put it as a second parameter to next, after wrapping the first parameter in parentheses.

How to sort a python list using insertion sort using 2 conditional attributes?

Lets say I want to sort a list that looks like this:
arr = ['45621', '78124', '24613']
The above list stores the IDs for various employees at a company. I don't want to sort based on the IDs alone, but based on attributes that correspond to the IDs, using the following dictionary:
d = {
'45621' : { 'rating' : 3, 'hours_worked' : 42 },
'78124' : { 'rating' : 4, 'hours_worked' : 78 },
'24613' : { 'rating' : 3, 'hours_worked' : 51 }
}
So its something like this: if an employee has a higher rating, his/her ID will come first. However, if 2 employees have the same rating, then we compare the hours_worked, and whoever has worked more will come before the other.
I want to implement an insertion sort myself. Right now, my code looks like this:
def insertionSort(arr):
for i in range(1, len(arr)):
key = d[arr[i]]['rating']
second_key = d[arr[i]]['hours_worked']
j = i - 1
while j >= 0 and key < d[arr[j]]['rating']:
arr[j+1] = arr[j]
j -= 1
d[arr[j+1]]['rating'] = key
return arr
I cant seem to use the second key for comparisons. And moreover, this simply does not work. Any help?
Please note, I cannot change the nature of the input and the dictionary, and please no answers using the built-in methods.
It's not that you can't use the second key for comparisons, it's that you simply don't.
Also, it would be good (even if you are not using the built-in methods) to mimic their shape, as it just makes sense.
So let's try that:
def insertionSort(arr, key=lambda x: x):
This says the normal comparison is by element itself. Inside the sort, you can basically check key(element1) and compare it with key(element2).
If you want to just sort some numbers, the default key function is fine: it will return that same number, so it can get compared.
If you want to use e.g. a name, you can do key=lambda person: person.name.
What to do if you have multiple criteria? Arrays and tuples have an interesting property: they are comparable! The result of the comparison is the comparison of the first element, unless it is equal, in which case the comparison will proceed to the next element. So, (0, 5) < (1, 5) < (1, 7) < (2, 9). How can we use this?
key=lambda e_id: (-d[e_id]['rating'], -d[e_id]['hours_worked'])
This key function constructs a tuple consisting of negative rating and hours_worked (negative because that way we sort in reverse, highest to lowest). The sort function does not need to change at all: you just need to implement the normal insertion sort for one criterion: whether key(element1) is larger, smaller or equal than key(element2).
You should swap items you find out of order, and you should compare the second key when the first key is equal:
def insertionSort(arr):
for i in range(1, len(arr)):
key = d[arr[i]]['rating']
second_key = d[arr[i]]['hours_worked']
j = i - 1
while j >= 0 and key > d[arr[j]]['rating'] or key == d[arr[j]]['rating'] and second_key > d[arr[j]]['hours_worked']:
arr[j+1], arr[j] = arr[j], arr[j+1]
j -= 1
d[arr[j+1]]['rating'] = key
return arr
so that:
insertionSort(arr)
returns:
['78124', '24613', '45621']

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.
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.
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
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.
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

Categories