5

I'm trying to display several pandas dataframes horizontally in a Jupyter notebook, but am running into a situation where the styler's HTML is not formatted correctly.

I have the following code snippet:

import numpy as np
import pandas as pd
from IPython.display import display, HTML

def display_multi_table(table_list, with_display):
    if with_display:
      for t in table_list: display(t)
    return HTML(
        '<table><tr style="background-color:white;">' + 
        ''.join(['<td style="height:300px;width:300px">' + table.render() + '</td>' for table in table_list]) +
        '</tr></table>'
    )

d = dict([(f"column_{i}", np.random.rand(5)) for i in range(3)])
tables = [pd.DataFrame(d).style.set_caption("Title") for i in range(3)]

The following call:

display_multi_table(tables, False)

Results in this output: enter image description here

The following call:

display_multi_table(tables, True)

Results in this output:enter image description here

My goal: Achieve the output at the bottom of the second screenshot without having to call display.

Things I've tried/checked:

  • If I don't style the pandas dataframe, and use simply call .to_html() on the dataframe instead of calling .render() on the styler, this issue does not exist. However, I need this to work on a style dataframe.
  • I've confirmed that the value returned by table.render() before and after calling display is indeed different.
Henry Ecker
  • 34,399
  • 18
  • 41
  • 57
Olshansky
  • 5,904
  • 8
  • 32
  • 47

3 Answers3

4

Try passing your html into display, rather than calling display() on each table individually:

import numpy as np
import pandas as pd
from IPython.display import display, HTML

def display_multi_table(table_list, with_display):
    html = HTML(
        '<table><tr style="background-color:white;">' + 
        ''.join(['<td style="height:300px;width:300px">' + table.render() + '</td>' for table in table_list]) +
        '</tr></table>'
    )
    if with_display:
        display(html)
    return html

Note that if you call display_multi_table(tables, True) at the end of a jupyter cell, without setting it equal to anything, it will effectively display the tables twice – once from display(html), and once from the return value of your function (return html). If that is undesired behavior, then just set the function call equal to a variable, (html = display_multi_table(tables, True)), or set with_display to False.

bug_spray
  • 1,445
  • 1
  • 9
  • 23
  • Tried a bunch of different variations around returning, not returning, displaying, but nothing worked. It might indeed be related to a pacakge or kernel version of some sort (as @Keldorn pointed out below), but it's hard to pinpoint the exact root cause. – Olshansky May 17 '20 at 17:06
  • I fail to see what the issue is. The output you gave above matches the code you wrote – bug_spray May 17 '20 at 17:26
  • `display()` is like using `print()`. It's separate from a function return value, but it will show you what you give it when it's run. You can make a function that both does `print('hello world')` and does `return 'hello world'`. My point was if you want the stuff from `display()` to be formatted, then you could pass HTML to `display()` directly. – bug_spray May 17 '20 at 17:30
  • I executed the following code and found that the output it prints is False. Any idea why that would happen? ``` h1 = table_list[0].render() for t in table_list: display(t) h2 = table_list[0].render() print(h2 == h1) ``` – Olshansky May 19 '20 at 16:50
  • Regarding my last comment, it seems to be because the generated CSS differs before and after I call `display` on an individual table. My current workaround is to add a `clear_output(wait=True)` after each individual display before generating the HTML. – Olshansky May 19 '20 at 17:34
3

Not really an answer, but some information still. I can't reproduce the issue, I have the desired output already. Maybe due to some package versions? Using pandas 1.0.3 and IPython 7.13.0 here.

enter image description here

Keldorn
  • 1,980
  • 15
  • 25
0

For posteriry, I'm posting my solution to the problem:

from IPython.display import display, HTML, clear_output

# source https://github.com/epmoyer/ipy_table/issues/24

def display_multi_table(table_list):
  for t in table_list:
    display(t)
    clear_output(wait=True)

  html = HTML('<table><tr style="background-color:white;">' + ''.join([
      '<td style="black;height:500px;width:600px">' + table.render() + '</td>'
      for table in table_list
  ]) + '</tr></table>')
  return html

I'm not sure why I need to make use of clear_output, but it worked...

Olshansky
  • 5,904
  • 8
  • 32
  • 47