Python - Adding count to output - python

I'm writing a simple program that takes a number and continually doubles it until it has reached an upper limit. The code I've written does this with a for loop and while loop, but I want to add a count to the output to see how many iterations through the while loop it took to get to the upper limit.
The code looks like this:
def double_function():
print('Enter an upper range to target')
upper_range = int(input())
for number in range(0, upper_range):
print('Enter a number to double')
number = float(input())
while number < upper_range:
number * 2
number += number
print(number)
else:
break
output_list = list(str(number))
iterations = enumerate(output_list)
print('It took ' + str(iterations) + ' iterations to reach ' + str(number))
double_function()
If upper_range = 1000 and number = 1, output is:
2.0
4.0
8.0
16.0
32.0
64.0
128.0
256.0
512.0
1024.0
It took <enumerate object at 0x7ffe02f74e40> iterations to reach 1024.0
I tried using enumerate because that's the only suggestion I've seen, but every other example used it with lists. I tried converting my output to a list, but I'm still not getting the output I want. I want it to look something like this:
1: 2.0
2: 4.0
3: 8.0
4: 16.0
5: 32.0
6: 64.0
7: 128.0
8: 256.0
9: 512.0
10: 1024.0
It took 10 iterations to reach 1000
Thanks for the help

enumerate returns an enumerate object see help(enumerate).
To get what you're looking for, just use len(output_list)
Just realized your "output_list" isn't actually a list containing the intermediate results. So to actually get the number of iterations, you can just initialize a counter before starting the while loop, then increment it by one in the while-loop block, and once it breaks, that variable will store the number of iterations performed.

RESOLVED. Initialized a counter before starting while loop and incremented by one inside the loop. Code looks like this:
def double_function():
print('Enter an upper range to target')
upper_range = int(input())
for number in range(0, upper_range):
print('Enter a number to double')
number = float(input())
iterations = 0
while number < upper_range:
number * 2
number += number
iterations += 1
print(iterations, number)
else:
break
print('It took ' + str(iterations) + ' iterations to reach ' + str(number))
double_function()
Output if upper_range = 1000, number = 1:
1 2.0
2 4.0
3 8.0
4 16.0
5 32.0
6 64.0
7 128.0
8 256.0
9 512.0
10 1024.0
It took 10 iterations to reach 1024.0

Related

Changing the final value in loop after one iteration

Design the source code to print the Knight sequence of a given number n.
Knight Sequence of a number starts with itself, the remaining terms of the sequence are
the sum of proper divisors of the immediate previous term.
Examples:
 The knight sequence for 10 is 10, 8, 7, 1, 0.
(Sum of proper divisors of 10 is 5 + 2 + 1 = 8.
Sum of proper divisors of 8 is 4 + 2 + 1 = 7.
The Sum of proper divisors of 7 is 1.
The Sum of proper divisors of 1 is 0.)
Note that there is no proper divisor of 1.
 The knight sequence for 6 is 6 (an infinite sequence of 6s).
When the knight sequence is repeating, print the repeating number and stop.
I am trying to solve this question and wrote this code:
n = int(input('Enter the value: '))
l = [n]
s = 0
for i in range(1, n):
if n % i == 0:
s += i
l.append(s)
print(set(l))
and this gives the output as:
Enter the value: 10
{8, 10}
The issue is that I am not able to figure out that how to continue this iteration to find the rest of the sequence....pls help me out

Python Pandas: calculate rolling mean (moving average) over variable number of rows

Say I have the following dataframe
import pandas as pd
df = pd.DataFrame({ 'distance':[2.0, 3.0, 1.0, 4.0],
'velocity':[10.0, 20.0, 5.0, 40.0] })
gives the dataframe
distance velocity
0 2.0 10.0
1 3.0 20.0
2 1.0 5.0
3 4.0 40.0
How can I calculate the average of the velocity column over the rolling sum of the distance column? With the example above, create a rolling sum over the last N rows in order to get a minimum cumulative distance of 5, and then calculate the average velocity over those rows.
My target output would then be like this:
distance velocity rv
0 2.0 10.0 NaN
1 3.0 20.0 15.0
2 1.0 5.0 11.7
3 4.0 40.0 22.5
where
15.0 = (10+20)/2 (2 because 3 + 2 >= 5)
11.7 = (10 + 20 + 5)/3 (3 because 1 + 3 + 2 >= 5)
22.5 = (5 + 40)/2 (2 because 4 + 1 >= 5)
Update: in Pandas-speak, my code should find the index of the reverse cumulative distance sum back from my current record (such that it is 5 or greater), and then use that index to calculate the start of the moving average.
Not a particularly pandasy solution, but it sounds like you want to do something like
df['rv'] = np.nan
for i in range(len(df)):
j = i
s = 0
while j >= 0 and s < 5:
s += df['distance'].loc[j]
j -= 1
if s >= 5:
df['rv'].loc[i] = df['velocity'][j+1:i+1].mean()
Update: Since this answer, the OP stated that they want a "valid Pandas solution (e.g. without loops)". If we take this to mean that they want something more performant than the above, then, perhaps ironically given the comment, the first optimization that comes to mind is to avoid the data frame unless needed:
l = len(df)
a = np.empty(l)
d = df['distance'].values
v = df['velocity'].values
for i in range(l):
j = i
s = 0
while j >= 0 and s < 5:
s += d[j]
j -= 1
if s >= 5:
a[i] = v[j+1:i+1].mean()
df['rv'] = a
Moreover, as suggested by #JohnE, numba quickly comes in handy for further optimization. While it won't do much on the first solution above, the second solution can be decorated with a #numba.jit out-of-the-box with immediate benefits. Benchmarking all three solutions on
pd.DataFrame({'velocity': 50*np.random.random(10000), 'distance': 5*np.random.rand(10000)})
I get the following results:
Method Benchmark
-----------------------------------------------
Original data frame based 4.65 s ± 325 ms
Pure numpy array based 80.8 ms ± 9.95 ms
Jitted numpy array based 766 µs ± 52 µs
Even the innocent-looking mean is enough to throw off numba; if we get rid of that and go instead with
#numba.jit
def numba_example():
l = len(df)
a = np.empty(l)
d = df['distance'].values
v = df['velocity'].values
for i in range(l):
j = i
s = 0
while j >= 0 and s < 5:
s += d[j]
j -= 1
if s >= 5:
for k in range(j+1, i+1):
a[i] += v[k]
a[i] /= (i-j)
df['rv'] = a
then the benchmark reduces to 158 µs ± 8.41 µs.
Now, if you happen to know more about the structure of df['distance'], the while loop can probably be optimized further. (For example, if the values happen to always be much lower than 5, it will be faster to cut the cumulative sum from its tail, rather than recalculating everything.)
How about
df.rolling(window=3, min_periods=2).mean()
distance velocity
0 NaN NaN
1 2.500000 15.000000
2 2.000000 11.666667
3 2.666667 21.666667
To combine them
df['rv'] = df.velocity.rolling(window=3, min_periods=2).mean()
It looks like something's a little off with the window shape.

use pandas to check first digit of a column

Problem
I need to test the first digit of each number in a column for conditions.
Conditions
is the first digit of checkVar greater than 5
or
is the first digit of checkVar less than 2
then set newVar=1
Solution
One thought that I had was to convert to it a string, left strip the space, and then take [0], but i can't figure out the code.
perhaps something like,
df.ix[df.checkVar.str[0:1].str.contains('1'),'newVar']=1
It isn't what I want, and for some reason i get this error
invalid index to scalar variable.
testing my original variable i get values that should meet the condition
df.checkVar.value_counts()
301 62
1 15
2 5
999 3
dtype: int64
ideally it would look something like this:
checkVar newVar
NaN 1 nan
2 nan
3 nan
4 nan
5 301.0
6 301.0
7 301.0
8 301.0
9 301.0
10 301.0
11 301.0
12 301.0
13 301.0
14 1.0 1
15 1.0 1
UPDATE
My final solution, since actual problem was more complex
w = df.EligibilityStatusSP3.dropna().astype(str).str[0].astype(int)
v = df.EligibilityStatusSP2.dropna().astype(str).str[0].astype(int)
u = df.EligibilityStatusSP1.dropna().astype(str).str[0].astype(int)
t = df.EligibilityStatus.dropna().astype(str).str[0].astype(int) #get a series of the first digits of non-nan numbers
df['MCelig'] = ((t < 5)|(t == 9)|(u < 5)|(v < 5)|(w < 5)).astype(int)
df.MCelig = df.MCelig.fillna(0)
t = df.checkVar.dropna().astype(str).str[0].astype(int) #get a series of the first digits of non-nan numbers
df['newVar'] = ((t > 5) | (t < 2)).astype(int)
df.newVar = df.newVar.fillna(0)
this might be slightly better, unsure, but another, very similar way to approach it.
t = df.checkVar.dropna().astype(str).str[0].astype(int)
df['newVar'] = 0
df.newVar.update(((t > 5) | (t < 2)).astype(int))
It helpful to break up the steps a bit when you are uncertain how to proceed.
def checkvar(x):
s = str(x)
first_d = int(s[0])
if first_d < 2 or first_d > 5:
return 1
else:
return 0
Change the "else: return" value to whatever you want (e.g., "else: pass"). Also, if you want to create a new column:
*Update - I didn't notice the NaNs before. I see that you are still having problems even with the dropna(). Does the following work for you, like it does for me?
df = pd.DataFrame({'old_col': [None, None, None, 13, 75, 22, 51, 61, 31]})
df['new_col'] = df['old_col'].dropna().apply(checkvar)
df
If so Maybe the issue in your data is with the dtype of 'old_col'? Have you tried converting it to float first?
df['old_col'] = df['old_col'].astype('float')

Weird output on converting code from C to Python

I could successfully rum a simple program to check whether the number is prime or not in C. The code looks like this
void isPrime(int n)
{
int a=0,i;
for(i=1;i<=n;i++)
{
if(n%i==0)
a++;
}
if(a==2)
{
printf("\n%d is prime",n);
}
else
{
printf("\n%d is not prime",n);
}
}
int _tmain(int argc, _TCHAR* argv[])
{
for(int i=2;i<=20;i++)
{
isPrime(i);
}
return 0;
}
The above code runs perfectly when I compile. I am a beginner in python and I have converted the same code into python, which looks like this.
def isPrime(n):
a=0
for x in range(1,n):
if n%x==0:
a=a+1
if a==2:
print("{} is prime".format(n))
else:
print("{} is not prime".format(n))
for n in range(2,20):
isPrime(n)
But I get a wrong output in python. The output is somewhat weird which says
2 is not prime
3 is not prime
4 is prime
5 is not prime
6 is not prime
7 is not prime
8 is not prime
9 is prime
10 is not prime
11 is not prime
12 is not prime
13 is not prime
14 is not prime
15 is not prime
16 is not prime
17 is not prime
18 is not prime
19 is not prime
I have found out that the count of 'a' is 1 less than the actual count required. For example, in case of n=8, a should be 4. But its getting counted as 3.
What could be the reason?
Why don't you add some print statements so you can see where the code fails? Adding some prints should be your first reflex when debugging.
def isPrime(n):
a=0
for x in range(1,n):
print('x,a', x,a)
if n%x==0:
print('incrementing a...')
a=a+1
print('a after loop:', a)
if a==2:
print("{} is prime".format(n))
else:
print("{} is not prime".format(n))
Output for isPrime(2):
x,a 1 0
incrementing a...
a after loop: 1
2 is not prime
Output for isPrime(7):
x,a 1 0
incrementing a...
x,a 2 1
x,a 3 1
x,a 4 1
x,a 5 1
x,a 6 1
a after loop: 1
7 is not prime
As you can see, a is never 2 because the n%n test is never executed, because with x in range(1,n), the last value for x is n-1. However, if you change your range to range(1,n+1), the test will be made:
x,a 1 0
incrementing a...
x,a 2 1
x,a 3 1
x,a 4 1
x,a 5 1
x,a 6 1
x,a 7 1
incrementing a...
a after loop: 2
7 is prime
The issue you have is that the last value produced range(start, stop) is stop-1; see the docs. Thus, isPrime should have the following for loop:
for x in range(1, n+1):
This will faithfully replicate the C code, and produce the correct output. (Note that this is also why you are only checking the numbers [2, 19] for whether they're prime, as opposed to [2, 20].)
I think the problem was with your range as mentioned above. Can I give you some short tips to make your code more "Pythonic"? :)
Instead of
if n%x==0
you would simply write
if not n%x
and instead of
a=a+1
you could use the in-place operator, e.g.,
a += 1
Here, it wouldn't make much of a difference, but if you are working with mutable objects (due to the __iadd__ method, I have more details here) you would gain a significant performance increase, e.g.,
def slow(a):
for i in range(1000):
a = a + [1,2]
def fast(a):
for i in range(1000):
a += [1,2]
a = []
b = []
%timeit -r 3 -n 1000 slow(a)
%timeit -r 3 -n 1000 fast(b)
1000 loops, best of 3: 2.94 ms per loop
1000 loops, best of 3: 181 µs per loop
Same for the binary operator instead of the format() method, however, I like the latter one better (due to its more powerful minilanguange and I would recommend it if you don't care about speed in certain computations)
%timeit -r 3 -n 1000 '{} World'.format('Hello')
%timeit -r 3 -n 1000 '%s World' %('Hello')
1000 loops, best of 3: 472 ns per loop
1000 loops, best of 3: 27.3 ns per loop
def isPrime(n):
a = 0
for x in range(1, n+1):
if not n%x:
a += 1
if a==2:
print("{} is prime".format(n))
else:
print("{} is not prime".format(n))
for n in range(2, 20):
isPrime(n)

Calculating the average grade in Python?

I am trying to get the average of two passing grades. The output should be:
0.0 if neither of the grades is a passing grade both <50
The passing grade, if only one of the grades is a passing grade (if
one is >50)
The average of the two grades, if both are passing grades (if both
are greater than 50)
Here is my code so far:
def passing_grade(grade1,grade2):
'''(number, number)--> number
This function definition prints the average of all passing grade(s)
'''
# Function 1 - If both numbers are outside the grading range (0-100)
if 0.0 < grade1 > 100.0 and 0 < grade2 > 100.0:
print ('Not available grading')
elif 0.0 >= grade1 <= 50.0 and 0.0 >= grade2 <= 50.0:
print (0.0)
#Function 2 - If one of the grades is passing then, print passing grade
elif 0.0 >= grade1 <= 50.0 and 0.0 >= grade2 >= 50.0:
print (grade2)
elif 0.0 >= grade1 >= 50.0 and 0.0 >= grade2 <= 50.0:
print (grade1)
#Function 3 - If both grades are passing >50 then print the average
elif 50.0 > grade1 <= 100.0 and 50.0> grade2 <= 100.0:
print ((grade1+grade2)/2)
I'm just guessing about your problem here, since you haven't specified, but it looks like you bad logic in the second part of "Function #2":
elif 0.0 >= grade1 <= 50.0 and 0.0 >= grade2 >= 50.0:
print (grade2)
elif 0.0 >= grade1 >= 50.0 and 0.0 >= grade2 <= 50.0:
print (grade1)
Should be:
elif grade1 <= 50.0 and grade2 >= 50.0:
print (grade2)
elif grade1 >= 50.0 and grade2 <= 50.0:
print (grade1)
If you look at your original conditions, you keep checking 0.0 >= gradeN, which means is only true if the grade is a negative number. There are similar problems in some of your other sections.
Your comparisons are screwed up. They don't say what you mean, and many evaluate to False always. There can be no grade1 such that 0.0 >= grade1 >= 50.0, as there are no nonpositive numbers greater-equal 50. I suggest you write out your multiple comparisons "the long way" until you're clear about what you mean to say, rather than using this keystroke-saving feature of Python. a < b < c in Python means a < b and b < c not a < b or b < c which is the form of what you want to say in your first 'if' statement.
Finally, when writing multiple comparisons in one expression, don't mix directions of the comparisons, it's needlessly confusing (for you, to start with).
A more concise way of writing (the calculation part of) your function:
def avg_passing_grade(grade1, grade2):
passing_grades = [g for g in (grade1, grade2) if 50 <= g <= 100]
return sum(passing_grades)/max(1, len(passing_grades))
This makes a list passing_grades containing only the grades supplied to the function that are passing. The function returns their average, taking care not to divide by 0 in case no grades are passing.
Although the following may be overkill, it's within such easy reach that I have to mention it: the function above generalizes easily to one that takes an arbitrary number of grades:
def average_passing_grade(* grades):
'''Return the average of the passing grades among grades.'''
passing_grades = [g for g in grades if 50 <= g <= 100]
return sum(passing_grades)/max(1, len(passing_grades))
which you can use like this:
>>> average_passing_grade()
0.0
>>> average_passing_grade(35.3)
0.0
>>> average_passing_grade(75.5)
75.5
>>> average_passing_grade(35.3, 88)
88.0
>>> average_passing_grade(88, 20)
88.0
>>> average_passing_grade(50, 100)
75.0
>>> average_passing_grade(40, 50, 60, 70, 80, 90)
70.0
Besides the bad logic pointed out in the other answers, you could use max and min to do a single logical check for certain cases.
if grade1 >= 50.0 and grade2 >= 50.0:
can be
if min (grade1, grade2) >= 50.0: # Both are >= 50.0
similarly
if max(grade1, grade2) < 50.0 # both are less than 50.0
Once those two have been shown false, the the else means that one is on each side of the limit.
Similarly, to test for the invalid values you can use
if max(grade1, grade2) > 100.0 or min(grade1, grade2) < 0:
means that at least one grade is invalid
if min(grade1, grade2) > 100 or max(grade1, grade2) <0:
means that both grades are invalid in the same way.
I am no expert in Python. However, I'm pretty sure your error is because of your if conditions.
if 0.0 < grade1 > 100.0 and 0 < grade2 > 100.0:
Should be something like
if (grade1 < 0 or grade1 > 100) and (grade2 < 0 or grade2 > 100):

Categories

Resources