assembling lists with common central element - python

I'm creating all the possible permutations composed of three elements from 0 to a given number using this:
for i in itertools.permutations(range(len(atoms)), 3):
if i[0] < i[-1]:
angles = list(i)
The condition avoids having (0, 1, 2) and (2, 1, 0) "angles" at the same time on my list, what is already great. Now, I need to separate this list into smaller groups being composed of "angles" which have the same central element.
In this way I'd have:
A = ([0, 1, 2], [0, 1, 3], [3, 1, 4])...
B = ([0, 2, 3], [0, 2, 4], [3, 2, 4])...
and so on.
Could you please help me out?

You could use defaultdict to group the permutations:
from collections import defaultdict
angles = defaultdict(list)
for i in itertools.permutations(range(len(atoms)), 3):
if i[0] < i[-1]:
angles[i[1]].append(i)
If len(atoms) is 4 then you'd get following result:
defaultdict(<type 'list'>, {
0: [(1, 0, 2), (1, 0, 3), (2, 0, 3)],
1: [(0, 1, 2), (0, 1, 3), (2, 1, 3)],
2: [(0, 2, 1), (0, 2, 3), (1, 2, 3)],
3: [(0, 3, 1), (0, 3, 2), (1, 3, 2)]
})

The itertools.groupby function can be used to create those lists containing the same central element, but you have to sort the list first so that permutations with the same central element are next to each other. To do that, you need to pass both sort and groupby a key function that looks at the central element. One way to do that is like this:
def keyfunc(s):
return s[1]
or, as a lambda:
keyfunc = lambda s: s[1]
Or you can just use itemgetter from the operator module, which is succinct and significantly faster than using a lambda or def function.
The code below is based on your code but it creates the initial list in a list comprehension. Then it puts the groups in a dictionary, with the central element as the dict key.
from itertools import permutations, groupby
from operator import itemgetter
atoms = 'abcd'
perms = permutations(range(len(atoms)), 3)
angles = [list(u) for u in perms if u[0] < u[-1]]
keyfunc = itemgetter(1)
angles.sort(key=keyfunc)
print(angles)
groups = {k: list(g) for k, g in groupby(angles, keyfunc)}
print(groups)
output
[[1, 0, 2], [1, 0, 3], [2, 0, 3], [0, 1, 2], [0, 1, 3], [2, 1, 3], [0, 2, 1], [0, 2, 3], [1, 2, 3], [0, 3, 1], [0, 3, 2], [1, 3, 2]]
{0: [[1, 0, 2], [1, 0, 3], [2, 0, 3]], 1: [[0, 1, 2], [0, 1, 3], [2, 1, 3]], 2: [[0, 2, 1], [0, 2, 3], [1, 2, 3]], 3: [[0, 3, 1], [0, 3, 2], [1, 3, 2]]}

While the above answers provide excellent solutions to a more general problem, with just 3 element to choose a list comprehension is sufficient.
atoms = [0, 1, 2, 3]
angles = [[(a, b, c) for i, a in enumerate(atoms, start=1) if a != b
for c in atoms[i:] if c != b] for b in atoms]
# [[(1, 0, 2), (1, 0, 3), (2, 0, 3)],
# [(0, 1, 2), (0, 1, 3), (2, 1, 3)],
# [(0, 2, 1), (0, 2, 3), (1, 2, 3)],
# [(0, 3, 1), (0, 3, 2), (1, 3, 2)]]
It is also about 40% faster than permutations + groupby approach on my machine.

You might consider itertools.groupby:
from itertools import groupby, permutations
perms = filter(lambda x: x[0] < x[-1], permutations(range(4), 3))
key = lambda x: x[1] # sort and group by second element
angles = [list(g) for k, g in groupby(sorted(perms, key=key), key=key)]
# here, any comprehension can be used, e.g.
# angles = {k: list(g) for k, g in groupby(sorted(perms, key=key), key=key)}
# will produce the dict from #niemmi's answer
>>> angles
[
[(1, 0, 2), (1, 0, 3), (2, 0, 3)],
[(0, 1, 2), (0, 1, 3), (2, 1, 3)],
[(0, 2, 1), (0, 2, 3), (1, 2, 3)],
[(0, 3, 1), (0, 3, 2), (1, 3, 2)]
]

Related

Sorting a list of multi-dimensional points

I have a list of D-dimensional points (where D is a constant), and I would like to sort them first based on the 1st dimension values, then by the 2nd dimesion values and so on, so if 2 points have the same values at the first x dimensions, they would be sorted based on the values of dimension x+1.
I know that if my number of dimension is final, I could use this solution: https://stackoverflow.com/a/37111840
But since I have D dimensions where D is a constant number in the code, I'm not sure how to define the sorting "key" values well.
As #iz_ points out, this is how python sorting works by default. Here is an example illustrating this point:
import itertools
import random
# generate all length 3 tuples of 0s 1s and 2s
foo = list(itertools.product(*([range(3)]*3)))
#mix them all up
random.shuffle(foo)
print(foo)
# this sorts by the first, then the second, then the last
foo.sort()
print(foo)
[(0, 0, 0), (0, 0, 1), (0, 0, 2), (0, 1, 0), (0, 1, 1), (0, 1, 2), (0, 2, 0), (0, 2, 1), (0, 2, 2), (1, 0, 0), (1, 0, 1), (1, 0, 2), (1, 1, 0), (1, 1, 1), (1, 1, 2), (1, 2, 0), (1, 2, 1), (1, 2, 2), (2, 0, 0), (2, 0, 1), (2, 0, 2), (2, 1, 0), (2, 1, 1), (2, 1, 2), (2, 2, 0), (2, 2, 1), (2, 2, 2)]
example list l1 with three dimensions:
l1 = [[0, 3, 0], [0, 0, 3], [1, 0, 3], [2, 2, 1], [2, 1, 1], [2, 1, 0], [1, 0, 0], [2, 0, 0], [1, 1, 2], [1, 2, 3]]
l2 = sorted(l1, key = lambda k: k[::-1])
print(l2)
[[1, 0, 0], [2, 0, 0], [2, 1, 0], [0, 3, 0], [2, 1, 1], [2, 2, 1], [1, 1, 2], [0, 0, 3], [1, 0, 3], [1, 2, 3]]
I'm not sure whether that's exactly the sort order that you wanted, but it keys on k values of arbitrary length (as long as all elements of l1 are the same length).

How to Pythonically generate lists of variable length containing exhuastive combinations of specified values? [duplicate]

This question already has answers here:
How to generate list combinations?
(4 answers)
Closed 5 years ago.
I'm hoping that there's a 'Pythonic' way to do this. Let's say that I have a list and an integer.
foo = [1, 2]
bar = 2
I want to return a series of lists where each list is the length specified by the integer (or length -1), where the lists represent an expression of each possible combination of values in the list. So for instance, the output I'm looking for would be something like this:
>> [[1, 1], [1, 2], [2, 1], [2, 2]]
if bar was equal to three, then:
>> [[1, 1, 1], [1, 1, 2], [1, 2, 2], [2, 1, 2], [2, 2, 1], [1, 2, 1], [2, 1, 1], [2, 2, 2]]
... and so on.
>>> import itertools
>>> foo = [1,2]
>>> list(itertools.product(foo, repeat=2))
[(1, 1), (1, 2), (2, 1), (2, 2)]
>>> list(itertools.product(foo, repeat=3))
[(1, 1, 1), (1, 1, 2), (1, 2, 1), (1, 2, 2), (2, 1, 1), (2, 1, 2), (2, 2, 1), (2, 2, 2)]
>>>

Python itertools combinations customizing

I am doing a question based on combinations and just stuck in it. And yes I am not so good in python.
The itertools combinations function using ncr just return the r possible combinations from n. I want something which will return the r possible combinations selected and also the other remaining elements from n numbers which were not selected in that iteration.
Example:
>>>from itertools import combinations
>>>list = [1, 2, 3, 4, 5]
>>>rslt = combinations(list, 2)
when [2, 4] is selected it should also return [1, 3, 5]
so it should return like [[2, 4], [1, 3, 5]]
Thanks in advance
itertools.combinations
Return r length subsequences of elements from the input iterable.
You can use a list comprehension to get the other items [j for j in l if j not in i]:
from itertools import combinations
l = [1, 2, 3, 4, 5]
for i in combinations(l,2):
print(list(i),[j for j in l if j not in i])
And you will get :
[1, 2] [3, 4, 5]
[1, 3] [2, 4, 5]
[1, 4] [2, 3, 5]
[1, 5] [2, 3, 4]
[2, 3] [1, 4, 5]
[2, 4] [1, 3, 5]
[2, 5] [1, 3, 4]
[3, 4] [1, 2, 5]
[3, 5] [1, 2, 4]
[4, 5] [1, 2, 3]
By the way, it's not recommended to use list as a variable name.
The simplest way would be to make a copy of the original list, removing the elements that are in the combination:
from itertools import combinations
def combinations_and_remaining(l, n):
for c in combinations(l, n):
diff = [i for i in l if i not in c]
yield c, diff
for i in combinations_and_remaining([1, 2, 3, 4, 5], 2):
print(i)
Will output
((1, 2), [3, 4, 5])
((1, 3), [2, 4, 5])
((1, 4), [2, 3, 5])
((1, 5), [2, 3, 4])
((2, 3), [1, 4, 5])
((2, 4), [1, 3, 5])
((2, 5), [1, 3, 4])
((3, 4), [1, 2, 5])
((3, 5), [1, 2, 4])
((4, 5), [1, 2, 3])
(The combinations returns tuples; remaining elements are returned as lists for efficiency)
One slightly extravagant but fun way would be to use combinations twice:
from itertools import combinations
n = 5
k = 2
lst = list(range(1, n+1))
rslt = zip(combinations(lst, k), map(tuple, reversed(list(combinations(lst, n-k)))))
print(list(rslt))
# -> [((1, 2), (3, 4, 5)), ((1, 3), (2, 4, 5)), ((1, 4), (2, 3, 5)),
# ((1, 5), (2, 3, 4)), ((2, 3), (1, 4, 5)), ((2, 4), (1, 3, 5)),
# ((2, 5), (1, 3, 4)), ((3, 4), (1, 2, 5)), ((3, 5), (1, 2, 4)),
# ((4, 5), (1, 2, 3))]
You can avoid loops and improve readability by using sets, as such:
from itertools import combinations
input = [1,2,3,4,5]
for n in itertools.combinations(input, 2):
print(n , set(n) ^ set(input))
That is, of course, assuming there are no duplicates in the original, which will be lost in the conversion to a set.

how to permute n elements from list recursively in python?

I'm trying to figure out how to create all possible n sized permutations from a list recursively.
For example the result of n=2 and list=[0,1,5] would be:
[[0, 0], [1, 0], [5, 0], [0, 1], [1, 1], [5, 1], [0, 5], [1, 5], [5, 5]]
or n=3 and list=[2,3]:
[[2, 2, 2], [3, 2, 2], [2, 3, 2], [3, 3, 2], [2, 2, 3], [3, 2, 3], [2, 3, 3], [3, 3, 3]]
(sort of like cartesian product).
I've managed to come up with this piece of code:
def perm(nlist, m):
if m > len(nlist):
return
if m == 0 or m == len(nlist):
return [[]]
results = []
for list_i in nlist:
temp = list(nlist)
temp.remove(list_i)
results.extend(map(lambda x: [list_i] + x, perm(temp, m-1)))
return results
but it doesn't give permutations like [0,0] [1,1] [2,2] etc.
Does anybody have a solution for me?
How can I make this without using map() and lambda()?
This isn't sort of like cartesian product; it's exactly like cartesian product.
>>> from itertools import product
>>> list(product([0,1,5], repeat=2))
[(0, 0), (0, 1), (0, 5), (1, 0), (1, 1), (1, 5), (5, 0), (5, 1), (5, 5)]
>>> list(product([2,3], repeat=3))
[(2, 2, 2), (2, 2, 3), (2, 3, 2), (2, 3, 3), (3, 2, 2), (3, 2, 3), (3, 3, 2), (3, 3, 3)]
The polyfill for itertools.product is the following:
def product(*args, **kwds):
# product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
# product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111
pools = map(tuple, args) * kwds.get('repeat', 1)
result = [[]]
for pool in pools:
result = [x+[y] for x in result for y in pool]
for prod in result:
yield tuple(prod)
But since you can't use itertools, you might as well take the liberty of writing a slightly more efficient solution to your problem. Since we are just computing the product of n identical iterables, let's just call it a cartesian exponent:
def cartesian_exponent(li, e=1):
pools = [li] * e
result = [[]]
for pool in pools:
result = [x+[y] for x in result for y in pool]
return result
Or recursively using yet another incomprehensible list comprehension:
def cartesian_exponent(li, e=1):
if e == 1:
return [[x] for x in li]
else:
return [[x]+y for x in li for y in cartesian_exponent(li, e=e-1)]
Which can be compressed to one line:
def cartesian_exponent(li, e=1):
return [[x] for x in li] if e == 1 else [[x] + y for x in li for y in cartesian_exponent(li, e=e-1)]
But then you would be sacrificing readability for terseness and that's no bueno. The incomprehensible list comprehension is already opaque enough.
Some tests:
>>> cartesian_exponent([0,1,5], e=2)
[[0, 0], [0, 1], [0, 5], [1, 0], [1, 1], [1, 5], [5, 0], [5, 1], [5, 5]]
>>> cartesian_exponent([2,3], e=3)
[[2, 2, 2], [2, 2, 3], [2, 3, 2], [2, 3, 3], [3, 2, 2], [3, 2, 3], [3, 3, 2], [3, 3, 3]]
The result you are looking for is the Cartesian product which is the set of all ordered pairs (a, b) where a ∈ A and b ∈ B. Or, all permutations of all combinations.
from itertools import combinations_with_replacement as iter_combs
from itertools import permutations
def perms_combs(l, n):
all_combs = []
for l in list(iter_combs(l, n)):
[all_combs.append(perm) for perm in list(permutations(l, n))]
return list(set(all_combs))
>> print list(product([0, 1, 5], repeat=2))
[(0, 0), (0, 0), (0, 1), (1, 0), (0, 5), (5, 0), (1, 1), (1, 1), (1, 5), (5, 1), (5, 5), (5, 5)]
>> print list(product([2, 3], repeat=3))
[(3, 3, 3), (2, 2, 2), (2, 3, 2), (3, 3, 2), (2, 3, 3), (3, 2, 2), (3, 2, 3), (2, 2, 3)]
This process, however, is already covered by an itertools functions: product
from itertools import product
>> print product([0, 1, 5], 2)
[(0, 0), (0, 0), (0, 1), (1, 0), (0, 5), (5, 0), (1, 1), (1, 1), (1, 5), (5, 1), (5, 5), (5, 5)]
>> print product([2, 3], 3)
[(3, 3, 3), (2, 2, 2), (2, 3, 2), (3, 3, 2), (2, 3, 3), (3, 2, 2), (3, 2, 3), (2, 2, 3)]

Creating sub-lists from a list

I am trying to create a list containing sub-lists in python; like, the proper subset of a set. For example,
A = [1, 2, 3, 4]
Desired List = [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4], [1, 2, 3], [1, 2, 4], [2, 3, 4]]
Thanks!
Since it seems you only want subsets of size 2 or more:
from itertools import combinations, chain
A = range(1, 5)
list(chain(*(combinations(A, r) for r in range(2, len(A)))))
# [(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4), (1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4)]
If you want all proper subsets, just change range(2, len(A)) to range(len(A)).
It looks like you want to get all the combinations from the list. Try using itertools.combinations
desired_list = itertools.combinations(A, 2)

Categories

Resources