1

I would like a piece of code (an analytical model) to produce a series out Jupter outputs so that when the module is run within Jupyter Notebook, it outputs to the notebook a number of tables, HTML outputs and matplotlib charts, in a specific order.

The idea is that the model stores an list of objects, which we can later loop through, displaying each one.

I have managed to make this work with tables and HTML with code like as follows:

from IPython.display import display
from IPython.display import HTML, Image

a = df.head(1)
b = HTML("<p>Hello</p>")
c = df.head(2)

display(a)
display(b)
display(c)

#A more general case would be:
for i in [a,b,c]:
    display(i)

However, I am unable to make matplotlib charts (e.g. using df.plot() ) appear in the correct order. Calling plt.show() enables me to output a single chart in the right order, but doesn't seem to help me if there are multiple charts.

I workaround I've managed to implement is outputting the matplotlib charts to .png and then using Image to display these png images. However, I'd rather avoid having to output loads of .png charts to files if I can help it.

The idea behind all of this is that it allows my analytical model written in Python to output a kind of 'rich' version of logging, where you can 'log' a table or a chart.

piRSquared
  • 285,575
  • 57
  • 475
  • 624
RobinL
  • 11,009
  • 8
  • 48
  • 68

2 Answers2

3

You could also do something like this:

df = pd.DataFrame({'a':[1,2,3],'b':[3,2,1]})
plt.interactive(False) # This will prevent matplotlib from showing the plots immediately 
# You would want to create placeholders for different figures to show:
fig1, ax1 = plt.subplots()  
fig2, ax2 = plt.subplots()
# Then place your plots on the relevant placeholders:
df.a.plot(ax=ax1)
df.b.plot(ax=ax2)

Adding you original code:

a = df.head(1)
b = HTML("<p>Hello</p>")
c = df.head(2)

for i in [a,b,fig1,c,fig2]:
    display(i)

And this makes the desired order of tables, text, figures:

enter image description here

Primer
  • 10,092
  • 5
  • 43
  • 55
2

I solved this problem to some extent HERE

Below is the same content of the answer linked.

It should provide some guidance.

The essential points are:

  1. print the png data to an StringIO object. fig.canvas.pring_png(sio)
  2. convert this from binary to ascii. binascii.b2a_base64(sio.getvalue())
  3. insert this into an img tag. '<img src="data:image/png;base64,{}&#10;">'.format(img_data)

Example

from IPython.core.display import HTML
import binascii
from StringIO import StringIO
import matplotlib.pyplot as plt

# open IO object
sio = StringIO()

# generate random DataFrame
np.random.seed(314)
df = pd.DataFrame(np.random.randn(1000, 2), columns=['x', 'y'])

# initialize figure and axis
fig, ax = plt.subplots(1, 1)

# plot DataFrame
ax.scatter(df.iloc[:, 0], df.iloc[:, 1]);

# print raw canvas data to IO object
fig.canvas.print_png(sio)

# convert raw binary data to base64
# I use this to embed in an img tag
img_data = binascii.b2a_base64(sio.getvalue())

# keep img tag outter html in its own variable
img_html = '<img src="data:image/png;base64,{}&#10;">'.format(img_data)

HTML("<h1>Hello</h1><hr/>"+img_html)

I end up with:

enter image description here

Community
  • 1
  • 1
piRSquared
  • 285,575
  • 57
  • 475
  • 624
  • Thank you. This kind of solution did occur to me but I wasn't sure how to implement so that's really useful. I will wait to see if someone suggests a more 'native' answer and if not will accept this one – RobinL May 23 '16 at 18:44