pythonpandasjupyterdisplayjupyter-lab

Apply style to PANDAS table and print with truncation (Jupyter Lab/Notebook)


Is there a way to apply a style to a pandas table in Jupyter Lab and then print it while keeping the table truncation functionality?

I have large tables where both the columns and the rows are truncated automatically when printed to the output cells. I would like to retain this functionality and still use the formatting capability of a Styler object, e.g., use a formatter to show up to two decimal places.

I would not like to use head(), tail(), or truncate(). I would like keep the three dots that imply that the data shown is not the full table.

I also know that some pandas display options, such as "chop", can change decimal formatting, but there are other kinds of formatting that I would like to apply, like formatting the index.

Is this possible?

For example:

import numpy as np
import pandas as pd

n_rows = 100
n_cols = 50
rng = np.random.default_rng()
data = rng.uniform(low=0., high=100., size=(n_rows, n_cols))
df = pd.DataFrame(data)
df

I would like it to be truncated like this:

Truncated table

– while applying a formatter like this:

df.style.format(formatter='{:.2f}')

Untruncated table


Solution

  • I was able to come up with my own answer. The code is below.

    Notes:

    1. This only applies to number formatting but can easily be modified for any Styler format.
    2. pandas display options usually print the minimum number of rows and the maximum number of columns (no option display.min_columns).

    I had some help from the thread StackOverflow: Merging Pandas styler object side by side.

    from IPython.display import display, HTML
    
    # Function
    def style_with_truncation(df, formatter='{:.2f}', min_rows=10, max_columns=20):
        half_rows = min_rows // 2
        half_cols = max_columns // 2
    
        # Left half
        upper_left = df.iloc[:half_rows ,:half_cols].style.format(formatter=formatter)
        lower_left = df.iloc[-half_rows:,:half_cols].style.format(formatter=formatter)
    
        ellipsis_half_row_left = pd.DataFrame([['...'] * (half_cols)],
                                              index=['...'], columns=upper_left.data.columns)
        
        left_half = upper_left.concat(ellipsis_half_row_left.style).concat(lower_left)
    
        # Right half
        upper_right = df.iloc[:half_rows ,-half_cols:].style.format(formatter=formatter)
        lower_right = df.iloc[-half_rows:,-half_cols:].style.format(formatter=formatter)
        
        ellipsis_half_row_right = pd.DataFrame([['...'] * (half_cols)],
                                              index=['...'], columns=upper_right.data.columns)
    
        right_half = upper_right.concat(ellipsis_half_row_right.style).concat(lower_right)
    
        # Middle
        ellipsis_column = pd.DataFrame({'...' : ['...'] * (min_rows+1)}, columns=['...'])
        ellipsis_column = ellipsis_column.style
        
        # Set the Styler attribute to be shown side by side
        left_half.set_table_attributes("style='display:inline'")
        right_half.set_table_attributes("style='display:inline'")
        ellipsis_column.set_table_attributes("style='display:inline'")
    
        # Display the styler objects inline
        row_col_text = f"<p>{df.shape[0]:d} rows × {df.shape[1]:d} columns</p>"
        display(HTML(left_half._repr_html_() +
                     ellipsis_column.hide(axis="index")._repr_html_() +
                     right_half.hide(axis="index")._repr_html_()
                     + row_col_text))
    
    # Example of a function call
    min_rows = pd.options.display.min_rows
    max_columns = pd.options.display.max_columns
    style_with_truncation(df, formatter='{:.2f}', min_rows=min_rows, max_columns=max_columns)
    
    

    Output: Successful truncation of a stylized DataFrame