0

I'm using this class to represent the dataframes in a .ipynb file:

# Handy class for DataFrame viz
class display(object):
    '''
    Display HTML representation of multiple objects
    '''
    
    template = '''<div style="float: left; padding: 10px;">
                  <p style='font-family:"Courier New", Courier, monospace'>{0}</p>{1}
                  </div>'''
    
    def __init__(self, *args):
        self.args = args
        
    def _repr_html_(self):
        return '\n'.join(self.template.format(a, eval(a)._repr_html_()) for a in self.args)
    
    def __repr__(self):
        return '\n\n'.join(a + '\n' + repr(eval(a)) for a in self.args)

This class is not working inside an if-else block and I don't understand why.

Can anyone give an explanation? Thx in advance!


Chunk to replicate the issue:

import pandas as pd

# Handy class for DataFrame viz

# Create a sample DataFrame with 3 rows and 5 columns
df = pd.DataFrame({
    'A': [1, 2, 3],
    'B': [4, 5, 6],
    'C': [7, 8, 9],
    'D': [10, 11, 12],
    'E': [13, 14, 15]
})

# Display the DataFrame
display('df')

>>>

df

A   B   C   D   E
0   1   4   7   10  13
1   2   5   8   11  14
2   3   6   9   12  15
inside = True

if inside:
    display('df')

>>>

mabatalla
  • 45
  • 10

1 Answers1

1

I think it may be because the indentation of the if inside if-else makes it not on the outermost point on the last line of the cell so IPython/Jupyter doesn't use the InteractiveShell to 'show' it using it's special handling of the last unindented object in a cell, see how last_expr is the default for InteractiveShell.ast_node_interactivity here. In the same way as this... Say you run the following in a cell:

a = 4
a

That's going to show 4 as Out.

But if you do the following in the next cell, it isn't going to show 4:

inside = True

if inside:
    a

The fancy display handling that IPython / Jupyter applies to the item on the last line doesn't get applied because it's not the outermost item evaluated on that line and so doesn't get passed to IPython's fancy representational handling. (Or at least that is how I think about it. See Thomas K's explanation here about how it only shows the bare expression at the end of the cell when the expressions are in a for loop and hence indented similar to in your if-else.) And I think that mirrors what you were doing, maybe?

But this works:

from IPython.display import display
inside = True

if inside:
    display(a)

You can do that to force applying the fancy handling it would apply if you just put a in a cell and ran it.

Or if you first adjust the setting for `InteractiveShell.ast_node_interactivity you can get the following to work:

inside = True

if inside:
    a

Before trying that in separate cell, you run either the following:

from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "last"

-OR-

from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all"

(Because it needs to be run & applied before you run the cell you want to the new settings to be effective for, you cannot just add the code to adjust InteractiveShell.ast_node_interactivity in the same cell where you are going to run if inside:.)

OPTION A: adjust InteractiveShell.ast_node_interactivity to try to fix what you are doing:

And so to make your original example code work, you have the option of first running the following as a cell itself prior to running your inside = True block of code in your original post:

from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "last"

Alternative OPTION B: Applying the use if IPython's display to try to fix what you are doing:

So to make your version of the dataframe display class work, let's not shadow IPython's display because we'll need it. So make this a cell:

class spec_display(object):
    '''
    Display HTML representation of multiple objects
    '''
    
    template = '''<div style="float: left; padding: 10px;">
                  <p style='font-family:"Courier New", Courier, monospace'>{0}</p>{1}
                  </div>'''
    
    def __init__(self, *args):
        self.args = args
        
    def _repr_html_(self):
        return '\n'.join(self.template.format(a, eval(a)._repr_html_()) for a in self.args)
    
    def __repr__(self):
        return '\n\n'.join(a + '\n' + repr(eval(a)) for a in self.args)

Then as a separate cell, this works like your original:

import pandas as pd

# Handy class for DataFrame viz

# Create a sample DataFrame with 3 rows and 5 columns
df = pd.DataFrame({
    'A': [1, 2, 3],
    'B': [4, 5, 6],
    'C': [7, 8, 9],
    'D': [10, 11, 12],
    'E': [13, 14, 15]
})

# Display the DataFrame
spec_display('df')

Except now in another cell, what you see not working can be made to work by explicitly invoking IPython's display, just as we can use that to print multiple dataframes in one cell and add titles (I'll come back to that):

from IPython.display import display
inside = True

if inside:
    display(spec_display('df'))

Finally, I mentioned above you can use IPython's display to print multiple dataframes from code in one cell and add titles, all in one output cell. (I bring this up because I see among the comments of your code, "Display HTML representation of multiple objects".) Example of that:

import pandas as pd
df1 = pd.DataFrame({
    'A': [1, 2, 3],
    'B': [4, 5, 6],
    'C': [7, 8, 9],
    'D': [10, 11, 12],
    'E': [13, 14, 15]
})

df2 = pd.DataFrame({
    'A': [1, 2, 3],
    'B': [4, 5, 6],
    'C': [7, 8, 9],
    'D': [10, 11, 12],
    'E': [13, 14, 15]
})
from IPython.display import display, HTML
display(HTML('<b>Dataframe One:</b>'))
display(df1)
display(HTML('<b>Dataframe Two:</b>'))
display(df2)

We can tell IPython how to handle things and not rely on the special handling it usually just applies to the unindented final object on the last line.

Wayne
  • 6,607
  • 8
  • 36
  • 93