Circular range of indices in Python - python

I am trying to process batches of a vector
let say, I have this vector
v = [10, 20, 30, 40, 70, 80, 90]
I want to print every three elements, if it reaches the end, it goes back to the beginning and takes the first to element and so on. For example, the output would be something like
10, 20, 30
40, 70, 80
90, 10, 20 <== starts over
30, 40, 70
80, 90, 10 <== starts over
and so on...
I know I can do this creating a function and calculating the start and end index using module, but I was thinking if there is a way just playing with the sintaxis, something like
v[8:10 % 9] #would print v[8], v[0] and v[1]
I know you can do this for a single index v[index % len(v)], but is it possible to do it for a range v[range % len(v)] ?

Though computing indexes is possible, I would use itertools.cycle with the zip(*[] * x) grouper:
from itertools import cycle
v = [10, 20, 30, 40, 70, 80, 90]
n = 10
for index, *group in zip(range(n), *[cycle(v)] * 3):
print(group)
Change n to however many times you want to loop. Output:
[10, 20, 30]
[40, 70, 80]
[90, 10, 20]
[30, 40, 70]
[80, 90, 10]
[20, 30, 40]
[70, 80, 90]
[10, 20, 30]
[40, 70, 80]
[90, 10, 20]

itertools have a number of tools that can help you with that:
from itertools import cycle, islice
def chunks(iterable, size):
it = iter(iterable)
item = list(islice(it, size))
while item:
yield item
item = list(islice(it, size))
v = [10, 20, 30, 40, 70, 80, 90]
for subset in chunks(cycle(v), 3):
print(subset)
which produces (yields):
[10, 20, 30]
[40, 70, 80]
[90, 10, 20]
...
The chunks function has been taken from here

If I understand your problem correctly, you want circular indexing. I think there are two ways of achieving this. Through explicit functions, as you mentioned, and other answers have elaborated. But you want to achieve the same through usual list syntax (including slicing). It is only possible if you make a subclass out of base list class. Below is an example of doing this:
class circularlist(list):
def map_key(self, key):
length = len(self)
return key%length
def __getitem__(self, key):
if isinstance(key, int):
return super().__getitem__(self.map_key(key))
elif isinstance(key, slice):
step = key.step if key.step else 1
idx = [i for i in \
range(key.start, key.stop, step)]
return [self.__getitem__(i) for i in idx]
else:
print(type(key))
return super().__getitem__(key)
v = [10, 20, 30, 40, 70, 80, 90]
vv = circularlist(v)
print(vv[42])
print(vv[20:30])
Note: I do not recommend this, and suggest the other methods above.

Related

How to split an array according to conditional statement?

I have a time signal (37913 ms to 40010) and I want to split it in every 20 ms gap.
For example for first 20 ms:
for t in time:
if t>=37913 and t< 37933:
list1.append(t)
This gives me the list [37913.496549, 37916.878267, 37918.506757].
I want to make several different lists on every 20 ms gap. I know it should be really simple but somehow I cannot think of a solution.
****Edited****
So to further explain my point, what I actually want to achieve is that, have an incoming acceleration (with no upper time limit) signal (Green Bars) and I want to check if these incoming samples are in the range 0-20 ms, 10-30 ms or 20-40 ms and so on. If they are in such an interval then I have to approximate points (black dots) using this data. for example, if current values are in between 0-20 ms then I can use all of these values to approximate value at 10 ms by some approximation (let's suppose there is no current value at 10 ms). Approximation method is not important right now, I just want to capture these values
Any help or suggestion is highly appreciated thanks to all in advance.
Here's something that I think does what you desire. To test it I had to create some sample input data since you don't have any in your question—which is what is going on at the very beginning of the snippet.
For each time range interval, it creates a separate "bucket" which contains the corresponding time values within the time range. Note that some time values may end up being place in two buckets since the interval ranges overlap.
from pprint import pprint
import random
random.seed(42) # Create same "random" sequence each run for testing.
# First create some test data.
INTERVAL = 0.02 # 20 ms
start = 37913
times = []
nvalues = 2, 3, 4, 2, 1 # Number of values in each interval.
for i, nvalue in enumerate(nvalues):
lowerbounds = start + i*INTERVAL
upperbounds = start + (i+1)*INTERVAL
for _ in range(nvalue):
times.append(random.uniform(lowerbounds, upperbounds))
print('There are {} time values:'.format(len(times)))
times.sort() # Put into ascending order.
pprint(times)
#=======
# Split the times up into "buckets" of values depending on their range.
HALF_INTERVAL = INTERVAL / 2
brackets = []
print()
print('Time brackets:')
st = int(min(times))
for i in range(4):
begin = round(st + i*HALF_INTERVAL, 6)
end = round(begin + INTERVAL, 6)
brackets.append((begin, end))
print(' ', begin, end)
buckets = [[] for _ in range(len(brackets))] # Create empty buckets.
for t in times: # Put each time in the cooresponding bucket of times.
for i, (begin, end) in enumerate(brackets):
if begin <= t <= end:
buckets[i].append(t)
print()
print('Stored in corresponding interval bucket:')
for i, bracket in enumerate(brackets):
print('bucket[{}]: range: {!r}, values: {}'.format(
i, bracket, buckets[i]))
Sample Output:
There are 12 time values:
[37913.0005002151,
37913.01278853597,
37913.02446421476,
37913.025500586366,
37913.03472942428,
37913.04173877665,
37913.048438436395,
37913.05353398975,
37913.05784359135,
37913.060595944386,
37913.064372759494,
37913.09010710576]
Time brackets:
37913.0 37913.02
37913.01 37913.03
37913.02 37913.04
37913.03 37913.05
Stored in corresponding interval bucket:
bucket[0]: range: (37913.0, 37913.02), values: [37913.0005002151, 37913.01278853597]
bucket[1]: range: (37913.01, 37913.03), values: [37913.01278853597, 37913.02446421476, 37913.025500586366]
bucket[2]: range: (37913.02, 37913.04), values: [37913.02446421476, 37913.025500586366, 37913.03472942428]
bucket[3]: range: (37913.03, 37913.05), values: [37913.03472942428, 37913.04173877665, 37913.048438436395]
If your list of timestamps is sorted:
from itertools import groupby
sample = range(100)
INTERVAL_SIZE = 20
key = lambda x: x // INTERVAL_SIZE
list(list(v) for k, v in groupby(sample, key=key))
Will give you:
[
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
[20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39],
[40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
[60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79],
[80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
]
If it is not sorted, add a sort (by key) before you use groupby, since it requires a sorted iterable.
I would suggest this method:
arrs = []
while len(arr) > 20:
pice = arr[:20]
arrs.append(pice)
arr = arr[20:]
arrs.append(arr)
you can make function out of it which gives you a piece each time you call it.
If I understand your question, you could use a range:
r=range(37913,40010,20)
output:
[37913, 37933, 37953, 37973, 37993, 38013, 38033, 38053, 38073, 38093,
38113, 38133, 38153, 38173, 38193, 38213, 38233, 38253, 38273, 38293,
38313, 38333, 38353, 38373, 38393, 38413, 38433, 38453, 38473, 38493,
38513, 38533, 38553, 38573, 38593, 38613, 38633, 38653, 38673, 38693,
38713, 38733, 38753, 38773, 38793, 38813, 38833, 38853, 38873, 38893,
38913, 38933, 38953, 38973, 38993, 39013, 39033, 39053, 39073, 39093,
39113, 39133, 39153, 39173, 39193, 39213, 39233, 39253, 39273, 39293,
39313, 39333, 39353, 39373, 39393, 39413, 39433, 39453, 39473, 39493,
39513, 39533, 39553, 39573, 39593, 39613, 39633, 39653, 39673, 39693,
39713, 39733, 39753, 39773, 39793, 39813, 39833, 39853, 39873, 39893,
39913, 39933, 39953, 39973, 39993]
If you don't mind using numpy, you can try this:
import numpy as np
#a = np.arange(37913,40010+1,1)
a = np.arange(0,10+1,1) # comment this and uncomment above for your case
nlen = 2
parts = np.array_split(a,int(np.ceil(len(a) / nlen)))
print("a = {}".format(a)) # [ 0 1 2 3 4 5 6 7 8 9 10]
print("len(a) = {}".format(len(a))) # 11
print("parts[0] = {}".format(parts[0])) # [0 1]
print("parts[-1] = {}".format(parts[-1])) # [10]
If you just want to break whole array into n parts, just do this:
nparts = 6
np.array_split(a,nparts)

for loop based on dynamic list python

Edit:
The desired behaviour of the program is to find the number sequences that have an increasing trend, so I want to generate from ks list a list like this:
desiredList=[[97,122],[98,111],[98,101,103,103,104]]
I have the following, my goal is to run the for loop based on the length of the list, the list length gets changed inside the for loop itself. Python takes into account only the length before the for loop, when the length of the list is changed in the loop it still takes the older value before the loop. Here is the code:
ks=[97,122,111,98,111,98,101,103,103,104,97]
splitLine=2
counter=[]
for i in range(0,len(ks)):
a=ks[i:splitLine]
while len(a)>1:
for j in range(0,len(a)):
m=j
n=j+1
if(a[m]-a[n]<=0):
c=c+1
k=splitLine+c-1
a.append(ks[k]) #When append happens, the for loop still takes the older value of len(a) instead of new value
else:
a.pop(-1)
counter.append(a)
splitLine=splitLine+1
a=[]
break
A quick fix for your looping problem would be to swap out your for loop for a while loop. Change this:
for j in range(0,len(a)):
# <loop contents>
to this:
j = 0
while j < len(a):
# <loop contents>
j += 1
The for loop is grabbing values of j out of a range (a list in Python 2, and a generator object in Python 3). This range is calculated when the for loop is run the first time; it will not update after that, no matter what you do to a.
The while loop gives you more control in this situation, because you can specify the condition under which you want to exit the loop.
Your implementation is probably nesting too many loops for the problem it is trying to solve.
This first implementation contains an error. See below for the fix.
Try something along these lines perhaps:
l = [97,122,111,98,111,98,101,103,103,104,97]
out = []
acc = []
for v in l:
if len(acc)==0 or v >= acc[-1]:
acc.append(v)
else:
if len(acc) > 1:
out.append(acc)
acc = [v]
print(out)
>>>[[97, 122], [98, 111], [98, 101, 103, 103, 104]]
That previous code is slow and can drop the last found fragment. I found that error while running random tests on it to try an optimized version. The following code shows the original code with the correction and the optimized version which can be 30% faster.
def original(l):
out = []
acc = []
added = False
for v in l:
if len(acc)==0 or v >= acc[-1]:
acc.append(v)
else:
added = False
acc = [v]
if acc is not None and len(acc)>1 and not added:
added = True
out.append(acc)
return out
def optimized(l):
out = []
acc = None
tmp = None
deb_v = False
for v in l:
prev = acc[-1] if (acc is not None and len(acc)) else tmp
if prev is not None and v >= prev:
if tmp is not None:
acc = []
acc.append(tmp)
out.append(acc)
tmp = None
acc.append(v)
else:
acc = None
tmp = v
return out
# The original test data
l = [97,122,111,98,111,98,101,103,103,104,97]
assert original(l) == optimized(l) == [[97,122],[98,111],[98,101,103,103,104]]
# A list that triggered last-fragment-dropped error
l = [57, 16, 6, 19, 40, 3, 4, 13, 2, 70, 85, 65, 32, 69, 54, 51, 95, 74, 92, 46, 45, 26, 0, 61, 99, 43, 67, 71, 97, 10, 18, 73, 88, 47, 33, 82, 25, 75, 93, 80, 23, 37, 87, 90, 49, 15, 35, 63, 17, 64, 5, 72, 89, 21, 50, 8, 41, 86, 31, 78, 52, 76, 56, 42, 77, 36, 11, 60, 39, 22, 68, 27, 24, 28, 59, 96, 29, 38, 12, 79, 53, 9, 83, 94, 34, 14, 7, 48, 30, 20, 66, 62, 91, 58, 81, 1, 98, 44, 55, 84]
assert original(l) == optimized(l)
# Random testing
import random
l = list(range(100))
random.shuffle(l)
assert original(l) == optimized(l)
# Timing!
import timeit
print(timeit.timeit("original(l)", globals={"l":l, "original": original}))
# 43.95869998800117
print(timeit.timeit("optimized(l)", globals={"l":l, "optimized": optimized}))
# 34.82134292599949
As Moinuddin says, the root of your problem isn't clear to us. However, the code below shows how you can keep iterating over a list as its length changes:
def iterate_for_static_list_length(l):
for i in range(len(l)):
yield i
l.append(object())
def iterate_for_dynamic_list_length(l):
for i, _ in enumerate(l):
yield i
l.append(object())
if __name__ == '__main__':
l = [object()] * 3
print('Static implementation')
for value in iterate_for_static_list_length(l):
input(value)
print('\nDynamic implementation')
for value in iterate_for_dynamic_list_length(l):
input(value)
Output
Static implementation
0
1
2
Dynamic implementation
0
1
2
3
4
5
6
7
8
This program will keep going forever. In your code I can see that you conditionally append to the list within the loop, so it seems like it should terminate eventually.

"TypeError: 'int' object is not iterable" when moving itertools product inside function

I'm trying to move a usage of itertools.product inside a function. When I try to do this, I am presented with the following error message and I'm not sure why:
TypeError: 'int' object is not iterable
The code is as follows. In the main function, you can see the algorithm used outside the function and then you can see the algorithm used when packaged in the function:
#!/usr/bin/env python
import itertools
def main():
elements_specification = [[10, 20], [30, 40], [50, 60]]
lists = [list(list_generated) for index, element_specification in enumerate(elements_specification) for list_generated in itertools.product(*elements_specification[:index + 1])]
for list_configuration in lists:
print(list_configuration)
print("---")
for list_configuration in list_element_combinations_variadic(
[[10, 20], [30, 40], [50, 60]]
):
print(list_configuration)
def list_element_combinations_variadic(
elements_specification
):
"""
This function accepts a specification of lists of elements for each place in
lists in the form of a list, the elements of which are lists of possible
elements and returns a list of lists corresponding to the combinations of
elements of the specification with varying numbers of elements.
For example, the list elements specification [[10, 20], [30, 40], [50, 60]]
yields the following lists:
[10]
[20]
[10, 30]
[10, 40]
[20, 30]
[20, 40]
[10, 30, 50]
[10, 30, 60]
[10, 40, 50]
[10, 40, 60]
[20, 30, 50]
[20, 30, 60]
[20, 40, 50]
[20, 40, 60]
"""
lists = [list(list_generated) for index, elements_specification in enumerate(elements_specification) for list_generated in itertools.product(*elements_specification[:index + 1])]
return lists
if __name__ == "__main__":
main()
Basically, you have a typo between the main method and the other one.
In main, you correctly have element_specification in the for
for index, element_specification in enumerate(elements_specification)
But in the other method you have elements_specification in the for
for index, elements_specification in enumerate(elements_specification)
Which just so happens to be the name of the parameter for that method, so you're reassigning that parameter in your list-comprehension
Try this instead
lists = [list(list_generated) for index, element in enumerate(elements_specification) for list_generated in itertools.product(*elements_specification[:index + 1])]
return lists
Or since you don't even need the element from the enumerate, just use range.
lists = [list(list_generated) for index in range(len(elements_specification)) for list_generated in itertools.product(*elements_specification[:index + 1])]
return lists

finding multiples of a number in Python

I'm trying to write a code that lets me find the first few multiples of a number. This is one of my attempts:
def printMultiples(n, m):
for m in (n,m):
print(n, end = ' ')
I figured out that, by putting for m in (n, m):, it would run through the loop for whatever number was m.
def printMultiples(n, m):
'takes n and m as integers and finds all first m multiples of n'
for m in (n,m):
if n % 2 == 0:
while n < 0:
print(n)
After multiple searches, I was only able to find a sample code in java, so I tried to translate that into python, but I didn't get any results. I have a feeling I should be using the range() function somewhere in this, but I have no idea where.
If you're trying to find the first count multiples of m, something like this would work:
def multiples(m, count):
for i in range(count):
print(i*m)
Alternatively, you could do this with range:
def multiples(m, count):
for i in range(0,count*m,m):
print(i)
Note that both of these start the multiples at 0 - if you wanted to instead start at m, you'd need to offset it by that much:
range(m,(count+1)*m,m)
Does this do what you want?
print range(0, (m+1)*n, n)[1:]
For m=5, n=20
[20, 40, 60, 80, 100]
Or better yet,
>>> print range(n, (m+1)*n, n)
[20, 40, 60, 80, 100]
For Python3+
>>> print(list(range(n, (m+1)*n, n)))
[20, 40, 60, 80, 100]
Based on mathematical concepts, I understand that:
all natural numbers that, divided by n, having 0 as remainder, are all multiples of n
Therefore, the following calculation also applies as a solution (multiples between 1 and 100):
>>> multiples_5 = [n for n in range(1, 101) if n % 5 == 0]
>>> multiples_5
[5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100]
For further reading:
https://www.mathsisfun.com/definitions/natural-number.html
https://www.mathwizz.com/arithmetic/help/help9.htm
https://www.calculatorsoup.com/calculators/math/multiples.php
For the first ten multiples of 5, say
>>> [5*n for n in range(1,10+1)]
[5, 10, 15, 20, 25, 30, 35, 40, 45, 50]
You can do:
def mul_table(n,i=1):
print(n*i)
if i !=10:
mul_table(n,i+1)
mul_table(7)
If this is what you are looking for -
To find all the multiples between a given number and a limit
def find_multiples(integer, limit):
return list(range(integer,limit+1, integer))
This should return -
Test.assert_equals(find_multiples(5, 25), [5, 10, 15, 20, 25])
Another method that can be done is trying to make a list. Here's my example for getting the first 20 multiples of 7.
Input:
multiples_7 = [x * 7 for x in range(1,21)]
print(multiples_7)
Output:
[7, 14, 21, 28, 35, 42, 49, 56, 63, 70, 77, 84, 91, 98, 105, 112, 119, 126, 133, 140]
def multiples(n,m,starting_from=1,increment_by=1):
"""
# Where n is the number 10 and m is the number 2 from your example.
# In case you want to print the multiples starting from some other number other than 1 then you could use the starting_from parameter
# In case you want to print every 2nd multiple or every 3rd multiple you could change the increment_by
"""
print [ n*x for x in range(starting_from,m+1,increment_by) ]
For first 10 multiples of 5 you can do as
import numpy as np
#np.arange(1,11) array from 1 to 10
array_multipleof5 = [5*n for n in np.arange(1,11)]
array_multipleof5 = np.array(array_multipleof5)
print(array_multipleof5)
How to calculate the first n multiples of a given number x, in the compact python's lambda notation
n_multiples_of_x = lambda n,x : list( range(x, x*n + 1, x) )
Tests:
assert n_multiples_of_x(5, 5) == [5, 10, 15, 20, 25]

How to find the maximum number in a list using a loop?

So I have this list and variables:
nums = [14, 8, 9, 16, 3, 11, 5]
big = nums[0]
spot = 0
I'm confused about how to actually do it. I want to use this exercise to give me a starter. How do I do that on Python?
Usually, you could just use
max(nums)
If you explicitly want to use a loop, try:
max_value = None
for n in nums:
if max_value is None or n > max_value: max_value = n
Here you go...
nums = [14, 8, 9, 16, 3, 11, 5]
big = max(nums)
spot = nums.index(big)
This would be the Pythonic way of achieving this. If you want to use a loop, then loop with the current max value and check if each element is larger, and if so, assign to the current max.
nums = [14, 8, 9, 16, 3, 11, 5]
big = None
spot = None
for i, v in enumerate(nums):
if big is None or v > big:
big = v
spot = i
Python already has built in function for this kind of requirement.
list = [3,8,2,9]
max_number = max(list)
print (max_number) # it will print 9 as big number
however if you find the max number with the classic vay you can use loops.
list = [3,8,2,9]
current_max_number = list[0]
for number in list:
if number>current_max_number:
current_max_number = number
print (current_max_number) #it will display 9 as big number
Why not simply using the built-in max() function:
>>> m = max(nums)
By the way, some answers to similar questions might be useful:
Pythonic way to find maximum value and its index in a list?
How to find all positions of the maximum value in a list?
To address your second question, you can use a for loop:
for i in range(len(list)):
# do whatever
You should note that range() can have 3 arguments: start, end, and step. Start is what number to start with (if not supplied, it is 0); start is inclusive.. End is where to end at (this has to be give); end is exclusive: if you do range(100), it will give you 0-99. Step is also optional, it means what interval to use. If step is not provided, it will be 1. For example:
>>> x = range(10, 100, 5) # start at 10, end at 101, and use an interval of 5
>>> x
[10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95] # note that it does not hit 100
Since end is exclusive, to include 100, we could do:
>>> x = range(10, 101, 5) # start at 10, end at 101, and use an interval of 5
>>> x
[10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100] # note that it does hit 100
For the Max in List Code HS I've managed to get most of the auto grader to work for me using this code:
list = [-3,-8,-2,0]
current_max_number = list[0]
for number in list:
if number>current_max_number:
current_max_number = number
print current_max_number
def max_int_in_list():
print "Here"
I'm not sure where the max_int_in_list goes though. It needs to have exactly 1 parameter.
To print the Index of the largest number in a list.
numbers = [1,2,3,4,5,6,9]
N = 0
for num in range(len(numbers)) :
if numbers[num] > N :
N = numbers[num]
print(numbers.index(N))
student_scores[1,2,3,4,5,6,7,8,9]
max=student_scores[0]
for n in range(0,len(student_scores)):
if student_scores[n]>=max:
max=student_scores[n]
print(max)
# using for loop to go through all items in the list and assign the biggest value to a variable, which was defined as max.
min=student_scores[0]
for n in range(0,len(student_scores)):
if student_scores[n]<=min:
min=student_scores[n]
print(min)
# using for loop to go through all items in the list and assign the smallest value to a variable, which was defined as min.
Note: the above code is to pick up the max and min by using for loop, which can be commonly used in other programming languages as well. However, the max() and min() functions are the easiest way to use in Python to get the same results.
I would add this as a reference too. You can use the sort and then print the last number.
nums = [14, 8, 9, 16, 3, 11, 5]
nums.sort()
print("Highest number is: ", nums[-1])
scores = [12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 24, 25, 26, 27,
28, 29, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 31, 31, 37,
56, 75, 23, 565]
# initialize highest to zero
highest = 0
for mark in scores:
if highest < mark:
highest = mark
print(mark)

Categories