Mimicing 'n' Nested For Loops Using Recursion - python

I would like to create some number of for-loops equal to the length of a list, and iterate through the values in that list. For example, if I had the list:
[1,2,3,4]
I would like the code to function like:
for i in range(1):
for j in range(2):
for k in range(3):
for l in range(4):
myfunc(inputs)
I understand I would need to do this recursively, but I'm not quite sure how. Ideally, I would even be able to iterate through these list values by a variable step; perhaps I want to count by two's for one loop, by .8's for another, etc. In that case, I would probably deliver the information in a format like this:
[[value,step],[value,step] ... [value,step],[value,step]]
So, how could I do this?

Not quite sure what you want at the very end, but here's a way to recursively set-up your loops:
test = [1,2,3,4]
def recursive_loop(test):
if len(test) == 1:
for i in range(test[0]):
print('hi') # Do whatever you want here
elif len(test) > 1:
for i in range(test[0]):
recursive_loop(test[1:])
recursive_loop(test)

You can certainly do it with recursion, but there's already a library function for that:
itertools.product
from itertools import product
def nested_loops(myfunc, l):
for t in product(*(range(n) for n in l)):
myfunc(*t)
## OR EQUIVALENTLY
# def nested_loops(myfunc, l):
# for t in product(*map(range, l)):
# myfunc(l)
nested_loops(print, [1, 2, 3, 4])
# 0 0 0 0
# 0 0 0 1
# 0 0 0 2
# 0 0 0 3
# 0 0 1 0
# 0 0 1 1
# ...
# 0 1 2 1
# 0 1 2 2
# 0 1 2 3
You can of course include steps too. Library function zip can be useful.
def nested_loops_with_steps_v1(myfunc, upperbounds, steps):
for t in product(*(range(0, n, s) for n,s in zip(upperbounds, steps))):
myfunc(*t)
nested_loops_with_steps_v1(print, [1,2,8,10], [1,1,4,5])
# 0 0 0 0
# 0 0 0 5
# 0 0 4 0
# 0 0 4 5
# 0 1 0 0
# 0 1 0 5
# 0 1 4 0
# 0 1 4 5
Or if your steps and upperbounds are already zipped together:
def nested_loops_with_steps_v2(myfunc, l):
for t in product(*(range(0, n, s) for n,s in l)):
myfunc(*t)
nested_loops_with_steps_v2(print, [(1,1),(2,1),(8,4),(10,5)])
# 0 0 0 0
# 0 0 0 5
# 0 0 4 0
# 0 0 4 5
# 0 1 0 0
# 0 1 0 5
# 0 1 4 0
# 0 1 4 5

Related

Creating matrix with for loop in python

I have a list with 4 elements. Each element is a correct score that I am pulling from a form. For example:
scoreFixed_1 = 1
scoreFixed_2 = 2
scoreFixed_3 = 3
scoreFixed_4 = 4
scoreFixed = [scoreFixed_1, scoreFixed_2, scoreFixed_3, scoreFixed_4]
Then, I need to add:
scoreFixed_1 to fixture[0][0]
scoreFixed_2 to fixture[0][1]
scoreFixed_3 to fixture[1][0]
scoreFixed_4 to fixture[1][1]
Hence, I need to create a triple for loop that outputs the following sequence so I can index to achieve the result above:
0 0 0
1 0 1
2 1 0
3 1 1
I have tried to use this to create this matrix, however I am only able to get the first column correct. Can anyone help?
for x in range(1):
for y in range(1):
for z in range(4):
print(z, x, y)
which outputs:
0 0 0
1 0 0
2 0 0
3 0 0
Your logic does not generate the table, you want something like:
rownum = 0
for x in range(2):
for y in range(2):
print (rownum, x, y)
rownum += 1
(Edit: The question has been changed, to accomplish the new desire, you want something like this:)
scoreIndex = 0
for x in range(2):
for y in range(2):
fixture[x][y] += scoreFixed[scoreIndex]
scoreIndex += 1
After your edit, it seems like we can split the 'sequence' into:
First column, regular ascending variable ( n += 1)
Second and third column, binary counter (00, 01, 10, 11)
0 0 0
1 0 1
2 1 0
3 1 1
^ ^------- These seem like a binary counter
(00, 01, 10, 11)
^------ A regular ascending variable
( n += 1 )
Using that 'logic' we can create a code that looks like
import itertools
scoreFixed = 0
for i in itertools.product([0,1],repeat=2):
print(scoreFixed, ' '.join(map(str,i)))
scoreFixed += 1
And wil output:
0 0 0
1 0 1
2 1 0
3 1 1
As you can test in this online demo
for x in range(4):
z = int(bin(x)[-1])
y = bin(x)[-2]
y = int(y) if y.isdigit() else 0
print(x, y, z)

Recursion on Enumerate: Padding / normalizing the size of an uneven nested list of lists [of lists ...] to a numpy array - recusively

I am attempting to make a more robust padding / size normalization for getting list of lists into a numpy array format.
(I know There are a ton of questions regarding this "list of lists to numpy array" problem, but I have seen no one attempt to deal with variable depths of lists.
Therefore, I am using recursion to deal with the unknown depth of nested lists.)
I have a function that gets the np.array desired shape.
Now, when filling the array, recursion with enumeration has become problematic - due to keeping track of the indices at every depth.
Simply put:
I need a recursive function that does this to an undetermined depth:
# fill in the values of the ndarray `mat` with values from l
for row_ix, row in enumerate(l):
for col_ix, col in enumerate(row):
for val_ix, val in enumerate(col):
# ...
# ...
mat[row_ix, col_ix, val_ix] = l[row_ix][col_ix][val_ix]
Extra Detail
Here is my MCVE (minimal, complete and verifiable example) for the desired output / functionality:
import numpy as np
# nested shapes of each list ( 2, [2, 4], [ [5,7], [3,7,4,9] ])
# desired shape (max of level) --> (2,4,9)
l = [[[1,2,5,6,7],
[0,2,5,34,5,6,7]],
[[5,6,7],
[0,2,5,7,34,5,7],
[0,5,6,7],
[1,2,3,4,5,6,7,8,9]]]
def nested_list_shape(lst):
# Provides the *maximum* length of each list at each depth of the nested list.
# (Designed to take uneven nested lists)
if not isinstance(lst[0], list):
return tuple([len(lst)])
return tuple([len(lst)]) + max([nested_list_shape(i) for i in lst])
shape = nested_list_shape(l) # (2,4,9)
mat = np.zeros(shape) # make numpy array of shape (2,4,9)
# fill in the values of the ndarray `mat` with values from l
for row_ix, row in enumerate(l):
for col_ix, col in enumerate(row):
for val_ix, val in enumerate(col):
mat[row_ix, col_ix, val_ix] = l[row_ix][col_ix][val_ix]
print(mat)
And here is what I have attempted so far:
import numpy as np
# nested shapes of each list ( 2, [2, 4], [ [5,7], [3,7,4,9] ])
# desired shape (max of level) --> (2,4,9)
l = [[[1,2,5,6,7],
[0,2,5,34,5,6,7]],
[[5,6,7],
[0,2,5,7,34,5,7],
[0,5,6,7],
[1,2,3,4,5,6,7,8,9]]]
def nested_list_shape(lst):
# Provides the *maximum* length of each list at each depth of the nested list.
# (Designed to take uneven nested lists)
if not isinstance(lst[0], list):
return tuple([len(lst)])
return tuple([len(lst)]) + max([nested_list_shape(i) for i in lst])
# Useful for setting values in nested list
def get_element(lst, idxs):
# l[x][y][z][t] <==> get_element(l, [x,y,z,t])
#
# Given a list (e.g. `l = [[2,3],[5,6,7]]`),
# index the elements with an list of indices (one value for each depth)
# (e.g. if `idxs = [1,1]` then get_element returns the equivalent of l[1][1])
if len(idxs) == 1:
return lst[idxs[0]]
else:
return get_element(lst[idxs[0]], idxs[1:])
# ::Problem function::
def fill_mat(lst):
# Create numpy array for list to fill
shape = nested_list_shape(lst)
depth = len(shape)
x = np.zeros(shape)
# Use list of indices to keep track of location within the nested enumerations
ixs = [0] * depth
# ::PROBLEM::
# Recursive setting of ndarray values with nested list values
# d = depth of recursion
# l = list at that depth of recursion
# lst = full nested list
def r(l, ixs, d = 0):
for ix, item in enumerate(l):
# Change list of indices to match the position
ixs[d] = ix
# if the item is a value, we reach the max depth
# so here we set the values
if not isinstance(item, list):
x[tuple(ixs)] = get_element(lst, ixs)
else:
# increase the depth if we see a nested list (but then how to decrease ... :/)
d += 1
# return statement should likely go here, and somehow return x
r(item, ixs, d)
return x # ?? bad use of recursion
return r(lst, ixs)
shape = nested_list_shape(l) # (2,4,9)
mat = np.zeros(shape) # make numpy array of shape (2,4,9)
# fill in the values of the ndarray `mat` with values from l
print(fill_mat(l))
The get_element function makes l[row_ix][col_ix][val_ix] an equivalent function with ixs = [row_ix, col_ix, val_ix], but there is still an issue with tracking each of these.
Is anyone familiar with a simpler technique to handle these indices recursively?
hpaulj pointed me in the right direction with this - so I have lifted the code directly from here and provided an MCVE to deal with this issue.
I hope this will help everyone who continually has this problem.
import numpy as np
l = [[[1,2,3,4,5,6,7,8,9,10],
[0,2,5],
[6,7,8,9,10],
[0,2,5],
[0,2,5,6,7],
[0,2,5,34,5,6,7]],
[[2,5],
[6,7],
[7,8,9]],
[[2,5],
[6,7],
[7,8,9]],
[[2,5],
[6,7],
[7,8,9]]]
def nested_list_shape(lst):
# Provides the *maximum* length of each list at each depth of the nested list.
# (Designed to take uneven nested lists)
if not isinstance(lst[0], list):
return tuple([len(lst)])
return tuple([len(lst)]) + max([nested_list_shape(i) for i in lst])
def iterate_nested_array(array, index=()):
try:
for idx, row in enumerate(array):
yield from iterate_nested_array(row, (*index, idx))
except TypeError: # final level
for idx, item in enumerate(array):
yield (*index, idx), item
def pad(array, fill_value):
dimensions = nested_list_shape(array) #get_max_shape(array) # used my shape function, as it operated in 1/3 the time
result = np.full(dimensions, fill_value)
for index, value in iterate_nested_array(array):
result[index] = value
return result
print(pad(l, fill_value = 0))
.
output:
[[[ 1 2 3 4 5 6 7 8 9 10]
[ 0 2 5 0 0 0 0 0 0 0]
[ 6 7 8 9 10 0 0 0 0 0]
[ 0 2 5 0 0 0 0 0 0 0]
[ 0 2 5 6 7 0 0 0 0 0]
[ 0 2 5 34 5 6 7 0 0 0]]
[[ 2 5 0 0 0 0 0 0 0 0]
[ 6 7 0 0 0 0 0 0 0 0]
[ 7 8 9 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0]]
[[ 2 5 0 0 0 0 0 0 0 0]
[ 6 7 0 0 0 0 0 0 0 0]
[ 7 8 9 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0]]
[[ 2 5 0 0 0 0 0 0 0 0]
[ 6 7 0 0 0 0 0 0 0 0]
[ 7 8 9 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0]
[ 0 0 0 0 0 0 0 0 0 0]]]

Series calculation based on shifted values / recursive algorithm

I have the following:
df['PositionLong'] = 0
df['PositionLong'] = np.where(df['Alpha'] == 1, 1, (np.where(np.logical_and(df['PositionLong'].shift(1) == 1, df['Bravo'] == 1), 1, 0)))
This lines basically only take in df['Alpha'] but not the df['PositionLong'].shift(1).. It cannot recognize it but I dont understand why?
It produces this:
df['Alpha'] df['Bravo'] df['PositionLong']
0 0 0
1 1 1
0 1 0
1 1 1
1 1 1
However what I wanted the code to do is this:
df['Alpha'] df['Bravo'] df['PositionLong']
0 0 0
1 1 1
0 1 1
1 1 1
1 1 1
I believe the solution is to loop each row, but this will take very long.
Can you help me please?
You are looking for a recursive function, since a previous PositionLong value depends on Alpha, which itself is used to determine PositionLong.
But numpy.where is a regular function, so df['PositionLong'].shift(1) is evaluated as a series of 0 values, since you initialise the series with 0.
A manual loop need not be expensive. You can use numba to efficiently implement your recursive algorithm:
from numba import njit
#njit
def rec_algo(alpha, bravo):
res = np.empty(alpha.shape)
res[0] = 1 if alpha[0] == 1 else 0
for i in range(1, len(res)):
if (alpha[i] == 1) or ((res[i-1] == 1) and bravo[i] == 1):
res[i] = 1
else:
res[i] = 0
return res
df['PositionLong'] = rec_algo(df['Alpha'].values, df['Bravo'].values).astype(int)
Result:
print(df)
Alpha Bravo PositionLong
0 0 0 0
1 1 1 1
2 0 1 1
3 1 1 1
4 1 1 1

Using itertools to generate an exponential binary space

I am interested in generating all binary combination of N variables without having to implement a manual loop of iterating N times over N and each time looping over N/2 and so on.
Do we have such functionality in python?
E.g:
I have N binary variables:
pool=['A','B','C',...,'I','J']
len(pool)=10
I would like to generate 2^10=1024 space out of these such as:
[A B C ... I J]
iter0 = 0 0 0 ... 0 0
iter1 = 0 0 0 ... 0 1
iter2 = 0 0 0 ... 1 1
...
iter1022 = 1 1 1 ... 1 0
iter1023 = 1 1 1 ... 1 1
You see that I don't have repetitions here, each variable is enabled once per each of these iter's sequences. How can I do that using Python's itertools?
itertools.product with the repeat parameter is the simplest answer:
for A, B, C, D, E, F, G, H, I, J in itertools.product((0, 1), repeat=10):
The values of each variable will cycle fastest on the right, and slowest on the left, so you'll get:
0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 1
0 0 0 0 0 0 0 0 1 0
0 0 0 0 0 0 0 0 1 1
0 0 0 0 0 0 0 1 0 0
etc. This may be recognizable to you: It's just the binary representation of an incrementing 10 bit number. Depending on your needs, you may actually want to just do:
for i in range(1 << 10):
then mask i with 1 << 9 to get the value of A, 1 << 8 for B, and so on down to 1 << 0 (that is, 1) for J. If the goal is just to print them, you can even get more clever, by binary stringifying and then using join to insert the separator:
for i in range(1 << 10):
print(' '.join('{:010b}'.format(i)))
# Or letting print insert the separator:
print(*'{:010b}'.format(i)) # If separator isn't space, pass sep='sepstring'

Python Ignoring What is in a list?

Working on a project for CS1 that prints out a grid made of 0s and adds shapes of certain numbered sizes to it. Before it adds a shape it needs to check if A) it will fit on the grid and B) if something else is already there. The issue I am having is that when run, the function that checks to make sure placement for the shapes is valid will always do the first and second shapes correctly, but any shape added after that will only "see" the first shape added when looking for a collision. I checked to see if it wasnt taking in the right list after the first time but that doesnt seem to be it. Example of the issue....
Shape Sizes = 4, 3, 2, 1
Python Outputs:
4 4 4 4 1 2 3 0
4 4 4 4 2 2 3 0
4 4 4 4 3 3 3 0
4 4 4 4 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
It Should Output:
4 4 4 4 3 3 3 1
4 4 4 4 3 3 3 0
4 4 4 4 3 3 3 0
4 4 4 4 2 2 0 0
0 0 0 0 2 2 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
What's going on here? Full Code is below...
def binCreate(size):
binlist = [[0 for col in range(size)] for row in range(size)]
return binlist
def binPrint(lst):
for row in range(len(lst)):
for col in range(len(lst[row])):
print(lst[row][col], end = " ")
print()
def itemCreate(fileName):
lst = []
for i in open(fileName):
i = i.split()
lst = i
lst = [int(i) for i in lst]
return lst
def main():
size = int(input("Bin Size: "))
fileName = str(input("Item Size File: "))
binList = binCreate(size)
blockList = itemCreate(fileName)
blockList.sort(reverse = True)
binList = checker(binList, len(binList), blockList)
binPrint(binList)
def isSpaceFree(binList, r, c, size):
if r + size > len(binList[0]):
return False
elif c + size > len(binList[0]):
return False
for row in range(r, r + size):
for col in range(c, c + size):
if binList[r][c] != 0:
return False
elif binList[r][c] == size:
return False
return True
def checker(binList, gSize, blockList):
for i in blockList:
r = 0
c = 0
comp = False
while comp != True:
check = isSpaceFree(binList, r, c, i)
if check == True:
for x in range(c, c+ i):
for y in range(r, r+ i):
binList[x][y] = i
comp = True
else:
print(c)
print(r)
r += 1
if r > gSize:
r = 0
c += 1
if c > gSize:
print("Imcompadible")
comp = True
print(i)
binPrint(binList)
input()
return binList
Your code to test for open spaces looks in binList[r][c] (where r is a row value and c is a column value). However, the code that sets the values once an open space has been found sets binList[x][y] (where x is a column value and y is a row value).
The latter is wrong. You want to set binList[y][x] instead (indexing by row, then column).
That will get you a working solution, but it will still not be exactly what you say you expect (you'll get a reflection across the diagonal). This is because your code updates r first, then c only when r has exceeded the bin size. If you want to place items to the right first, then below, you need to swap them.
I'd suggest using two for loops for r and c, rather than a while too, but to make it work in an elegant way you'd probably need to factor out the "find one item's place" code so you could return from the inner loop (rather than needing some complicated code to let you break out of both of the nested loops).

Categories