remove redundancies in a list with tuple of three elements - python

I have a list of tuples similar to A:
A = [[(90, 1, 5), (126, 1, 3), (139, 1, 3), (1000, 1, 5), (111, 1, 2), (176, 1, 5)],
[(160, 2, 5), (1000, 2, 5), (111, 1, 2)],
[(134, 3, 5), (126, 1, 3), (128, 3, 4), (139, 1, 3)],
[(128, 3, 4)],
[(90, 1, 5), (160, 2, 5), (134, 3, 5), (1000, 2, 5), (1000, 1, 5), (176, 1, 5)]]
In each row of this list, there might be tuples which their second and third elements are the same. For example in A[0]:
A[0] = [(90, 1, 5), (126, 1, 3), (139, 1, 3), (1000, 1, 5), (111, 1, 2), (176, 1, 5)]
(90, 1, 5), (1000, 1, 5) and (176, 1, 5) have the same second and third elements. Among these, I need to keep the one which has the max value for the first element and remove the other two. So, I should be able to keep (1000, 1, 5) and remove (90, 1, 5) and (176, 1, 5) from A[0].
It would be better to keep the ordering of the list.
Is there any way to do that iteratively for all the rows in A? Any help would be appreciated!

If I understand correctly, here's an itertools.groupby solution. I'm assuming order in the final result does not matter.
from itertools import groupby
def keep_max(lst, groupkey, maxkey):
'groups lst w.r.t. to groupkey, keeps maximum of each group w.r.t. maxkey'
sor = sorted(lst, key=groupkey)
groups = (tuple(g) for _, g in groupby(sor, key=groupkey))
return [max(g, key=maxkey) for g in groups]
In action:
>>> from operator import itemgetter
>>> groupkey = itemgetter(1, 2)
>>> maxkey = itemgetter(0)
>>> A = [[(90, 1, 5), (126, 1, 3), (139, 1, 3), (1000, 1, 5), (111, 1, 2), (176, 1, 5)], [(160, 2, 5), (1000, 2, 5), (111, 1, 2)], [(134, 3, 5), (126, 1, 3), (128, 3, 4), (139, 1, 3)], [(128, 3, 4)], [(90, 1, 5), (160, 2, 5), (134, 3, 5), (1000, 2, 5), (1000, 1, 5), (176, 1, 5)]]
>>>
>>> [keep_max(sub, groupkey, maxkey) for sub in A]
[[(111, 1, 2), (139, 1, 3), (1000, 1, 5)],
[(111, 1, 2), (1000, 2, 5)],
[(139, 1, 3), (128, 3, 4), (134, 3, 5)],
[(128, 3, 4)],
[(1000, 1, 5), (1000, 2, 5), (134, 3, 5)]]

This solution keeps the original ordering of the tuples assuming each tuple (as a whole) is unique; in the case there are duplicates tuples this will return the last appearance of each tuple:
from operator import itemgetter
A = [[(90, 1, 5), (126, 1, 3), (139, 1, 3), (1000, 1, 5), (111, 1, 2), (176, 1, 5)],
[(160, 2, 5), (1000, 2, 5), (111, 1, 2)],
[(134, 3, 5), (126, 1, 3), (128, 3, 4), (139, 1, 3)],
[(128, 3, 4)],
[(90, 1, 5), (160, 2, 5), (134, 3, 5), (1000, 2, 5), (1000, 1, 5), (176, 1, 5)]]
def uniques(lst):
groups = {}
for t in lst:
groups.setdefault(t[1:], []).append(t)
lookup = {t: i for i, t in enumerate(lst)}
index = lookup.get
first = itemgetter(0)
return sorted(map(lambda x: max(x, key=first), groups.values()), key=index)
result = [uniques(a) for a in A]
print(result)
Output
[[(139, 1, 3), (1000, 1, 5), (111, 1, 2)], [(1000, 2, 5), (111, 1, 2)], [(134, 3, 5), (128, 3, 4), (139, 1, 3)], [(128, 3, 4)], [(134, 3, 5), (1000, 2, 5), (1000, 1, 5)]]

If you can afford to ignore ordering, you can use itertools.groupby to group elements by the 2nd and 3rd elements on a list sorted by ascending order of 2nd and 3rd elements and descending order of the first element. Then the first element of each group is your desired choice:
from itertools import groupby
A = [[(90, 1, 5), (126, 1, 3), (139, 1, 3), (1000, 1, 5), (111, 1, 2), (176, 1, 5)],
[(160, 2, 5), (1000, 2, 5), (111, 1, 2)],
[(134, 3, 5), (126, 1, 3), (128, 3, 4), (139, 1, 3)],
[(128, 3, 4)],
[(90, 1, 5), (160, 2, 5), (134, 3, 5), (1000, 2, 5), (1000, 1, 5), (176, 1, 5)]]
def max_duplicate(lst):
res = []
for k, g in groupby(sorted(lst, key=lambda x: (x[1], x[2], -x[0])), key=lambda x: (x[1], x[2])):
res.append(next(g))
return res
result = [max_duplicate(l) for l in A]
for r in result:
print(r)
Output
[(111, 1, 2), (139, 1, 3), (1000, 1, 5)]
[(111, 1, 2), (1000, 2, 5)]
[(139, 1, 3), (128, 3, 4), (134, 3, 5)]
[(128, 3, 4)]
[(1000, 1, 5), (1000, 2, 5), (134, 3, 5)]

You can do this by using a hashmap as follows:
d = {}
for a in A:
for aa in a:
v, k1, k2 = aa
if (k1, k2) in d:
d[(k1, k2)] = max(v, d[(k1, k2)])
else:
d[(k1, k2)] = v
l = [[v, k1, k2] for (k1, k2), v in d.iteritems()]

Using dictionaries:
fin = []
for row in A:
dict = {}
for tup in row:
dict[tup[1:2]] = tup[0]
fin.append(dict)
A = [[value, t1, t1] for (t1, t2), value in dict.iteritems()]
Using this, your dict will transform A[0] from
A[0] = [(90, 1, 5), (126, 1, 3), (139, 1, 3), (1000, 1, 5), (111, 1, 2), (176, 1, 5)]
to
{ (1,5): 1000, (1,3): 139, (1,2): 111 } (as a dict)
and can then be converted back to an array using iteritems
This way, the order will also be preserved.

Related

Rearrange the tuple by sorting in python

I have two lists. One list contains X coordinate values and second list contains Y coordinate values. Using these two lists, I want to make a tupple which is sorted by their first element.
X coordinate = [2, 3, 4, 4, 3, 2, 1, 0, 0, 1, 1, 1, 1, 2, 2, 2]
Y coordinate = [3, 3, 3, 4, 4, 4, 4, 4, 3, 3, 2, 1, 0, 0, 1, 2]
I want my output like this:
[(0, 3), (0, 4), (1, 0), (1, 1), (1, 2), (1, 3), (1, 4), (2, 0), (2, 1), (2, 2), (2, 3), (2, 4), (3, 3), (3, 4), (4, 3), (4, 4)]
To achieve this result, I wrote below code and got an output mentioend below.
merged_list = list(tuple(zip(X3_coordinate, Y3_coordinate)))
merged_list.sort(key=lambda x: x[0])
merged_list
Output:
[(0, 4), (0, 3), (1, 4), (1, 3), (1, 2), (1, 1), (1, 0), (2, 3), (2, 4), (2, 0), (2, 1), (2, 2), (3, 3), (3, 4), (4, 3), (4, 4)]
Kindly let me know what I am doing wrong and give some suggestions of code.
instead of list(tuple()) just do list()
sort with no key, it'll do element-wise by default
use sorted do both generate the list and sort
X_coordinate = [2, 3, 4, 4, 3, 2, 1, 0, 0, 1, 1, 1, 1, 2, 2, 2]
Y_coordinate = [3, 3, 3, 4, 4, 4, 4, 4, 3, 3, 2, 1, 0, 0, 1, 2]
merged_list = sorted(zip(X_coordinate, Y_coordinate))
print(merged_list)
You've sorted by x, but not later by y:
merged_list.sort(key=lambda x: (x[0], x[1]))
[(0, 3), (0, 4), (1, 0), (1, 1), (1, 2), (1, 3), (1, 4), (2, 0), (2, 1), (2, 2), (2, 3), (2, 4), (3, 3), (3, 4), (4, 3), (4, 4)]
By using "key=lambda x: x[0]", you are forcing it to be sorted only by the first element, what you seek is a sort where-in you give priority to the first element, but if the values are same you wish to sort it by the subsequent elements.
X3_coordinate = [2, 3, 4, 4, 3, 2, 1, 0, 0, 1, 1, 1, 1, 2, 2, 2]
Y3_coordinate = [3, 3, 3, 4, 4, 4, 4, 4, 3, 3, 2, 1, 0, 0, 1, 2]
merged_list = list(zip(X3_coordinate, Y3_coordinate))
merged_list.sort()
print(merged_list)
Output:
[(0, 3), (0, 4), (1, 0), (1, 1), (1, 2), (1, 3), (1, 4), (2, 0), (2, 1), (2, 2), (2, 3), (2, 4), (3, 3), (3, 4), (4, 3), (4, 4)]

Dynamic Nested Loops Python

This is the array I require:
N = 6
A = [[x,y,z] for x in range(N+1) for y in range(N+1) for z in range(N+1) if x+y+z== N]
Is there any other way of doing this by only specifying the variable N and 3 instead of x,y,z?
I tried []*3 but can't get the required output.
You are looking for iterttols.product:
from itertools import product
N = 6
A = [(x,y,z) for x in range(N+1) for y in range(N+1) for z in range(N+1) if x+y+z== N]
B = [tup for tup in product(range(N+1), repeat=3) if sum(tup) == N]
print(A, B, A == B, sep='\n')
Gives:
[(0, 0, 6), (0, 1, 5), (0, 2, 4), (0, 3, 3), (0, 4, 2), (0, 5, 1), (0, 6, 0), (1, 0, 5), (1, 1, 4), (1, 2, 3), (1, 3, 2), (1, 4, 1), (1, 5, 0), (2, 0, 4), (2, 1, 3), (2, 2, 2), (2, 3, 1), (2, 4, 0), (3, 0, 3), (3, 1, 2), (3, 2, 1), (3, 3, 0), (4, 0, 2), (4, 1, 1), (4, 2, 0), (5, 0, 1), (5, 1, 0), (6, 0, 0)]
[(0, 0, 6), (0, 1, 5), (0, 2, 4), (0, 3, 3), (0, 4, 2), (0, 5, 1), (0, 6, 0), (1, 0, 5), (1, 1, 4), (1, 2, 3), (1, 3, 2), (1, 4, 1), (1, 5, 0), (2, 0, 4), (2, 1, 3), (2, 2, 2), (2, 3, 1), (2, 4, 0), (3, 0, 3), (3, 1, 2), (3, 2, 1), (3, 3, 0), (4, 0, 2), (4, 1, 1), (4, 2, 0), (5, 0, 1), (5, 1, 0), (6, 0, 0)]
True
Use itertools.combinations_with_replacement:
from itertools import combinations_with_replacement
n = 6
k = 3
# If you want a list of tuples:
lst = [item for item in list(combinations_with_replacement(range(n), k)) if sum(item) == n]
print(lst)
# [(0, 1, 5), (0, 2, 4), (0, 3, 3), (1, 1, 4), (1, 2, 3), (2, 2, 2)]
# If you want a list of lists:
lst = [list(item) for item in list(combinations_with_replacement(range(n), k)) if sum(item) == n]
print(lst)
# [[0, 1, 5], [0, 2, 4], [0, 3, 3], [1, 1, 4], [1, 2, 3], [2, 2, 2]]

How to get unique combinations of pairs in nested tuples?

I have a list of pairs of the form [(tuple1), (tuple2)],where the first tuple has variable length and the second has length 1.
Example:
[((0, 1, 2), 0),
((3, 4, 5), 0),
((12,), 1),
((0, 1, 4, 7), 1),
((12,), 1),
((3, 4, 5), 0)]
I want set(pairs), where tuple1_pair_X = tuple1_pair_Y && tuple2_pair_X = tuple2_pair_Y and not all the possible combinations of tuple1 elements with tuple2.
Desired Output:
[((0, 1, 2), 0),
((3, 4, 5), 0),
((12,), 1),
((0, 1, 4, 7), 1)]
If you want to keep order of your list:
lst = [((0, 1, 2), 0),
((3, 4, 5), 0),
((12,), 1),
((0, 1, 4, 7), 1),
((12,), 1),
((3, 4, 5), 0)]
sorted(set(lst), key=lst.index)
outputs:
[((0, 1, 2), 0),
((3, 4, 5), 0),
((12,), 1),
((0, 1, 4, 7), 1)]
I guess simply using the set() command on the original list of tuples should work and give your desired output.
tup_list = [((0, 1, 2), 0), ((3, 4, 5), 0),((12,), 1),((0, 1, 4, 7), 1),((12,), 1),((3, 4, 5), 0)]
output = list(set(tup_list))

partitioned permutations in python

I wish to have a list of permutations on n elements [0,1,2,...], for n = n1 + n2 + n3. But such permutations are partitioned into m partitions.
For example for n1,n2 = 3,2 I would have:
0,1,2 | 3,4
0,1,2 | 4,3
0,2,1 | 3,4
0,2,1 | 4,3
...
2,1,0 | 4,3
if I use itertools:
product(permutations([0,1,2]),permutations([3,4]))
I get:
[((0, 1, 2), (3, 4)), ((0, 1, 2), (4, 3)), ((0, 2, 1), (3, 4)), ((0, 2, 1), (4, 3)), ((1, 0, 2), (3, 4)), ((1, 0, 2), (4, 3)), ((1, 2, 0), (3, 4)), ((1, 2, 0), (4, 3)), ((2, 0, 1), (3, 4)), ((2, 0, 1), (4, 3)), ((2, 1, 0), (3, 4)), ((2, 1, 0), (4, 3))]
But I would like:
[(0, 1, 2, 3, 4), (0, 1, 2, 4, 3), ...]
Also it would be great if the input could simply be the length of the partitions:
input = [3,2]
or
input = [4,3,2]
In the latter case I would get:
[(0,1,2,3, 4,5,6, 7,8),
(0,1,2,3, 4,5,6, 8,7),
(0,1,2,3, 4,6,5, 7,8),
...]
Any ideas?
As I understand the problem, the following code should work for your needs.
from itertools import permutations, product
def part_perm_iter(ns):
inds = [int(sum(ns[:i])) for i in xrange(len(ns)+1)]
pair_inds = zip(inds,inds[1:])
for p in product( *[permutations(xrange(a,b)) for a, b in pair_inds ] ):
yield sum(p,())
For example, print list(part_perm_iter([2,2])) will print:
[(0, 1, 2, 3), (0, 1, 3, 2), (1, 0, 2, 3), (1, 0, 3, 2)]

Create list of combinations

I would like to create a list of numbers with three values and would like to cover every combination from 0 - 3. For example:
0, 0, 0
0, 0, 1
...
1, 0, 3
1, 1, 3
all the way to 3, 3, 3.
Is there a better way to do this than using multiple for loops?
Here is the code that I used:
for i in range (0, 4):
for x in range (0, 4):
for t in range (0, 4):
assign = [i, x, t]
Usually itertools.product:
list(itertools.product(range(4), repeat=3))
You can use the itertools.product() function for that:
from itertools import product
for i, x, t in product(range(4), repeat=3):
print (i, x, t)
Demo:
>>> from itertools import product
>>> for i, x, t in product(range(4), repeat=3):
... print (i, x, t)
...
(0, 0, 0)
(0, 0, 1)
(0, 0, 2)
(0, 0, 3)
(0, 1, 0)
# ... truncated for readability ...
(3, 2, 3)
(3, 3, 0)
(3, 3, 1)
(3, 3, 2)
(3, 3, 3)
You can use itertools.product:
>>> from itertools import product
>>> list(product({0, 1, 2, 3}, repeat=3))
[(0, 0, 0), (0, 0, 1), (0, 0, 2), (0, 0, 3), (0, 1, 0), (0, 1, 1), (0, 1, 2), (0, 1, 3), (0, 2, 0), (0, 2, 1), (0, 2, 2), (0, 2, 3), (0, 3, 0), (0, 3, 1), (0, 3, 2), (0, 3, 3), (1, 0, 0), (1, 0, 1), (1, 0, 2), (1, 0, 3), (1, 1, 0), (1, 1, 1), (1, 1, 2), (1, 1, 3), (1, 2, 0), (1, 2, 1), (1, 2, 2), (1, 2, 3), (1, 3, 0), (1, 3, 1), (1, 3, 2), (1, 3, 3), (2, 0, 0), (2, 0, 1), (2, 0, 2), (2, 0, 3), (2, 1, 0), (2, 1, 1), (2, 1, 2), (2, 1, 3), (2, 2, 0), (2, 2, 1), (2, 2, 2), (2, 2, 3), (2, 3, 0), (2, 3, 1), (2, 3, 2), (2, 3, 3), (3, 0, 0), (3, 0, 1), (3, 0, 2), (3, 0, 3), (3, 1, 0), (3, 1, 1), (3, 1, 2), (3, 1, 3), (3, 2, 0), (3, 2, 1), (3, 2, 2), (3, 2, 3), (3, 3, 0), (3, 3, 1), (3, 3, 2), (3, 3, 3)]
>>>
The itertools.product() is a great solution, but if you should happen to want a list of lists and not tuples, you could use this:
[ [x,y,z] for x,y,z in itertools.product(range(4), repeat=3)]
or the equivalent list comprehension:
[ [x,y,z] for x in range(0,4)
for y in range(0,4)
for z in range(0,4)]

Categories