4

I have a very large code that takes some time to run. In order to make sure the process hasn't stalled somewhere I print to screen the percentage of the code that has already been executed, which depends on a for loop and an integer.

To display the percentage of the for loop already processed I use flags to indicate how much of the loop already passed.

The MWE might make it a bit more clear:

import time

N = 100

flag_15, flag_30, flag_45, flag_60, flag_75, flag_90 = False, False,\
False, False, False, False

for i in range(N):

    # Large block of code.
    time.sleep(0.1)

    if i + 1 >= 0.15 * N and flag_15 is False:
        print '15%'
        flag_15 = True
    elif i + 1 >= 0.3 * N and flag_30 is False:
        print '30%'
        flag_30 = True
    elif i + 1 >= 0.45 * N and flag_45 is False:
        print '45%'
        flag_45 = True
    elif i + 1 >= 0.6 * N and flag_60 is False:
        print '60%'
        flag_60 = True
    elif i + 1 >= 0.75 * N and flag_75 is False:
        print '75%'
        flag_75 = True
    elif i + 1 >= 0.9 * N and flag_90 is False:
        print '90%'
        flag_90 = True
    elif i + 1 == N:
        print '100%'

This works but is quite verbose and truly ugly. I was wondering if there might be a better/prettier way of doing this.

Gabriel
  • 40,504
  • 73
  • 230
  • 404

5 Answers5

3

I like to use modulus to periodically print status messages.

import time

N = 100
for i in range(N):
    #do work here
    if i % 15 == 0:
        print "{}% complete".format(int(100 * i / N))
print "100% complete"

Result:

0% complete
15% complete
30% complete
45% complete
60% complete
75% complete
90% complete
100% complete

for values of N other than 100, if you want to print every 15%, you'll have to dynamically calculate the stride instead of just using the literal 15 value.

import time
import math

N = 300
percentage_step = 15
stride = N * percentage_step / 100 
for i in range(N):
    #do work
    if i % stride == 0:
        print "{}% complete".format(int(100 * i / N))
Kevin
  • 74,910
  • 12
  • 133
  • 166
  • This almost works but it has some issues. If I set `N=10` and `percentage_step = 25` for example, it will use steps of `20%` instead of `25%`. Can this be worked around? – Gabriel Mar 07 '14 at 15:39
  • Not really. Since 10 can't evenly be divided into four parts, the stride gets bumped down a little, and you get one more status message than you otherwise would. – Kevin Mar 07 '14 at 15:51
2

(Posting a second answer because this solution uses a completely different technique)

You could create a list of milestone values, and print a message when the percentage complete reaches the lowest value.

milestones = [15, 30, 45, 60, 75, 90, 100]
for i in range(N):
    #do work here
    percentage_complete = (100.0 * (i+1) / N)
    while len(milestones) > 0 and percentage_complete >= milestones[0]:
        print "{}% complete".format(milestones[0])
        #remove that milestone from the list
        milestones = milestones[1:]

Result:

15% complete
30% complete
45% complete
60% complete
75% complete
90% complete
100% complete

Unlike the "stride" method I posted earlier, here you have precise control over which percentages are printed. They don't need to be evenly spaced, they don't need to be divisible by N, they don't even need to be integers! You could do milestones = [math.pi, 4.8, 15.16, 23.42, 99] if you wanted.

Kevin
  • 74,910
  • 12
  • 133
  • 166
2

You can use combination of write() and flush() for nice ProgressBar:

import sys
import time

for i in range(100):
    row = "="*i + ">"
    sys.stdout.write("%s\r%d%%" %(row, i + 1))
    sys.stdout.flush()
    time.sleep(0.1)

sys.stdout.write("\n")

Progress will be displaying like this:

    69%====================================================================>
Misha
  • 136
  • 1
  • 7
  • The `flush` does not seem to work. Tried running this with `Sublime` and got 100 lines each one with a larger bar. _Add_: on the other hand, it does work as advertised when executed from terminal. – Gabriel Sep 17 '14 at 02:18
0

You don't need any flags. You can just print the completion based on the current value of i.

for i in range(N):
    # lots of code

    print '{0}% completed.'.format((i+1)*100.0/N)
Jayanth Koushik
  • 9,476
  • 1
  • 44
  • 52
0

Just add a "\r" in Misha's answer:

import sys
import time

for i in range(100):
    row = "="*i + ">"
    sys.stdout.write("%s\r %d%%\r" %(row, i + 1))
    sys.stdout.flush()
    time.sleep(0.1)

sys.stdout.write("\n")

Output:

65%======================================================>

In colab.research.google.com works like this:

import sys
import time

for i in range(100):
    row = "="*i + ">"
    sys.stdout.write("\r %d%% %s " %( i + 1,row))
    sys.stdout.flush()
    time.sleep(0.1)

sys.stdout.write("\n")