what is the meaning of this line - python

Can someone explain to me what this line of code means, keep in mind im new to python.
I have to print the name of the best student and his grade.
d is a composed of names of students and their respective means(grades mean)
I know this code works but i can't understand it.
b_p = d.keys()[d.values().index(max(d.values()))]
print '%s %.2f' % (b_p,d[b_p])
I tried sorting d and print d.keys()[-1] but
AttributeError: 'list' object has no attribute 'keys'
. I also have to print his grade next to the name.

Decipher it step by step, from the inside outwards:
d.values()
gets you a list (in Python 2) of values of your dictionary. That is, the grades themselves.
The highest value is then obtained:
max(d.values())
Then, the index (position) is found in the list of values for that highest value:
d.values().index(max(d.values()))
That index applies to the list of values, but also to the list of keys. So you can use that index to find the key (student name) related to the highest value, and index that list (d.keys()) with it:
d.keys()[d.values().index(max(d.values()))]
And thus, the result is the name of the person with the highest grade.
A clearer variant is to split it up into multiple lines:
grades = d.values()
maxgrade = max(grades)
maxindex = grades.index(grades)
names = d.keys()
maxname = names[maxindex]
Why your attempt failed:
I tried sorting d and print d.keys()[-1] but [got an error message:]
AttributeError: 'list' object has no attribute 'keys'
When you sort a dictionary, the result is a sorted list of just the keys: you lose the dictionary information. So after d = sorted(d), d is now a list, and does not have a method, or more generally, attribute, keys(). Hence the error message.
As to how to do this properly, refer to this question. In particular, the clearest answer there is
max(d, key=d.get)
The max function can take a key argument, and using d.get will actually use whatever d.get returns (which are the values, not the keys) to find the maximum.
You could use the same with sorted:
sorted(d, key=d.get)
will list the names, ordered from lowest to highest grade.

Notice that sorted(d) is a shorthand for sorted(d.keys()), not sorted(d.values()) or sorted(d.items())
To sort the key-value pairs by value, try:
from operator import itemgetter
best_student = sorted(d.items(), key=itemgetter(1), reverse=True)[0]

Related

Add the key of a duplicated value to a list python

I have a dictionary of students like dic={1:[1,2],2:[1],3:[1,4]}. I need to sort it, and see if the students have the same values. If that happens, the student that comes up first has priority and the other student has 2 options. For example, in the example above, nothing happens to student 1, student 2 should be added to a separate list since the value 1 has already been used, and student 3 since it has a value that hasn't appeared before, nothing happens to it too. So basicly the output I would need for that example would be [2].
I managed to sort the dictionary by doing sorted(dic.items(), key=lambda t: t[0]) but I don't know how to compare them, the only thing I know is that after the comparison I would have to do something like
list=[]
list.append(number)
You should use a set to cumulatively keep the previous values, and iterate your dictionary.
Code could be:
prev = set() # no values initialy seen
new_list = [] # no student intialy in the new liest
for st, vals in dic.items(): # iterate on dic
if set(vals).issubset(prev): # is there no new value?
new_list.append(st) # add the student to new_list
prev = prev.union(set(vals)) # and add its values to the seen set
With your example, we get as expected [2] for new_list.
For each element you can check whether it is a subset of any previous values with O(n2) complexity
dic={1:[1,2],2:[1],3:[1,4]}
st_values = list(dic.values())
keys = list(dic.keys())
output = []
for i,v in enumerate(st_values):
for j in range(0, i):
if(set(v).issubset(st_values[j])):
output.append(keys[i])
print(output)
[2]
You can use sets. Since the sorted() function you used returns a list of tuples:
dic = sorted(dic.items(), key=lambda t: t[0])
appeared=set()
t=[]
for item in dic:
if not set(item[1]).issubset(appeared):
appeared.update(item[1])
else:
t.append(item[0])
print(f'Result: {t}')
Output:
Result: [2]

how to get the corresponding key for the maximum value in dictionary list in the most effecient way?

Let's assume that there is a dictionary list like this one:
lst = {(1,1):2, (1,2):5, (1,3):10, (1,4):14, (1,6):22}
I want a simple (the most efficient) function that returns the dictionary key which its value is the maximum.
For example:
key_for_max_value_in_dict(lst) = (1,6)
because the tuple (1,6) has the most value (22).
I came up with this code which might be the most efficient one:
max(lst, key=lambda x: lst[x])
Use a comprehension for that like:
Code:
max((v, k) for k, v in lst.items())[1]
How does it work?
Iterate over the items() in the dict, and emit them as tuples of (value, key) with the value first in the tuple. max() can then find the largest value, because tuples sort by each element in the tuple, with first element matching first element. Then take the second element ([1]) of the max tuple since it is the key value for the max value in the dict.
Test Code:
lst = {(1,1):2, (1,2):5, (1,3):10, (1,4):14, (1,6):22}
print(max((v, k) for k, v in lst.items())[1])
Results;
(1, 6)
Assuming you're using a regular unsorted dictionary, you'll need to walk down the entire thing once. Keep track of what the largest element is and update it if you see a larger one. If it is the same, add to the list.
largest_key = []
largest_value = 0
for key, value in lst.items():
if value > largest_value:
largest_value = value
largest_key = [key]
elif value == largest_value:
largest_key.append(key)

Iterating through a dictionary for X number of times

Assume the dictionary contains more than 10 key-value pairs. The dictionary ought to be sorted by values (of integers). Print out the top 10 values (and corresponding keys). I think there is a better solution that what I have given here.
for keys in sorted(x):
c=c+1
if c>10:
break
else:
print keys, x['keys']
for key in sorted(x, key=x.get, reverse=True)[:10]:
print key, x[key]
For really large dict you should consider using a heapq
from heapq import nlargest
for key in nlargest(10, x, key=x.get):
print key, x[key]
There is no order defined on dictionary keys, so the "first" keys are not well defined. Specifically, what you did is easier done with x.keys()[:10].
topten = sorted(x.items(), key=lambda x:-x[1])[:10]
You can iterate over dict for X number of times using the following code.
Python 3.8
def highest_contributor(self, top=1):
slice_dict_only_to_keys = list(self.contributors.keys())[:top]
for _key in slice_dict_only_to_keys:
self.log("Top Contributor: {} ({})".format(_key, self.contributors[_key]))
Don't worry about integers and incrementing them with your code. You don't need them.
Simple, readable and Maintainable.

How to solve dictionary changed size during iteration error?

I want pop out all the large values and its keys in a dictionary, and keep the smallest. Here is the part of my program
for key,value in dictionary.items():
for key1, value1 in dictionary.items():
if key1!= key and value > value1:
dictionary.pop(key)
print (dictionary)
Which results in
RuntimeError: dictionary changed size during iteration
How can I avoid this error?
In Python3, Try
for key in list(dict.keys()):
if condition:
matched
del dict[key]
1 more thing should be careful when looping a dict to update its key:
Code1:
keyPrefix = ‘keyA’
for key, value in Dict.items():
newkey = ‘/’.join([keyPrefix, key])
Dict[newkey] = Dict.pop(key)
Code2:
keyPrefix = ‘keyA’
for key, value in Dict.keys():
newkey = ‘/’.join([keyPrefix, key])
Dict[newkey] = Dict.pop(key)
Result of code1/code2 is:
{‘keyA/keyA/keyB’ : ”, ‘keyA/keyA/keyA’: ”}
My way to resolve this unexpected result:
Dict = {‘/’.join([keyPrefix, key]): value for key, value in Dict.items()}
Link: https://blog.gainskills.top/2016/07/21/loop-a-dict-to-update-key/
Alternative solutions
If you're looking for the smallest value in the dictionary you can do this:
min(dictionary.values())
If you cannot use min, you can use sorted:
sorted(dictionary.values())[0]
Why do I get this error?
On a side note, the reason you're experiencing an Runtime Error is that in the inner loop you modify the iterator your outer loop is based upon. When you pop an entry that is yet to be reached by the outer loop and the outer iterator reaches it, it tries to access a removed element, thus causing the error.
If you try to execute your code on Python 2.7 (instead of 3.x) you'll get, in fact, a Key Error.
What can I do to avoid the error?
If you want to modify an iterable inside a loop based on its iterator you should use a deep copy of it.
You can use copy.deepcopy to make a copy of the original dict, loop over the copy while change the original one.
from copy import deepcopy
d=dict()
for i in range(5):
d[i]=str(i)
k=deepcopy(d)
d[2]="22"
print(k[2])
#The result will be 2.
Your problem is iterate over something that you are changing.
Record the key during the loop and then do dictionary.pop(key) when loop is done. Like this:
for key,value in dictionary.items():
for key1, value1 in dictionary.items():
if key1!= key and value > value1:
storedvalue = key
dictionary.pop(key)
Here is one way to solve it:
From the dictionary, get a list of keys, sorted by value
Since the first key in this list has the smallest value, you can do what you want with it.
Here is a sample:
# A list of grades, not in any order
grades = dict(John=95,Amanda=89,Jake=91,Betty=97)
# students is a list of students, sorted from lowest to highest grade
students = sorted(grades, key=lambda k: grades[k])
print 'Grades from lowest to highest:'
for student in students:
print '{0} {1}'.format(grades[student], student)
lowest_student = students[0]
highest_student = students[-1]
print 'Lowest grade of {0} belongs to {1}'.format(grades[lowest_student], lowest_student)
print 'Highest grade of {0} belongs to {1}'.format(grades[highest_student], highest_student)
The secret sauce here is in the sorted() function: instead of sorting by keys, we sorted by values.
If you want to just keep the key with the smallest value, I would do it by first finding that item and then creating a new dictionary containing only it. If your dictionary was d, something like this would do that in one line:
d = dict((min(d.items(), key=lambda item: item[1]),))
This will not only avoid any issues about updating the dictionary while iterating it, it is probably faster than removing all the other elements.
If you must do the modifications in-place for some reason, the following would work because it makes a copy of all the keys before modifying the dictionary:
key_to_keep = min(d.items(), key=lambda item: item[1])[0]
for key in list(d):
if key != key_to_keep:
d.pop(key)
As I read your loop right now, you're looking to keep only the single smallest element, but without using min. So do the opposite of what your code does now, check if value1 < minValueSoFar, if so, keep key1 as minKeySoFar. Then at the end of the loop (as Zayatzz suggested), do a dictionary.pop(minKeySoFar)
As an aside, I note that the key1!=key test is irrelevant and computationally inefficient assuming a reasonably long list.
minValueSoFar = 9999999; # or whatever
for key,value in dictionary.items():
if value < minValueSoFar:
minValueSoFar = value
minKeySoFar = key
dictionary.pop(minKeySoFar) # or whatever else you want to do with the result
An alternative solution to dictionary changed size during iteration:
for key,value in list(dictionary.items()):
for key1, value1 in list(dictionary.items()):
if key1!= key and value > value1:
dictionary.pop(key)
print (dictionary)
Better use it with caution! when using this type of code, because list(dictionary.items()) calculated when the complier enters first time to loop. Therefore any change made on dictionary won't affect process inside the current loop.
You could create a list with the vaules you want to delete and than run a second for loop:
for entry in (listofkeystopop):
dictionary.pop(entry)

Correspendence between list indices originated from dictionary

I wrote the below code working with dictionary and list:
d = computeRanks() # dictionary of id : interestRank pairs
lst = list(d) # tuples (id, interestRank)
interestingIds = []
for i in range(20): # choice randomly 20 highly ranked ids
choice = randomWeightedChoice(d.values()) # returns random index from list
interestingIds.append(lst[choice][0])
There seems to be possible error because I'm not sure if there is a correspondence between indices in lst and d.values().
Do you know how to write this better?
One of the policies of dict is that the results of dict.keys() and dict.values() will correspond so long as the contents of the dictionary are not modified.
As #Ignacio says, the index choice does correspond to the intended element of lst, so your code's logic is correct. But your code should be much simpler: d already contains IDs for the elements, so rewrite randomWeightedChoice to take a dictionary and return an ID.
Perhaps it will help you to know that you can iterate over a dictionary's key-value pairs with d.items():
for k, v in d.items():
etc.

Categories

Resources