简体   繁体   中英

Count most common item in list crosschecking with dictionary value lists

I have a dictionary Songs with songs as the keys and their moods as values. I also have a Users dictionary with listened songs. I would now like to check what moods from the Songs dictionary are most prevalent for each user and put those moods in a new dictionary coupling user to the mood. This is what I have right now:

from collections import Counter

Songs = {
    'Song1' : ['techno', 'upbeat'],
    'Song2' : ['rock', 'upbeat'],
    'Song3' : ['pop', 'sad'],
    'Song5' : ['pop', 'calm'],
    'Song6' : ['rap', 'upbeat'],
    'Song7' : ['rock', 'sad']
}

Users = {
    'User1' : ['Song1', 'Song2', 'Song6'],
    'User2' : ['Song3', 'Song7'],
    'User3' : ['Song2', 'Song7']
}

Users_moods = dict.fromkeys(Users)

for user, song in Users.items():
    for song, mood in Songs.items():
        mood = set(mood)
        mood_counter = Songs[song]

Users_moods = {user: counter.most_common(1)[0][0] for user, counter in Users.items()}

print(Users_moods)
print(Songs)
print(Users)

But it gives me errors. This is what the Users_mood dictionary should look like for this example:

Users_moods = {
    'User1' : 'upbeat',
    'User2' : 'sad',
    'User3' : 'rock'
}
def getMoods(user, users, songs):
    userSongs = users[user]  # songs this user listens to
    moods = {}  # count of each mood this user listens to
    for song in userSongs:
        for mood in songs[song]:  # for each mood of that song
            moods.setdefault(mood, 0)
            moods[mood] += 1
    return max(moods, key=moods.get)  # the most prevalent mood


for user in users:
    print(user, 'likes', getMoods(user, users, songs))  # mood with the highest count

You're not actually using Counter and trying to call most_common on a list. Let's try to break it down:

  1. Create a new dict and for each user, find its associated moods (overall).
  2. Find the most common mood for each user from that dict.

To better handle 1, we can use a defaultdict :

from collections import Counter, defaultdict

Users_moods = defaultdict(list)

for user, songs in Users.items():
    for song in songs:
        Users_moods[user].extend(Songs[song])

common_moods = {user: Counter(moods).most_common(1)[0][0] for user, moods in Users_moods.items()}

print(Users_moods)
print(common_moods)

This gives:

defaultdict(<class 'list'>, {'User1': ['techno', 'upbeat', 'rock', 'upbeat', 'rap', 'upbeat'], 'User2': ['pop', 'sad', 'rock', 'sad'], 'User3': ['rock', 'upbeat', 'rock', 'sad']})
{'User1': 'upbeat', 'User2': 'sad', 'User3': 'rock'}

Alternatively, you can build the common dict on-the-fly with the same loop, by creating a separate Counter for each user:

common_moods = {}

for user, songs in Users.items():
    User_moods = Counter()
    for song in songs:
        User_moods += Counter(Songs[song])
    common_moods[user] = User_moods.most_common(1)[0][0]

print(common_moods)

If you intend to use collections.Counter, let's look at https://docs.python.org/3/library/collections.html#collections.Counter You're going to need

mood_counter = Counter(iterable)

Presumably, iterable here comes from looking at one user, then all their songs, then all the moods from those songs. Rather than try to make a one liner, let's just iterate over those normally.

def get_user_mood(user, Users, Songs):
    accumulated_moods = []
    for song_name in Users[user]:
        moods_for_this_song = Songs[song_name]
        accumulated_moods.extend(moods_for_this_song)

    mood_counter = Counter(accumulated_moods)
    return mood_counter.most_common(1)[0][0] # validate?

This allows you to build the dict fairly easily in a comprehension

Users_moods = {user: get_user_mood(user, Users, Songs) for user in Users}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM