Is there a cleaner way than using for loops - python

I would like to know if there are any other ways to loop and manipulate data located in separate arrays.
import numpy as np
a = np.arange(2)
b = np.arange(5)
c = np.arange(5)
l1 = []
for x in a:
l2 = []
for y in b:
l3 = []
y = x + 1
for z in c:
z = x + y
t = (x,y,z)
l3.append(t)
l2.append(l3)
l1.append(l2)
print l1

This code does exactly what you are doing.
def method(lst, range1, range2):
for i in lst:
yield [[(i, i+1, 1+(i*2))]*range2]*range1
Can even be turned into a generator expression:
def gen_method(lst, r1, r2):
return ([[(i, i+1, 1+(i*2))]*r2]*r1 for i in lst)
Test it yourself if you like.
My tests:
a = range(2)
b = range(5)
c = range(5)
def your_method(a, b, c):
l1 = []
for x in a:
l2 = []
for y in b:
l3 = []
y = x + 1
for z in c:
z = x + y
t = (x,y,z)
l3.append(t)
l2.append(l3)
l1.append(l2)
return l1
def my_method(lst, range1, range2):
for i in lst:
yield [[(i, i+1, 1+(i*2))]*range2]*range1
yours = your_method(a, b, c)
mine = list(my_method(a, len(b), len(c)))
print yours
print '==='
print mine
print '==='
print yours == mine
>>>
[[[(0, 1, 1), (0, 1, 1), (0, 1, 1), (0, 1, 1), (0, 1, 1)], [(0, 1, 1), (0, 1, 1), (0, 1, 1), (0, 1, 1), (0, 1, 1)], [(0, 1, 1), (0, 1, 1), (0, 1, 1), (0, 1, 1), (0, 1, 1)], [(0, 1, 1), (0, 1, 1), (0, 1, 1), (0, 1, 1), (0, 1, 1)], [(0, 1, 1), (0, 1, 1), (0, 1, 1), (0, 1, 1), (0, 1, 1)]], [[(1, 2, 3), (1, 2, 3), (1, 2, 3), (1, 2, 3), (1, 2, 3)], [(1, 2, 3), (1, 2, 3), (1, 2, 3), (1, 2, 3), (1, 2, 3)], [(1, 2, 3), (1, 2, 3), (1, 2, 3), (1, 2, 3), (1, 2, 3)], [(1, 2, 3), (1, 2, 3), (1, 2, 3), (1, 2, 3), (1, 2, 3)], [(1, 2, 3), (1, 2, 3), (1, 2, 3), (1, 2, 3), (1, 2, 3)]]]
===
[[[(0, 1, 1), (0, 1, 1), (0, 1, 1), (0, 1, 1), (0, 1, 1)], [(0, 1, 1), (0, 1, 1), (0, 1, 1), (0, 1, 1), (0, 1, 1)], [(0, 1, 1), (0, 1, 1), (0, 1, 1), (0, 1, 1), (0, 1, 1)], [(0, 1, 1), (0, 1, 1), (0, 1, 1), (0, 1, 1), (0, 1, 1)], [(0, 1, 1), (0, 1, 1), (0, 1, 1), (0, 1, 1), (0, 1, 1)]], [[(1, 2, 3), (1, 2, 3), (1, 2, 3), (1, 2, 3), (1, 2, 3)], [(1, 2, 3), (1, 2, 3), (1, 2, 3), (1, 2, 3), (1, 2, 3)], [(1, 2, 3), (1, 2, 3), (1, 2, 3), (1, 2, 3), (1, 2, 3)], [(1, 2, 3), (1, 2, 3), (1, 2, 3), (1, 2, 3), (1, 2, 3)], [(1, 2, 3), (1, 2, 3), (1, 2, 3), (1, 2, 3), (1, 2, 3)]]]
===
True

Well, you can compress the code using list comprehensions:
[[[(x,x+1,x*2 +1)]*len(c)]*len(b) for x in a]
What this does is loop for all x in a, and create a list of elements, where each element is a list generated for all y in b, where each element of that list is (x,x+1,2*x+1) for all z in c.

An alternative approach is to use itertools, convert the resulting list to numpy array and reshape it
import numpy as np
import itertools as it
a = np.arange(2)
b = np.arange(5)
c = np.arange(5)
l1 = np.array([(i, i+1, i*2+1) for i, rb, rb in it.product(a, b, c)])
l1 = l1.reshape(len(a), len(b), len(c), len(d[0]))
It might be that this gets more memory consuming than all the other approaches as the size increases, but it's only two lines and creates unique elements for each triplet instead of just creating multiple pointers to the same object.
Edit
An other way, that also allows to keep the triplet (i, i+1, i*1+1) as a list is:
l1 = np.empty([len(a), len(b), len(c)], dtype=object)
for i, rb, rb in it.product(a, b, c):
l1[i,ra, rb] = (i, i+1, i*2+1)

Related

Special kind of permutation

I am working on some code to produce all possible permutations from a list. Such products are constrained exclusively to one specific size. My code works for regular permutations, but I do not know how to obtain permutations that follow this type of logic. I'm supposed to get all of the permutations that follow this sort of criss-cross pattern, where the first element is followed by another element and then repeated until the product reaches size z, into one big array.
For example, given an array like [0,1,2,3] and z=3,
One should return something like [[0,1,0][1,0,1][2,0,2][0,2,0][0,3,0][0,4,0]...]
Of course the output would change based on z, so, if we had z=4 the array would have subarrays that are of size 4, like [[0,1,0,1],[1,0,1,0],[2,0,2,0],[0,2,0,2], etc.]
Fundamentally, you just need to alternate two elements in the original array so they form that type of a-b/a-b-a/a-b-a-b/pattern, so two elements at a time are taken.
This is my code for basic permutations with size z.
def comb(k,array):
if k<=0:
return [[]]
else:
final=[]
for part in comb(k-1,array):
for e in array:
final.append([e]+part)
return final
I don't want to use any library because I am studying recursion specifically and that would be sort of pointless learning-wise. Thank you.
Use itertools.product:
from itertools import product
lst = [0,1,2,3]
result = list(product(lst,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)]
Try permutations from itertools module:
l = [0, 1, 2, 3]
z = 3
out = list(permutations(l, r=z))
print(out)
# Output:
[(0, 1, 2),
(0, 1, 3),
(0, 2, 1),
(0, 2, 3),
(0, 3, 1),
(0, 3, 2),
(1, 0, 2),
(1, 0, 3),
(1, 2, 0),
(1, 2, 3),
(1, 3, 0),
(1, 3, 2),
(2, 0, 1),
(2, 0, 3),
(2, 1, 0),
(2, 1, 3),
(2, 3, 0),
(2, 3, 1),
(3, 0, 1),
(3, 0, 2),
(3, 1, 0),
(3, 1, 2),
(3, 2, 0),
(3, 2, 1)]
Check the code of permutations which is roughly equivalent.
For z=4:
l = [0, 1, 2, 3]
z = 4
out = list(permutations(l, r=z))
print(out)
# Output:
[(0, 1, 2, 3),
(0, 1, 3, 2),
(0, 2, 1, 3),
(0, 2, 3, 1),
(0, 3, 1, 2),
(0, 3, 2, 1),
(1, 0, 2, 3),
(1, 0, 3, 2),
(1, 2, 0, 3),
(1, 2, 3, 0),
(1, 3, 0, 2),
(1, 3, 2, 0),
(2, 0, 1, 3),
(2, 0, 3, 1),
(2, 1, 0, 3),
(2, 1, 3, 0),
(2, 3, 0, 1),
(2, 3, 1, 0),
(3, 0, 1, 2),
(3, 0, 2, 1),
(3, 1, 0, 2),
(3, 1, 2, 0),
(3, 2, 0, 1),
(3, 2, 1, 0)]

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]]

Is there a way to efficiently iterate over 'nested' combinations in Python?

I am unsure how to define the problem I wish to solve, but from a combinations of numbers, e.g.:
(4, 3, 2)
I wish to make an iterator that goes over all 'nested' combinations of these numbers. What I mean by this is that it iterates over:
(0, 0, 0), (0, 1, 0), (0, 2, 0), (0, 3, 0), (0, 1, 1), (0, 1, 2), (0, 2, 1), (0, 2, 2), ...
(1, 0, 0), (1, 1, 0), (1, 2, 0), (1, 3, 0), (1, 1, 1), (1, 1, 2), (1, 2, 1), (1, 2, 2), ...
...
(4, 0, 0), (4, 1, 0), (4, 2, 0), (4, 3, 0), (4, 1, 1), (4, 1, 2), (4, 2, 1), (4, 2, 2), ...
Preferably it could also be constrained by a maximum sum capacity during the generation of combinations (i.e. sum(combination) < capacity).
I have created a recursive algorithm that generates these combinations, but it is very slow and hope there is a more efficient method.
import numpy as np
def combinations(current, c, c_set):
c_rec = c.copy()
if(current == 0):
while(c_rec[current] + 1 <= numbers[current] and c_rec[current] + 1 < capacity):
c_rec[current] += 1
c_set.append(c_rec.copy())
while(c_rec[current] + 1 <= numbers[current] and c_rec[current] + 1 < capacity):
c_rec[current] += 1
combinations(current - 1, c_rec, c_set)
c_set.append(c_rec)
numbers = (4,3,2)
n = len(numbers)
capacity = 7
c_init = np.zeros(n)
c_set = [c_init]
combinations(n - 1, c_init, c_set)
You can make use of itertools.product for this
from itertools import product
li = [4, 3, 2]
#Create a list of ranges
res = [range(item+1) for item in li]
#[range(0, 5), range(0, 4), range(0, 3)]
#Calculate cartesian product between elements of each list
prod = product(*res)
#Iterate over the elements
for item in prod:
print(item)
The output will be
(0, 0, 0)
(0, 0, 1)
(0, 0, 2)
(0, 1, 0)
(0, 1, 1)
...
(1, 0, 0)
(1, 0, 1)
(1, 0, 2)
(1, 1, 0)
(1, 1, 1)
...
(2, 0, 0)
(2, 0, 1)
(2, 0, 2)
(2, 1, 0)
(2, 1, 1)
.....
(3, 0, 0)
(3, 0, 1)
(3, 0, 2)
(3, 1, 0)
(3, 1, 1)
.....
I might not have fully understood your question, but wouldn't a simple nested for-loop structure solve your problem?
for x in range(4):
for y in range(3):
for z in range(2):
print((x, y, z))
You can use recursion with a generator:
start = (4, 3, 2)
def groups(d):
yield d
for i, [a, b] in enumerate(zip(d, start)):
if a < b:
yield from groups(tuple([*d[:i], d[i]+1, *d[i+1:]]))
result = set(groups((0, 0, 0)))
Output:
[(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), (0, 3, 0), (0, 3, 1), (0, 3, 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), (1, 3, 0), (1, 3, 1), (1, 3, 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), (2, 3, 0), (2, 3, 1), (2, 3, 2), (3, 0, 0), (3, 0, 1), (3, 0, 2), (3, 1, 0), (3, 1, 1), (3, 1, 2), (3, 2, 0), (3, 2, 1), (3, 2, 2), (3, 3, 0), (3, 3, 1), (3, 3, 2), (4, 0, 0), (4, 0, 1), (4, 0, 2), (4, 1, 0), (4, 1, 1), (4, 1, 2), (4, 2, 0), (4, 2, 1), (4, 2, 2), (4, 3, 0), (4, 3, 1), (4, 3, 2)]

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