How to cycle a variable in a given range in python - python

is there an easy way in python to cycle a variable in a given range?
For example:
Given a range(), I want a variable goes like this:
0 1 2 3 2 1 0 1 2 3... till some conditions are satisfied.

You want to cycle the sequence 0, 1, ..., n, n-1, ..., 1.
You can easily build this sequence using chain
from itertools import chain, cycle
def make_base_sequence(n):
base = range(n+1) # 0, ..., n
rev_base = reversed(range(1, n)) # n-1, ..., 1
return chain(base, rev_base) # 0, ..., n, n-1, ..., 1
for x in cycle(make_base_sequence(5)):
print(x)
Sample run:
In [2]: from itertools import chain, cycle
...:
...: def make_base_sequence(n):
...: base = range(n+1)
...: rev_base = reversed(range(1, n))
...: return chain(base, rev_base)
...:
...: for i, x in enumerate(cycle(make_base_sequence(5))):
...: print(x, end=' ')
...: if i > 20:
...: break
...:
0 1 2 3 4 5 4 3 2 1 0 1 2 3 4 5 4 3 2 1 0 1

You want itertools.cycle(), see here:
https://docs.python.org/2/library/itertools.html#itertools.cycle

itertools.cycle is a good start. Else you can program it yourself:
cycle = [0,1,2,3,2,1]
i = 0
while some_condition:
value = cycle[i]
i = (i+1) % len(cycle)
#do stuff

import time
def cycle(range_):
num = -1
current = 0
a=time.time()
while 1:
print current
if current in (0, range_):
num*=-1
current += num
if time.time() - a > 0.002:
break
cycle(3)
output:
0 1 2 3 2 1 0 1 2 3 2 1 0 1 2 3 2 1 0 1 2 3 2 1 0 1 2 3 2

you need itertools.cycle:
demo:
>>> import itertools
>>> count = 0
>>> for x in itertools.cycle(range(3)):
... if count == 10:
... break
... print x,
... count += 1
...
0 1 2 0 1 2 0 1 2 0

import itertools
def f(cycle_range, condition_func):
sequence = range(cycle_range) + range(cycle_range)[-2:0:-1]
cycle_generator = itertools.cycle(sequence)
while not condition_func():
yield next(cycle_generator)
def condition_func():
"""Checks some condition"""
Essentially, you just want to loop and constantly check the condition. And each time yeild the next item from the cycle. Now, admittedly there are better ways to check a condition than a function call, but this is just an example.

Related

Mimicing 'n' Nested For Loops Using Recursion

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

How to save and work with previous iteration in loop?

You could somehow hold it in a loop after the first iteration and store it in a variable and then continue on. So that in one variable there is a standard loop and in the other a previous loop?
example:
import numpy as np
for j in np.arange(0,5):
print(j)
0
1
2
3
4
5
6
i need example
0
1 0
2 1
3 2
4 3
5 4
6 5
just save the variable you want to print next, and print it before change it with the next one
num = 0
for j in np.arange(0,5):
print(num)
num = j
import numpy as np
for j in np.arange(0,7):
if j == 0:
print(j)
continue
print(j, j-1)
Use zip and itertools.tee:
itr1, itr2 = tee(np.arange(0, 5))
i, j = next(itr1), None
print(i, j)
for i, j in zip(itr1, itr2):
print(i, j)
outputs
0 None
1 0
2 1
3 2
4 3
The question is poorly worded, but here's a good basic iteration. It keeps track of the current variable, as well as saving it.
In [161]: alist = [1,3,6,9,12]
...: blist = []
...: latest = None
...: for item in alist:
...: blist.append(item)
...: print(latest, blist[-1])
...: latest = item
...: print(blist)
None 1
1 3
3 6
6 9
9 12
[1, 3, 6, 9, 12]
In the iteration, the current value is item, the previous is latest. blist[-1] is the last added item.

Splitting a string of integers with spaces without converting to a list in python

Basically, I need a code that takes an integer and then prints out the string of numbers with the certain range.
For example:
n = 11
1 2 2 3 3 3 4 4 4 4
n = 7
1 2 2 3 3 3 4
a = []
n = int(input())
if n == 0:
print(n)
for i in range(int(n) + 1):
a += [i] * i
a = ' '.join(map(str, a))
print(a[:n])
This does the job but it counts spaces as characters, so I tried to convert it to an int
n = int(n)
print(' '.join(a[:n]))
But when the n >= 47, it starts to print out 10 as 1 0 which is incorrect
I also tried this code
n = int(input())
for i in range(n):
b = (str(i) * i)
print(b, end = ' ')
But I don't understand how to separate the b with spaces without converting the string to a list and printing it in one line either.
I am not sure if it is even possible.
Maybe something like this?
# initialize variables:
number = 11
counter = 0
for i in range(number):
for j in range(i):
# print number:
print('%d ' %i, end='')
# increment counter (tracks how many numbres to print):
counter += 1
# exit inner loop if counter reaches number:
if counter >= number-1: break
# exit outer loop if counter reaches number:
if counter >= number-1: break
Output:
1 2 2 3 3 3 4 4 4 4
Here's a solution using itertools. Generators, chain, repeat and islice are all lazily evaluated, so this uses O(1) space.
>>> n = 7
>>> from itertools import chain, repeat, islice
>>> gen = chain.from_iterable(repeat(i, i) for i in range(1, n+1))
>>> for i in islice(gen, n):
... print(i, end=' ')
...
1 2 2 3 3 3 4
This seems simple. But this does solve the problem?
>>> for i in t:
... if i!=" ": print i
...
1
2
2
3
3
3
4
4
4
4
or even string replace like,
print t.replace(" ","\n")
1
2
2
3
3
3
4
4
4
4

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

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