Search part of tuple list - python

I am trying to do a list comprehension of a tuples list based on another list of tuples with partial match.
x = [((1,1),(1,1),(1,2)),
((2,1),(1,3),(2,9)),
((2,1),(2,3),(2,9))]
y = [(2,1),(1,3)]
[i for i in x for k in y if k in i]
e.g. in this x is a list of tuples & y is the desired list of tuples. If y is found in any of the items in the list of tuples in x, it should return that item.
Result is: [((2, 1), (1, 3), (2, 9)), ((2, 1), (1, 3), (2, 9)), ((2, 1), (2, 3), (2, 9))]
But I want only : [((2, 1), (1, 3), (2, 9))]
I tried with single tuple & it gave the desired result. But not sure why it doesn't work when I pass a tuple list.
x = [((1,1),(1,1),(1,2)),
((2,1),(1,3),(2,9)),
((2,1),(2,3),(2,9))]
y = (2,1)
[i for i in x if y in i]
Result: [((2, 1), (1, 3), (2, 9)), ((2, 1), (2, 3), (2, 9))]

If it doesn't have to be a comprehension:
[*filter({*y}.issubset, x)]

all is the ticket here, determining if all tuples in y are in each element in x.
[
tpl
for tpl in x
if all(tpl2 in tpl
for tpl2 in y)
]
# [((2, 1), (1, 3), (2, 9))]

You can use:
x = [
((1, 1), (1, 1), (1, 2)),
((2, 1), (1, 3), (2, 9)),
((2, 1), (2, 3), (2, 9)),
]
y = [(2, 1), (1, 3)]
print([t for t in x if not set(y).difference(t)])
output:
[((2, 1), (1, 3), (2, 9))]
If you subtract tuples inside x from y with set operations, if all the sub tuples are present, you'll end up with an empty set, so you want that tuple.
You could also write if set(y).issubset(t) instead of if not set(y).difference(t) part.(borrowed from #kellyBundy's answer)
What if I want any tpl of y to be true but not all.
print([t for t in x if set(y).intersection(t)])

Related

Numpy: reshape list of tuples

I have the following list of tuples:
>>> import itertools
>>> import numpy as np
>>> grid = list(itertools.product((1,2,3),repeat=2))
>>> grid
[(1, 1), (1, 2), (1, 3), (2, 1), (2, 2), (2, 3), (3, 1), (3, 2), (3, 3)]
I'd like to reshape this list in a sensible way (e.g. using numpy if possible) to be 3x3 as follows:
[[(1, 1), (1, 2), (1, 3)],
[(2, 1), (2, 2), (2, 3)],
[(3, 1), (3, 2), (3, 3)]]
When I do np.reshape(grid, (3, 3)) I get the following error: ValueError: cannot reshape array of size 18 into shape (3,3) (size 18??)
I've tried variations of np.reshape(grid, (3, 3, 2)) but these don't return the 3x3 grid given above.
This will do the job:
new_grid = np.empty(len(grid), dtype='object')
new_grid[:] = grid
new_grid = new_grid.reshape(3, 3)
This outputs:
array([[(1, 1), (1, 2), (1, 3)],
[(2, 1), (2, 2), (2, 3)],
[(3, 1), (3, 2), (3, 3)]], dtype=object)
The object type will remain tuple:
type(new_grid[0, 0])
tuple
18 comes from the fact that you have a list of 9 tuples, each containing 2 items; thus, 9 * 2 = 18. numpy automatically converts the tuples to part of the array.
You can either use LeonardoVaz's answer or do it speedily with nested list comprehension:
reshaped_grid = [[grid[i+j] for j in range(3)] for i in range(0, len(grid), 3)]
Output:
>>> reshaped_grid
[
[(1, 1), (1, 2), (1, 3)],
[(2, 1), (2, 2), (2, 3)],
[(3, 1), (3, 2), (3, 3)]
]

Comparing tuple values inside list of lists?

I have a list as follows:
mylist=[[(1, 1)], [(1, 1), (1, 2)], [(1, 1), (1, 2), (1, 3)], [(1, 1), (1, 2), (1, 4)]]
Now, what I want is every element of this list is compared with all the other elements and if that element is the subset of the elements it is compared with, it should be popped. For example, [(1, 1)] is the subset of [(1, 1), (1, 2)] then [(1, 1)] should be popped from the list. Similarly, [(1, 1), (1, 2)] is the subset of [(1, 1), (1, 2), (1, 3)] then it should be popped also.
And in this case, we get the output as follows:
[[(1, 1), (1, 2), (1, 3)], [(1, 1), (1, 2), (1, 4)]]
I tried searching for all the possible answers but none was aimed at this particular case.
So far I have tried the following method but of little use:
for i, e in enumerate(mylist):
mylist[i] = tuple(e)
mylist = list(set(mylist))
You need to remove any list from mylist where all the tuples in the list are present in another list in mylist. This is most easily done by assigning to a new list:
newlist = []
for i, lst in enumerate(mylist):
if not any(all(t in l for t in lst) for l in mylist[:i] + mylist[i+1:]):
newlist.append(lst)
Or as a list comprehension:
newlist = [lst for i, lst in enumerate(mylist) if not any(all(t in l for t in lst) for l in mylist[:i] + mylist[i+1:])]
In both cases, for your sample data the output is:
[
[(1, 1), (1, 2), (1, 3)],
[(1, 1), (1, 2), (1, 4)]
]
For larger lists this might become slow, in which case you can speed it up by first mapping the entries in mylist to sets:
mylist=[[(1, 1), (1, 2)], [(1, 1), (1, 2), (1, 3)], [(1, 1), (1, 2), (1, 4)], [(1, 1)]]
mylist=list(map(set, (tuple(l) for l in mylist)))
newlist = [list(lst) for i, lst in enumerate(mylist) if not any(lst.issubset(l) for l in mylist[:i] + mylist[i+1:])]
You can use frozenset.issubset and do the comparaison like this example:
Thanks to #Nick suggestion, this is a more elaborated example:
mylist=[[(1, 1)], [(1, 1), (1, 2)], [(1, 1), (1, 2), (1, 3)], [(1, 1),
(1, 2), (1, 4)]]
out = []
for k, elm in enumerate(mylist):
for elm2 in mylist[:k] + mylist[k + 1:]:
if frozenset(elm).issubset(elm2):
break
else:
out.append(elm)
print(out)
Output:
[[(1, 1), (1, 2), (1, 3)], [(1, 1), (1, 2), (1, 4)]]
Neither solutions from #Nick and #ChihebNexus are efficient.
The answer from #Nick requires a time complexity of O(m ^ 2 x n ^ 2), while #ChihebNexus's answer requires a time complexity of O(m ^ 2 x n), where m is the length of the input list and n is the average length of the sub-lists.
For an approach that requires just a time complexity of O(m x n), you can create a dict that maps each tuple item to a set of the sub-lists the item appears in, keeping in mind that these sub-lists need to be converted to tuples first to become hashable and be added to a set:
mapping = {}
for lst in mylist:
for item in lst:
mapping.setdefault(item, set()).add(tuple(lst))
so that with your sample input, mapping becomes:
{(1, 1): {((1, 1),),
((1, 1), (1, 2)),
((1, 1), (1, 2), (1, 3)),
((1, 1), (1, 2), (1, 4))},
(1, 2): {((1, 1), (1, 2), (1, 3)), ((1, 1), (1, 2)), ((1, 1), (1, 2), (1, 4))},
(1, 3): {((1, 1), (1, 2), (1, 3))},
(1, 4): {((1, 1), (1, 2), (1, 4))}}
And then with the mappings of items to their belonging sub-lists built, we can then iterate through the sub-lists again, and take the intersection of the sets of sub-lists that the items in the current sub-list map to, in order to find the sub-lists that contain all the items in the current sub-list. If there are more than one of such qualifying sub-lists, it means that the current sub-list is a subset of the other qualifying sub-lists, and that we can remove the current sub-list from the result by removing it from all the sets its items map to. The sub-lists that survive this process will be the ones we want in the output, which we can obtain by aggregating the sets with a union operation:
for lst in mylist:
if len(set.intersection(*map(mapping.get, lst))) > 1:
t = tuple(lst)
for item in lst:
mapping[item].remove(t)
print(set.union(*mapping.values()))
This outputs:
{((1, 1), (1, 2), (1, 3)), ((1, 1), (1, 2), (1, 4))}
You can convert it to a list of lists if you really want the exact data types in the question:
list(map(list, set.union(*mapping.values())))
which returns:
[[(1, 1), (1, 2), (1, 3)], [(1, 1), (1, 2), (1, 4)]]

How to remove duplicate combinations tuples from python list?

I have a list with tuples as below:
mylist = [(1, 2),(1, 3),(2, 1),(2, 3),(3, 1),(3, 2),(3, 3)]
Expected (Unique combinations)
[(1, 2),(1, 3),(2, 3),(3, 3)]
I tried below
mylist = list(set(map(tuple, mylist)))
But didnt get the desired output. Any help here?
You can try this.
out=list(set(map(tuple,map(sorted,mylist))))
Thats because a Tuple (1, 2) is different from a Tuple (2, 1)
print((1, 2) == (1, 2))
print((1, 2) == (2, 1))
True
False
You can see it in:
my_set = set()
my_set.add((1, 2))
my_set.add((1, 2))
my_set.add((2, 1))
print(my_set)
{(1, 2), (2, 1)}
So, in your case, you can sort your tuples, so both (1, 2) and (2, 1) will be the same, and the set will get your job done
mylist = [(1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2), (3, 3)]
mylist = [sorted(item) for item in mylist]
mylist = list(set(map(tuple, mylist)))
print(mylist)
[(1, 2), (1, 3), (2, 3), (3, 3)]

Product of coordinates with specific order of iteration

Here's a snippet with a regular itertools.product usage:
from itertools import product
arr = [1,2,3]
pairs = list(product(arr, arr))
# pairs = [(1, 1), (1, 2), (1, 3), (2, 1), (2, 2), (2, 3), (3, 1), (3, 2), (3, 3)]
Now I would like to have these points yielded in an order which can be achieved by sorting the resulting tuples in the following way:
sorted(pairs, key=lambda y:max(y))
# [(1, 1), (1, 2), (2, 1), (2, 2), (1, 3), (2, 3), (3, 1), (3, 2), (3, 3)]
Is there a way for me to input those numbers to itertools.product so it yields the tuples in this order, or do I need to sort the pairs after iterating over the results of itertools.product?
You can probably use your own modified approach to achieve this in one go.
sorted([(x, y) for x in [1, 2, 3] for y in [1, 2, 3]], key=lambda y:max(y))
OUTPUT
[(1, 1), (1, 2), (2, 1), (2, 2), (1, 3), (2, 3), (3, 1), (3, 2), (3, 3)]

Using a parameter in a lambda function in takewhile [duplicate]

This question already has answers here:
What do lambda function closures capture?
(7 answers)
Closed 8 years ago.
I am trying to familiarize with itertools. As an exercise, I am trying to generate (x,y) integer pairs satisfying -1 < y < x <5. The following code using itertools produces incorrect results:
from itertools import *
xs = xrange(0,5)
allPairs = chain(*(izip(repeat(x), takewhile(lambda y: y < x, count())) for x in xs))
print list(allPairs)
The output is
[(0, 0), (0, 1), (0, 2), (0, 3), (1, 0), (1, 1), (1, 2), (1, 3), (2, 0), (2, 1), (2, 2), (2, 3), (3, 0), (3, 1), (3, 2), (3, 3), (4, 0), (4, 1), (4, 2), (4, 3)]
The problem seems to be that the lambda function within the takewhile is using x = 4, the maximum in the range.
If I modify the code above to explicitly construct a lambda function for each x as follows the output is computed correctly.
from itertools import *
xs = xrange(0,5)
xypair = lambda x: izip(repeat(x), takewhile(lambda y: y < x, count()))
allPairs = chain(*(xypair(x) for x in xs))
print list(allPairs)
Output:
[(1, 0), (2, 0), (2, 1), (3, 0), (3, 1), (3, 2), (4, 0), (4, 1), (4, 2), (4, 3)]
Any idea why in the first code the lambda is constructed only using the last value of the iteration over xs?
Try something a bit simpler:
combinations(iterable,r): r-length tuples, in sorted order, no repeated elements
>>> list(combinations(xrange(0,5),2))
[(0, 1), (0, 2), (0, 3), (0, 4), (1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]

Categories

Resources