I would like to be able to generate all unique permutations of a 2d array in python.
For example take this 2d array [[1,1],[0,0]] I would like back
[[0,0],
[1,1]]
[[0,1],
[0,1]]
[[0,1]
[1,0]]
[[1,0]
[0,1]]
[[1,0]
[1,0]]
[[1,1]
[0,0]]
I would like to be able to generate all unique permutations of a 2d array in python.
For example take this 2d array [[1,1],[0,0]] I would like back
[[0,0],
[1,1]]
[[0,1],
[0,1]]
[[0,1]
[1,0]]
[[1,0]
[0,1]]
[[1,0]
[1,0]]
[[1,1]
[0,0]]
You can do it like this
d = [[1, 1], [0, 0]]
from itertools import permutations, chain
from pprint import pprint
pprint(sorted([i[:2], i[2:]] for i in set(permutations(chain.from_iterable(d)))))
Output
[[[0, 0], [1, 1]],
[[0, 1], [0, 1]],
[[0, 1], [1, 0]],
[[1, 0], [0, 1]],
[[1, 0], [1, 0]],
[[1, 1], [0, 0]]]
Is this the approximate size of your array? If it's huge, this solution will be mightily slow, but will work eventually. For arrays of this size, python's built in itertools are the way to go, plus some numpy manipulation.
Additionally, the number of unique permutations depends on the number of elements in your initial array that are different. So flattening the array, producing all the permutations, reshaping into 2x2 (or your desired size), and comparing will get you the "unique" arrays, as you seem to mean it.
I've used loops here (rather than comprehensions) to make things easy to read/test/check. Definitely translate to comprehensions (faster, all around better) before you use for real.
a = np.array([[1,1],[0,0]]).flatten()
permutes = []
for i in permutations(a):
permutes.append((np.array(i).reshape((2,2))))
unique_permutes = [permutes[0]]
for i in permutes[1:]:
one_equal = False
for unique in unique_permutes:
if np.array_equal(i, unique):
one_equal = True
break
if not one_equal:
unique_permutes.append(i)
print len(unique_permutes) #same as what you wanted
for i in unique_permutes: #prints pretilly for sanity checking
print i
One -- not particularly efficient -- way to do it would be something like
from itertools import permutations, chain, islice
def uniperm_arrays(arr):
flat = chain.from_iterable(arr)
perms = set(permutations(flat))
for perm in perms:
pit = iter(perm)
yield [list(islice(pit, len(row))) for row in arr]
which gives
>>> uu = uniperm_arrays([[1,1],[0,0]])
>>> for u in uu:
... for row in u:
... print(row)
... print()
...
[1, 0]
[1, 0]
[1, 1]
[0, 0]
[0, 0]
[1, 1]
[1, 0]
[0, 1]
[0, 1]
[1, 0]
[0, 1]
[0, 1]
EDIT this should work on 2d arrays of any dimension and shape.
Based on the idea that the permutations are really just flat number sequences structured as a 2D list:
from itertools import permutations
def tbl_perms(table):
flat = (j for i in table for j in i)
flat_permutations = iter(sorted(set(permutations(flat))))
# convert back to the original structure
while flat_permutations:
flat_table = list(flat_permutations.next()) # because you can't pop() from tuple
yield [[flat_table.pop(0) for _ in row] for row in table]
result = tbl_perms([[1, 1], [0, 0]])
pprint(list(result))
result = tbl_perms([[1, 1, 1], [0, 0, 0]])
pprint(list(result))
result = tbl_perms([[1, 2, 3], ['a', 'b']])
pprint(list(result))
Output:
[[[0, 0], [1, 1]], [[0, 1], [0, 1]], [[0, 1], [1, 0]], [[1, 0], [0, 1]], [[1, 0], [1, 0]], [[1, 1], [0, 0]]]
[[[0, 0, 0], [1, 1, 1]], [[0, 0, 1], [0, 1, 1]], [[0, 0, 1], [1, 0, 1]], [[0, 0, 1], [1, 1, 0]], [[0, 1, 0], [0, 1, 1]], [[0, 1, 0], [1, 0, 1]], [[0, 1, 0], [1, 1, 0]], [[0, 1, 1], [0, 0, 1]], [[0, 1, 1], [0, 1, 0]], [[0, 1, 1], [1, 0, 0]], [[1, 0, 0], [0, 1, 1]], [[1, 0, 0], [1, 0, 1]], [[1, 0, 0], [1, 1, 0]], [[1, 0, 1], [0, 0, 1]], [[1, 0, 1], [0, 1, 0]], [[1, 0, 1], [1, 0, 0]], [[1, 1, 0], [0, 0, 1]], [[1, 1, 0], [0, 1, 0]], [[1, 1, 0], [1, 0, 0]], [[1, 1, 1], [0, 0, 0]]]
[[[1, 2, 3], ['a', 'b']], [[1, 2, 3], ['b', 'a']], [[1, 2, 'a'], [3, 'b']], [[1, 2, 'a'], ['b', 3]], [[1, 2, 'b'], [3, 'a']], [[1, 2, 'b'], ['a', 3]], [[1, 3, 2], ['a', 'b']], [[1, 3, 2], ['b', 'a']], [[1, 3, 'a'], [2, 'b']], [[1, 3, 'a'], ['b', 2]], [[1, 3, 'b'], [2, 'a']], [[1, 3, 'b'], ['a', 2]], [[1, 'a', 2], [3, 'b']], [[1, 'a', 2], ['b', 3]], [[1, 'a', 3], [2, 'b']], [[1, 'a', 3], ['b', 2]], [[1, 'a', 'b'], [2, 3]], [[1, 'a', 'b'], [3, 2]], [[1, 'b', 2], [3, 'a']], [[1, 'b', 2], ['a', 3]], [[1, 'b', 3], [2, 'a']], [[1, 'b', 3], ['a', 2]], [[1, 'b', 'a'], [2, 3]], [[1, 'b', 'a'], [3, 2]], [[2, 1, 3], ['a', 'b']], [[2, 1, 3], ['b', 'a']], [[2, 1, 'a'], [3, 'b']], [[2, 1, 'a'], ['b', 3]], [[2, 1, 'b'], [3, 'a']], [[2, 1, 'b'], ['a', 3]], [[2, 3, 1], ['a', 'b']], [[2, 3, 1], ['b', 'a']], [[2, 3, 'a'], [1, 'b']], [[2, 3, 'a'], ['b', 1]], [[2, 3, 'b'], [1, 'a']], [[2, 3, 'b'], ['a', 1]], [[2, 'a', 1], [3, 'b']], [[2, 'a', 1], ['b', 3]], [[2, 'a', 3], [1, 'b']], [[2, 'a', 3], ['b', 1]], [[2, 'a', 'b'], [1, 3]], [[2, 'a', 'b'], [3, 1]], [[2, 'b', 1], [3, 'a']], [[2, 'b', 1], ['a', 3]], [[2, 'b', 3], [1, 'a']], [[2, 'b', 3], ['a', 1]], [[2, 'b', 'a'], [1, 3]], [[2, 'b', 'a'], [3, 1]], [[3, 1, 2], ['a', 'b']], [[3, 1, 2], ['b', 'a']], [[3, 1, 'a'], [2, 'b']], [[3, 1, 'a'], ['b', 2]], [[3, 1, 'b'], [2, 'a']], [[3, 1, 'b'], ['a', 2]], [[3, 2, 1], ['a', 'b']], [[3, 2, 1], ['b', 'a']], [[3, 2, 'a'], [1, 'b']], [[3, 2, 'a'], ['b', 1]], [[3, 2, 'b'], [1, 'a']], [[3, 2, 'b'], ['a', 1]], [[3, 'a', 1], [2, 'b']], [[3, 'a', 1], ['b', 2]], [[3, 'a', 2], [1, 'b']], [[3, 'a', 2], ['b', 1]], [[3, 'a', 'b'], [1, 2]], [[3, 'a', 'b'], [2, 1]], [[3, 'b', 1], [2, 'a']], [[3, 'b', 1], ['a', 2]], [[3, 'b', 2], [1, 'a']], [[3, 'b', 2], ['a', 1]], [[3, 'b', 'a'], [1, 2]], [[3, 'b', 'a'], [2, 1]], [['a', 1, 2], [3, 'b']], [['a', 1, 2], ['b', 3]], [['a', 1, 3], [2, 'b']], [['a', 1, 3], ['b', 2]], [['a', 1, 'b'], [2, 3]], [['a', 1, 'b'], [3, 2]], [['a', 2, 1], [3, 'b']], [['a', 2, 1], ['b', 3]], [['a', 2, 3], [1, 'b']], [['a', 2, 3], ['b', 1]], [['a', 2, 'b'], [1, 3]], [['a', 2, 'b'], [3, 1]], [['a', 3, 1], [2, 'b']], [['a', 3, 1], ['b', 2]], [['a', 3, 2], [1, 'b']], [['a', 3, 2], ['b', 1]], [['a', 3, 'b'], [1, 2]], [['a', 3, 'b'], [2, 1]], [['a', 'b', 1], [2, 3]], [['a', 'b', 1], [3, 2]], [['a', 'b', 2], [1, 3]], [['a', 'b', 2], [3, 1]], [['a', 'b', 3], [1, 2]], [['a', 'b', 3], [2, 1]], [['b', 1, 2], [3, 'a']], [['b', 1, 2], ['a', 3]], [['b', 1, 3], [2, 'a']], [['b', 1, 3], ['a', 2]], [['b', 1, 'a'], [2, 3]], [['b', 1, 'a'], [3, 2]], [['b', 2, 1], [3, 'a']], [['b', 2, 1], ['a', 3]], [['b', 2, 3], [1, 'a']], [['b', 2, 3], ['a', 1]], [['b', 2, 'a'], [1, 3]], [['b', 2, 'a'], [3, 1]], [['b', 3, 1], [2, 'a']], [['b', 3, 1], ['a', 2]], [['b', 3, 2], [1, 'a']], [['b', 3, 2], ['a', 1]], [['b', 3, 'a'], [1, 2]], [['b', 3, 'a'], [2, 1]], [['b', 'a', 1], [2, 3]], [['b', 'a', 1], [3, 2]], [['b', 'a', 2], [1, 3]], [['b', 'a', 2], [3, 1]], [['b', 'a', 3], [1, 2]], [['b', 'a', 3], [2, 1]]]
At this point, this question has been up for 5 years, but I found a slightly better answer than the one given (which was quite helpful). This answer accounts for larger 2d arrays.
from itertools import permutations, chain
from pprint import pprint
d = np.array([[1, 1], [0, 0]])
pprint([np.array(i).reshape(d.shape).tolist() for i in set(permutations(chain.from_iterable(d)))])
OUTPUT:
[[[1, 1], [0, 2], [2, 0]],
[[1, 0], [1, 0], [2, 2]],
[[1, 0], [0, 2], [1, 2]],
[[1, 2], [1, 0], [2, 0]],
[[1, 0], [1, 2], [0, 2]],
[[2, 1], [0, 2], [1, 0]],
[[2, 1], [0, 0], [2, 1]],
[[1, 2], [0, 2], [1, 0]],
[[2, 0], [0, 2], [1, 1]],
[[2, 1], [0, 1], [0, 2]],
[[1, 1], [2, 0], [0, 2]],
[[2, 0], [1, 0], [1, 2]],
[[1, 0], [2, 2], [0, 1]],
[[1, 2], [0, 1], [0, 2]],
[[0, 2], [2, 0], [1, 1]],
...