Related
I'm trying to create a dictionary object with keys being people's names and values being sum of all scores by each individual
Here is the base list called scores. As you can see each element of the meta list is also a list containing tuples of names and scores
[[('Sebastian Vettel', 25),
('Lewis Hamilton', 18),
('Kimi Raikkonen', 15),
('Daniel Ricciardo', 12),
('Fernando Alonso', 10),
('Max Verstappen', 8),
('Nico Hulkenberg', 6),
('Valtteri Bottas', 4),
('Stoffel Vandoorne', 2),
('Carlos Sainz', 1)],
[('Sebastian Vettel', 25),
('Valtteri Bottas', 18),
('Lewis Hamilton', 15),
('Pierre Gasly', 12),
('Kevin Magnussen', 10),
('Nico Hulkenberg', 8),
('Fernando Alonso', 6), ...
I want to create a dictionary that contains unique names as keys, and sum of all scores as values ordered (in descending) by sum of scores. Also I'd like to limit the dictionary to Top 3 total score
Here's my attempt so far, but seems to be missing something.
scores_total = defaultdict(int)
for (name,score) in scores:
key = name
values = score
scores_total[key] += int(score)
scores_total
But I get this error: ValueError Traceback (most recent call last)
in ()
1 scores_total = defaultdict(int)
2
3 for (name,score) in scores:
4 key = name
5 values = score ValueError: too many values to unpack (expected 2)
Any idea how to tackle this? Much appreciating help.
first make a dictionary with all the scores summed as values for each person, then make a list that sorts the keys by the value which is now the sum, in reverse to get largest to smallest. Then slice that list for only three [:3] and create a dictionary using those names as keys retrieving the values from the old dictionary .
d = {}
for i in scores:
for j in i:
if j[0] not in d:
d[j[0]] = j[1]
else:
d[j[0]] += j[1]
l = sorted(d, key=lambda x: d[x], reverse = True)
final = {i: d[i] for i in l[:3]}
print(final)
{'Sebastian Vettel': 50, 'Lewis Hamilton': 33, 'Valtteri Bottas': 22}
I'm a little lost on your explanation, but here's what I'm getting.
Let's say this was for let's say soccer games and these 'scores' are for players goals made, right? And I'm also going to assume the use of the separate lists is for different games.
With this, you're trying to get an "overview" of them all in one 'neat' dict right? If that's so, you've got a good start. I'd use:
from sortedcontainers import SortedList #this fixes your top 3 question
games = [
[('name', score), ('name', score)...], #game 1
[('name', score), ('name', score)] # game 2
]
scores_review = defaultdict(SortedList) #makes a sorted list as the dafault for the dict
for game in games:
for name, score in scores: #no need to wrap name, score in parentheses
scores_total[name].add(score)
Now the scores_review variable is a dict with lists of all their scores for every game and it's sorted. This means to get the top 3 for someone you just use:
top_three = scores_review['name'][-3:]
And to get the sum just use:
all_scores = sum(scores_review['name'])
I'm trying to write a greedy algorithm and I have a list of tuples:
cow_list = [('Betsy', 9), ('Henrietta', 9), ('Herman', 7), ('Oreo', 6), ('Millie', 5), ('Maggie', 3), ('Moo Moo', 3), ('Milkshake', 2), ('Lola', 2), ('Florence', 2)]
I'm trying to iterate through the list:
def greedy_cow_transport(cow_list, maxWeight):
transport_name = []
taken_transport = []
not_taken = []
transport_weight = []
for i in range(len(cow_list)):
total_weight = sum(transport_weight)
if (total_weight+cow_list[i][1]) <= maxWeight:
transport_name.append(cow_list[i][0])
transport_weight.append(cow_list[i][1])
total_weight += cow_list[i][1]
elif (total_weight+cow_list[i][1]) > maxWeight:
taken_transport.append(cow_list[i][0])
And what I'm trying to do is delete the first tuple in the list if > maxWeight so the for loop can start the process over again without using that first tuple.
So I want the final result to be:
transport_name = [['Betsy'], ['Henrietta'], ['Herman', 'Maggie'], ['Herman', 'Moo Moo'], ['Herman', 'Milkshake'], etc. with all combinations where the sum of the values is <10.
Please let me know if my question doesn't make sense.
A very ad-hoc approach,
def greedy_cow_transport(cow_list, maxWeight):
transport_name = []
taken_transport = []
not_taken = []
transport_weight = []
for i in range(len(cow_list)):
# Store an initial element
temp_name.append(cow_list[i][0])
temp_weight.append(cow_list[i][1])
total_weight = sum(temp_weight)
# Iterate on all the elements to the right
for j in range(i, len(cow_list)):
if (total_weight + cow_list[j][1])<= maxWeight:
temp_name.append(cow_list[j][0])
temp_weight.append(cow_list[j][1])
total_weight = sum(temp_weight)
else:
taken_temp_transport.append(cow_list[j][0])
transport_name.append(temp_name)
transport_weight.append(temp_weight)
taken_transport.append(taken_temp_transport)
Please see the simplified example:
A=[(721,'a'),(765,'a'),(421,'a'),(422,'a'),(106,'b'),(784,'a'),(201,'a'),(206,'b'),(207,'b')]
I want group adjacent tuples with attribute 'a', every two pair wise and leave tuples with 'b' alone.
So the desired tuple would looks like:
A=[[(721,'a'),(765,'a')],
[(421,'a'),(422,'a')],
[(106,'b')],
[(784,'a'),(201,'a')],
[(206,'b')],[(207,'b')]]
What I can do is to build two separated lists contains tuples with a and b.
Then pair tuples in a, and add back. But it seems not very efficient. Any faster and simple solutions?
Assuming a items are always in pairs, a simple approach would be as follows.
Look at the first item - if it's an a, use it and the next item as a pair. Otherwise, just use the single item. Then 'jump' forward by 1 or 2, as appropriate:
A=[(721,'a'),(765,'a'),(421,'a'),(422,'a'),(106,'b'),(784,'a'),(201,'a'),(206,'b'),(207,'b')]
result = []
count = 0
while count <= len(A)-1:
if A[count][1] == 'a':
result.append([A[count], A[count+1]])
count += 2
else:
result.append([A[count]])
count += 1
print(result)
You can use itertools.groupby:
import itertools
A=[(721,'a'),(765,'a'),(421,'a'),(422,'a'),(106,'b'),(784,'a'),(201,'a'),(206,'b'),(207,'b')]
def split(s):
return [s[i:i+2] for i in range(0, len(s), 2)]
new_data = [i if isinstance(i, list) else [i] for i in list(itertools.chain(*[split(list(b)) if a == 'a' else list(b) for a, b in itertools.groupby(A, key=lambda x:x[-1])]))
Output:
[[(721, 'a'), (765, 'a')], [(421, 'a'), (422, 'a')], [(106, 'b')], [(784, 'a'), (201, 'a')], [(206, 'b')], [(207, 'b')]]
No need to use two lists. Edit: If the 'a' are not assumed to come always as pairs/adjacent
A = [(721,'a'),(765,'a'),(421,'a'),(422,'a'),(106,'b'),(784,'a'),(201,'a'),(206,'b'),(207,'b')]
new_list = []
i = 0
while i < len(A):
if i == len(A)-1:
new_list.append([A[i]])
i+=1
elif (A[i][1]==A[i+1][1]=='a') :
new_list.append([A[i], A[i+1]])
i += 2
else:
new_list.append([A[i]])
i += 1
print(new_list)
The list contains a number of tuples with the first element of the tuple being a name and the second element being a rating.
Tuple = [('Bob', 123),('Sky', 234),('Sara', 156)]
for i in range(0, len(Tuple)-1):
smallest = i
for j in range(i+1, len(Tuple)):
if Tuple[j] < Tuple[smallest]:
smallest = j
if smallest !=i:
Tuple[i], Tuple[smallest] = Tuple[smallest] , Tuple[i]
print(Tuple)
This will sort the list in alphabetical order but I want it to be sorted by rating highest to lowest.
I've tried this:
for i in range(0, len(Tuple)-1):
smallest = i[1]
for j in range(i+1, len(Tuple)):
if Tuple[j[1]] < Tuple[smallest]:
smallest = j
if smallest !=i:
Tuple[i], Tuple[smallest] = Tuple[smallest] , Tuple[i]
print(Tuple)
But I get an error saying "smallest = i[1] TypeError: 'int' object is not subscriptable'
I don't know how else to do this or how I can change it to make it work.
Note: I need to write the code myself and not use any built in functions.
With your original list,
names_ratings = [("Bob", 123), ("Sky", 234), ("Sara", 156)]
You can use the python builtin sorted.
names_ratings_sorted = sorted(names_ratings, key=lambda tup: tup[1])
print(names_ratings_sorted)
Output:
[("Bob", 123), ("Sara", 156), ("Sky", 234)]
Without builtins:
Using the same original list,
names_ratings = [("Bob", 123), ("Sky", 234), ("Sara", 156)]
This works without using any built-ins.
names_ratings_sorted = []
while names_ratings:
maximum = names_ratings[0]
for name, rating in names_ratings:
if rating > maximum[1]:
maximum = (name, rating)
name_ratings_sorted.append(maximum)
names_ratings.remove(maximum)
I have a dictionary with tuples as keys (containing a string and an int) and floats as values. An example:
first = {}
first['monkey', 1] = 130.0
first['dog', 2] = 123.0-
first['cat', 3] = 130.0
first['cat', 4] = 130.0
first['mouse', 6] = 100.0
Now, I need to make a new dictionary, which has the original dictionary key's second element as
it's key. The new dictionary's value should be the the place it stands if the keys were sorted. Added to this, there are two exceptions:
If two dicts have values that are equal, but have different strings in the key, the one with the lowest int in the key should be placed higher.
If two dicts have values that are equal, but have different ints in the key, they should be placed equal in the new dict and all get the same values.
So, the new dictionary should be as the following:
second[1] = 3
second[2] = 2
second[3] = 4
second[4] = 4
second[6] = 1
I know that it's ignorant to ask someone else to solve my problem without giving my code for it.
But i'm simply don't know how to approach the problem. I would be glad if you could provide me with an explanation how would you solve this problem , or even give me a pseudocode of the algorithm.
import itertools as IT
first = {
('monkey',1): 130.0,
('dog',2): 123.0,
('cat', 3): 130.0,
('cat', 4): 130.0,
('mouse', 6): 100.0
}
counter = 0
ordered = sorted(first, key = lambda k: (first[k], k[1], k[0]))
second = {}
for key, group in IT.groupby(ordered, first.__getitem__):
# group = list(group)
# print(key, group)
# (100.0, [('mouse', 6)])
# (123.0, [('dog', 2)])
# (130.0, [('monkey', 1), ('cat', 3), ('cat', 4)])
previous = None
for name, num in group:
if name != previous:
counter += 1
second[num] = counter
previous = name
print(second)
yields
{1: 3, 2: 2, 3: 4, 4: 4, 6: 1}
Explanation:
The first step is to order the (name, num) keys of first according to the associated values. However, in case of ties, the num is used. If there is still a tie, the name is used to break the tie.
In [96]: ordered = sorted(first, key = lambda k: (first[k], k[1], k[0]))
In [97]: ordered
Out[97]: [('mouse', 6), ('dog', 2), ('monkey', 1), ('cat', 3), ('cat', 4)]
Next, we need to group the items in ordered since there are special rules when the value first[k] is the same. The grouping can be achieved using itertools.groupby:
In [99]: for key, group in IT.groupby(ordered, first.__getitem__):
....: print(key, list(group))
....:
....:
(100.0, [('mouse', 6)])
(123.0, [('dog', 2)])
(130.0, [('monkey', 1), ('cat', 3), ('cat', 4)])
itertools.groupby is collecting the items in ordered into bunches according to the value of the key, first.__getitem__(item). For example,
In [100]: first.__getitem__(('monkey', 1))
Out[100]: 130.0
In [101]: first.__getitem__(('cat', 3))
Out[101]: 130.0
first.__getitem__(item) is just a fancy way of writing first[item]. The reason why I use first.__getitem__ is because itertools.groupby expects a function for its second argument, and first.__getitem__ is the function that fits the bill.
Finally, we iterate through each group. Basically, we want to do this:
for name, num in group:
counter += 1
second[num] = counter
except that, when the names are equal, we do not want to advance the counter. So to check if the names are equal, it helps to store the previous name:
previous = None
for name, num in group:
if name != previous:
counter += 1
...
previous = name
Warning: Note that rkd91's code and my code produce different answers for
first = {
('monkey',1): 130.0,
('dog',2): 123.0,
('cat', 3): 129.0,
('cat', 4): 130.0,
('mouse', 6): 100.0
}
probably due to different interpretations of the specifications. I'll leave it to you do decide which is yielding the desired output.
#rdk91's code yields
{1: 4, 2: 2, 3: 5, 4: 3, 6: 1}
my code yields
{1: 4, 2: 2, 3: 3, 4: 5, 6: 1}
1) Get a list of key-value tuples using first_list = first.items()
2) Create a custom comparator function that will sort the list according to your criteria.
3) Sort the list using first_list.sort(comparator)
4) Build your new dictionary from the sorted list.
rob#rivertam:~/Programming$ cat sorter.py
first = {}
first['monkey', 1] = 130.0
first['dog', 2] = 123.0
first['cat', 3] = 130.0
first['cat', 4] = 130.0
first['mouse', 6] = 100.0
# Get the keys of first, sorted by the value (ascending order), and then by the integer in the key (descending order) if two keys have the same value
s = sorted(first, key=lambda x: x[0])
s.reverse()
s = sorted(s, key=lambda x: first[x])
# Loop through these, and create a new list where the key is the integer in the old key, and the value is the position in the sorted order.
last_val = None
last = (None, None)
index = 0
new_dict = {}
for item in s:
if not ((first[item] == last_val) and (item[1] != last[1]) and item[0] == last[0]):
# When we have the same value, the same string but a different integer from the last key, consider it to be the same position in the sorted order.
index += 1
new_dict[item[1]] = index
last_val = first[item]
last = item
print new_dict
rob#rivertam:~/Programming$ python sorter.py
{1: 3, 2: 2, 3: 4, 4: 4, 6: 1}