Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a nested dictionary from existing dictionary with set and list of tuples

I have parsed a midi file, and I've successfully gotten a dictionary of notes broken up by instrument. An abbreviated example of this is note_dict below, truncated for the purposes of this question.

My end goal is to have a nested dictionary that provides me with the track name, then each possible note as a key, then a list of all possible "next" notes as values. The intent is to use this in as a Markov chain in Foxdot, a python interface for music generation.

It should look something like:

{'track1': {note: [note1, note2, note3], note2: [note1, note2, note3]}, 'track2': {note: [note1, note2, note3], note2: [note1, note2, note3]}

Here is an example of what I have:

import itertools 

def pairwise(iterable):
    a, b = itertools.tee(iterable)
    next(b, None)
    return list(zip(a, b))

note_dict = {'Vocal': [-2, -2, -1, -2], 'Guitar': [1, 1, 4, 1, -2, 1]}

note_dict_updated = { track: [{ n for n in notes }, pairwise(notes), notes] for track, notes in note_dict.items() }
print(note_dict_updated)

This gives me the following, where the first set is all distinct notes, the list of tuples is a pairing of (note, next note), and the last list is just a raw list of notes in order.

{'Vocal': [{-2, -1}, [(-2, -2), (-2, -1), (-1, -2)], [-2, -2, -1, -2]], 'Guitar': [{1, 4, -2}, [(1, 1), (1, 4), (4, 1), (1, -2), (-2, 1)], [1, 1, 4, 1, -2, 1]]}

I'd like the elements of the sets to act as keys, and when the first element of the tuple matches an element of the set, it is added to a list of values associated with the key.

My desired end result, based on note_dict above is:

{'Vocal': {-2: [-2, -1], -1: [-2]}, 'Guitar': {1: [1, 4, -2], 4: [1], -2: [1]}}

All that said, I am not locked into the method where I need to work with note_dict_updated. If there is a smarter way to get from note_dict to my desired end result, I'd love to hear.

edit: I've updated my question a bit. The first answer worked for my initial example, but I believe there are issues when the list of notes in each value overlap. Hopefully, my updated desired end result will be more helpful.

like image 208
Tim Avatar asked Jul 19 '20 23:07

Tim


People also ask

How do I create a nested dictionary?

You can create a nested dictionary in Python by placing comma-separated dictionaries within curly braces {}. A Python nested dictionary allows you to store and access data using the key-value mapping structure within an existing dictionary.

Can you nest a dictionary in a list?

You can have dicts inside of a list. The only catch is that dictionary keys have to be immutable, so you can't have dicts or lists as keys.

How do you make a nested dictionary dynamically in Python?

To create a nested dictionary, simply pass dictionary key:value pair as keyword arguments to dict() Constructor. You can use dict() function along with the zip() function, to combine separate lists of keys and values obtained dynamically at runtime.


1 Answers

The first loop creates an intermediary dictionary of dictionary with inner keys and the same unique sets. This is then cleaned with a second a for-loop as shown here:

Input:

{'Vocal': [-2, -2, -1, -2], 'Guitar': [1, 1, 4, 1]}

Output:

{'Guitar': {1: [1, 4], 4: [1]}, 'Vocal': {-2: [-1, -2], -1: [-2]}}

Code:

#create a new dictionary of dictionary with inner keys and same unique sets

note_dict_updated={}
for key, value in note_dict.iteritems():
    note_dict_updated[key]={}
    for element in set(note_dict[key]):
        note_dict_updated[key][element]=list(set(note_dict[key]))

# remove the values (of not interest) from list values of inner keys 
for key, value in note_dict_updated.iteritems():
    comb=[]
    for lkey, lvalue in note_dict_updated[key].iteritems():
        for val in lvalue:
            if (val,lkey) in comb:
                try:
                    note_dict_updated[key][lkey].remove(lkey)
                except ValueError as e:
                    print ('Issue in key {} for subkey {}'.format(key,lkey))
        for val in lvalue:
            comb.append((lkey,val))
like image 134
Sameeresque Avatar answered Nov 13 '22 14:11

Sameeresque