Heterogeneous combinations between two lists - python

What's a better way to generate unique combinations of pairs between two lists where pairs must also be heterogeneous (i.e. pair[0] != pair[1])? By combinations, I mean that I only want one copy of (2, 1) and (1, 2). (is there a better way to express this?)
For example:
a = [1, 4]
b = [1, 2, 3]
magic_functions(a, b)
should return:
[(1, 2), (4, 2), (1, 3), (4, 1), (4, 3)]
I can get there using the following but it seems a bit cumbersome:
prod = itertools.product(a, b)
pairs = set()
for x, y in prod:
if x != y and (y, x) not in pairs:
pairs.add((x, y))

You can use frozenset instead of tuple, a frozenset is immmutable so can be stored in a set:
>>> for x, y in prod:
if x != y:
pairs.add(frozenset((x, y)))
>>> pairs
set([frozenset([1, 3]), frozenset([1, 2]), frozenset([2, 3])])
You can easily extend this to store more than just pairs, for example if we had triads then checking for all of the unique combinations of it in the set will be cumbersome, but frozenset makes it easy:
>>> c = [7, 8, 9]
>>> prod = itertools.product(a, b, c)
>>> triplets = set()
>>> for p in prod:
... f = frozenset(p)
... if len(f) == 3:
... triplets.add(f)
...
>>> triplets
set([frozenset([1, 3, 7]), frozenset([1, 2, 9]), frozenset([8, 1, 2]), frozenset([2, 3, 7]), frozenset([8, 1, 3]), frozenset([1, 2, 7]), frozenset([9, 2, 3]), frozenset([8, 2, 3]), frozenset([1, 3, 9])])

You can use the itertools chain and combinations functions
import itertools
a = [1, 2]
b = [1, 2, 3]
q = set(itertools.chain(a,b))
w = list(itertools.combinations(q,2))
print w
which returns
[(1, 2), (1, 3), (2, 3)]

Related

Generating all the increasing subsequences

Given an array of integers, how can we generate all the increasing subsequences such that all of them have same length ?
Example: given this list
l = [1, 2, 4, 5, 3, 6]
The answer should be if we consider subsequences of length 4:
[1, 2, 3, 6]
[1, 2, 4, 5]
[1, 2, 4, 6]
[1, 2, 5, 6]
[1, 4, 5, 6]
[2, 4, 5, 6]
from itertools import combinations
# the second item of the tuple is the position of the element in the array
zeta = [(1, 1), (2, 2), (4, 3), (5, 4), (3, 5), (6, 6)]
comb = combinations(sorted(zeta, key=lambda x: x[0]), 4)
def verif(x):
l = []
for k in x:
l.append(k[1])
for i in range(len(l)-1):
if l[i+1]-l[i] < 0:
return 0
return 1
for i in comb:
if verif(list(i)):
print(i)
I want a better approach like dynamic programming solution, because obviously my solution is very slow for bigger list of integers. Is LIS problem helpful in this situation?

All possible combinations from the list without any member repeating its position

I have a list of numbers and I need all combinations without any member repeating its position in the list.
Example: If I have {1, 2, 3} then {3, 2, 1} is unacceptable because "2" is in the same position. The only good results are {3, 1, 2} and {2, 3, 1}. I need this on a larger scale, so far I have this:
import itertools
x = [1, 2, 3, 4, 5, 6, 7]
y = 5
comb = []
comb.extend(itertools.combinations(x,y))
print(comb)
This gives me all the combinations there are, but I need to eliminate those that have the same member at the same position, preferably not just when printing but in the comb list.
Not sure if I understood ... but maybe this is what you want ...
from collections import deque
from itertools import islice
def function(x, y):
Q = deque(x)
states = set()
for _ in range(len(x)):
states.add(tuple(islice(Q, 0, y)))
Q.appendleft(Q.pop())
return states
x = [1, 2, 3, 4, 5, 6, 7]
y = 5
resp = function(x, y)
print(resp)
>>> {(5, 6, 7, 1, 2), (1, 2, 3, 4, 5), (7, 1, 2, 3, 4), (2, 3, 4, 5, 6), (4, 5, 6, 7, 1), (6, 7, 1, 2, 3), (3, 4, 5, 6, 7)}
From the expected output you've written, it seems you are looking for permutations rather than combinations
import itertools
import numpy as np
x = [1, 2, 3]
y = 3
no_repeat_permutations = []
for candidate_perm in itertools.permutations(x,y):
for p in map(np.array, no_repeat_permutations):
if any(np.array(candidate_perm) == p):
break
else:
no_repeat_permutations.append(candidate_perm)
print(no_repeat_permutations)
>>> [(1, 2, 3), (2, 3, 1), (3, 1, 2)]
I am using numpy for element wise comparison, if any element-wise comparison in past results is True, we skip this permutations.
In a case we don't find such comparison we enter the else statement and save the permutation.
For further explanation about for-else see this SO question

How to convert a list to a list of tuples?

I am newbie to Python and need to convert a list to dictionary. I know that we can convert a list of tuples to a dictionary.
This is the input list:
L = [1,term1, 3, term2, x, term3,... z, termN]
and I want to convert this list to a list of tuples (or directly to a dictionary) like this:
[(1, term1), (3, term2), (x, term3), ...(z, termN)]
How can we do that easily in Python?
>>> L = [1, "term1", 3, "term2", 4, "term3", 5, "termN"]
# Create an iterator
>>> it = iter(L)
# zip the iterator with itself
>>> zip(it, it)
[(1, 'term1'), (3, 'term2'), (4, 'term3'), (5, 'termN')]
You want to group three items at a time?
>>> zip(it, it, it)
You want to group N items at a time?
# Create N copies of the same iterator
it = [iter(L)] * N
# Unpack the copies of the iterator, and pass them as parameters to zip
>>> zip(*it)
Try with the group clustering idiom:
zip(*[iter(L)]*2)
From https://docs.python.org/2/library/functions.html:
The left-to-right evaluation order of the iterables is guaranteed.
This makes possible an idiom for clustering a data series into
n-length groups using zip(*[iter(s)]*n).
List directly into a dictionary using zip to pair consecutive even and odd elements:
m = [ 1, 2, 3, 4, 5, 6, 7, 8 ]
d = { x : y for x, y in zip(m[::2], m[1::2]) }
or, since you are familiar with the tuple -> dict direction:
d = dict(t for t in zip(m[::2], m[1::2]))
even:
d = dict(zip(m[::2], m[1::2]))
Using slicing?
L = [1, "term1", 2, "term2", 3, "term3"]
L = zip(L[::2], L[1::2])
print L
Try this ,
>>> L = [1, "term1", 3, "term2", 4, "term3", 5, "termN"]
>>> it = iter(L)
>>> [(x, next(it)) for x in it ]
[(1, 'term1'), (3, 'term2'), (4, 'term3'), (5, 'termN')]
>>>
OR
>>> L = [1, "term1", 3, "term2", 4, "term3", 5, "termN"]
>>> [i for i in zip(*[iter(L)]*2)]
[(1, 'term1'), (3, 'term2'), (4, 'term3'), (5, 'termN')]
OR
>>> L = [1, "term1", 3, "term2", 4, "term3", 5, "termN"]
>>> map(None,*[iter(L)]*2)
[(1, 'term1'), (3, 'term2'), (4, 'term3'), (5, 'termN')]
>>>
[(L[i], L[i+1]) for i in xrange(0, len(L), 2)]
The below code will take care of both even and odd sized list :
[set(L[i:i+2]) for i in range(0, len(L),2)]

How to flatten a list with various data types (int, tuple)

Say I have a list with one or more tuples in it:
[0, 2, (1, 2), 5, 2, (3, 5)]
What's the best way to get rid of the tuples so that it's just an int list?
[0, 2, 1, 2, 5, 2, 3, 5]
One of solutions (using itertools.chain):
>>> from itertools import chain
>>> l = [0, 2, (1, 2), 5, 2, (3, 5)]
>>> list(chain(*(i if isinstance(i, tuple) else (i,) for i in l)))
[0, 2, 1, 2, 5, 2, 3, 5]
Using a nested list comprehension:
>>> lst = [0, 2, (1, 2), 5, 2, (3, 5)]
>>> [y for x in lst for y in (x if isinstance(x, tuple) else (x,))]
[0, 2, 1, 2, 5, 2, 3, 5]
def untuppleList(lst):
def untuppleList2(x):
if isinstance(x, tuple):
return list(x)
else:
return [x]
return [y for x in lst for y in untuppleList2(x)]
Then you can do untuppleList([0, 2, (1, 2), 5, 2, (3, 5)]).
A more general recursive solution, that should apply to any iterable (except strings) and any depth of elements:
import collections
def flatten(iterable):
results = []
for i in iterable:
if isinstance(i, collections.Iterable) and not isinstance(i, basestring):
results.extend(flatten(i))
else:
results.append(i)
return results
And usage:
>>> flatten((1, 2, (3, 4), ('happy')))
[1, 2, 3, 4, 'happy']
>>> flatten((1, 2, (3, 4, (5, 6)), ('happy'), {'foo': 'bar', 'baz': 123}))
[1, 2, 3, 4, 5, 6, 'happy', 'foo', 'baz']
You can use flatten function from my funcy library:
from funcy import flatten
flat_list = flatten(your_list)
You can also peek at its implementation.

Python: sort a list and change another one consequently

I have two lists: one contains a set of x points, the other contains y points. Python somehow manages to mix the x points up, or the user could. I'd need to sort them by lowest to highest, and move the y points to follow their x correspondants. They are in two separate lists.. how do I do it?
You could zip the lists and sort the result. Sorting tuples should, by default, sort on the first member.
>>> xs = [3,2,1]
>>> ys = [1,2,3]
>>> points = zip(xs,ys)
>>> points
[(3, 1), (2, 2), (1, 3)]
>>> sorted(points)
[(1, 3), (2, 2), (3, 1)]
And then to unpack them again:
>>> sorted_points = sorted(points)
>>> new_xs = [point[0] for point in sorted_points]
>>> new_ys = [point[1] for point in sorted_points]
>>> new_xs
[1, 2, 3]
>>> new_ys
[3, 2, 1]
>>> xs = [5, 2, 1, 4, 6, 3]
>>> ys = [1, 2, 3, 4, 5, 6]
>>> xs, ys = zip(*sorted(zip(xs, ys)))
>>> xs
(1, 2, 3, 4, 5, 6)
>>> ys
(3, 2, 6, 4, 1, 5)
>>> import numpy
>>> sorted_index = numpy.argsort(xs)
>>> xs = [xs[i] for i in sorted_index]
>>> ys = [ys[i] for i in sorted_index]
if you can work with numpy.array
>>> xs = numpy.array([3,2,1])
>>> xs = numpy.array([1,2,3])
>>> sorted_index = numpy.argsort(xs)
>>> xs = xs[sorted_index]
>>> ys = ys[sorted_index]
If the x and the y are meant to be a single unit (such as a point), it would make more sense to store them as tuples rather than two separate lists.
Regardless, here's what you should do:
x = [4, 2, 5, 4, 5,…]
y = [4, 5, 2, 3, 1,…]
zipped_list = zip(x,y)
sorted_list = sorted(zipped_list)

Categories

Resources