Search code examples
pythoncombinatoricspython-itertools

Pair combinations of elements in dictionary without repetition


In python, I have a dictionary like this...

pleio = {'firstLine': {'enf1': ['54', 'set'], 
                      'enf2': ['48', 'free'], 
                      'enf3': ['34', 'set'], 
                      'enf4': ['12', 'free']}

        'secondLine':{'enf5': ['56','bgb']
                      'enf6': ['67','kiol']
                      'enf7': ['11','dewd']
                      'enf8': ['464','cona']}}

I would like to make paired combinations with no repetition of the elements in the inner dictionary, to end up with a result like this...

{'enf3': ['34', 'set'], 'enf2': ['48', 'free']}
{'enf3': ['34', 'set'], 'enf1': ['54', 'set']}
{'enf3': ['34', 'set'], 'enf4': ['12', 'free']}
{'enf2': ['48', 'free'], 'enf1': ['54', 'set']}
{'enf2': ['48', 'free'], 'enf4': ['12', 'free']}
{'enf1': ['54', 'set'], 'enf4': ['12', 'free']}

I built a function which lets me do it...

import itertools

def pairwise():
    '''
    '''
    leti=[]
    for snp, enfs in pleio.items():        
        for x in itertools.combinations(enfs, 2 ):
            leti.append(x)    
    pleopairs=[]
    for i in leti:
        pipi={}
        for c in i:
            pipi[c]= enfs[c]
        pleopairs.append(pipi)

..but i was wondering if there's a more efficient way, like another specific function from itertools, or any other source. By the way, I found a function called "pairwise" in the itertools documentation. But I don't know how to adapt it, if would be possible in my case, or improve my attempt. Any help?


Solution

  • Your combinations approach was correct, you just need to turn the results of each combination into a dict again:

    import itertools
    
    def pairwise(input):
        for values in input.itervalues():
            for pair in itertools.combinations(values.iteritems(), 2):
                yield dict(pair)
    

    This version is a generator, yielding pairs efficiently, nothing is held in memory any longer than absolutely necessary. If you need a list, just call list() on the generator:

    list(pairwise(pleio))
    

    Output:

    >>> from pprint import pprint
    >>> pprint(list(pairwise(pleio)))
    [{'enf2': ['48', 'free'], 'enf3': ['34', 'set']},
     {'enf1': ['54', 'set'], 'enf3': ['34', 'set']},
     {'enf3': ['34', 'set'], 'enf4': ['12', 'free']},
     {'enf1': ['54', 'set'], 'enf2': ['48', 'free']},
     {'enf2': ['48', 'free'], 'enf4': ['12', 'free']},
     {'enf1': ['54', 'set'], 'enf4': ['12', 'free']}]
    

    You can even combine the whole thing into a one-liner generator:

    from itertools import combinations
    
    for paired in (dict(p) for v in pleio.itervalues() for p in combinations(v.iteritems(), 2)):
        print paired
    

    Which outputs:

    >>> for paired in (dict(p) for v in pleio.itervalues() for p in combinations(v.iteritems(), 2)):
    ...     print paired
    ... 
    {'enf3': ['34', 'set'], 'enf2': ['48', 'free']}
    {'enf3': ['34', 'set'], 'enf1': ['54', 'set']}
    {'enf3': ['34', 'set'], 'enf4': ['12', 'free']}
    {'enf2': ['48', 'free'], 'enf1': ['54', 'set']}
    {'enf2': ['48', 'free'], 'enf4': ['12', 'free']}
    {'enf1': ['54', 'set'], 'enf4': ['12', 'free']}
    

    If you are on Python 3, replace .itervalues() and .iteritems() by .values() and .items() respectively.