how to add/merge values together in a python dictionary - python

I have two list
alist = [1,2,5,3,7,3,21,7,2]
blist = [mary, tom, ken, mary, tom, peter, joseph, mary, ken]
in the end I would like to have a python dictionary:
{"mary": 11, "tom": 9, "ken": 7, "peter": 7, "joseph":21}
adding all their marks together according to their names.
I tried something like this:
for (marks, name) in zip(alist,blist):
dict[name] += marks

I have this solution:
d = dict()
for (marks, name) in zip(alist,blist):
if name in d:
d[name] += marks
else:
d[name] = marks
Maybe something more efficient could be written but I think this works.

You can use collections.Counter, it is probably the most efficient way of doing this.
from collections import Counter
c = Counter()
for mark, name in zip(alist, blist):
c[name] += mark
print(c)
Output:
Counter({'joseph': 21, 'mary': 11, 'tom': 9, 'ken': 7, 'peter': 3})
Counter works just like a dictionary, but extends it with some additional methods like:
print(c.most_common(3))
Output:
[('joseph', 21), ('mary', 11), ('tom', 9)]

zip(*iterables)
Make an iterator that aggregates elements from each of the iterables.
https://docs.python.org/3.3/library/functions.html#zip
from collections import defaultdict
alist = [1,2,5,3,7,3,21,7,2]
blist = ['mary', 'tom', 'ken', 'mary', 'tom', 'peter', 'joseph', 'mary', 'ken']
data_dict = defaultdict(int)
for i, count in zip(blist, alist):
data_dict[i] += count

You can use this solution alternatively
alist = [1,2,5,3,7,3,21,7,2]
blist = ['mary', 'tom', 'ken', 'mary', 'tom', 'peter', 'joseph', 'mary', 'ken']
my_dict = {}
for key, value in zip(blist, alist):
my_dict[key] = my_dict.get(key, 0) + value

dict(zip(alist, blist))
This may help taken from this article https://www.geeksforgeeks.org/python-convert-two-lists-into-a-dictionary/amp/

Related

Python: Way to build a dictionary with a variable key and append to a list as the value inside a loop

I have a list of dictionaries. I want to loop through this list of dictionary and for each specific name (an attribute inside each dictionary), I want to create a dictionary where the key is the name and the value of this key is a list which dynamically appends to the list in accordance with a specific condition.
For example, I have
d = [{'Name': 'John', 'id': 10},
{'Name': 'Mark', 'id': 21},
{'Name': 'Matthew', 'id': 30},
{'Name': 'Luke', 'id': 11},
{'Name': 'John', 'id': 20}]
I then built a list with only the names using names=[i['Name'] for i in dic1] so I have a list of names. Notice John will appear twice in this list (at the beginning and end). Then, I want to create a for-loop (for name in names), which creates a dictionary 'ID' that for its value is a list which appends this id field as it goes along.
So in the end I'm looking for this ID dictionary to have:
John: [10,20]
Mark: [21]
Matthew: [30]
Luke: [11]
Notice that John has a list length of two because his name appears twice in the list of dictionaries.
But I can't figure out a way to dynamically append these values to a list inside the for-loop. I tried:
ID={[]} #I also tried with just {}
for name in names:
ID[names].append([i['id'] for i in dic1 if i['Name'] == name])
Please let me know how one can accomplish this. Thanks.
Don't loop over the list of names and go searching for every one in the list; that's very inefficient, since you're scanning the whole list all over again for every name. Just loop over the original list once and update the ID dict as you go. Also, if you build the ID dict first, then you can get the list of names from it and avoid another list traversal:
names = ID.keys()
The easiest solution for ID itself is a dictionary with a default value of the empty list; that way ID[name].append will work for names that aren't in the dict yet, instead of blowing up with a KeyError.
from collections import defaultdict
ID = defaultdict(list)
for item in d:
ID[item['Name']].append(item['id'])
You can treat a defaultdict like a normal dict for almost every purpose, but if you need to, you can turn it into a plain dict by calling dict on it:
plain_id = dict(ID)
The Thonnu has a solution using get and list concatenation which works without defaultdict. Here's another take on a no-import solution:
ID = {}
for item in d:
name, number = item['Name'], item['id']
if name in ID:
ID[name].append(number)
else:
ID[name] = [ number ]
Using collections.defaultdict:
from collections import defaultdict
out = defaultdict(list)
for item in dic1:
out[item['Name']].append(item['id'])
print(dict(out))
Or, without any imports:
out = {}
for item in dic1:
out[item['Name']] = out.get(item['Name'], []) + [item['id']]
print(out)
Or, with a list comprehension:
out = {}
[out.update({item['Name']: out.get(item['Name'], []) + [item['id']]}) for item in dic1]
print(out)
Output:
{'John': [10, 20], 'Mark': [21], 'Matthew': [30], 'Luke': [11]}
dic1 = [{'Name': 'John', 'id': 10}, {'Name': 'Mark', 'id': 21}, {'Name': 'Matthew', 'id': 30}, {'Name': 'Luke', 'id': 11}, {'Name': 'John', 'id': 20}]
id_dict = {}
for dic in dic1:
key = dic['Name']
if key in id_dict:
id_dict[key].append(dic['id'])
else:
id_dict[key] = [dic['id']]
print(id_dict) # {'John': [10, 20], 'Mark': [21], 'Matthew': [30], 'Luke': [11]}
You can use defaultdict for this to initiate a dictionary with a default value. In this case the default value will be empty list.
from collections import defaultdict
d=defaultdict(list)
for item in dic1:
d[item['Name']].append(item['id'])
Output
{'John': [10, 20], 'Mark': [21], 'Matthew': [30], 'Luke': [11]} # by converting (not required) into pure dict dict(d)
You can do in a easy version
dic1=[{'Name': 'John', 'id':10}, {'Name': 'Mark', 'id':21},{'Name': 'Matthew', 'id':30}, {'Name': 'Luke', 'id':11}, {'Name': 'John', 'id':20}]
names=[i['Name'] for i in dic1]
ID = {}
for i, name in enumerate(names):
if name in ID:
ID[name].append(dic1[i]['id'])
else:
ID[name] = [dic1[i]['id']]
print(ID)

Add list values to dictionary of lists

I have a dict of lists:
my_dict = {'Name': ['John', 'Peter', 'Greg'], 'Age': [26, 39, 48]}
I want to add new values from a flat list to each key of the dictionary:
new_row = ['Bob', 23]
So the final dictionary would look like this:
{'Name': ['John', 'Peter', 'Greg', 'Bob'], 'Age': [26, 39, 48, 23]}
What's the most efficient and smartest way to do this, preferably without relying on imported modules?
This would work, but is there a better way?
i = 0
for k in my_dict.keys():
my_dict[k].append(new_row[i])
i += 1
You can use append for the values in keys Name and Age since both of them are lists, and lists are mutable
my_dict['Name'].append(new_row[0])
my_dict['Age'].append(new_row[1])
my_dict
Out[27]: {'Name': ['John', 'Peter', 'Greg', 'Bob'], 'Age': [26, 39, 48, 23]}
You can zip the values of the dict with the list.
for l, x in zip(my_dict.values(), new_row):
l.append(x)
You could also use a dictionary comprehension to create a new dict without modifying the original.
new_dict = {k: v + [x] for (k,v), x in zip(my_dict.items(), new_row)}

How to find which element occurs the most in a python dictionary

If you have a dictionary in python how would you find which element occurs in it the most amount of times. For example if you had the following dictionary, the name Bob occurs the most(3 times)(Once as a key and twice as a value). How would you find that name that occurs the most?
Also, I would prefer not to import anything (as I am a beginner)
dict = {'Mark': ['Paul', 'Bob', 'Carol', 'Leanne', 'Will'], 'Paul': ['Will', 'Zach'], 'Bob': ['Sarah', 'Don'], 'Tim': ['Bob', 'Carol']}
You can count the keys using a Counter, and update it with the count of the values.
You can then use the most_common method of the Counter to get the most common name:
from collections import Counter
from itertools import chain
d = {'Mark': ['Paul', 'Bob', 'Carol', 'Leanne', 'Will'], 'Paul': ['Will', 'Zach'], 'Bob': ['Sarah', 'Don'], 'Tim': ['Bob', 'Carol']}
count = Counter(d.keys())
count.update(chain.from_iterable(d.values()))
print(count.most_common(1))
# [('Bob', 3)]
print(count.most_common(1)[0][0])
# Bob
I guess what you mean is how to find which element is the most common among all the lists that appear in your dictionary as values. If that's the case, the following should do the trick:
from collections import Counter
from itertools import chain
dict = {
'Mark': ['Paul', 'Bob', 'Carol', 'Leanne', 'Will'],
'Paul': ['Will', 'Zach'],
'Bob': ['Sarah', 'Don'],
'Tim': ['Bob', 'Carol']
}
counter = Counter(chain.from_iterable(list(dict.values())))
counter.most_common()
[('Bob', 2), ('Carol', 2), ('Will', 2), ('Paul', 1), ('Leanne', 1), ('Zach', 1), ('Sarah', 1), ('Don', 1)]
If you also need to take keys into account, then:
counter = Counter(chain.from_iterable(list(dict.values()) + [dict.keys()]))
counter.most_common()
[('Bob', 3), ('Paul', 2), ('Carol', 2), ('Will', 2), ('Leanne', 1), ('Zach', 1), ('Sarah', 1), ('Don', 1), ('Mark', 1), ('Tim', 1)]
If you don't want to use external libraries:
l = list(dict.keys()) + sum(list(dict.values()), []) # flatten list of lists
max(l, key=l.count)
>>> 'Bob'
Here is a way to do this without imports. It defines a check function, iterates through dic once to generate a dic_count, then uses another for-loop to get the max_count and the most_common_name.
Sidenote: Never name variables or functions after built-in Python functions or objects. This is why I renamed dict to dic.
dic = {'Mark': ['Paul', 'Bob', 'Carol', 'Leanne', 'Will'], 'Paul': ['Will', 'Zach'], 'Bob': ['Sarah', 'Don'], 'Tim': ['Bob', 'Carol']}
dic_count = {}
# Adds string to dic_count if it's not in,
# otherwise increments its count
def check(string):
if string in dic_count:
dic_count[string] += 1
else:
dic_count[string] = 1
for key, value in dic.items():
# Calls the check function for both keys and values
check(key)
for name in value:
check(name)
max_num = 0
most_common_name = ""
for key, value in dic_count.items():
# If the count is greater than max_num,
# updates both max_num and most_common_name
if value > max_num:
max_num = value
most_common_name = key
print(most_common_name)
# Prints Bob
If you would like to get multiple names, change the last part to
max_num = 0
most_common_names = ""
for key, value in dic_count.items():
# If the count is greater than max_num,
# updates both max_num and most_common_name
if value > max_num:
max_num = value
most_common_names = key
elif value == max_num:
most_common_names += " " + key
print(most_common_names)
# Prints Bob Will after adding an extra
# 'Will' to the dictionary
Alternatively, if you would like to avoid defining a function, simply replace the top part with:
for key, value in dic.items():
# Adds string to dic_count if it's not in,
# otherwise increments its count
if key in dic_count:
dic_count[key] += 1
else:
dic_count[key] = 1
for name in value:
if name in dic_count:
dic_count[name] += 1
else:
dic_count[name] = 1
You can create a list with all items (keys + values) of the dict and use collections.Counter. d is your dictionary (dict that you used is not a proper name for Python as its already used fro built in structure)
from collections import Counter
l=[i for i in d.keys()]+[i for k in d.values() for i in k]
res=Counter(l)
>>> print(res)
Counter({'Bob': 3, 'Paul': 2, 'Carol': 2, 'Will': 2, 'Mark': 1, 'Tim': 1, 'Leanne': 1, 'Zach': 1, 'Sarah': 1, 'Don': 1})
By any chance are you looking for something like this
dic = {'Mark': ['Paul', 'Bob', 'Carol', 'Leanne', 'Will'], 'Paul': ['Will', 'Zach'], 'Bob': ['Sarah', 'Don'], 'Tim': ['Bob', 'Carol']}
#getting all the keys
keyslist=dic.keys()
#fetting all the values of dic as list
valuelist=list(dic.values())
#valuelist.append(keyslist)
test_list=[]
test_list.extend(list(keyslist))
for x in valuelist:
test_list.extend(x)
#list with all elements from dict
print(test_list)
# get most frequent element
max = 0
res = test_list[0]
for i in test_list:
freq = test_list.count(i)
if freq > max:
max = freq
res = i
# printing result
print ("Most frequent element is : " + str(res)+ " Frequency :" +str(max))
Output:
Most frequent element is : Bob Frequency :3
I know this is not the best way ..if anybody have any suggestion to make please leave them in the comment i will edit my answer with those
Please check the comments in the code
Use chain to combine keys and values
Use defaultdict which is a special case of dict
where key is appended if not present
Code:
from itertools import chain
from collections import defaultdict
# do not use dict - no shadowing built-in dict
my_dict = {'Mark': ['Paul', 'Bob', 'Carol', 'Leanne', 'Will'], 'Paul': ['Will', 'Zach'], 'Bob': ['Sarah', 'Don'], 'Tim': ['Bob', 'Carol']}
#searching for one specific name occurence
name_to_search = 'Bob'
name_ctr = sum([1 for ele in chain(my_dict.keys(), *(my_dict.values())) if ele == name_to_search])
print(f'{name_to_search} occurs {name_ctr} times')
#searching for max occuring name in a dictionary
my_dict_name_ctr = defaultdict(int)
for name in chain(my_dict.keys(), *(my_dict.values())):
my_dict_name_ctr[name] += 1
max_occuring_val = max(my_dict_name_ctr.values())
most_occuring_names = [name for name,val in my_dict_name_ctr.items() if val == max_occuring_val]
print(most_occuring_names, 'occurs', max_occuring_val, 'times')
Output:
Bob occurs 3 times
['Bob'] occurs 3 times

Nested If Statement not Evaluating as expected

I have two lists that I am trying to compare :
a = [{'name': 'ORANGES_060222', 'bushels': 5}, {'name': 'BANANAS_062620', 'bushels': 3}]
b = ['oranges', 'bananas', 'apples']
I just want to see if any element of a's name value can be matched to b and if so append the bushel value to an empty list. Like so:
f = []
a = [{'name': 'ORANGES_060222', 'bushels': 5}, {'name': 'BANANAS_062620', 'bushels': 3}]
b = ['oranges', 'bananas', 'apples']
for item in a:
if item['name'].lower() in b:
f.append(item['bushels'])
print(f)
f is returning [], the problem seems to be in the if statement. Not sure but seems like I might be attempting the wrong operation there?
item['name'].lower() in b won't work as you intend because the strings don't match exactly. For instance 'ORANGES_060222' isn't the same as 'orange' (even with lower).
Instead you can remove all non alphabet characters using isalpha
item_name = ''.join([c for c in item['name'].lower() if c.isalpha()])
if item_name in b:
...
You are probably looking for something like this -
f = []
a = [{'name': 'ORANGES_060222', 'bushels': 5}, {'name': 'BANANAS_062620', 'bushels': 3}]
b = ['oranges', 'bananas', 'apples']
for item in a:
for b_items in b:
if b_items in item['name'].lower():
f.append(item['bushels'])
print(f)
Output :
[5, 3]
What this does is checks if any of b exists in a. In your code, you were doing item['name'].lower() which returns ORANGES_060222 which doesn't exists in b. What you probably wanted to check if orange part from a[0] existed in b.
You can also do it as -
f = []
a = [{'name': 'ORANGES_060222', 'bushels': 5}, {'name': 'BANANAS_062620', 'bushels': 3}]
b = ['oranges', 'bananas', 'apples']
for item in a:
if item['name'].lower().split('_')[0] in b:
f.append(item['bushels'])
print(f)
Output :
[5, 3]
This method can be used if every element in a occurs in the format - NAME_NUMBER. So, item['name'].lower().split('_')[0] will only return the name part ( oranges in the first case ) and then you can check if it exists in b to get the desired results.
Hope this helps !
You should do the reverse:
f = []
a = [{'name': 'ORANGES_060222', 'bushels': 5}, {'name': 'BANANAS_062620', 'bushels': 3}]
b = ['oranges', 'bananas', 'apples']
for item in a:
for x in b:
if b in item['name'].lower():
f.append(item['bushels'])
break
print(f)
You can simply use this nested list comprehension:
f = [item['bushels'] for item in a for d in b if d in item['name'].lower()]

Adding spaces to duplicate elements in list

I have the following list1:
['Tom', 'Michael', 'Tom', 'Tom']
I would like to add a space to duplicate values in order to obtain the following ouput:
['Tom', 'Michael', 'Tom ', 'Tom ']
As Tom is repeated 2 times the 1st it has I space and the second 2 spaces at the end
I was thing in using a line of code like this
[ f'{x} ' for x in list1 if .... ]
But not really sure how could I apply it for my case
This is what I tried so far:
from collections import Counter
d = Counter(list1)
res = [k for k, v in d.items() if v > 1]
print([ f'{x} ' for x in res ])
Is there a more efficient way to do it including the addition of spaces?
because you want to add spaces as the number of occurences until each element, you can use count method of lists. Then multiply this count with " ":
l = ['Tom', 'Michael', 'Tom', 'Tom']
res = []
for i in range(len(l)):
cnt = l[:i].count(l[i])
res.append(l[i] + " "*cnt)
Or in one line:
res = [l[i] + " " * l[:i].count(l[i]) for i in range(len(l))]
And both give as expected:
['Tom', 'Michael', 'Tom ', 'Tom ']
Another different approach can be to use defaultdict and "remember" the counts in one go (O(n)), instead of counting for each element (O(n^2)):
from collections import defaultdict
l = ['Tom', 'Michael', 'Tom', 'Tom']
res = []
d = defaultdict(int)
for x in l:
cnt = d[x]
res.append(x + " "*cnt)
d[x] = cnt + 1
This way if an element is seen for the first time its count will be initiazized to 0.
Or, lastly, without any imports. We can just use a regular dictionary and take advantage of the setdefault method:
l = ['Tom', 'Michael', 'Tom', 'Tom']
res = []
d = {}
for x in l:
res.append(x + " "*d.setdefault(x, 0))
d[x] += 1
This gives the output that you want:
x = ['Tom', 'Michael', 'Tom', 'Tom']
z=[]
for y in range(len(x)):
z.append(x[y] + " "*x[:y].count(x[y]))
This yields a z of ['Tom', 'Michael', 'Tom ', 'Tom ']
As a list comprehension, this looks like:
[x[y] + " "*x[:y].count(x[y]) for y in range(len(x))]
This seems like a good opportunity to use a Counter collection, like this:
from collections import Counter
lst = ['Tom', 'Michael', 'Tom', 'Tom']
nums = Counter()
ans = []
for name in lst:
ans.append(name + ' ' * nums[name])
nums[name] += 1
The trick is to keep track of the number of occurrences of the name using the Counter, and update it as we go. Notice that the ' ' * nums[name] part simply says: "print this number of spaces". It works as expected:
ans
=> ['Tom', 'Michael', 'Tom ', 'Tom ']
Here is a solution that avoids repeatedly using count on the original list.
We build the output list, keeping track with a collections.Counter of the previously encountered items:
from collections import Counter
data = ['Tom', 'Michael', 'Tom', 'Tom']
out = []
counts = Counter()
for item in data:
out.append(item + ' '*counts[item])
counts.update((item,))
print(out)
# ['Tom', 'Michael', 'Tom ', 'Tom ']
Note the comma in counts.update((item,)): as update expects an iterable, we pass it a tuple containing just one element, our item. Passing directly the string would count the individual letters.

Categories