How to run 3 counters simultaneously in Python? - python

I'd like to run simultaneously 3 different counters following a very specific pattern:
counter_3 displays the number of iteration from 0 to 2144
counter_1 increases by 1 every 65 iterations
counter_2 goes from 1 to 65, then starts back from 2 to 65, then from 3 to 65 ...
The result should look like this:
counter_1 counter_2 counter_3
0 1 0
0 2 1
0 3 2
0 4 3
0 5 4
... ... ...
0 65 64
1 2 65
1 3 66
1 4 67
... ... ...
1 65 128
1 3 129
2 4 130
2 5 131
... ... ...
32 64 2142
32 65 2143
32 65 2144
I know how to run these counters separately.
For counter_1 and counter_3 (refered below as i):
counter_1 = 0
for i, e in enumerate(range(2145)):
if i > 1 and i % 65 == 0:
counter_1 += 1
print(counter_1, i)
For counter_2 (refered below as e):
n = 0
g = 1
while n <= 2145:
for e in np.arange(g, 66):
print(e)
g += 1
n += 1
QUESTION: How can i run these 3 counters simultaneously ?

You have the right general idea: determine when you have to fiddle with the counters. However, note that coutner1 is a value you can derive simply from counter3 with integer division. Just worry about counter2, and you're fine. Here's a solution more at your level of coding:
c2 = 65
c2_new = 0 # Track where to start counter2 next time
for c3 in range(2145):
if c2 == 65:
c2 = c2_new
c2_new += 1
c2 += 1
c1 = c3 // 65 # Simple division gets the c1 value
print c1, c2, c3

Third counter is just the index and first counter is index/65. Only the middle counter is a little less trivial, so I'd use that one to drive this.
for c, b in enumerate(b for start in range(1, 66) for b in range(start, 66)):
print c / 65, b, c
And an itertools version:
for c, (_, b) in enumerate(combinations(range(66), 2)):
print c / 65, b, c

Not looking for the simplest or slickest specific answer to your question, I wanted to offer a pattern that might be applied more broadly.
You could use a generator to provide each of the series of numbers that you want, then combine them. This means that you could test them independently. It also means that you could parameterise each of them to allow for smaller numbers during testing. Here, for instance, Counter_1 is parameterised so with the number of repetitions allowed before its behaviour changes.
Counter_2 is almost certainly more complicated than it needs to be. My brain is in a fog.
def Counter_1(rep=65):
n = -1
while True:
n += 1
k = n//rep
yield k
def Counter_2(rep=65):
n = 0
inc = 0
while True:
n += 1
if n==rep:
k = n//rep
yield n
inc += 1
n = inc
else:
yield n
counter_1 = Counter_1()
counter_2 = Counter_2()
for counter_3 in range(2145):
c_1 = next(counter_1)
c_2 = next(counter_2)
print (c_1, c_2, counter_3)
if counter_3>10:
break

Another iterative approach using generator functions (in Python 3).
Code
import itertools as it
def counter1(item=0):
"""Yield increments every 65 iterations."""
for _ in range(1, 66):
yield item
yield from counter1(item+1)
def counter2(item=1, stop=66):
"""Yield `item` to 65, incrementing `item` after `stop-1`."""
yield from range(item, stop)
if item != stop:
yield from counter2(item+1)
def counter3(stop=2150-5):
"""Yield numbers 0 to `stop`."""
for item in range(stop):
yield item
cts = list(zip(counter1(), counter2(), counter3()))
Demo
# Sample results (see OP results)
counters = it.chain(cts[:5], cts[63:68], cts[128:132], cts[-3:])
for iteration in counters:
print("{:<10} {:<10} {:<10}".format(*iteration))
Sample Output
0 1 0
0 2 1
0 3 2
0 4 3
0 5 4
0 64 63
0 65 64
1 2 65
1 3 66
1 4 67
1 65 128
1 3 129
2 4 130
2 5 131
32 64 2142
32 65 2143
32 65 2144
Details
counter1: an infinite generator; yields a value for over a range of numbers. Repeat for incremented values recursively.
counter2: an infinite generator; for every iteration, yield a value from a range. Repeat for incremented counters recursively.
counter3: a finite generator; a simple iteration over a range().
The resulting counters are zipped together, exhausted after the termination of the finite counter3.
This example is Python 2 compatible after transforming yield from statements to for loops, e.g.
for i in range(x, stop):
yield i

Related

Finding the n-th number that consists of only 2 or 3

I am really struggling with this program. I would appreciate any kind of help.
For a natural number we say that it is strange if it is completely composed of digits 2 and 3. The user enters a natural number. The program prints the n-th strange number.
Numbers that are considered strange are 2, 3, 22, 23, 33...
n = int(input())
current_number = 1
counter_strange = 0
counter = 0
while counter_strange < n:
x = current_number
while x < n:
k = x % 10
if k != 2 or k != 3:
counter += 1
else:
break
if counter >= 1:
counter_strange += 1
current_number += 1
print(current_number-1)
Strange numbers come in blocks. A block of 2 1-digit numbers, followed by a block of 4 2-digit numbers, then 8 3-digit numbers. You can do the math and determine which block of k-digit numbers a given index n is, and how far into that block it lies. Convert that distance into a base-2 number, zero-filled to k digits, then replace 0 by 2 and 1 by 3. Convert the result back to an int:
from math import log2, ceil
def strange(n):
"""returns the nth strange number"""
#first determine number of digits:
k = ceil(log2(n+2)) - 1
#determine how far is in the block of strange k-digit numbers
d = n - (2**k - 1)
#convert to base 2, zfilling to k digits:
s = bin(d)[2:].zfill(k)
#swap 2,3 for 0,1:
s = s.replace('0','2').replace('1','3')
#finally:
return int(s)
for n in range(1,10): print(n,strange(n))
Output:
1 2
2 3
3 22
4 23
5 32
6 33
7 222
8 223
9 232
You can use a while loop with itertools.product in a generator function. Using a generator will allow you to create a stream from which you can access strange numbers on the fly:
import itertools
def strange():
c = 0
while True:
yield from map(''.join, itertools.product(*([['2', '3']]*(c:=c+1))))
s = strange()
for _ in range(10):
print(int(next(s)))
Output:
2
3
22
23
32
33
222
223
232
233

Sliding Window and comparing elements of DataFrame to a threshold

Assume I have the following dataframe:
Time Flag1
0 0
10 0
30 0
50 1
70 1
90 0
110 0
My goal is to identify if within any window that time is less than lets the number in the row plus 35, then if any element of flag is 1 then that row would be 1. For example consider the above example:
The first element of time is 0 then 0 + 35 = 35 then in the window of values less than 35 (which is Time =0, 10, 30) all the flag1 values are 0 therefore the first row will be assigned to 0 and so on. Then the next window will be 10 + 35 = 45 and still will include (0,10,30) and the flag is still 0. So the complete output is:
Time Flag1 Output
0 0 0
10 0 0
30 0 1
50 1 1
70 1 1
90 1 1
110 1 1
To implement this type of problem, I thought I can use two for loops like this:
Output = []
for ii in range(Data.shape[0]):
count =0
th = Data.loc[ii,'Time'] + 35
for jj in range(ii,Data.shape[0]):
if (Data.loc[jj,'Time'] < th and Data.loc[jj,'Flag1'] == 1):
count = 1
break
output.append(count)
However this looks tedious. since the inner for loop should go for continue for the entire length of data. Also I am not sure if this method checks the boundary cases for out of bound index when we are reaching to end of the dataframe. I appreciate if someone can comment on something easier than this. This is like a sliding window operation only comparing number to a threshold.
Edit: I do not want to compare two consecutive rows only. I want if for example 30 + 35 = 65 then as long as time is less than 65 then if flag1 is 1 then output is 1.
The second example:
Time Flag1 Output
0 0 0
30 0 1
40 0 1
60 1 1
90 1 1
140 1 1
200 1 1
350 1 1
Assuming a window k rows before and k rows after as mentioned in my comment:
import pandas as pd
Data = pd.DataFrame([[0,0], [10,0], [30,0], [50,1], [70,1], [90,1], [110,1]],
columns=['Time', 'Flag1'])
k = 1 # size of window: up to k rows before and up to k rows after
n = len(Data)
output = [0]*n
for i in range(n):
th = Data['Time'][i] + 35
j0 = max(0, i - k)
j1 = min(i + k + 1, n) # the +1 is because range is non-inclusive of end
output[i] = int(any((Data['Time'][j0 : j1] < th) & (Data['Flag1'][j0 : j1] > 0)))
Data['output'] = output
print(Data)
gives the same output as the original example. And you can change the size of the window my modifying k.
Of course, if the idea is to check any row afterward, then just use j1 = n in my example.
import pandas as pd
Data = pd.DataFrame([[0,0],[10,0],[30,0],[50,1],[70,1],[90,1],[110,1]],columns=['Time','Flag1'])
output = Data.index.map(lambda x: 1 if any((Data.Time[x+1:]<Data.Time[x]+35)*(Data.Flag1[x+1:]==1)) else 0).values
output[-1] = Data.Flag1.values[-1]
Data['output'] = output
print(Data)
# show
Time Flag1 output
0 0 0
30 0 1
40 0 1
50 1 1
70 1 1
90 1 1
110 1 1

sum of Fibbonaci Sequences?

Trying to add the sum of Fibonacci, using definite loops. It's meant to calculate the summation of Fibonacci number with each number too. Below is the sample for the Fibonacci sequence and its summation, how do i add the sum of the fibonacci eg 1,1,2,3,5,8
Fibonacci Summation
0 0
1 1
1 2
2 4
3 7
5 12
8 20
n = int(input("enter"))
def fibonacciSeries():
a=0
b=1
for i in range (n-2):
x = a+b
a=b
b=x
int(x)
x[i]= x+x[i-1]
#should add the previous sequences
print(x)
fibonacciSeries()
You don't need to keep track of the whole sequence. Plus your Fibonacci implementation doesn't start with 1, 1 but rather 1, 2 so I fixed that.
def fibonacciSeries(n):
a=0
b=1
x=1
series_sum = 0
for i in range (n-2):
series_sum += x
print(f'{x} {series_sum}')
x = a+b
a=b
b=x
n = 10
fibonacciSeries(n)
Output:
1 1
1 2
2 4
3 7
5 12
8 20
13 33
21 54
def fibonacciSeries(n):
sum = 0
a = 0
b = 1
x = 1
sum = 0
for i in range(0,n - 2):
sum += x
print(x,sum)
x = a + b
a = b
b = x
n = int(input("enter : ")) # n = 8
fibonacciSeries(n)
Output:
enter : 8
1 1
1 2
2 4
3 7
5 12
8 20

'int' object is not subscriptable on second iteration of loop when trying to convert a string item in a list to a list of integers

I'm new to Python and I'm pretty confused as to why this part of my program is causing problems.
n = int(input())
c = []
l = []
i = 0
while(i<n):
z = int(input())
c.append(z)
x = str(input())
l.append(x)
i += 1
listCount = 0
while(listCount < n ):
binNumber = c[listCount]
dataList = [int(n) for n in l[listCount].split()]
dataList.sort()
listLength = len(dataList)
lowestData = dataList[0]
l = lowestData
while( l <= dataList[listLength-1]):
intervalCounter = 0
traverseCounter = 0
while(traverseCounter < listLength):
if(dataList[traverseCounter] >= l and dataList[traverseCounter] <= l + binNumber - 1):
intervalCounter += 1
traverseCounter += 1
else:
traverseCounter += 1
print( str(l) +'-'+ str(l + binNumber - 1) + ' ' + str(intervalCounter))
l = l + binNumber
print('\b')
listCount += 1
The program outputs correctly on the first iteration of the loop but on the second or more iteration of the loop, it gives me this
Traceback (most recent call last):
File "solution.py", line 21, in <module>
dataList = [int(n) for n in l[listCount].split()]
TypeError: 'int' object is not subscriptable
Does first iteration of the loop change the code in any way to make it behave like this, I'm really confused as to how this works for the first loop but not the second.
The inputs are n test cases followed by 2 lines each first being the intervals and the second line being the values
input
2
10
36 25 38 46 55 68 72 55 36 38 67 45 22 48 91 46 52 61 58 55
50
2 2 1 255 0 3 4 5 6 7 200 100 10 50 0
This is how the output is supposed to be:
22-31 2
32-41 4
42-51 4
52-61 6
62-71 2
72-81 1
82-91 1
0-49 11
50-99 1
100-149 1
150-199 0
200-249 1
250-299 1
But all I get is:
22-31 2
32-41 4
42-51 4
52-61 6
62-71 2
72-81 1
82-91 1

Grouping list of integers in a range into chunks

Given a set or a list (assume its ordered)
myset = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
I want to find out how many numbers appear in a range.
say my range is 10. Then given the list above, I have two sets of 10.
I want the function to return [10,10]
if my range was 15. Then I should get [15,5]
The range will change. Here is what I came up with
myRange = 10
start = 1
current = start
next = current + myRange
count = 0
setTotal = []
for i in myset:
if i >= current and i < next :
count = count + 1
print str(i)+" in "+str(len(setTotal)+1)
else:
current = current + myRange
next = myRange + current
if next >= myset[-1]:
next = myset[-1]
setTotal.append(count)
count = 0
print setTotal
Output
1 in 1
2 in 1
3 in 1
4 in 1
5 in 1
6 in 1
7 in 1
8 in 1
9 in 1
10 in 1
12 in 2
13 in 2
14 in 2
15 in 2
16 in 2
17 in 2
18 in 2
19 in 2
[10, 8]
notice 11 and 20 where skipped. I also played around with the condition and got wired results.
EDIT: Range defines a range that every value in the range should be counted into one chuck.
think of a range as from current value to currentvalue+range as one chunk.
EDIT:
Wanted output:
1 in 1
2 in 1
3 in 1
4 in 1
5 in 1
6 in 1
7 in 1
8 in 1
9 in 1
10 in 1
11 in 2
12 in 2
13 in 2
14 in 2
15 in 2
16 in 2
17 in 2
18 in 2
19 in 2
[10, 10]
With the right key function, thegroupbymethod in the itertoolsmodule makes doing this fairly simple:
from itertools import groupby
def ranger(values, range_size):
def keyfunc(n):
key = n/(range_size+1) + 1
print '{} in {}'.format(n, key)
return key
return [len(list(g)) for k, g in groupby(values, key=keyfunc)]
myset = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
print ranger(myset, 10)
print ranger(myset, 15)
You want to use simple division and the remainder; the divmod() function gives you both:
def chunks(lst, size):
count, remainder = divmod(len(lst), size)
return [size] * count + ([remainder] if remainder else [])
To create your desired output, then use the output of chunks():
lst = range(1, 21)
size = 10
start = 0
for count, chunk in enumerate(chunks(lst, size), 1):
for i in lst[start:start + chunk]:
print '{} in {}'.format(i, count)
start += chunk
count is the number of the current chunk (starting at 1; python uses 0-based indexing normally).
This prints:
1 in 1
2 in 1
3 in 1
4 in 1
5 in 1
6 in 1
7 in 1
8 in 1
9 in 1
10 in 1
11 in 2
12 in 2
13 in 2
14 in 2
15 in 2
16 in 2
17 in 2
18 in 2
19 in 2
20 in 2
If you don't care about what numbers are in a given chunk, you can calculate the size easily:
def chunk_sizes(lst, size):
complete = len(lst) // size # Number of `size`-sized chunks
partial = len(lst) % size # Last chunk
if partial: # Sometimes the last chunk is empty
return [size] * complete + [partial]
else:
return [size] * complete

Categories

Resources