python-datetime outputting -1 day instead of desired output - python

So, my goal is to change this output from datetime:
time left: -1 day, 23:57:28
To this:
time left: 0:00:30
Now, this needs to be dynamic, as the code is supposed to be changed in the dictionary. I'm trying to figure out why it is outputting with
-1 day, 23:57:28
I've tried moving where it executes and even changing some other code. I just don't understand why it's showing with -1 day. It seems likes it is executing one too many times
Also, a side note, the purpose of this program is to figure out how many songs can fit into a playlist given a time restraint. I can't seem to figure out the right if statement for it to work. Could someone also help with this?
This is the current output of the program:
0:02:34
0:06:30
Reached limit of 0:07:00
time left: -1 day, 23:57:28
See code below:
import datetime
#durations and names of songs are inputted here
timeDict = {
'Song1' : '2:34',
'Song2' : '3:56',
'Song3' : '3:02'
}
def timeAdder():
#assigns sum to the datetime library's timedelta class
sum = datetime.timedelta()
#sets the limit, can be whatever
limit = '0:07:00'
#calculates the sum
for i in timeDict.values():
(m, s) = i.split(':')
d = datetime.timedelta(minutes=int(m), seconds=int(s))
sum += d
#checks whether the limit has been reached
while str(sum)<limit:
print(sum)
break
#commits the big STOP when limit is reached
if str(sum)>limit:
print("Reached limit of " + limit)
break
#timeLeft variable created as well as datetime object conversion to a string
x = '%H:%M:%S'
timeLeft = datetime.datetime.strptime(limit, x) - datetime.datetime.strptime(str(sum), x)
for i in timeDict:
if timeDict[i] <= str(timeLeft):
print("You can fit " + i + " into your playlist.")
print("time left: " + str(timeLeft))
def main():
timeAdder()
main()
Any help with this would be appreciated.

It seems likes it is executing one too many times
Bingo. The problem is here:
sum += d
...
#commits the big STOP when limit is reached
if str(sum)>limit:
print("Reached limit of " + limit)
break
You are adding to your sum right away, and then checking whether it has passed the limit. Instead, you need to check whether adding to the sum will pass the limit before you actually add it.
Two other things: first, sum is a Python keyword, so you don't want to use it as a variable name. And second, you never want to compare data as strings, you will get weird behavior. Like:
>>> "0:07:30" > "2:34"
False
So all of your times should be timedelta objects.
Here is new code:
def timeAdder():
#assigns sum to the datetime library's timedelta class
sum_ = datetime.timedelta()
#sets the limit, can be whatever
limit = '0:07:00'
(h, m, s) = (int(i) for i in limit.split(":"))
limitDelta = datetime.timedelta(hours=h, minutes=m, seconds=s)
#calculates the sum
for i in timeDict.values():
(m, s) = i.split(':')
d = datetime.timedelta(minutes=int(m), seconds=int(s))
if (sum_ + d) > limitDelta:
print("Reached limit of " + limit)
break
# else, loop continues
sum_ += d
print(sum_)
timeLeft = limitDelta - sum_
for songName, songLength in timeDict.items():
(m, s) = (int(i) for i in songLength.split(':'))
d = datetime.timedelta(minutes=m, seconds=s)
if d < timeLeft:
print("You can fit " + songName + " into your playlist.")
print("time left: " + str(timeLeft))
Demo

Related

Calculation loop in python new values

I would like to do the calculation loop using my function.
b=list(data.iloc[1])
def balance(rate, payment, os):
interest_amount=os*rate/100/12
principal_amount=payment-interest_amount
next_balance=os+interest_amount-principal_amount
return next_balance
c=balance(b[9], b[11], b[8])
d=balance(b[9], b[11], c)
e=balance(b[9], b[11], d)
I would have to start with b[8] as the amount for calculation. After I got the next amount from the function balance, the next amount will be the beginning of the third calculation and so on until the next amount eqaul of less than 0. It should stop the loop.
I need to append calculated values since b[8] until the last (before getting 0 or less).
Any suggestion on this, thank you!
Edit: based on Zaraki Kenpachi
b[8] is amount of money given 17183
b[9] is rate of interest given 3.39
b[11] is payment per month given 5759
The output, which I am trying to do is:
[17183, 11,521, 5,826, 99]
Perhaps something like this
x = b[8]
output = []
output.append(x)
while x > 0:
x = balance(b[9], b[11], X)
output.append(x)
Here you go:
def balance(rate, payment, os):
interest_amount=os*rate/100/12
principal_amount=payment-interest_amount
next_balance=os+interest_amount-principal_amount
return next_balance
next_balance = 17183 # = b[8]
results = []
results.append(next_balance)
while next_balance > 0:
next_balance = balance(3.39, 5759, next_balance) # b[9], b[11]
if next_balance > 0:
results.append(next_balance)
Output:
[17183, 11521.08395, 5827.1780743175, 101.10163043739522]
Python doesn't have a builtin way to unfold / iterate but the normal way you'd implenent a specific unfold is through a generator, which can keep computational state and yield values:
def balances(balance, rate, payment):
while True:
interest_amount = balance*rate/100/12
principal_amount = payment-interest_amount
balance = os+interest_amount-principal_amount
# TO DO: end the computation when balance <= 0, maybe find a way to
# note extra in the last payment, or reject the last payment entirely
# and handle that case separately outside the generator
yield balance
then you can either call next() on your balances iterator to get successive values
bs = balances(b[8], b[9], b[11])
c = next(bs)
d = next(bs)
e = next(bs)
or iterate the entire thing
for balance in balances(b[8], b[9], b[11]):
if balance < 0:
"do something when the last payment was excessive and stop the loop"
...

Find highest possible number under certain time span?

For Python 3, is there a possibility to find the highest possible calculated number in a function under a specific time span?
For example if something would take almost 'forever', is there a way to find out the highest possible number to be calculated under 1 minute?
Here is the code:
def fibonacci5(n):
f1, f2 = 1, 0
while n > 0:
f1, f2 = f1 + f2, f1
n -= 1
return f2
I am trying to use the possible solution for finding the number that takes 1 second via timeit.
repeats = 10
t = timeit.Timer("fibonacci5(500000)", globals=globals())
time = t.timeit(repeats)
print ("average execution time:", time/repeats)
But 500.000 takes on average 2,6s, while 250.000 takes on average 0,6s - so that solution can't work.
you could add a timer to your function to make it stop after a given time:
from datetime import datetime, timedelta
max_runtime = timedelta(seconds=1)
def fibonacci5(n):
stop_time = datetime.now() + max_runtime
f1, f2 = 1, 0
while n > 0:
f1, f2 = f1 + f2, f1
n -= 1
if datetime.now() > stop_time:
return f2, 'timelimit reached'
return f2
note that if it returns when the time has run out that it will not just return a number, but a tuple with the number and the string 'timelimit reached'. that way you can differentiate between normal termination and timeout (there may be better ways to handle that...).
the caveat here is that the if line (at least as long as your ints are still very small) is probably the line of the function that takes up the most amount of time... the results will therefore not represent the actual run-times very exactly...
also note that there are way more efficient ways to calculate fibonacci numbers.
if we write Fibonacci sequence generator like
def fibonacci():
a, b = 0, 1
while True:
yield b
a, b = b, a + b
it looks naive but works fast enough, e.g. if you need 500000th Fibonacci number we can use itertools.islice
from itertools import islice
fibonacci_500000 = next(islice(fibonacci(), 500000, 500001))
print(fibonacci_500000)
which took about 5 seconds on my old machine, output is too big to insert, but it looks like
47821988144175...more digits here...2756008390626
but if you really need to find out which value we've calculated after some time – we can use timedelta and datetime objects like
from datetime import datetime, timedelta
def fibonacci():
a, b = 0, 1
while True:
yield b
a, b = b, a + b
if __name__ == '__main__':
duration = timedelta(seconds=5)
fibonacci_numbers = fibonacci()
stop = datetime.now() + duration
for index, number in enumerate(fibonacci_numbers, start=1):
if datetime.now() >= stop:
break
print(index)
which gives us 505352th Fibonacci number calculated after approximately 5 seconds (we can also print number, but it is too long)

Why count() method is faster than a for loop python

Here are 2 functions that do exactly the same thing, but does anyone know why the one using the count() method is much faster than the other? (I mean how does it work? How is it built?)
If possible, I'd like a more understandable answer than what's found here : Algorithm used to implement the Python str.count function
or what's in the source code : https://hg.python.org/cpython/file/tip/Objects/stringlib/fastsearch.h
def scoring1(seq):
score = 0
for i in range(len(seq)):
if seq[i] == '0':
score += 1
return score
def scoring2(seq):
score = 0
score = seq.count('0')
return score
seq = 'AATTGGCCGGGGAG0CTTC0CTCC000TTTCCCCGGAAA'
# takes 1min15 when applied to 100 sequences larger than 100 000 characters
score1 = scoring1(seq)
# takes 10 sec when applied to 100 sequences larger than 100 000 characters
score2 = scoring2(seq)
Thanks a lot for your reply
#CodeMonkey has already given the answer, but it is potentially interesting to note that your first function can be improved so that it runs about 20% faster:
import time, random
def scoring1(seq):
score=0
for i in range(len(seq)):
if seq[i]=='0':
score+=1
return score
def scoring2(seq):
score=0
for x in seq:
score += (x =='0')
return score
def scoring3(seq):
score = 0
score = seq.count('0')
return score
def test(n):
seq = ''.join(random.choice(['0','1']) for i in range(n))
functions = [scoring1,scoring2,scoring3]
for i,f in enumerate(functions):
start = time.clock()
s = f(seq)
elapsed = time.clock() - start
print('scoring' + str(i+1) + ': ' + str(s) + ' computed in ' + str(elapsed) + ' seconds')
test(10**7)
Typical output:
scoring1: 5000742 computed in 0.9651326495293333 seconds
scoring2: 5000742 computed in 0.7998054195159483 seconds
scoring3: 5000742 computed in 0.03732172598339578 seconds
Both of the first two approaches are blown away by the built-in count().
Moral of the story: when you are not using an already optimized built-in method, you need to optimize your own code.
Because count is executed in the underlying native implementation. The for-loop is executed in slower interpreted code.

What is the best way to calculate percentage of an iterating operation?

I've written a function that saves all numbers between two digit groups to a text file, with a step option to save some space and time, and I couldn't figure out how to show a percentage value, so I tried this.
for length in range(int(limit_min), int(limit_max) + 1):
percent_quotient = 0
j=0
while j <= (int(length * "9")):
while len(str(j)) < length:
j = "0" + str(j)
percent_quotient+=1
j = int(j) + int(step) # increasing dummy variable
for length in range(int(limit_min), int(limit_max) + 1):
counter=1
i = 0
while i <= (int(length * "9")):
while len(str(i)) < length:
i = "0" + str(i) #
print "Writing %s to file. Progress: %.2f percent." % (str(i),(float(counter)/percent_quotient)*100)
a.write(str(i) + "\n") # this is where everything actually gets written
i = int(i) + int(step) # increasing i
counter+=1
if length != int(limit_max):
print "Length %i done. Moving on to length of %i." % (length, length + 1)
else:
print "Length %i done." % (length)
a.close() # closing file stream
print "All done. Closed file stream. New file size: %.2f megabytes." % (os.path.getsize(path) / float((1024 ** 2)))
print "Returning to main..."
What I tried to do here was make the program do an iteration as many times as it would usually do it, but instead of writing to a file, I just made percent_quotient variable count how many times iteration is actually going to be repeated. (I called j dummy variable since it's there only to break the loop; I'm sorry if there is another expression for this.) The second part is the actual work and I put counter variable, and I divide it with percent_quotient and multiply with 100 to get a percentage.
The problem is, when I tried to make a dictionary from length of 1 to length of 8, it actually took a minute to count everything. I imagine it would take much longer if I wanted to make even bigger dictionary.
My question is, is there a better/faster way of doing this?
I can't really work out what this is doing. But it looks like it's doing roughly this:
a = file('d:/whatever.txt', 'wb')
limit_min = 1
limit_max = 5
step = 2
percent_quotient = (10 ** (limit_max - limit_min)) / step
for i in range(limit_min, 10**limit_max, step):
output = str(i).zfill(limit_max) + '\r\n'
a.write(output)
if i % 100 < 2:
print "Writing %s to file. Progress: %.2f percent." % (str(i),(float(i)/percent_quotient)*100)
a.close()
If that's right, then I suggest:
Do less code looping and more math
Use string.zfill() instead of while len(str(num)) < length: "0" + str(num)
Don't overwhelm the console with output every single number, only print a status update every hundred numbers, or every thousand numbers, or so.
Do less str(int(str(int(str(int(str(int(...
Avoid "" + blah inside tight loops, if possible, it causes strings to be rebuilt every time and it's particularly slow.
Okay, the step variable is giving me a lot of headache, but without it, this would be the right way to calculate how many numbers are going to be written.
percent_quota=0 #starting value
for i in range(limit_min,limit_max+1): #we make sure all lengths are covered
percent_quota+=(10**i)-1 #we subtract 1 because for length of 2, max is 99
TessellatingHeckler, thank you, your answer helped me figure this out!

Python iterating - problems finding the product of digits in a string

Trying to iterate through a number string in python and print the product of the first 5 numbers,then the second 5, then the third 5, etc etc. Unfortunately, I just keep getting the product of the first five digits over and over. Eventually I'll append them to a list. Why is my code stuck?
edit: Original number is an integer so I have to make it a string
def product_of_digits(number):
d= str(number)
for integer in d:
s = 0
k = []
while s < (len(d)):
print (int(d[s])*int(d[s+1])*int(d[s+2])*int(d[s+3])*int(d[s+4]))
s += 1
print (product_of_digits(a))
Let me list out the mistakes in the program.
You are iterating over d for nothing. You don't need that.
s += 1 is not part of the while loop. So, s will never get incremented, leading to infinite loop.
print (product_of_digits(a)) is inside the function itself, where a is not defined.
To find the product of all the consecutive 5 numbers, you cannot loop till the end of d. So, the loop should have been while s <= (len(d)-5):
You have initialized k, but used it nowhere.
So, the corrected program looks like this
def product_of_digits(number):
d, s = str(number), 0
while s <= (len(d)-5):
print(int(d[s]) * int(d[s+1]) * int(d[s+2]) * int(d[s+3]) * int(d[s+4]))
s += 1
product_of_digits(123456)
Output
120
720
You can also use a for loop, like this
def product_of_digits(number):
d = str(number)
for s in range(len(d) - 4):
print(int(d[s]) * int(d[s+1]) * int(d[s+2]) * int(d[s+3]) * int(d[s+4]))
There are a few problems with your code:
1) Your s+=1 indentation is incorrect
2) It should be s+=5 instead (assuming you want products of 1-5, 6-10, 11-15 and so on otherwise s+=1 is fine)
def product_of_digits(number):
d = str(number)
s = 0
while s < (len(d)-5):
print (int(d[s])*int(d[s+1])*int(d[s+2])*int(d[s+3])*int(d[s+4]))
s += 5 (see point 2)
print (product_of_digits(124345565534))
numpy.product([int(i) for i in str(s)])
where s is the number.

Categories