Sometimes you want to print progress in-place, but don't want
to keep growing the output area. In terminals, there is the carriage-return
('\r'
) for overwriting a single line, but the notebook frontend does not support this
behavior (yet).
What the notebook does support is explicit clear_output
, and you can use this to replace previous
output (specifying stdout/stderr or the special IPython display outputs).
A simple example printing our progress iterating through a list:
import sys
import time
from IPython.display import clear_output
for i in range(10):
time.sleep(0.25)
clear_output()
print i
sys.stdout.flush()
The AsyncResult object has a special wait_interactive()
method, which prints its progress interactively,
so you can watch as your parallel computation completes.
This example assumes you have an IPython cluster running, which you can start from the cluster panel
from IPython import parallel
rc = parallel.Client()
view = rc.load_balanced_view()
amr = view.map_async(time.sleep, [0.5]*100)
amr.wait_interactive()
You can also use clear_output()
to clear figures and plots.
This time, we need to make sure we are using inline pylab (requires matplotlib)
%pylab inline
from scipy.special import jn
x = np.linspace(0,5)
f, ax = plt.subplots()
ax.set_title("Bessel functions")
for n in range(1,10):
time.sleep(1)
ax.plot(x, jn(x,n))
clear_output()
display(f)
# close the figure at the end, so we don't get a duplicate
# of the last plot
plt.close()
clear_output()
is still something of a hack, and if you want to do a progress bar in the notebook
it is better to just use Javascript/HTML if you can.
Here is a simple progress bar using HTML/Javascript:
import uuid
from IPython.display import HTML, Javascript, display
divid = str(uuid.uuid4())
pb = HTML(
"""
<div style="border: 1px solid black; width:500px">
<div id="%s" style="background-color:blue; width:0%%"> </div>
</div>
""" % divid)
display(pb)
for i in range(1,101):
time.sleep(0.1)
display(Javascript("$('div#%s').width('%i%%')" % (divid, i)))
The above simply makes a div that is a box, and a blue div inside it with a unique ID (so that the javascript won't collide with other similar progress bars on the same page).
Then, at every progress point, we run a simple jQuery call to resize the blue box to the appropriate fraction of the width of its containing box, and voilà a nice HTML/Javascript progress bar!
And finally, here is a progress bar class extracted from PyMC, which will work in regular Python as well as in the IPython Notebook
import sys, time
try:
from IPython.display import clear_output
have_ipython = True
except ImportError:
have_ipython = False
class ProgressBar:
def __init__(self, iterations):
self.iterations = iterations
self.prog_bar = '[]'
self.fill_char = '*'
self.width = 40
self.__update_amount(0)
if have_ipython:
self.animate = self.animate_ipython
else:
self.animate = self.animate_noipython
def animate_ipython(self, iter):
print '\r', self,
sys.stdout.flush()
self.update_iteration(iter + 1)
def update_iteration(self, elapsed_iter):
self.__update_amount((elapsed_iter / float(self.iterations)) * 100.0)
self.prog_bar += ' %d of %s complete' % (elapsed_iter, self.iterations)
def __update_amount(self, new_amount):
percent_done = int(round((new_amount / 100.0) * 100.0))
all_full = self.width - 2
num_hashes = int(round((percent_done / 100.0) * all_full))
self.prog_bar = '[' + self.fill_char * num_hashes + ' ' * (all_full - num_hashes) + ']'
pct_place = (len(self.prog_bar) // 2) - len(str(percent_done))
pct_string = '%d%%' % percent_done
self.prog_bar = self.prog_bar[0:pct_place] + \
(pct_string + self.prog_bar[pct_place + len(pct_string):])
def __str__(self):
return str(self.prog_bar)
p = ProgressBar(1000)
for i in range(1001):
p.animate(i)