for loop based on dynamic list python - 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.

Related

Collatz Conjecture Sequence iterations and multiple divisor

For the case, the sequence does not end in 1. How can I create a code that has some way of stopping the execution if 1 is not reached after a certain number of iterations? I just don't know how to add this to my code below. And is there a way to make it work with a list of divisors such as [2, 3]? I'm using Python.
import numpy
#change the function to have 3 input args, the number, the multiplier and the
divisor
def collatz(n,multiplier,divisor):
list1 = [n]
if n == 1 :
return [1]
elif n % 2 and n % 3 == 0 :
#edit the function's input args for both the else and elif loops
list1.extend(collatz(n//divisor,multiplier,divisor))
else:
list1.extend(collatz(n*multiplier+1,multiplier,divisor))
return list1
#driver function to get the input number, multiplier and divisor
if __name__=="__main__":
n=int(input("Enter any positive integer N: "))
multiplier=int(input("Enter the multiplier: "))
divisor=int(input("Enter the divisor: "))
print("\n",collatz(n,multiplier,divisor))
You will get a stack overflow error after ~1000 recursions I believe.The way to override this: https://stackoverflow.com/a/3323013/10875953. But 2^64 is immensly large, you won't be able to do that. Rather try making a iterative function.
You should use your divisor parameter in the condition that select the operation on the number.
To make this easier to manage, you shouldn't use recursion. A simple while loop will suffice:
def collatz(n,divs=2,mult=3,inc=1,maxSize=-1):
result = []
while n not in result and len(result)!=maxSize:
result.append(n)
n = (n*mult+inc)if n%divs else n//divs
return result + ['...']*(n not in result)
output:
print(collatz(20))
[20, 10, 5, 16, 8, 4, 2, 1]
print(collatz(30,2,6,2,maxSize=10))
[30, 15, 92, 46, 23, 140, 70, 35, 212, 106, '...']
print(collatz(30,2,6,2))
[30, 15, 92, 46, 23, 140, 70, 35, 212, 106, 53, 320, 160, 80,
40, 20, 10, 5, 32, 16, 8, 4, 2, 1]
print(collatz(30,2,5,3))
[30, 15, 78, 39, 198, 99, 498, 249, 1248, 624, 312, 156]
print(collatz(60,4,3,-2,maxSize=10))
[60, 15, 43, 127, 379, 1135, 3403, 10207, 30619, 91855, '...']

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)

what is most pythonic way to find a element in a list that is different with other elements?

Suppose we have a list with unknown size and there is an element in the list that is different with other elements but we don't know the index of the element. the list only contains numerics and is fetched from a remote server and the length of the list and the index of the different element is changed every time. what is the most pythonic way to find that different element?
I tried this but I'm not sure if it's the best solution.
a = 1
different_element = None
my_list = fetch_list()
b = my_list[0] - a
for elem in my_list[1::]:
if elem - a != b:
different_element = elem
print(different_element)
This is a great use for numpy
Given some random uniform list with a single uniquely different number in it:
>>> li=[1]*100+[200]+[1]*250
If the uniform value is known (in this case 1 and the unknown value is 200) you can use np.where on an array to get that value:
>>> import numpy as np
>>> a=np.array(li)
>>> a[a!=1]
array([200])
If the uniform values are not known, you can use np.uniques to get the counts of uniques:
>>> np.unique(a, return_counts=True)
(array([ 1, 200]), array([350, 1]))
For a pure Python solution, use a generator with next to get the first value that is different than all the others:
>>> next(e for i, e in enumerate(li) if li[i]!=1)
200
Or, you can use dropwhile from itertools:
>>> from itertools import dropwhile
>>> next(dropwhile(lambda e: e==1, li))
200
If you do not know what the uniform value is, use a Counter on a slice big enough to get it:
>>> uniform=Counter(li[0:3]).most_common()[0][0]
>>> uniform
1
>>> next(e for i, e in enumerate(li) if li[i]!=uniform)
200
In these cases, next will short-circuit at the first value that satisfies the condition.
Would this work for you?
In [6]: my_list = [1,1,1,2,1,1,1]
In [7]: different = [ii for ii in set(my_list) if my_list.count(ii) == 1]
In [8]: different
Out[8]: [2]
You can use Counter from collections package
from collections import Counter
a = [1,2,3,4,3,4,1]
b = Counter(a) # Counter({1: 2, 2: 1, 3: 2, 4: 2})
elem = list(b.keys())[list(b.values()).index(1)] # getting elem which is key with value that equals 1
print(a.index(elem))
Another possible solution that just differently compute elem
a = [1,2,3,4,3,4,1]
b = Counter(a) # Counter({1: 2, 2: 1, 3: 2, 4: 2})
elem = (k for k, v in b.items() if v == 1)
print(a.index(next(elem)))
UPDATE
Time consumption:
As #Jblasco mentioned, Jblasco's method is not really efficient one, and i was curious to measure it.
So the initial data is array with 200-400 elements, with only one unique value. The code that generate that array is. At the end of snipped there is 100 first elements that prove that it has one unique
import random
from itertools import chain
f = lambda x: [x]*random.randint(2,4)
a=list(chain.from_iterable(f(random.randint(0,100)) for _ in range(100)))
a[random.randint(1, 100)] = 101
print(a[:100])
# [5, 5, 5, 84, 84, 84, 46, 46, 46, 46, 6, 6, 6, 68, 68, 68, 68, 38,
# 38, 38, 44, 44, 61, 61, 15, 15, 15, 15, 36, 36, 36, 36, 73, 73, 73,
# 28, 28, 28, 28, 6, 6, 93, 93, 74, 74, 74, 74, 12, 12, 72, 72, 22,
# 22, 22, 22, 78, 78, 17, 17, 17, 93, 93, 93, 12, 12, 12, 23, 23, 23,
# 23, 52, 52, 88, 88, 79, 79, 42, 42, 34, 34, 47, 47, 1, 1, 1, 1, 71,
# 71, 1, 1, 45, 45, 101, 45, 39, 39, 50, 50, 50, 50]
That's the code that show us results, i choose to execute 3 times with 10000 executions:
from timeit import repeat
s = """\
import random
from itertools import chain
f = lambda x: [x]*random.randint(2,4)
a=list(chain.from_iterable(f(random.randint(0,100)) for _ in range(100)))
a[random.randint(1, 100)] = 101
"""
print('my 1st method:', repeat(stmt="""from collections import Counter
b=Counter(a)
elem = (k for k, v in b.items() if v == 1)
a.index(next(elem))""",
setup=s, number=10000, repeat=3)
print('my 2nd method:', repeat(stmt="""from collections import Counter
b = Counter(a)
elem = list(b.keys())[list(b.values()).index(1)]
a.index(elem)""",
setup=s, number=10000, repeat=3))
print('#Jblasco method:', repeat(stmt="""different = [ii for ii in set(a) if a.count(ii) == 1]
different""", setup=s, number=10000, repeat=3))
# my 1st method: [0.303596693000145, 0.27322746600111714, 0.2701447969993751]
# my 2nd method: [0.2715420649983571, 0.28590541199810104, 0.2821485950007627]
# #Jblasco method: [3.2133491599997797, 3.488262927003234, 2.884892332000163]
I would try maybe something like this:
newList = list(set(my_list))
print newList.pop()
Assuming there's only 1 different value and the rest are all the same.
There's a little bit of ambiguity in your question which makes it difficult to answer but that's all I could think of optimally.

Infinite Recursion in Python Function if Argument Too Long

I wrote this recursive function that returns the largest value in a list of integers:
def max_r(l: [int]) -> int:
if len(l) == 1:
return l[0]
else:
return l[0] if max_r(l[1:]) < l[0] else max_r(l[1:])
The call max_r([1,4,3,2,3,4,89,2,30,1] returns 89, but calling the function on a longer list:
max_r([96, 84, 87, 81, 94, 74, 65, 42, 45, 76, 5, 37, 86, 8, 46, 54, 62, 63, 35, 85, 16, 23, 18, 57, 51, 90, 58, 33, 47, 10, 64, 49, 67, 29, 71, 30, 9, 99, 75, 3, 97, 32, 59, 25, 27, 72, 61])
results in infinite recursion. Why?
It isn't infinitely recursive, but you are doing the same recursive call twice when you don't need to. Seems to complete quickly enough with the following change:
def max_r(l: [int]) -> int:
if len(l) == 1:
return l[0]
else:
result = max_r(l[1:])
return l[0] if result < l[0] else result
This isn't just a matter of calling the function recursively twice as many times, I'm not sure on the exact growth rate but it seems be exponential since each extra recursive call will be making more extra recursive calls.

ValueError in python.

Here is my code:
def f(x): return x%2!=0 and x%3!=0
primes = filter(f , range (6,50))
for x in primes:
for a in filter(f, range(2,x-1)):
if x%a == 0:
primes.remove(x);
print(primes)
I get this error:
File "primes.py", line 12, in <module>
primes.remove(x);
ValueError: list.remove(x): x not in list
But the funny thing is if i write like that:
def f(x): return x%2!=0 and x%3
primes = filter(f , range (6,20))
for x in primes:
for a in filter(f, range(2,x-1)):
if x%a == 0:
primes.remove(x);
print(primes)
Why? and how can i fix it?
Let's put in some print statements to see what's going on:
In [81]: %paste
def f(x): return x%2!=0 and x%3!=0
primes = filter(f , range (6,50))
print primes
for x in primes:
for a in filter(f, range(2,x-1)):
if x%a == 0:
print x
primes.remove(x);
print(primes)
## -- End pasted text --
[7, 11, 13, 17, 19, 23, 25, 29, 31, 35, 37, 41, 43, 47, 49]
25
35
35
So your problem is that you're producing the number 35 twice (and you get an exception the second time because it's not there after you remove it the first time!)
Let's look at the result:
>>> primes = filter(f , range (6,50))
>>> primes
[7, 11, 13, 17, 19, 23, 25, 29, 31, 35, 37, 41, 43, 47, 49]
You have the value 35 here, which is 5*7. In your nested loop, when a is 5, the condition is met and 35 is removed from the list of primes; when a goes to 7, the condition is met again, but this time 35 is already removed, so a exception is raised.
So a simple fixed is like the one below. such that x is removed only once when the condition is first met:
for x in primes:
for a in filter(f, range(2,x-1)):
if x%a == 0:
primes.remove(x)
break
But now it will skip the element right after a non-prime number, such as 95 in this list:
primes=[7, 11, 13, 17, 19, 23, 25, 29, 31, 35, 37, 41, 43, 47, 49, 53, 55, 59, 61, 65, 67, 71, 73, 77, 79, 83, 85, 89, 91, 95, 97]
which is fixed by making a slice of primes:
for x in primes[:]:
for a in filter(f, range(2,x-1)):
if x%a == 0:
primes.remove(x)
break

Categories