I have a dictionary called playlist with a timestamp as the key and song title and artist as the values, stored in a tuple, formatted as below:
{datetime.datetime(2019, 11, 4, 20, 2): ('Closer', 'The Chainsmokers'),
datetime.datetime(2019, 11, 4, 19, 59): ('Piano Man', 'Elton John'),
datetime.datetime(2019, 11, 4, 19, 55): ('Roses', 'The Chainsmokers')}
I am trying to set the artist from this dictionary/tuple and set it as the key in a new dictionary, with the values being songs by that artist and the frequency it occurs in the dictionary. Example output is:
{'Chainsmokers': {'Closer': 3, 'Roses': 1},
'Elton John': {'Piano Man': 2}, … }
This is what I have for code so far:
dictionary = {}
for t in playlist.values():
if t[1] in dictionary:
artist_song[t[1]] += 1
else:
artist_songs[t[1]] = 1
print(dictionary)
However, this only returns the artist as the key and the frequency of artist plays as values.
Thanks in advance for any help.
Use a defaultdict that has a defaultdict as it's default and finally has an int as nested default:
from collections import defaultdict
d = defaultdict(lambda: defaultdict(int))
for song, artist in playlist.values():
d[artist][song] += 1
print(d)
# {'The Chainsmokers': {'Closer': 1, 'Roses': 1}), 'Elton John': {'Piano Man': 1})}
Non defaultdict method is a bit long-winded as we need to be sure that the dicts exist, which is what the defaultdict handles for us.
d = {}
for song, artist in playlist.values():
d.setdefault(artist, {})
d[artist].setdefault(song, 0)
d[artist][song] += 1
Just for fun, here's an alternative version that makes use of collections.Counter:
from collections import defaultdict, Counter
song_count = defaultdict(dict)
for (song, artist), count in Counter(playlist.values()).items():
song_count[artist][song] = count
Related
I'd like to write a function that will take one argument (a text file) to use its contents as keys and assign values to the keys. But I'd like the keys to go from 1 to n:
{'A': 1, 'B': 2, 'C': 3, 'D': 4... }.
I tried to write something like this:
Base code which kind of works:
filename = 'words.txt'
with open(filename, 'r') as f:
text = f.read()
ready_text = text.split()
def create_dict(lst):
""" go through the arg, stores items in it as keys in a dict"""
dictionary = dict()
for item in lst:
if item not in dictionary:
dictionary[item] = 1
else:
dictionary[item] += 1
return dictionary
print(create_dict(ready_text))
The output: {'A': 1, 'B': 1, 'C': 1, 'D': 1... }.
Attempt to make the thing work:
def create_dict(lst):
""" go through the arg, stores items in it as keys in a dict"""
dictionary = dict()
values = list(range(100)) # values
for item in lst:
if item not in dictionary:
for value in values:
dictionary[item] = values[value]
else:
dictionary[item] = values[value]
return dictionary
The output: {'A': 99, 'B': 99, 'C': 99, 'D': 99... }.
My attempt doesn't work. It gives all the keys 99 as their value.
Bonus question: How can I optimaze my code and make it look more elegant/cleaner?
Thank you in advance.
You can use dict comprehension with enumerate (note the start parameter):
words.txt:
colorless green ideas sleep furiously
Code:
with open('words.txt', 'r') as f:
words = f.read().split()
dct = {word: i for i, word in enumerate(words, start=1)}
print(dct)
# {'colorless': 1, 'green': 2, 'ideas': 3, 'sleep': 4, 'furiously': 5}
Note that "to be or not to be" will result in {'to': 5, 'be': 6, 'or': 3, 'not': 4}, perhaps what you don't want. Having only one entry out of two (same) words is not the result of the algorithm here. Rather, it is inevitable as long as you use a dict.
Your program sends a list of strings to create_dict. For each string in the list, if that string is not in the dictionary, then the dictionary value for that key is set to 1. If that string has been encountered before, then the value of that key is increased by 1. So, since every key is being set to 1, then that must mean there are no repeat keys anywhere, meaning you're sending a list of unique strings.
So, in order to have the numerical values increase with each new key, you just have to increment some number during your loop:
num = 0
for item in lst:
num += 1
dictionary[item] = num
There's an easier way to loop through both numbers and list items at the same time, via enumerate():
for num, item in enumerate(lst, start=1): # start at 1 and not 0
dictionary[item] = num
You can use this code. If an item has been in the lst more than once, the idx is considered one time in dictionary!
def create_dict(lst):
""" go through the arg, stores items in it as keys in a dict"""
dictionary = dict()
idx = 1
for item in lst:
if item not in dictionary:
dictionary[item]=idx
idx += 1
return dictionary
i have a dic in this form:
dic = {'movie1':{('bob',1),('jim',3),('dianne',4)},
'movie2': {('dianne',1),('bob',3),('asz',4)}}
it consists of of movie_name as keys, and tuples of values with (name,score) for each moviee
i want to convert this into:
{ 'bob': { 'movie1': 1,'movie2': 3},
'jim': {'movie1': 3},
'dianne': {'movie1': 4,'movie2': 1},
'asz': {'movie2: 4} }
i.e movie reviewed by each person, and the score for each movie.
Im lookingo for a function in which i can pass my dictionary 'dic' and it gives this result
What i tried was:
def reviewer_rank(db):
main_l=[]
for i in dic.values():
temp_l = list(i)
temp_l=dict(temp_l).keys()
main_l.extend(temp_l)
return main_l
i was able to get all the names of the dict in the list
You could use a defaultdict to avoid some bumpy code checking for the existence of each name in the result dict:
from collections import defaultdict
d = defaultdict(dict)
for movie, scores in dic.items():
for name, score in scores:
d[name][movie] = score
d
# defaultdict: {'bob': {'movie1': 1, 'movie2': 3}, 'asz': {'movie2': 4}, 'jim': {'movie1': 3}, 'dianne': {'movie1': 4, 'movie2': 1}}
You can iterate over the key-value tuples k,vs of the outer dictionary, and then for every tuple u, s in the value vs, you assign result[u][k] with s.
Since it is not certain that u is already in the result dictionary, we better use a defaultdict:
from collections import defaultdict
result = defaultdict(dict)
for k, vs in dic.items():
for u, s in vs:
result[u][k] = s
You can later cast the result back to a dict, with:
dict(result)
Note however that a defaultdict is a subclass of dict, so all dictionary operations are supported.
Here is a naive solution. It is just a simple case of looping over your input dict restructuring it into a new one with the required structure:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from collections import defaultdict
dic = {'movie1': {('bob', 1), ('jim', 3), ('dianne', 4)},
'movie2': {('dianne', 1), ('bob', 3), ('asz', 4)}}
raters = defaultdict(dict)
for movie, ratings in dic.items():
for rater, rating in ratings:
raters[rater][movie] = rating
Hope this helps!
You can organize your current data structure into tuples, and create a new dict of dict based on that. Do that with a generator, and it will yield one item at a time.
Using namedtuple you can access your data using strings instead of ints, which is more readable.
from collections import defaultdict, namedtuple
entries = namedtuple('Entries', 'name movie score')
new_dic = defaultdict(dict)
keys = (entries(name[0], k, name[1]) for k, v in dic.items() for name in v)
for item in keys:
new_dic[item.name][item.movie] = item.score
Hello I have a dictionary that looks like this:
dictionary = {'John': {'car':12, 'house':10, 'boat':3},
'Mike': {'car':5, 'house':4, 'boat':6}}
I want to gain access and extract the keys within the sub-dictionary and assign them to variables like this:
cars_total = dictionary['car']
house_total = dictionary['house']
boat_total = dictionary['boat']
Now, when I run the variables above I get a 'Key Error'. It is understandable because I need to first access the outer dictionary. I would appreciate if someone gave a helping hand on how to access keys and the values within the sub-dictionary as those are the values I want to use.
Also i would like to do create a new key, this may not be right but something on these lines:
car = dictionary['car']
house = dictionary['house']
boat = dictionary['boat']
dictionary['total_assets'] = car + house + boat
I want to be able to access all those keys in the dictionary and create a new key. The outer keys such as "John' and 'Mike' should both contain the newly made key at the end.
I know this throws an error but it will give you an idea on what I want to achieve. Thanks for the help
I would just use a Counter object to get the totals:
>>> from collections import Counter
>>> totals = Counter()
>>> for v in dictionary.values():
... totals.update(v)
...
>>> totals
Counter({'car': 17, 'house': 14, 'boat': 9})
>>> totals['car']
17
>>> totals['house']
14
>>>
This has the added benefit of working nicely even if the keys aren't always present.
If you want the total assets, you can then simply sum the values:
>>> totals['total_assets'] = sum(totals.values())
>>> totals
Counter({'total_assets': 40, 'car': 17, 'house': 14, 'boat': 9})
>>>
To sum the total assets for each person and add it as a new key:
for person in dictionary:
dictionary[person]['total_assets'] = sum(dictionary[person].values())
which will result in:
dictionary = {'John': {'car':12, 'house':10, 'boat':3, 'total_assets':25},
'Mike': {'car':5, 'house':4, 'boat':6, 'total_assets':15}}
dictionary doens't have a key car, as you've seen. But dictionary['John'] does.
$ >>> dictionary['John']
{'car': 12, 'boat': 3, 'house': 10}
>>> dictionary['John']['car']
12
>>>
The value associated with each key in dictionary is, itself, another dictionary, which you index separately. There is no single object that contains, e.g., the car value for each subdictionary; you have to iterate
over each value.
# Iterate over the dictionary once per aggregate
cars_total = sum(d['car'] for d in dictionary.values())
house_total = sum(d['house'] for d in dictionary.values())
boat_total = sum(d['boat'] for d in dictionary.values())
or
# Iterate over the dictionary once total
cars_total = 0
house_total = 0
boat_total = 0
for d in dictionary.values():
cars_total += d['car']
house_total += d['house']
boat_total += d['boat']
dictionary = {'John': {'car':12, 'house':10, 'boat':3},'Mike': {'car':5, 'house':4, 'boat':6}}
total_cars=sum([dictionary[x]['car'] for x in dictionary ])
total_house=sum([dictionary[x]['house'] for x in dictionary ])
total_boats=sum([dictionary[x]['boat'] for x in dictionary ])
print(total_cars)
print(total_house)
print(total_boats)
Sample iteration method:
from collections import defaultdict
totals = defaultdict(int)
for person in dictionary:
for element in dictionary[person]:
totals[element] += dictionary[person][element]
print(totals)
Output:
defaultdict(<type 'int'>, {'car': 17, 'boat': 9, 'house': 14})
I have a word occurrence dictionary, and a synonym dictionary.
Word occurrence dictionary example:
word_count = {'grizzly': 2, 'panda': 4, 'beer': 3, 'ale': 5}
Synonym dictionary example:
synonyms = {
'bear': ['grizzly', 'bear', 'panda', 'kodiak'],
'beer': ['beer', 'ale', 'lager']
}
I would like to comibine/rename aggregate the word count dictionary as
new_word_count = {'bear': 6, 'beer': 8}
I thought I would try this:
new_dict = {}
for word_key, word_value in word_count.items(): # Loop through word count dict
for syn_key, syn_value in synonyms.items(): # Loop through synonym dict
if word_key in [x for y in syn_value for x in y]: # Check if word in synonyms
if syn_key in new_dict: # If so:
new_dict[syn_key] += word_value # Increment count
else: # If not:
new_dict[syn_key] = word_value # Create key
But this isn't working, new_dict ends up empty. Also, is there an easier way to do this? Maybe using dictionary comprehension?
Using dict comprehension, sum and dict.get:
In [11]: {w: sum(word_count.get(x, 0) for x in ws) for w, ws in synonyms.items()}
Out[11]: {'bear': 6, 'beer': 8}
Using collections.Counter and dict.get:
from collections import Counter
ec = Counter()
for x, vs in synonyms.items():
for v in vs:
ec[x] += word_count.get(v, 0)
print(ec) # Counter({'bear': 6, 'beer': 8})
Let's change your synonym dictionary a little. Instead of mapping from a word to a list of all its synonyms, let's map from a word to its parent synonym (i.e. ale to beer). This should speed up lookups
synonyms = {
'bear': ['grizzly', 'bear', 'panda', 'kodiak'],
'beer': ['beer', 'ale', 'lager']
}
synonyms = {syn:word for word,syns in synonyms.items() for syn in syns}
Now, let's make your aggregate dictionary:
word_count = {'grizzly': 2, 'panda': 4, 'beer': 3, 'ale': 5}
new_word_count = {}
for word,count in word_count:
word = synonyms[word]
if word not in new_word_count:
new_word_count[word] = 0
new_word_count[word] += count
So I've been stuck for a full hour now. I've seen other posts regarding this same issue and I cannot get mine to work.
Here's the dictionary within dictionary I'm trying to sort:
diction = {'z': {'golf': 3, 'bowling': 9}, 'a': {'fed': 5, 'alvin': 10}, 'r': {'yell': 7, 'shout': 11}}
I'm trying to first sort the outermost of the dictionary, so that's where t[0] comes in. Then I would like to sort the elements that get paired with the letter alphabetically. The desired output --
{a:{alvin:10, fed:5}, r:{shout:11, yell:7}, z:{bowling:9, golf:3}}
Here's my code:
import collections
diction = {'z': {'golf': 3, 'bowling': 9}, 'a': {'fed': 5, 'alvin': 10}, 'r': {'yell': 7, 'shout': 11}}
a= collections.OrderedDict(sorted(diction.items(),key=lambda t:t[0][1]))
This obviously isn't working.
EDIT:
So as of now, this is only sorting by letter. I'm getting:
{a: {fed:5, alvin:10}, r:{yell:7, shout:11}, z:{golf:3, bowling:9}}
What I would like for it to show:
{a:{alvin:10, fed:5}, r:{shout:11, yell:7}, z:{bowling:9, golf:3}}
Your inner dictionaries are not OrderedDict so they will not maintain their order:
from collections import OrderedDict
diction ={'z': {'golf': 3, 'bowling': 9}, 'a': {'fed': 5, 'alvin': 10}, 'r': {'yell': 7, 'shout': 11}}
a = OrderedDict(sorted(diction.items()))
for key, subdict in a.items():
a[key] = OrderedDict(sorted(subdict.items()))
You have a dict whose values are dicts.
Turning that outer object into an OrderedDict doesn't change the inner ones. You have to change them too.
And of course you need to sort each one of them; a single sorted call can't work on both levels at once.
So:
sorted_items = ((innerkey, sorted(innerdict.items(), key=lambda t: t[0]))
for innerkey, innerdict in diction.items())
a = collections.OrderedDict(sorted(sorted_items, key=lambda t: t[0]))