Skip to content

Instantly share code, notes, and snippets.

@d1manson
Last active April 19, 2018 23:10
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save d1manson/f2589d4e4b44ff3ba85b to your computer and use it in GitHub Desktop.
Save d1manson/f2589d4e4b44ff3ba85b to your computer and use it in GitHub Desktop.
show images when displaying 2d arrays in iPython and Pandas

As of writing, this is only possible with my fork of pandas, but hopefully it will make its way into the main pandas stable branch.

The purpose of the fork is to let you specify a custom html formatter for individual columns in a data frame.


In this example we create a formatting function which takes a numpy array and returns a string of the form <img src='--base64-encoded-data'/>. This means that the numpy array is displayed as an image.

Below is the code we use to define our custom format function. Note that the function takes a single element from the data frame and returns and html string::

import numpy as np
from io import BytesIO
from PIL import Image
from base64 import b64encode
from matplotlib import cm
from matplotlib.colors import Normalize
   
def matshow_func(x):
   b = BytesIO()  
   norm = Normalize(clip=True)         
   if not np.ma.isMaskedArray(x):
      x = np.ma.array(x,mask = np.isnan(x))            
   c = cm.jet(norm(x), bytes=True)
   Image.fromarray(c).save(b, format='png')
   return '<img alt="2d array" src="data:image/png;base64,' + b64encode(b.getvalue()) + '" />'
   
matshow_func.escape = False  # This prevents the "<" tags getting escaped
matshow_func.justify = 'all' # This prevents the long string of data getting abrieviated with "..."

Now lets create an example data frame:

import pandas as pd
df = pd.DataFrame(columns=('boring','interesting'),index=arange(4))

df.interesting[0] = np.arange(225).reshape((15,15))
df.interesting[2] = np.arange(225).reshape((15,15)).T
df.interesting[3] = np.random.randint(100,size=(15,15))
df.boring[2] = np.arange(225).reshape((15,15))

Right, now whenever we type df.to_html(formatters=dict(interesting=matshow_func)) on the IPython prompt, we see:

{pandas_screenshot.png - see attachment below}

You may want to wrap your df in another class which stores the dict of formatters and implementes _repr_html_, passing the dict to df.to_html. Attached below are some sample function factories for creating slightly more customized formatters.

import numpy as np
from io import BytesIO
from PIL import Image
from base64 import b64encode
from matplotlib import cm
from matplotlib.colors import Normalize
def pd_matshow(vmin=None,vmax=None,cmap=cm.jet,zoom=None):
""" This is an extended version of the example given above. It is for turning 2d arrays into images """
def matshow_func(x):
b = BytesIO()
norm = Normalize(vmin,vmax,clip=True)
if not np.ma.isMaskedArray(x):
x = np.ma.array(x,mask = np.isnan(x))
c = cmap(norm(x), bytes=True)
img = Image.fromarray(c)
if zoom is not None:
img = img.resize(tuple(int(s*zoom) for s in img.size),Image.ANTIALIAS)
img.save(b, format='png')
return '<img alt="2d array" src="data:image/png;base64,' + b64encode(b.getvalue()) + '" />'
matshow_func.escape = False # This prevents the "<" tags getting escaped
matshow_func.justify = 'all' # This prevents the long string of data getting abrieviated with "..."
return matshow_func
def pd_circshow(vmin=0,vmax=5,cmap=cm.jet_r):
"""
This plots one circle with radius and colour reflecting the scalar value of the cell.
TODO: really we probably need an extra argument to separate pixel size from vmin and vmax.
"""
norm = Normalize(vmin, vmax, clip=True)
def circshow_func(v):
x = norm(v)
k = np.arange(-vmax,+vmax+1)**2
m = k[:,np.newaxis] + k[np.newaxis,:] < (x*vmax)**2
c = np.zeros((vmax*2+1,vmax*2+1,4),dtype=np.uint8)
c[m] = cmap(x,bytes=True)
b = BytesIO()
Image.fromarray(c).save(b, format='png')
return "{:g}".format(v) + ' <img alt="2d array" src="data:image/png;base64,' + b64encode(b.getvalue()) + '" />'
circshow_func.escape = False # This prevents the "<" tags getting escaped
circshow_func.justify = 'all' # This prevents the long string of data getting abrieviated with "..."
return circshow_func
def pd_boolshow(true_str='{ tick }',false_str='{ cross }'):
"""
This plots a green tick for Truthy values and a red cross for Flasy values.
You can add additional text by setting the values of `true_str` and `false_str`.
Icons from: https://www.iconfinder.com/icons/22536/.
"""
def boolshow_func(x):
if x:
return true_str.replace('{ tick }', ("<img src='data:image/png;base64,"
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACOElEQVQ4T2NkoADUr1MWYyRXf8NaFRlGRsbjZBnQsFZNiYnx/4m///"
"5zk2xAzQpVTVZ2xkP//v4X+ffnXyRJBtSuUTFkYWbcw872l+/rV8bNzWF3g4g2oHaViiUT8/9tYiJ/BV68YH7568N/9c70ex/BBtQv0"
"bNgZmQ5zsL3yarK985x9ICtWi7nzMLCslZO9i//4wdMn779+u/XHffgIEgdI0xzXHA0w/Jd8779Y/zuV+17by/MkJrVcj7//zItUVH+"
"z//6JcPXd+8Y53bEPciHyTM2LTX6r2f6h+Hrt88MhkrRDKt2L//I8I/BqyH07rHa1QoR/34zzDQ14eN7+erj/zu3Ge49ePhQa3UDwy+"
"4AVlTRXnYWThvGJkyCzEz/+MUFVBmOHDk3gd2Fs51P35/DzEz4+P78vULw8Vzfz/9+P3fcnLGo2vIXgSHQUG/gAATD98lQxM2cWbmP2x"
"cHHwMly/8ZtA1YGX4/uMzw43r/758/sjQ2Jf2sAc9fOCxkNYjJcLDy3LJyIxDlIn5F0uk7n2G5ZcUGN6+Zvn74N6fM71pDy0ZGBj+4z"
"QAJJE1VVSCk4XropYRhwgn+0+mf/84GM6f+v7h65e/OjOKnjzFluwx0kHWVClZThaW87rGvEK3b3z5+u3z/+T+zEercOUZbAlJUteW18"
"wlWnDp01vfq1b1vQali38MDAw/GRgYvjEwMHxnYGB4Do8FLCZLMzAwsKuacvMx/P398/a5X28ZGMDR9gGbKwA40dqSw4SMsQAAAABJRU"
"5ErkJggg=='/>"))
else:
return false_str.replace('{ cross }',("<img src='data:image/png;base64,"
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACO0lEQVQ4T41SzWsTQRx9k91kN19LWkNMrPZYECrRgoKoB0XFgh/YBb/Ol"
"voXeJVe+w/4dbd4GBUUKij2oObQSykVCnqzpmmNjW1S87HZzMjM7CQhB3EuOzvv997vzfsNQd/6ejM6ZZqE+j53x543XvwPRnSRJh+6N4"
"f1R/fRL/IvTAqIAsMgNHtuEq0vnxE9eRnFV8/AGHcFHjIIHbl2B/XCG9hHTqH0YQGso1wSQQYJ0ZHzl9BY+QgQAnAgfvoK1l/Oiy1Gr9/G"
"n0+vA7Mc0fwZFN+/BThzydqNOM9N5OF9/wYIP4IhFgGGbj2Q29/zs/JY2g1q7LGjKBYKkA68jkH3pS1F5EFNUCgNibPgqwW2t5uIGMztZl"
"BvGzQ9HFZdBpY2pQ2UKz5iYV9l0D+FWtOg6SEDRNpQWQwqlisMSVuRtaDUeHc2NeUkGM2kdL/BvkqtvAPs7hnuhcWdnoAgx2OMZpxOr6O8e"
"F+o2ioBtnZN1OshKUIE2bIYzTpedwB9tTpXPRjwQHWraqHVIi5ZvJjih9M1lCod2PFIdxLNuofcsCH/JRaLdB1pbO1XAtIBMUDHM0okbJto"
"N31JXv2ZBOHA+H6FRSwTXstH2DKxuZcAZ1BjFCKMgOazqlCQVzYdhLh6ygyg+VwPE2erpaTKQN9XiLQ5oRMHqlguOTDBu0kLzAehx3JVLG84"
"MEkPEwJZAKbI+/GJ5OTBqPl0o+HfnV6qicfvBXOwHx5PXh2NmU9+NPzpmaXaQnDu/wVrzRtU8J7mzAAAAABJRU5ErkJggg==' />"))
boolshow_func.escape = False # This prevents the "<" tags getting escaped
boolshow_func.justify = 'all' # This prevents the long string of data getting abrieviated with "..."
return boolshow_func
@brycepg
Copy link

brycepg commented May 29, 2016

FYI anyone for anyone reading, this has become part of pandas with 0.17.1

However to get these examples to work, you need to set

pandas.set_option('display.max_colwidth', -1)
To stop string truncating

and set html escaping to False when displaying the html for the DataFrame's to_html method
df.to_html(..., escape=False)

@chiachun
Copy link

Thank you for sharing! Exactly the function I am searching for!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment