Reverse key value pairing in python dictionary [closed] - python

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I need a way to reverse my key values pairing. Let me illustrate my requirements.
dict = {1: (a, b), 2: (c, d), 3: (e, f)}
I want the above to be converted to the following:
dict = {1: (e, f), 2: (c, d), 3: (a, b)}

You just need:
new_dict = dict(zip(old_dict, reversed(old_dict.values())))
Note, prior to Python 3.8, where dict_values objects are not reversible, you will need something like:
new_dict = dict(zip(old_dict, reversed(list(old_dict.values()))))

List instead of a dict
Assuming that your keys are always the integers from 1 to N, it seems that your dict should actually be a list. And whatever you use, you shouldn't use dict as a variable name.
You would not lose any information with a list:
d = {1: ('a', 'b'), 3: ('e', 'f'), 2: ('c', 'd')}
l = [v for k, v in sorted(d.items())]
# [('a', 'b'), ('c', 'd'), ('e', 'f')]
You also wouldn't lose any information by shifting the indices by -1.
Getting the information back
You have the sorted values directly inside l.
If you need the keys, you can simply call:
range(len(l))
# range(0, 3)
If you want the index i, you can call l[i].
l[1] # Indices have been shifted. 2 is now 1
# ('c', 'd')
If you want the original dict, you can call:
>>> dict(enumerate(l))
{0: ('a', 'b'), 1: ('c', 'd'), 2: ('e', 'f')}
>>> dict(enumerate(l, 1))
{1: ('a', 'b'), 2: ('c', 'd'), 3: ('e', 'f')}
In order to get the reversed values, you can simply reverse the list:
>>> l[::-1]
[('e', 'f'), ('c', 'd'), ('a', 'b')]
>>> l[::-1][0]
('e', 'f')
And, in order to answer your original question, if you really want to keep the proposed data format, you can call:
>>> dict(list(enumerate(l[::-1])))
{0: ('e', 'f'), 1: ('c', 'd'), 2: ('a', 'b')}
>>> dict(list(enumerate(l[::-1], 1)))
{1: ('e', 'f'), 2: ('c', 'd'), 3: ('a', 'b')}

This should accomplish the desired outcome.
def rev_keys(d: dict) -> dict:
'''Return dictionary structure with the
keys reasigned in opposite order'''
old_keys = list(d.keys())
new_keys = old_keys[::-1]
nd = {}
for ki in range(len(new_keys)):
nd[new_keys[ki]]= d[old_keys[ki]]
return nd
Given and input looking like:
dt = {'1': ('a','b'), '2': ('c','d'), '3': ('e','f')}
rev_keys(dt)
returns:
{'3': ('a', 'b'), '2': ('c', 'd'), '1': ('e', 'f')}

Try the following
dict_ = {1: ('a','b'), 2: ('c','d'), 3: ('e','f')}
values = [y for x, y in dict_.items()][::-1]
res = {}
for x, y in enumerate(dict_.items()):
res[y[0]] = values[x]
print(res)
This is the output:
{1: ('e', 'f'), 2: ('c', 'd'), 3: ('a', 'b')}

You can zip the original dictionary's keys with the original dictionary's values, and since you want the values to be reversed, you can use the negative striding [::-1].
Note that dict.values() cannot be subscripted, hence you will need to convert it into a list first:
dct = {1: ('a', 'b'), 2: ('c', 'd'), 3: ('e', 'f')}
dct = dict(zip(dct, list(dct.values())[::-1]))
print(dct)
Output:
{1: ('e', 'f'), 2: ('c', 'd'), 3: ('a', 'b')}

Related

Remove tuples with same elements in two lists

a=['-a', ('-c', 'd'), ('-d', 'c')]
b=['-b', ('c', '-d'), ('d', '-c')]
Basicly, for each list, if there is some tuple in another list that has the same elements with it, then remove all of those tuples have this elements from both lists.
(I was using sets instead of tuples, but somewhere in my code got an error
says: unhashable type: 'set', so I changed it to tuples...)
res=[]
for i in a:
for j in b:
if type(i) == tuple and type(j) == tuple:
if i[0] in j and i[1] in j:
res.append(i)
res.append(j)
a,b=list(set(a)-set(res)),list(set(b)-set(res))
print(a,b)
This gives a=['a'],b=['-b'], is there better methods (maybe some simple build in function) to do the same thing ?
More examples
>>>a=['-a', ('-c', 'd'), ('-d', 'c'), ('-d', 'c')]
>>>b=['-b', ('c', '-d'), ('d', '-c'), ('-d', 'c')]
>a=['a'],b=['-b']
>>>a=['-a', ('a', 'b'),('-c', 'd'), ('-d', 'c'), ('-d', 'c')]
>>>b=['-b', ('c', '-d'), ('d', '-c'), ('-d', 'c')]
>a=['a',('a', 'b')],b=['-b']
Use frozenset On the sub items and you will be able to use set.difference:
a = ['-a', ('a', 'b'),('-c', 'd'), ('-d', 'c'), ('-d', 'c')]
b = ['-b', ('c', '-d'), ('d', '-c'), ('-d', 'c')]
seta = {frozenset(i) if isinstance(i, tuple) else i for i in a}
setb = {frozenset(i) if isinstance(i, tuple) else i for i in b}
print(seta - setb, setb - seta)
Prints:
{'-a', frozenset({'a', 'b'})} {'-b'}
I only say this because you said you were using sets before but had issues. You can always turn the frozen sets back to tuple.
Please find the following answer. Basically, you just need to find out duplicates first.
a = ['-a', ('-c', 'd'), ('-d', 'c')]
b = ['-b', ('-c', 'd'), ('d', '-c')]
to_remove = set(a).intersection(set(b))
a = [i for i in a if i not in to_remove or type(i) != tuple]
b = [j for j in b if j not in to_remove or type(j) != tuple]
print a, b

Python calculate co-occurrence of tuples in list of lists of tuples

I have a big list of lists of tuples like
actions = [ [('d', 'r'), ... ('c', 'e'),('', 'e')],
[('r', 'e'), ... ('c', 'e'),('d', 'r')],
... ,
[('a', 'b'), ... ('c', 'e'),('c', 'h')]
]
and i want to find the co-occurrences of the tuples.
I have tried the sugestions from this question but the accepted answer is just too slow. For example in a list with 1494 list of tuple, the resulting dictionary size is 18225703 and took hours to run for 2 tuple co-occurence. So plain permutation and counting doesn't seem to be the answer since i have a bigger list.
I expect the output to somewhat extract the most common pairs (2) or more (3,4,5 at most) tuples that co-occur the most. Using the previous list as example:
('c', 'e'),('d', 'r')
would a common co-occurence when searching for pairs since they co-occur frequently. Is there an efficient method to achieve this?
I think there is no hope for a faster algorithm: you have to compute the combinations to count them. However, if there is threshold of co-occurrences under which you are not interested, you can rty to reduce the complexity of the algorithm. In both cases, there is a hope for less space complexity.
Let's take a small example:
>>> actions = [[('d', 'r'), ('c', 'e'),('', 'e')],
... [('r', 'e'), ('c', 'e'),('d', 'r')],
... [('a', 'b'), ('c', 'e'),('c', 'h')]]
General answer
This answer is probably the best for a large list of lists, but you can avoid creating intermediary lists. First, create an iterable on all present pairs of elements (elements are pairs too in your case, but that doesn't matter):
>>> import itertools
>>> it = itertools.chain.from_iterable(itertools.combinations(pair_list, 2) for pair_list in actions)
If we want to see the result, we have to consume the iteratable:
>>> list(it)
[(('d', 'r'), ('c', 'e')), (('d', 'r'), ('', 'e')), (('c', 'e'), ('', 'e')), (('r', 'e'), ('c', 'e')), (('r', 'e'), ('d', 'r')), (('c', 'e'), ('d', 'r')), (('a', 'b'), ('c', 'e')), (('a', 'b'), ('c', 'h')), (('c', 'e'), ('c', 'h'))]
Then count the sorted pairs (with a fresh it!)
>>> it = itertools.chain.from_iterable(itertools.combinations(pair_list, 2) for pair_list in actions)
>>> from collections import Counter
>>> c = Counter((a,b) if a<=b else (b,a) for a,b in it)
>>> c
Counter({(('c', 'e'), ('d', 'r')): 2, (('', 'e'), ('d', 'r')): 1, (('', 'e'), ('c', 'e')): 1, (('c', 'e'), ('r', 'e')): 1, (('d', 'r'), ('r', 'e')): 1, (('a', 'b'), ('c', 'e')): 1, (('a', 'b'), ('c', 'h')): 1, (('c', 'e'), ('c', 'h')): 1})
>>> c.most_common(2)
[((('c', 'e'), ('d', 'r')), 2), ((('', 'e'), ('d', 'r')), 1)]
At least in term of space, this solution should be efficient since everything is lazy and the number of elements of the Counter is the number of combinations from elements in the same list, that is at most N(N-1)/2 where N is the number of distinct elements in all the lists ("at most" because some elements never "meet" each other and therefore some combination never happen).
The time complexity is O(M . L^2) where M is the number of lists and L the size of the largest list.
With a threshold on the co-occurences number
I assume that all elements in a list are distinct. The key idea is that if an element is present in only one list, then this element has strictly no chance to beat anyone at this game: it will have 1 co-occurence with all his neighbors, and 0 with the elements of other lists. If there are a lot of "orphans", it might be useful to remove them before processing computing the combinations:
>>> d = Counter(itertools.chain.from_iterable(actions))
>>> d
Counter({('c', 'e'): 3, ('d', 'r'): 2, ('', 'e'): 1, ('r', 'e'): 1, ('a', 'b'): 1, ('c', 'h'): 1})
>>> orphans = set(e for e, c in d.items() if c <= 1)
>>> orphans
{('a', 'b'), ('r', 'e'), ('c', 'h'), ('', 'e')}
Now, try the same algorithm:
>>> it = itertools.chain.from_iterable(itertools.combinations((p for p in pair_list if p not in orphans), 2) for pair_list in actions)
>>> c = Counter((a,b) if a<=b else (b,a) for a,b in it)
>>> c
Counter({(('c', 'e'), ('d', 'r')): 2})
Note the comprehension: no brackets but parentheses.
If you have K orphans in a list of N elements, your time complexity for that list falls from N(N-1)/2 to (N-K)(N-K-1)/2, that is (if I'm not mistaken!) K.(2N-K-1) combinations less.
This can be generalized: if an element is present in two or less lists, then it will have at most 2 co-occurrences with other elements, and so on.
If this is still to slow, then switch to a faster language.

How to convert a Python multilevel dictionary into tuples?

I have a multi level dictionary, example below, which needs to be converted into tuples in reverse order i.e, the innermost elements should be used to create tuple first.
{a: {b:c, d:{e:f, g:h, i:{j:['a','b']}}}}
Output should be something like this:
[(j,['a','b']), (i,j), (g,h), (e,f), (d,e), (d,g), (d,i), (b,c), (a,b), (a,d)]
There you go, this will produce what you want (also tested):
def create_tuple(d):
def create_tuple_rec(d, arr):
for k in d:
if type(d[k]) is not dict:
arr.append((k, d[k]))
else:
for subk in d[k]:
arr.append((k, subk))
create_tuple_rec(d[k], arr)
return arr
return create_tuple_rec(d, [])
# Running this
d = {'a': {'b':'c', 'd':{'e':'f', 'g':'h', 'i':{'j':['a','b']}}}}
print str(create_tuple(d))
# Will print:
[('a', 'b'), ('a', 'd'), ('b', 'c'), ('d', 'i'), ('d', 'e'), ('d', 'g'), ('i', 'j'), ('j', ['a', 'b']), ('e', 'f'), ('g', 'h')]

Checking if a set of tuple contains items from another set

Let's say I have a set of tuples like this:
foo = {('A', 'B'), ('C', 'D'), ('B', 'C'), ('A', 'C')}
var = {'A', 'C', 'B'}
I want to check if every item from var is in any place in the set of tuples and returning True if it is and False if it isn't.
I tried with this but I don't have luck so far.
all((x for x in var) in (a,b) for (a,b) in foo)
Desired output : True
Actual output : False
However if:
var = {'A','C','D'}
I want it to return False, the logic is checking if the strings 'know' eachother.
Alright, let's explain this, for my last var.
A is paired with C, C is paired D, however D is not paired with A.
For my first logic,
A is paired with B,B is paired with C,C is paired with B, C is paired with A, Everyone 'knows' each other.
.
Generate all the pairs you expect to be present and see if they're there with a subset check:
from itertools import combinations
def _norm(it):
return {tuple(sorted(t)) for t in it}
def set_contains(foo, var):
return _norm(combinations(var, 2)) <= _norm(foo)
print(set_contains({('A', 'B'), ('C', 'D'), ('B', 'C'), ('A', 'C')},
{'A', 'C', 'B'})) # True
print(set_contains({('A', 'B'), ('C', 'D'), ('B', 'C'), ('A', 'C')},
{'A', 'C', 'D'})) # False
It may be possible to reduce on the amount of sorting, depending on how exactly combinations works (I'm not 100% sure what to make of the docs) and if you reuse either foo or var several times and can thus sort one of the parts just once beforehand.
Try this:
foo = {('A', 'B'), ('C', 'D'), ('B', 'C'), ('A', 'C')}
var = {'A', 'C', 'B'}
for elem in var:
if any(elem in tuples for tuples in foo):
print(True)
This is not as 'compact' as the others but works the same.
for x in var:
for y in foo:
if x in y:
print('Found %s in %s' % (x, y))
else:
print('%s not in %s' % (x, y))
B not in ('C', 'D')
B not in ('A', 'C')
Found B in ('A', 'B')
Found B in ('B', 'C')
A not in ('C', 'D')
Found A in ('A', 'C')
Found A in ('A', 'B')
A not in ('B', 'C')
Found C in ('C', 'D')
Found C in ('A', 'C')
C not in ('A', 'B')
Found C in ('B', 'C')

Removing duplicates from tuples within a list

I have a list of tuples:
lst = [('a','b'), ('c', 'b'), ('a', 'd'), ('e','f'), ('a', 'b')]
I want the following output list:
output = [('a','b'), ('e','f')]
i.e I want to compare the elements of first tuple with remaining tuples and remove the tuple which contains either one or more duplicate elements.
My attempt:
I was thinking of using for loops, but that wont be feasible once i have very large list. I browsed through following posts but could not get the right solution:
Removing duplicates members from a list of tuples
How do you remove duplicates from a list in whilst preserving order?
If somebody could guide me the right direction, it will be very helpful. Thanks!
Assuming that you want "duplicates" of all elements to be suppressed, and not just the first one, you could use:
lst = [('a','b'), ('c', 'b'), ('a', 'd'), ('e','f'), ('a', 'b')]
def merge(x):
s = set()
for i in x:
if not s.intersection(i):
yield i
s.update(i)
gives
>>> list(merge(lst))
[('a', 'b'), ('e', 'f')]
>>> list(merge([('a', 'b'), ('c', 'd'), ('c', 'e')]))
[('a', 'b'), ('c', 'd')]
>>> list(merge([('a', 'b'), ('a', 'c'), ('c', 'd')]))
[('a', 'b'), ('c', 'd')]
Sets should help:
>>> s = map(set, lst)
>>> first = s[0]
>>> [first] + [i for i in s if not i & first]
[set(['a', 'b']), set(['e', 'f'])]
Or with ifilterfalse:
>>> from itertools import ifilterfalse
>>> s = map(set, lst)
>>> [first] + list(ifilterfalse(first.intersection, s))
[set(['a', 'b']), set(['e', 'f'])]

Categories

Resources